Files
2026-09-03 12:21:17 +02:00

111 lines
4.6 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
/**
* Pulpit / Mapa Obiektów. Nanosi studnie jako kropki na płaszczyznę wg
* LOKALNYCH współrzędnych geodezyjnych (seg_wells.WLL_LATITUDE/WLL_LONGITUDE -
* siatka kopalniana w metrach, NIE WGS84). Normalizacja min-max do obszaru mapy
* daje dokładny względny układ pola studni (bez internetu - air-gap OK).
*
* Kolor kropki: zielony = pompa pracuje (ON), czerwony = nie pracuje (OFF).
*/
class DashboardController extends Controller
{
/** Margines obszaru mapy (%), żeby kropki nie dotykały krawędzi. */
private const PAD = 6;
public function index(Request $request)
{
$department = $request->session()->get('department');
$rows = DB::connection('softspm')->table('seg_wells as w')
->join('seg_dev_resources as drw', function ($j) {
$j->on('drw.DRS_DEV_ID', '=', 'w.WLL_ID')->where('drw.DRS_DEV_TYPE', '=', 12);
})
->where('drw.DRS_DEPARTMENT', $department)
->where('drw.DRS_STATUS_DIC_ID', 3) // w eksploatacji
->select('drw.DRS_ID as drs_id', 'w.WLL_NAME as name', 'w.WLL_LATITUDE as lat', 'w.WLL_LONGITUDE as lon')
->get();
// Parsowanie współrzędnych (przecinek lub kropka dziesiętna), odrzucenie błędnych.
$num = function ($v) {
$s = str_replace([',', ' '], ['.', ''], (string) $v);
return is_numeric($s) ? (float) $s : null;
};
$wells = $rows->map(function ($r) use ($num) {
$r->lat = $num($r->lat);
$r->lon = $num($r->lon);
return $r;
})->filter(fn ($r) => $r->lat !== null && $r->lon !== null && $r->lat > 0 && $r->lon > 0)->values();
// Stan ON/OFF dla naniesionych studni (DRD_RESOURCES_DETAIL=507).
$onoff = $this->onOff($wells->pluck('drs_id')->all());
// Normalizacja do obszaru mapy. Zakres liczony z PERCENTYLI (2-98%), żeby
// pojedyncze studnie ze skrajnymi współrzędnymi nie rozciągały skali i nie
// zwijały reszty w jeden punkt. Pozycje przycięte (clamp) do obszaru.
$minLat = $this->percentile($wells->pluck('lat')->all(), 2);
$maxLat = $this->percentile($wells->pluck('lat')->all(), 98);
$minLon = $this->percentile($wells->pluck('lon')->all(), 2);
$maxLon = $this->percentile($wells->pluck('lon')->all(), 98);
$latRange = max(1e-6, $maxLat - $minLat);
$lonRange = max(1e-6, $maxLon - $minLon);
$span = 100 - 2 * self::PAD;
$clamp = fn ($v) => max(self::PAD, min(100 - self::PAD, $v));
$points = $wells->map(function ($r) use ($minLat, $maxLat, $minLon, $lonRange, $latRange, $span, $clamp, $onoff) {
return (object) [
'drs_id' => $r->drs_id,
'name' => $r->name,
// X = długość (wschód), Y = szerokość (północ, odwrócona - północ u góry).
'left' => round($clamp(self::PAD + (($r->lon - $minLon) / $lonRange) * $span), 3),
'top' => round($clamp(self::PAD + (($maxLat - $r->lat) / $latRange) * $span), 3),
'on' => ($onoff[$r->drs_id] ?? null) === 'ON',
];
});
return view('dashboard', [
'points' => $points,
'onCount' => $points->where('on', true)->count(),
'offCount' => $points->where('on', false)->count(),
]);
}
/** Percentyl p (0-100) z tablicy liczb (interpolacja liniowa). */
private function percentile(array $values, float $p): float
{
if (empty($values)) {
return 0.0;
}
sort($values);
$i = ($p / 100) * (count($values) - 1);
$lo = (int) floor($i);
$hi = (int) ceil($i);
return $lo === $hi ? (float) $values[$lo] : $values[$lo] + ($values[$hi] - $values[$lo]) * ($i - $lo);
}
/** Ostatni stan ON/OFF (507) dla zbioru DRS_ID - jednym zapytaniem. */
private function onOff(array $ids): \Illuminate\Support\Collection
{
if (empty($ids)) {
return collect();
}
return DB::connection('softspm')->table('seg_dev_resources_det as d')
->joinSub(
DB::connection('softspm')->table('seg_dev_resources_det')
->select('DRD_DRS_ID', DB::raw('MAX(DRD_ID) as max_id'))
->where('DRD_RESOURCES_DETAIL', 507)
->whereIn('DRD_DRS_ID', $ids)
->groupBy('DRD_DRS_ID'),
'mx', 'mx.max_id', '=', 'd.DRD_ID'
)
->pluck('d.DRD_VALUE', 'd.DRD_DRS_ID');
}
}