karakteristik petani
This commit is contained in:
@@ -11,6 +11,7 @@ use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use App\Models\Pertanian\Lahan;
|
||||
use App\Models\Pertanian\PendapatanKomoditas;
|
||||
use App\Models\Pertanian\SosialEkonomi;
|
||||
|
||||
class DemografiPetaniController extends Controller
|
||||
{
|
||||
@@ -20,7 +21,35 @@ class DemografiPetaniController extends Controller
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$petaniCounts = Petani::selectRaw("
|
||||
return redirect()->route('dashboard.profil-pertanian', array_merge($request->query(), [
|
||||
'tab' => 'demografi-petani',
|
||||
]));
|
||||
}
|
||||
|
||||
public function profil(Request $request)
|
||||
{
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$activeTab = $request->query('tab') === 'indeks-petani'
|
||||
? 'indeks-petani'
|
||||
: 'demografi-petani';
|
||||
|
||||
$demografiData = $this->contentData($request);
|
||||
$indeksData = app(IndeksPetaniController::class)->contentData($request);
|
||||
|
||||
return view('dashboard.profil-pertanian', [
|
||||
'activeTab' => $activeTab,
|
||||
'indeksFilterAction' => route('dashboard.profil-pertanian'),
|
||||
] + $demografiData + $indeksData);
|
||||
}
|
||||
|
||||
public function contentData(Request $request): array
|
||||
{
|
||||
$filteredPetaniIds = $this->filteredPetaniIdsQuery($request);
|
||||
|
||||
$petaniCounts = Petani::whereIn('petani.id', clone $filteredPetaniIds)->selectRaw("
|
||||
COUNT(*) as total,
|
||||
COUNT(*) FILTER (WHERE jenis_kelamin = true) as laki,
|
||||
COUNT(*) FILTER (WHERE jenis_kelamin = false) as perempuan
|
||||
@@ -30,14 +59,18 @@ class DemografiPetaniController extends Controller
|
||||
$petaniLakilaki = $petaniCounts->laki;
|
||||
$petaniPerempuan = $petaniCounts->perempuan;
|
||||
|
||||
$totalKomoditas = Petani::whereNotNull('komoditas_id')
|
||||
->distinct()
|
||||
->count('komoditas_id');
|
||||
$totalKomoditas = Komoditas::query()
|
||||
->where('is_utama', true)
|
||||
->whereHas('petani', fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds))
|
||||
->count();
|
||||
|
||||
$totalLuasLahan = Lahan::has('petani')->sum('luas_lahan');
|
||||
$totalLuasLahan = Lahan::has('petani')
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->sum('luas_lahan');
|
||||
$ratarataLuasLahan = $totalLuasLahan && $totalPetani ? $totalLuasLahan / $totalPetani : 0;
|
||||
|
||||
$umurCounts = Petani::whereNotNull('tanggal_lahir')->selectRaw("
|
||||
$umurCounts = Petani::whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->whereNotNull('tanggal_lahir')->selectRaw("
|
||||
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) < 30) as kurang_30,
|
||||
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 30 AND 39) as umur_30_39,
|
||||
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 40 AND 49) as umur_40_49,
|
||||
@@ -45,7 +78,8 @@ class DemografiPetaniController extends Controller
|
||||
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) >= 60) as umur_60_keatas
|
||||
")->first();
|
||||
|
||||
$pendidikanCounts = Petani::whereNotNull('pendidikan_terakhir')
|
||||
$pendidikanCounts = Petani::whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->whereNotNull('pendidikan_terakhir')
|
||||
->select('pendidikan_terakhir', DB::raw('COUNT(*) as total'))
|
||||
->groupBy('pendidikan_terakhir')
|
||||
->pluck('total', 'pendidikan_terakhir');
|
||||
@@ -59,20 +93,71 @@ class DemografiPetaniController extends Controller
|
||||
's1_keatas' => (int) ($pendidikanCounts['06_sarjana'] ?? 0) + (int) ($pendidikanCounts['07_magister'] ?? 0),
|
||||
];
|
||||
|
||||
$jumlahTanggunganBuckets = SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->whereNotNull('jumlah_tanggungan')
|
||||
->selectRaw("
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan = 0) as tanggungan_0,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan BETWEEN 1 AND 2) as tanggungan_1_2,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan BETWEEN 3 AND 4) as tanggungan_3_4,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan >= 5) as tanggungan_5_keatas
|
||||
")
|
||||
->first();
|
||||
|
||||
$jumlahTanggungan = [
|
||||
'0' => (int) ($jumlahTanggunganBuckets->tanggungan_0 ?? 0),
|
||||
'1_2' => (int) ($jumlahTanggunganBuckets->tanggungan_1_2 ?? 0),
|
||||
'3_4' => (int) ($jumlahTanggunganBuckets->tanggungan_3_4 ?? 0),
|
||||
'5_keatas' => (int) ($jumlahTanggunganBuckets->tanggungan_5_keatas ?? 0),
|
||||
];
|
||||
|
||||
$kelompokTaniBuckets = Petani::query()
|
||||
->whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->leftJoin('sosial_ekonomi', function ($join) {
|
||||
$join->on('sosial_ekonomi.petani_id', '=', 'petani.id')
|
||||
->whereNull('sosial_ekonomi.deleted_at');
|
||||
})
|
||||
->selectRaw("
|
||||
COUNT(DISTINCT petani.id) FILTER (WHERE sosial_ekonomi.terdaftar_kelompok_tani = 'ya') as terdaftar,
|
||||
COUNT(DISTINCT petani.id) FILTER (WHERE sosial_ekonomi.terdaftar_kelompok_tani = 'tidak') as tidak_terdaftar,
|
||||
COUNT(DISTINCT petani.id) FILTER (WHERE sosial_ekonomi.terdaftar_kelompok_tani IS NULL OR sosial_ekonomi.terdaftar_kelompok_tani NOT IN ('ya', 'tidak')) as belum_terisi
|
||||
")
|
||||
->first();
|
||||
|
||||
$kelompokTani = [
|
||||
'terdaftar' => (int) ($kelompokTaniBuckets->terdaftar ?? 0),
|
||||
'tidak_terdaftar' => (int) ($kelompokTaniBuckets->tidak_terdaftar ?? 0),
|
||||
'belum_terisi' => (int) ($kelompokTaniBuckets->belum_terisi ?? 0),
|
||||
];
|
||||
|
||||
$desaSasaranRows = Petani::query()
|
||||
->leftJoin('master_desa_kelurahan as desa', 'petani.desa_kelurahan_id', '=', 'desa.id')
|
||||
->whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->selectRaw('petani.desa_kelurahan_id as desa_id')
|
||||
->selectRaw("COALESCE(desa.nama, 'Tidak diketahui') as desa")
|
||||
->selectRaw('COUNT(*) as total')
|
||||
->groupBy('petani.desa_kelurahan_id', 'desa.nama')
|
||||
->orderByDesc('total')
|
||||
->orderBy('desa')
|
||||
->get();
|
||||
|
||||
$komoditasUtama = Komoditas::query()
|
||||
->whereHas('petani')
|
||||
->withCount('petani')
|
||||
->whereHas('petani', fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds))
|
||||
->withCount(['petani' => fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds)])
|
||||
->orderByDesc('petani_count')
|
||||
->get(['id', 'nama']);
|
||||
|
||||
$luasLahanBuckets = DB::query()->fromSub(
|
||||
Lahan::query()->select('petani_id')->selectRaw('SUM(luas_lahan) as total_luas')->groupBy('petani_id'),
|
||||
Lahan::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->select('petani_id')
|
||||
->selectRaw('luas_lahan'),
|
||||
'agg'
|
||||
)->selectRaw("
|
||||
COUNT(*) FILTER (WHERE total_luas < 5000) as kurang_05,
|
||||
COUNT(*) FILTER (WHERE total_luas >= 5000 AND total_luas <= 10000) as luas_05_1,
|
||||
COUNT(*) FILTER (WHERE total_luas > 10000 AND total_luas <= 20000) as luas_1_2,
|
||||
COUNT(*) FILTER (WHERE total_luas > 20000) as lebih_2
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan < 0.5) as kurang_05,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan >= 0.5 AND luas_lahan <= 1) as luas_05_1,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan > 1 AND luas_lahan <= 2) as luas_1_2,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan > 2) as lebih_2
|
||||
")->first();
|
||||
|
||||
$luasLahan = [
|
||||
@@ -82,12 +167,28 @@ class DemografiPetaniController extends Controller
|
||||
'lebih_2' => $luasLahanBuckets->lebih_2,
|
||||
];
|
||||
|
||||
$statusKepemilikanLahanRows = Lahan::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->whereNotNull('status_kepemilikan')
|
||||
->selectRaw('status_kepemilikan as status')
|
||||
->selectRaw('COUNT(DISTINCT petani_id) as total')
|
||||
->groupBy('status_kepemilikan')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
$pendapatanPerPetaniQuery = PendapatanKomoditas::query()
|
||||
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
|
||||
->whereNotNull('sosial_ekonomi.petani_id')
|
||||
->whereIn('sosial_ekonomi.petani_id', clone $filteredPetaniIds)
|
||||
->groupBy('sosial_ekonomi.petani_id')
|
||||
->selectRaw('SUM(pendapatan_komoditas.pendapatan) as total_pendapatan');
|
||||
|
||||
$ratarataPenghasilan = (float) DB::query()
|
||||
->fromSub(clone $pendapatanPerPetaniQuery, 'agg')
|
||||
->avg('total_pendapatan');
|
||||
|
||||
$penghasilanBuckets = DB::query()->fromSub(
|
||||
PendapatanKomoditas::query()
|
||||
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
|
||||
->whereNotNull('sosial_ekonomi.petani_id')
|
||||
->groupBy('sosial_ekonomi.petani_id')
|
||||
->selectRaw('SUM(pendapatan_komoditas.pendapatan) as total_pendapatan'),
|
||||
clone $pendapatanPerPetaniQuery,
|
||||
'agg'
|
||||
)->selectRaw("
|
||||
COUNT(*) FILTER (WHERE total_pendapatan < 1000000) as kurang_1,
|
||||
@@ -112,6 +213,7 @@ class DemografiPetaniController extends Controller
|
||||
'total_komoditas' => $totalKomoditas,
|
||||
'total_luas_lahan' => $totalLuasLahan,
|
||||
'ratarata_luas_lahan' => $ratarataLuasLahan,
|
||||
'ratarata_penghasilan' => $ratarataPenghasilan,
|
||||
'kelompok_umur' => [
|
||||
'kurang_30' => $umurCounts->kurang_30,
|
||||
'30_39' => $umurCounts->umur_30_39,
|
||||
@@ -120,31 +222,94 @@ class DemografiPetaniController extends Controller
|
||||
'60_keatas' => $umurCounts->umur_60_keatas,
|
||||
],
|
||||
'pendidikan' => $pendidikan,
|
||||
'jumlah_tanggungan' => $jumlahTanggungan,
|
||||
'kelompok_tani' => $kelompokTani,
|
||||
'desa_sasaran' => [
|
||||
'total' => $desaSasaranRows->count(),
|
||||
'ids' => $desaSasaranRows->pluck('desa_id')->values(),
|
||||
'labels' => $desaSasaranRows->pluck('desa')->values(),
|
||||
'series' => $desaSasaranRows->pluck('total')->map(fn ($v) => (int) $v)->values(),
|
||||
],
|
||||
'komoditas_utama' => [
|
||||
'ids' => $komoditasUtama->pluck('id')->values(),
|
||||
'labels' => $komoditasUtama->pluck('nama')->values(),
|
||||
'series' => $komoditasUtama->pluck('petani_count')->map(fn ($v) => (int) $v)->values(),
|
||||
],
|
||||
'luas_lahan' => $luasLahan,
|
||||
'status_kepemilikan_lahan' => [
|
||||
'keys' => $statusKepemilikanLahanRows->pluck('status')->values(),
|
||||
'labels' => $statusKepemilikanLahanRows
|
||||
->pluck('status')
|
||||
->map(fn ($status) => $this->statusLahanLabel((string) $status))
|
||||
->values(),
|
||||
'series' => $statusKepemilikanLahanRows->pluck('total')->map(fn ($v) => (int) $v)->values(),
|
||||
],
|
||||
'penghasilan' => $penghasilan,
|
||||
];
|
||||
|
||||
$daftarPetani = Petani::with(['komoditas'])
|
||||
->whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->withSum('lahans as total_luas_lahan', 'luas_lahan')
|
||||
->orderBy('id')
|
||||
->paginate(10);
|
||||
|
||||
return view('dashboard.demografi-petani', compact('data', 'daftarPetani'));
|
||||
return compact('data', 'daftarPetani');
|
||||
}
|
||||
|
||||
private function filteredPetaniIdsQuery(Request $request)
|
||||
{
|
||||
$query = Petani::query()
|
||||
->leftJoin('master_desa_kelurahan as desa', 'petani.desa_kelurahan_id', '=', 'desa.id')
|
||||
->leftJoin('master_kecamatan as kecamatan', 'desa.kecamatan_id', '=', 'kecamatan.id')
|
||||
->leftJoin('master_kabupaten_kota as kabupaten', 'kecamatan.kabupaten_kota_id', '=', 'kabupaten.id')
|
||||
->when($request->filled('provinsi_id'), fn ($query) => $query->where('kabupaten.provinsi_id', $request->provinsi_id))
|
||||
->when($request->filled('kabupatenKota_id'), fn ($query) => $query->where('kecamatan.kabupaten_kota_id', $request->kabupatenKota_id))
|
||||
->when($request->filled('kecamatan_id'), fn ($query) => $query->where('desa.kecamatan_id', $request->kecamatan_id))
|
||||
->when($request->filled('desaKelurahan_id'), fn ($query) => $query->where('petani.desa_kelurahan_id', $request->desaKelurahan_id))
|
||||
->when($request->filled('komoditas_id'), fn ($query) => $query->where('petani.komoditas_id', $request->komoditas_id));
|
||||
|
||||
if ($request->filled('segmen')) {
|
||||
$query->whereHas('skor', function ($query) use ($request) {
|
||||
match ($request->segmen) {
|
||||
'Penggerak' => $query->where('skor_kesiapan_adopsi', '>=', 4),
|
||||
'Perintis' => $query->where('skor_kesiapan_adopsi', '>=', 3)
|
||||
->where('skor_kesiapan_adopsi', '<', 4),
|
||||
'Pemerhati' => $query->where('skor_kesiapan_adopsi', '>=', 2)
|
||||
->where('skor_kesiapan_adopsi', '<', 3),
|
||||
'Pemula' => $query->where('skor_kesiapan_adopsi', '>=', 1)
|
||||
->where('skor_kesiapan_adopsi', '<', 2),
|
||||
'Belum Lengkap' => $query->where(function ($query) {
|
||||
$query->whereNull('skor_kesiapan_adopsi')
|
||||
->orWhere('skor_kesiapan_adopsi', '<', 1);
|
||||
}),
|
||||
default => null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return $query->select('petani.id');
|
||||
}
|
||||
|
||||
private function statusLahanLabel(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
'sendiri' => 'Milik Pribadi',
|
||||
'sewa' => 'Sewa',
|
||||
'adat' => 'Tanah Adat',
|
||||
default => ucwords(str_replace('_', ' ', $status)),
|
||||
};
|
||||
}
|
||||
|
||||
public function data(Request $request)
|
||||
{
|
||||
$filteredPetaniIds = $this->filteredPetaniIdsQuery($request);
|
||||
$pendapatanSub = PendapatanKomoditas::query()
|
||||
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
|
||||
->whereColumn('sosial_ekonomi.petani_id', 'petani.id')
|
||||
->selectRaw('SUM(pendapatan_komoditas.pendapatan)');
|
||||
|
||||
$query = Petani::with('komoditas')
|
||||
->whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->withSum('lahans as total_luas_lahan', 'luas_lahan')
|
||||
->addSelect(['total_pendapatan' => $pendapatanSub]);
|
||||
|
||||
@@ -187,6 +352,45 @@ class DemografiPetaniController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('jumlah_tanggungan')) {
|
||||
$tanggunganQuery = SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->whereNotNull('jumlah_tanggungan')
|
||||
->select('petani_id')
|
||||
->distinct();
|
||||
|
||||
match ($request->jumlah_tanggungan) {
|
||||
'0' => $tanggunganQuery->where('jumlah_tanggungan', 0),
|
||||
'1_2' => $tanggunganQuery->whereBetween('jumlah_tanggungan', [1, 2]),
|
||||
'3_4' => $tanggunganQuery->whereBetween('jumlah_tanggungan', [3, 4]),
|
||||
'5_keatas' => $tanggunganQuery->where('jumlah_tanggungan', '>=', 5),
|
||||
default => null,
|
||||
};
|
||||
|
||||
$query->whereIn('petani.id', $tanggunganQuery);
|
||||
}
|
||||
|
||||
if ($request->filled('kelompok_tani')) {
|
||||
match ($request->kelompok_tani) {
|
||||
'terdaftar' => $query->whereIn('petani.id', SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->where('terdaftar_kelompok_tani', 'ya')
|
||||
->select('petani_id')
|
||||
->distinct()),
|
||||
'tidak_terdaftar' => $query->whereIn('petani.id', SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->where('terdaftar_kelompok_tani', 'tidak')
|
||||
->select('petani_id')
|
||||
->distinct()),
|
||||
'belum_terisi' => $query->whereNotIn('petani.id', SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->whereIn('terdaftar_kelompok_tani', ['ya', 'tidak'])
|
||||
->select('petani_id')
|
||||
->distinct()),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
if ($request->filled('komoditas')) {
|
||||
$query->where('komoditas_id', $request->komoditas);
|
||||
}
|
||||
@@ -213,15 +417,28 @@ class DemografiPetaniController extends Controller
|
||||
}
|
||||
|
||||
if ($request->filled('luas_lahan')) {
|
||||
$lahanQuery = Lahan::query()->select('petani_id')->groupBy('petani_id');
|
||||
$lahanQuery = Lahan::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->select('petani_id')
|
||||
->distinct();
|
||||
|
||||
match ($request->luas_lahan) {
|
||||
'kurang_05' => $lahanQuery->havingRaw('SUM(luas_lahan) < 5000'),
|
||||
'05_1' => $lahanQuery->havingRaw('SUM(luas_lahan) >= 5000 AND SUM(luas_lahan) <= 10000'),
|
||||
'1_2' => $lahanQuery->havingRaw('SUM(luas_lahan) > 10000 AND SUM(luas_lahan) <= 20000'),
|
||||
'lebih_2' => $lahanQuery->havingRaw('SUM(luas_lahan) > 20000'),
|
||||
'kurang_05' => $lahanQuery->where('luas_lahan', '<', 0.5),
|
||||
'05_1' => $lahanQuery->whereBetween('luas_lahan', [0.5, 1]),
|
||||
'1_2' => $lahanQuery->where('luas_lahan', '>', 1)->where('luas_lahan', '<=', 2),
|
||||
'lebih_2' => $lahanQuery->where('luas_lahan', '>', 2),
|
||||
default => null,
|
||||
};
|
||||
$query->whereIn('id', $lahanQuery->pluck('petani_id'));
|
||||
|
||||
$query->whereIn('petani.id', $lahanQuery);
|
||||
}
|
||||
|
||||
if ($request->filled('status_kepemilikan_lahan')) {
|
||||
$query->whereIn('petani.id', Lahan::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->where('status_kepemilikan', $request->status_kepemilikan_lahan)
|
||||
->select('petani_id')
|
||||
->distinct());
|
||||
}
|
||||
|
||||
$daftarPetani = $query->orderBy('id')->paginate(10);
|
||||
@@ -275,4 +492,4 @@ class DemografiPetaniController extends Controller
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,9 +103,21 @@ class IndeksPetaniController extends Controller
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return redirect()->route('dashboard.profil-pertanian', array_merge($request->query(), [
|
||||
'tab' => 'indeks-petani',
|
||||
]));
|
||||
}
|
||||
|
||||
public function contentData(Request $request): array
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
|
||||
$title = 'Dashboard Profil Petani';
|
||||
$segments = $this->segments();
|
||||
$colors = collect($segments)
|
||||
->pluck('color')
|
||||
->map(fn($c) => "var(--bs-{$c})")
|
||||
->values();
|
||||
|
||||
$komoditas = Komoditas::where('is_utama', true)->get();
|
||||
$petaniQuery = $this->petaniQuery($filters);
|
||||
@@ -125,6 +137,11 @@ class IndeksPetaniController extends Controller
|
||||
->distinct()
|
||||
->get();
|
||||
|
||||
$provinsi = $desaKelurahan
|
||||
->unique('provinsi_id')
|
||||
->sortBy('provinsi')
|
||||
->values();
|
||||
|
||||
$wilayah = $desaKelurahan
|
||||
->groupBy('provinsi_id')
|
||||
->map(function ($provinsi) {
|
||||
@@ -157,12 +174,15 @@ class IndeksPetaniController extends Controller
|
||||
];
|
||||
})->values();
|
||||
|
||||
return view('dashboard.indeks-petani', array_merge($filters,
|
||||
return array_merge($filters,
|
||||
compact(
|
||||
'komoditas',
|
||||
'desaKelurahan',
|
||||
'provinsi',
|
||||
'wilayah',
|
||||
)));
|
||||
'segments',
|
||||
'colors',
|
||||
));
|
||||
}
|
||||
|
||||
public function summary(Request $request): JsonResponse
|
||||
@@ -655,4 +675,4 @@ class IndeksPetaniController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Web\Dashboard;
|
||||
|
||||
use App\Enums\KeparahanSerangan;
|
||||
use App\Enums\PenyakitTanaman;
|
||||
use App\Enums\PraktikBudidaya\KondisiPengelolaanAir;
|
||||
use App\Enums\PraktikBudidaya\PemberianPupuk;
|
||||
use App\Enums\SeranganHama;
|
||||
use App\Enums\SosialEkonomi\StatusKepemilikanLahanSosialEkonomi;
|
||||
@@ -13,12 +14,14 @@ use App\Models\Pertanian\Lahan;
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\PraktikBudidaya;
|
||||
use App\Models\Pertanian\Produksi;
|
||||
use App\Models\Pertanian\SurveyPanen;
|
||||
use App\Models\Pertanian\SkorPetani;
|
||||
use App\Models\Pertanian\SosialEkonomi;
|
||||
use App\Models\Pertanian\SurveyProduktivitas;
|
||||
use App\Models\Wilayah\DesaKelurahan;
|
||||
use App\Models\Wilayah\KabupatenKota;
|
||||
use App\Models\Wilayah\Kecamatan;
|
||||
use App\Models\Wilayah\Provinsi;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
@@ -95,11 +98,180 @@ class IndikatorPertanianController extends Controller
|
||||
private function dashboardPayload(array $filters): array
|
||||
{
|
||||
return [
|
||||
'overviewData' => $this->overviewData($filters),
|
||||
'landProfileData' => $this->landProfileData($filters),
|
||||
'summaryData' => $this->summaryData($filters),
|
||||
'chartData' => $this->chartData($filters),
|
||||
];
|
||||
}
|
||||
|
||||
private function overviewData(array $filters): array
|
||||
{
|
||||
$totalPetani = $this->applyPetaniFilters(Petani::query(), $filters)->count();
|
||||
$totalLahan = $this->applyLahanFilters(Lahan::query(), $filters)->count();
|
||||
$totalLuasLahan = (float) $this->applyLahanFilters(Lahan::has('petani'), $filters)->sum('luas_lahan');
|
||||
$jumlahDesa = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->whereNotNull('desa_kelurahan_id')
|
||||
->distinct('desa_kelurahan_id')
|
||||
->count('desa_kelurahan_id');
|
||||
$jumlahKecamatan = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'map_lahan.desa_kelurahan_id')
|
||||
->whereNotNull('d.kecamatan_id')
|
||||
->distinct('d.kecamatan_id')
|
||||
->count('d.kecamatan_id');
|
||||
$totalProduksiKg = $this->totalProduksiKg($filters);
|
||||
$rataRataProduktivitas = $totalLuasLahan > 0 ? $totalProduksiKg / $totalLuasLahan : 0;
|
||||
$tanamanProduktif = $this->tanamanProduktifCount($filters);
|
||||
$batchPanen = $this->batchPanenCount($filters);
|
||||
|
||||
return [
|
||||
'kpiCards' => [
|
||||
[
|
||||
'value' => number_format($totalPetani, 0, ',', '.'),
|
||||
'label' => 'Jumlah Petani',
|
||||
'description' => 'Petani yang tercakup dalam filter aktif',
|
||||
'icon' => 'tabler-users',
|
||||
'variant' => 'primary',
|
||||
],
|
||||
[
|
||||
'value' => number_format($totalLahan, 0, ',', '.'),
|
||||
'label' => 'Jumlah Kebun/Lahan',
|
||||
'description' => 'Kebun atau lahan terdata',
|
||||
'icon' => 'tabler-trees',
|
||||
'variant' => 'success',
|
||||
],
|
||||
[
|
||||
'value' => number_format($totalLuasLahan, 2, ',', '.').' ha',
|
||||
'label' => 'Luas Lahan',
|
||||
'description' => 'Akumulasi luas lahan terdata',
|
||||
'icon' => 'tabler-ruler-measure',
|
||||
'variant' => 'warning',
|
||||
],
|
||||
[
|
||||
'value' => number_format($jumlahDesa, 0, ',', '.').' desa',
|
||||
'label' => 'Desa/Kecamatan',
|
||||
'description' => number_format($jumlahKecamatan, 0, ',', '.').' kecamatan tercakup',
|
||||
'icon' => 'tabler-map-pin',
|
||||
'variant' => 'info',
|
||||
],
|
||||
[
|
||||
'value' => number_format($totalProduksiKg, 0, ',', '.').' kg',
|
||||
'label' => 'Total Produksi',
|
||||
'description' => $this->productionSummaryDescription($filters),
|
||||
'icon' => 'tabler-wheat',
|
||||
'variant' => 'primary',
|
||||
],
|
||||
[
|
||||
'value' => number_format($rataRataProduktivitas, 0, ',', '.').' kg/ha',
|
||||
'label' => 'Produktivitas Rata-rata',
|
||||
'description' => 'Berdasarkan produksi dan luas lahan terdata',
|
||||
'icon' => 'tabler-chart-bar',
|
||||
'variant' => 'success',
|
||||
],
|
||||
[
|
||||
'value' => number_format($tanamanProduktif, 0, ',', '.'),
|
||||
'label' => 'Tanaman Produktif',
|
||||
'description' => 'Tanaman dengan kondisi baik',
|
||||
'icon' => 'tabler-plant-2',
|
||||
'variant' => 'warning',
|
||||
],
|
||||
[
|
||||
'value' => number_format($batchPanen, 0, ',', '.'),
|
||||
'label' => 'Batch Panen Tercatat',
|
||||
'description' => 'Catatan produksi dan survey panen',
|
||||
'icon' => 'tabler-basket',
|
||||
'variant' => 'info',
|
||||
],
|
||||
],
|
||||
'commodityFocus' => $this->commodityFocusData($filters),
|
||||
'map' => [
|
||||
'features' => $this->farmMapFeatures($filters),
|
||||
'levels' => $this->mapLevelData($filters),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function landProfileData(array $filters): array
|
||||
{
|
||||
$totalTanaman = (int) $this->applyLahanFilters(Lahan::query(), $filters)->sum('jumlah_tanaman');
|
||||
$rataRataUmurTanaman = (float) $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->whereNotNull('umur_tanaman')
|
||||
->avg('umur_tanaman');
|
||||
$komoditasCount = (int) $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->whereNotNull('komoditas_id')
|
||||
->distinct('komoditas_id')
|
||||
->count('komoditas_id');
|
||||
$statusLahanCount = (int) $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->whereNotNull('status_kepemilikan')
|
||||
->distinct('status_kepemilikan')
|
||||
->count('status_kepemilikan');
|
||||
$tanamanPenaung = $this->shadowTreeSummary($filters);
|
||||
$tanamanPendamping = $this->companionPlantSummary($filters);
|
||||
$diversityAverage = $this->plantDiversityAverage($filters);
|
||||
|
||||
return [
|
||||
'conditionCards' => [
|
||||
[
|
||||
'value' => number_format($komoditasCount, 0, ',', '.'),
|
||||
'label' => 'Luas Lahan per Komoditas',
|
||||
'description' => 'Komoditas dengan luas lahan terdata',
|
||||
'icon' => 'tabler-chart-treemap',
|
||||
'variant' => 'primary',
|
||||
],
|
||||
[
|
||||
'value' => number_format($totalTanaman, 0, ',', '.'),
|
||||
'label' => 'Jumlah Tanaman',
|
||||
'description' => 'Akumulasi jumlah tanaman pada lahan',
|
||||
'icon' => 'tabler-trees',
|
||||
'variant' => 'success',
|
||||
],
|
||||
[
|
||||
'value' => number_format($rataRataUmurTanaman, 1, ',', '.').' tahun',
|
||||
'label' => 'Umur Tanaman',
|
||||
'description' => 'Rata-rata umur tanaman terdata',
|
||||
'icon' => 'tabler-calendar-stats',
|
||||
'variant' => 'warning',
|
||||
],
|
||||
[
|
||||
'value' => number_format($statusLahanCount, 0, ',', '.'),
|
||||
'label' => 'Status Lahan',
|
||||
'description' => 'Jumlah kategori status kepemilikan lahan',
|
||||
'icon' => 'tabler-certificate',
|
||||
'variant' => 'info',
|
||||
],
|
||||
],
|
||||
'agroecologyItems' => [
|
||||
['label' => 'Sumber Air Lahan', 'value' => $this->sourceWaterSummary($filters), 'status' => 'available'],
|
||||
['label' => 'Pengelolaan Air & Irigasi', 'value' => $this->waterManagementSummary($filters), 'status' => 'available'],
|
||||
['label' => 'Kebutuhan Air per Musim', 'value' => $this->seasonalWaterNeedSummary($filters), 'status' => 'available'],
|
||||
['label' => 'Praktik Konservasi Tanah', 'value' => $this->soilConservationSummary($filters), 'status' => 'available'],
|
||||
],
|
||||
'agroforestryCards' => [
|
||||
[
|
||||
'value' => number_format($tanamanPenaung['total'], 0, ',', '.'),
|
||||
'label' => 'Jumlah Tanaman Penaung',
|
||||
'description' => $tanamanPenaung['types'].' jenis tanaman penaung',
|
||||
'icon' => 'tabler-leaf',
|
||||
'variant' => 'success',
|
||||
],
|
||||
[
|
||||
'value' => number_format($tanamanPendamping['types'], 0, ',', '.'),
|
||||
'label' => 'Jenis Tanaman Pendamping',
|
||||
'description' => number_format($tanamanPendamping['total'], 0, ',', '.').' entri tanaman pendamping',
|
||||
'icon' => 'tabler-flower',
|
||||
'variant' => 'warning',
|
||||
],
|
||||
[
|
||||
'value' => number_format(round($diversityAverage), 0, ',', '.'),
|
||||
'label' => 'Tingkat Campuran Tanaman',
|
||||
'description' => 'Rata-rata kombinasi tanaman utama, pendamping, dan penaung',
|
||||
'icon' => 'tabler-binary-tree',
|
||||
'variant' => 'primary',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function summaryData(array $filters): array
|
||||
{
|
||||
$totalPetani = $this->applyPetaniFilters(Petani::query(), $filters)->count();
|
||||
@@ -166,6 +338,10 @@ class IndikatorPertanianController extends Controller
|
||||
'statusPanen' => $this->statusPanenChart($filters),
|
||||
'surveyProduktivitasHa' => $this->surveyProduktivitasByUnitChart($filters, 'ha_tahun'),
|
||||
'surveyProduktivitasPohon' => $this->surveyProduktivitasByUnitChart($filters, 'pohon_tahun'),
|
||||
'luasLahanPerKomoditas' => $this->luasLahanPerKomoditasChart($filters),
|
||||
'jumlahTanamanPerKomoditas' => $this->jumlahTanamanPerKomoditasChart($filters),
|
||||
'umurTanaman' => $this->umurTanamanChart($filters),
|
||||
'sumberAir' => $this->sumberAirChart($filters),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -355,6 +531,82 @@ class IndikatorPertanianController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
private function luasLahanPerKomoditasChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->leftJoin('master_komoditas as k', 'k.id', '=', 'map_lahan.komoditas_id')
|
||||
->whereNotNull('map_lahan.luas_lahan')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||
->selectRaw('SUM(map_lahan.luas_lahan) as total')
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->havingRaw('SUM(map_lahan.luas_lahan) > 0')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
return [
|
||||
'labels' => $rows->pluck('label')->values()->all(),
|
||||
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function jumlahTanamanPerKomoditasChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->leftJoin('master_komoditas as k', 'k.id', '=', 'map_lahan.komoditas_id')
|
||||
->whereNotNull('map_lahan.jumlah_tanaman')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||
->selectRaw('SUM(map_lahan.jumlah_tanaman) as total')
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->havingRaw('SUM(map_lahan.jumlah_tanaman) > 0')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
return [
|
||||
'labels' => $rows->pluck('label')->values()->all(),
|
||||
'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function umurTanamanChart(array $filters): array
|
||||
{
|
||||
$bucketOrder = ['< 3 tahun', '3-7 tahun', '8-15 tahun', '> 15 tahun'];
|
||||
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->whereNotNull('umur_tanaman')
|
||||
->pluck('umur_tanaman')
|
||||
->map(function ($age) {
|
||||
$age = (float) $age;
|
||||
|
||||
return match (true) {
|
||||
$age < 3 => '< 3 tahun',
|
||||
$age <= 7 => '3-7 tahun',
|
||||
$age <= 15 => '8-15 tahun',
|
||||
default => '> 15 tahun',
|
||||
};
|
||||
})
|
||||
->countBy()
|
||||
->sortBy(fn ($value, $label) => array_search($label, $bucketOrder, true));
|
||||
|
||||
return [
|
||||
'labels' => $rows->keys()->values()->all(),
|
||||
'series' => $rows->values()->map(fn ($value) => (int) $value)->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function sumberAirChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
|
||||
->get(['sumber_air'])
|
||||
->flatMap(fn (PraktikBudidaya $praktik) => $this->arrayValues($praktik->sumber_air))
|
||||
->filter()
|
||||
->countBy()
|
||||
->sortDesc();
|
||||
|
||||
return [
|
||||
'labels' => $rows->keys()->values()->all(),
|
||||
'series' => $rows->values()->map(fn ($value) => (int) $value)->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function kondisiTanamanChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
|
||||
@@ -572,6 +824,415 @@ class IndikatorPertanianController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
private function tanamanProduktifCount(array $filters): int
|
||||
{
|
||||
return (int) $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
|
||||
->whereNull('map_tanaman.deleted_at')
|
||||
->where('map_tanaman.kondisi_tanaman', 'baik')
|
||||
->count('map_tanaman.id');
|
||||
}
|
||||
|
||||
private function batchPanenCount(array $filters): int
|
||||
{
|
||||
$produksiCount = $this->applyProduksiFilters(Produksi::query(), $filters)->count();
|
||||
$surveyPanenCount = $this->applySurveyPanenFilters(SurveyPanen::query(), $filters)->count();
|
||||
|
||||
return (int) $produksiCount + (int) $surveyPanenCount;
|
||||
}
|
||||
|
||||
private function productionSummaryDescription(array $filters): string
|
||||
{
|
||||
$chart = $this->produksiPerKomoditasChart($filters);
|
||||
|
||||
if (empty($chart['series'])) {
|
||||
return 'Belum ada produksi aktual per komoditas';
|
||||
}
|
||||
|
||||
return collect($chart['labels'])
|
||||
->map(fn ($label, $index) => $label.': '.number_format((float) ($chart['series'][$index] ?? 0), 0, ',', '.').' kg')
|
||||
->take(3)
|
||||
->implode(' | ');
|
||||
}
|
||||
|
||||
private function commodityFocusData(array $filters): array
|
||||
{
|
||||
$focusCommodities = ['Kopi', 'Kakao', 'Vanili', 'Kemiri'];
|
||||
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->leftJoin('master_komoditas as k', 'k.id', '=', 'map_lahan.komoditas_id')
|
||||
->whereIn(DB::raw('LOWER(k.nama)'), collect($focusCommodities)->map(fn ($name) => strtolower($name))->all())
|
||||
->selectRaw('k.nama as label')
|
||||
->selectRaw('COUNT(map_lahan.id) as total')
|
||||
->groupBy('k.nama')
|
||||
->get()
|
||||
->mapWithKeys(fn ($row) => [strtolower((string) $row->label) => (int) $row->total]);
|
||||
|
||||
return collect($focusCommodities)
|
||||
->map(fn ($name) => [
|
||||
'label' => $name,
|
||||
'total' => $rows[strtolower($name)] ?? 0,
|
||||
])
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
private function farmMapFeatures(array $filters): array
|
||||
{
|
||||
return $this->applyLahanFilters(
|
||||
Lahan::query()->with(['desaKelurahan.kecamatan', 'komoditas']),
|
||||
$filters
|
||||
)
|
||||
->where(function ($query) {
|
||||
$query
|
||||
->whereNotNull('lat')
|
||||
->orWhereNotNull('long')
|
||||
->orWhereNotNull('path');
|
||||
})
|
||||
->limit(250)
|
||||
->get()
|
||||
->map(fn (Lahan $lahan) => [
|
||||
'id' => $lahan->id,
|
||||
'name' => $lahan->nama,
|
||||
'commodity' => $lahan->komoditas?->nama ?? 'Belum ditentukan',
|
||||
'area' => (float) $lahan->luas_lahan,
|
||||
'village' => $lahan->desaKelurahan?->nama,
|
||||
'district' => $lahan->desaKelurahan?->kecamatan?->nama,
|
||||
'lat' => is_numeric($lahan->lat) ? (float) $lahan->lat : null,
|
||||
'lng' => is_numeric($lahan->long) ? (float) $lahan->long : null,
|
||||
'path' => is_array($lahan->path) ? $lahan->path : null,
|
||||
])
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
private function mapLevelData(array $filters): array
|
||||
{
|
||||
$lahanSummaryByDesa = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->whereNotNull('desa_kelurahan_id')
|
||||
->select('desa_kelurahan_id')
|
||||
->selectRaw('COUNT(*) as total_lahan')
|
||||
->selectRaw('COALESCE(SUM(luas_lahan), 0) as total_luas')
|
||||
->groupBy('desa_kelurahan_id')
|
||||
->get();
|
||||
|
||||
$desaIds = $lahanSummaryByDesa->pluck('desa_kelurahan_id')->filter()->unique()->values();
|
||||
|
||||
if ($filters['desa'] !== 'all') {
|
||||
$desaIds->push($filters['desa']);
|
||||
}
|
||||
|
||||
$desaList = DesaKelurahan::query()
|
||||
->select('id', 'nama', 'path', 'kecamatan_id')
|
||||
->when($desaIds->isNotEmpty(), fn ($query) => $query->whereIn('id', $desaIds->unique()->values()))
|
||||
->with([
|
||||
'kecamatan:id,nama,path,kabupaten_kota_id',
|
||||
'kecamatan.kabupatenKota:id,nama,path,provinsi_id',
|
||||
'kecamatan.kabupatenKota.provinsi:id,nama,path',
|
||||
])
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
$geoProvinsi = [];
|
||||
$geoKabupaten = [];
|
||||
$geoKecamatan = [];
|
||||
$geoDesa = [];
|
||||
$geoLahan = [];
|
||||
|
||||
foreach ($lahanSummaryByDesa as $summary) {
|
||||
$desa = $desaList->get($summary->desa_kelurahan_id);
|
||||
$kec = $desa?->kecamatan;
|
||||
$kab = $kec?->kabupatenKota;
|
||||
$prov = $kab?->provinsi;
|
||||
$totalLahan = (int) $summary->total_lahan;
|
||||
$totalLuas = (float) $summary->total_luas;
|
||||
|
||||
if ($prov && $prov->path) {
|
||||
$this->accumulateGeoLevel($geoProvinsi, $prov->id, null, $prov->nama, $prov->path, $totalLahan, $totalLuas);
|
||||
}
|
||||
|
||||
if ($kab && $kab->path) {
|
||||
$this->accumulateGeoLevel($geoKabupaten, $kab->id, $prov?->id, $kab->nama, $kab->path, $totalLahan, $totalLuas);
|
||||
}
|
||||
|
||||
if ($kec && $kec->path) {
|
||||
$this->accumulateGeoLevel($geoKecamatan, $kec->id, $kab?->id, $kec->nama, $kec->path, $totalLahan, $totalLuas);
|
||||
}
|
||||
|
||||
if ($desa && $desa->path) {
|
||||
$this->accumulateGeoLevel($geoDesa, $desa->id, $kec?->id, $desa->nama, $desa->path, $totalLahan, $totalLuas);
|
||||
}
|
||||
}
|
||||
|
||||
$this->ensureSelectedRegionOnMap($filters, $geoProvinsi, $geoKabupaten, $geoKecamatan, $geoDesa);
|
||||
|
||||
$mapLahan = $this->applyLahanFilters(
|
||||
Lahan::query()->with([
|
||||
'profile:id,nama',
|
||||
'petani:id,nama',
|
||||
'komoditas:id,nama',
|
||||
'tanaman:id,lahan_id,komoditas_id',
|
||||
'tanaman.komoditas:id,nama',
|
||||
]),
|
||||
$filters
|
||||
)
|
||||
->whereNotNull('path')
|
||||
->orderByDesc('updated_at')
|
||||
->limit(500)
|
||||
->get();
|
||||
|
||||
foreach ($mapLahan as $lahan) {
|
||||
if (! $this->hasMapPolygon($lahan->path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tanaman = $lahan->tanaman
|
||||
->map(fn ($tanaman) => $tanaman->komoditas?->nama)
|
||||
->filter()
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
if ($tanaman->isEmpty() && $lahan->komoditas?->nama) {
|
||||
$tanaman = collect([$lahan->komoditas->nama]);
|
||||
}
|
||||
|
||||
$geoLahan[] = [
|
||||
'id' => $lahan->id,
|
||||
'parent_id' => $lahan->desa_kelurahan_id,
|
||||
'polygon' => $lahan->path,
|
||||
'nama_petani' => $lahan->petani?->nama ?? $lahan->profile?->nama ?? 'N/A',
|
||||
'luas' => (float) $lahan->luas_lahan,
|
||||
'tanaman' => $tanaman,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'provinsi' => array_values($geoProvinsi),
|
||||
'kabupaten' => array_values($geoKabupaten),
|
||||
'kecamatan' => array_values($geoKecamatan),
|
||||
'desa' => array_values($geoDesa),
|
||||
'lahan' => $geoLahan,
|
||||
];
|
||||
}
|
||||
|
||||
private function accumulateGeoLevel(array &$bucket, int|string $id, int|string|null $parentId, string $name, mixed $polygon, int $totalLahan, float $totalLuas): void
|
||||
{
|
||||
if (! isset($bucket[$id])) {
|
||||
$bucket[$id] = [
|
||||
'id' => $id,
|
||||
'parent_id' => $parentId,
|
||||
'nama' => $name,
|
||||
'polygon' => $polygon,
|
||||
'total_lahan' => 0,
|
||||
'total_luas' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$bucket[$id]['total_lahan'] += $totalLahan;
|
||||
$bucket[$id]['total_luas'] += $totalLuas;
|
||||
}
|
||||
|
||||
private function ensureSelectedRegionOnMap(array $filters, array &$geoProvinsi, array &$geoKabupaten, array &$geoKecamatan, array &$geoDesa): void
|
||||
{
|
||||
$kabupaten = $filters['kabupaten'] !== 'all'
|
||||
? KabupatenKota::query()->with('provinsi:id,nama,path')->find($filters['kabupaten'])
|
||||
: null;
|
||||
|
||||
if ($kabupaten && $kabupaten->path && ! isset($geoKabupaten[$kabupaten->id])) {
|
||||
$this->accumulateGeoLevel($geoKabupaten, $kabupaten->id, $kabupaten->provinsi_id, $kabupaten->nama, $kabupaten->path, 0, 0);
|
||||
}
|
||||
|
||||
if ($kabupaten?->provinsi && $kabupaten->provinsi->path && ! isset($geoProvinsi[$kabupaten->provinsi->id])) {
|
||||
$this->accumulateGeoLevel($geoProvinsi, $kabupaten->provinsi->id, null, $kabupaten->provinsi->nama, $kabupaten->provinsi->path, 0, 0);
|
||||
}
|
||||
|
||||
if ($filters['kecamatan'] !== 'all') {
|
||||
$kecamatan = Kecamatan::query()->find($filters['kecamatan']);
|
||||
|
||||
if ($kecamatan && $kecamatan->path && ! isset($geoKecamatan[$kecamatan->id])) {
|
||||
$this->accumulateGeoLevel($geoKecamatan, $kecamatan->id, $kecamatan->kabupaten_kota_id, $kecamatan->nama, $kecamatan->path, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ($filters['desa'] !== 'all') {
|
||||
$desa = DesaKelurahan::query()->find($filters['desa']);
|
||||
|
||||
if ($desa && $desa->path && ! isset($geoDesa[$desa->id])) {
|
||||
$this->accumulateGeoLevel($geoDesa, $desa->id, $desa->kecamatan_id, $desa->nama, $desa->path, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function hasMapPolygon(mixed $path): bool
|
||||
{
|
||||
if (is_array($path)) {
|
||||
return ! empty($path);
|
||||
}
|
||||
|
||||
if (! is_string($path) || blank($path) || strtolower($path) === 'null') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$decoded = json_decode($path, true);
|
||||
|
||||
return json_last_error() === JSON_ERROR_NONE && ! empty($decoded) && $decoded !== 'null';
|
||||
}
|
||||
|
||||
private function shadowTreeSummary(array $filters): array
|
||||
{
|
||||
$items = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->get(['tanaman_penaung'])
|
||||
->flatMap(fn (Lahan $lahan) => $this->arrayValues($lahan->tanaman_penaung))
|
||||
->filter()
|
||||
->values();
|
||||
|
||||
return [
|
||||
'total' => $items->count(),
|
||||
'types' => $items->unique(fn ($item) => strtolower((string) $item))->count(),
|
||||
];
|
||||
}
|
||||
|
||||
private function companionPlantSummary(array $filters): array
|
||||
{
|
||||
$items = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->get(['id', 'komoditas_lain'])
|
||||
->flatMap(fn (Lahan $lahan) => $this->arrayValues($lahan->komoditas_lain))
|
||||
->filter()
|
||||
->values();
|
||||
|
||||
return [
|
||||
'total' => $items->count(),
|
||||
'types' => $items->unique(fn ($item) => strtolower((string) $item))->count(),
|
||||
];
|
||||
}
|
||||
|
||||
private function plantDiversityAverage(array $filters): float
|
||||
{
|
||||
$lahan = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->get(['komoditas_id', 'komoditas_lain', 'tanaman_penaung']);
|
||||
|
||||
if ($lahan->isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (float) $lahan->avg(function (Lahan $item) {
|
||||
$total = $item->komoditas_id ? 1 : 0;
|
||||
$total += count($this->arrayValues($item->komoditas_lain));
|
||||
$total += count($this->arrayValues($item->tanaman_penaung));
|
||||
|
||||
return $total;
|
||||
});
|
||||
}
|
||||
|
||||
private function sourceWaterSummary(array $filters): string
|
||||
{
|
||||
$chart = $this->sumberAirChart($filters);
|
||||
|
||||
if (empty($chart['labels'])) {
|
||||
return 'Belum ada data';
|
||||
}
|
||||
|
||||
return collect($chart['labels'])
|
||||
->map(fn ($label, $index) => $label.' ('.number_format((int) ($chart['series'][$index] ?? 0), 0, ',', '.').')')
|
||||
->take(2)
|
||||
->implode(', ');
|
||||
}
|
||||
|
||||
private function waterManagementSummary(array $filters): string
|
||||
{
|
||||
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
|
||||
->whereNotNull('kondisi_pengelolaan_air')
|
||||
->pluck('kondisi_pengelolaan_air')
|
||||
->map(fn ($value) => $this->waterManagementShortLabel((int) $value))
|
||||
->countBy()
|
||||
->sortDesc();
|
||||
|
||||
return $this->topCountSummary($rows);
|
||||
}
|
||||
|
||||
private function seasonalWaterNeedSummary(array $filters): string
|
||||
{
|
||||
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
|
||||
->whereNotNull('kebutuhan_air_permusim')
|
||||
->pluck('kebutuhan_air_permusim')
|
||||
->map(fn ($value) => $this->shortEnumLabel((string) $value))
|
||||
->countBy()
|
||||
->sortDesc();
|
||||
|
||||
return $this->topCountSummary($rows);
|
||||
}
|
||||
|
||||
private function soilConservationSummary(array $filters): string
|
||||
{
|
||||
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
|
||||
->get(['kebiasaan_konservasi_tanah'])
|
||||
->flatMap(fn (PraktikBudidaya $praktik) => $this->arrayValues($praktik->kebiasaan_konservasi_tanah))
|
||||
->filter()
|
||||
->countBy()
|
||||
->sortDesc();
|
||||
|
||||
return $this->topCountSummary($rows);
|
||||
}
|
||||
|
||||
private function soilConservationScoreSummary(array $filters): string
|
||||
{
|
||||
$average = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
|
||||
->whereNotNull('nilai_konservasi_tanah')
|
||||
->avg('nilai_konservasi_tanah');
|
||||
|
||||
if ($average === null) {
|
||||
return 'Belum ada data';
|
||||
}
|
||||
|
||||
return 'Rata-rata '.number_format((float) $average, 1, ',', '.');
|
||||
}
|
||||
|
||||
private function topCountSummary($rows): string
|
||||
{
|
||||
if ($rows->isEmpty()) {
|
||||
return 'Belum ada data';
|
||||
}
|
||||
|
||||
return $rows
|
||||
->map(fn ($count, $label) => $label.' ('.number_format((int) $count, 0, ',', '.').')')
|
||||
->take(2)
|
||||
->implode(', ');
|
||||
}
|
||||
|
||||
private function waterManagementShortLabel(int $value): string
|
||||
{
|
||||
return match (KondisiPengelolaanAir::tryFrom($value)) {
|
||||
KondisiPengelolaanAir::KURANG_TIDAK_ADA_UPAYA => 'Kurang, tanpa upaya',
|
||||
KondisiPengelolaanAir::KURANG_TIDAK_SUDAH_BERUPAYA => 'Kurang, ada upaya',
|
||||
KondisiPengelolaanAir::CUKUP_DARI_HUJAN => 'Cukup dari hujan',
|
||||
KondisiPengelolaanAir::CUKUP_IRIGASI_PINJAM => 'Cukup, irigasi pinjam',
|
||||
KondisiPengelolaanAir::CUKUP_IRIGASI_SENDIRI => 'Cukup, irigasi sendiri',
|
||||
default => 'Belum ditentukan',
|
||||
};
|
||||
}
|
||||
|
||||
private function arrayValues(mixed $value): array
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return collect($value)
|
||||
->flatten()
|
||||
->filter(fn ($item) => filled($item))
|
||||
->map(fn ($item) => is_scalar($item) ? (string) $item : json_encode($item))
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
if (is_string($value) && filled($value) && strtolower($value) !== 'null') {
|
||||
$decoded = json_decode($value, true);
|
||||
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
return $this->arrayValues($decoded);
|
||||
}
|
||||
|
||||
return [$value];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function chartFromRows($rows, callable $labelResolver): array
|
||||
{
|
||||
return [
|
||||
@@ -676,6 +1337,13 @@ class IndikatorPertanianController extends Controller
|
||||
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']));
|
||||
}
|
||||
|
||||
private function applySurveyPanenFilters($query, array $filters)
|
||||
{
|
||||
return $query->whereHas('petani', function ($petani) use ($filters) {
|
||||
$this->applyPetaniFilters($petani, $filters);
|
||||
});
|
||||
}
|
||||
|
||||
private function applyTanamanTableFilters($query, array $filters)
|
||||
{
|
||||
return $query
|
||||
|
||||
Reference in New Issue
Block a user