Analisis Kasus Kusta di Kabupaten/Kota
Provinsi Jawa Barat Tahun
2021
Ditujukan Guna Memenuhi UAS Mata Kuliah Epidemiologi
Disusun oleh:
Muhammad Imamul Caesar
(140610230019)
Dosen Pengampu:
I Gede Nyoman Mindra Jaya,
Ph.D
PROGRAM STUDI S-1 STATISTIKA
FAKULTAS MATEMATIKA DAN ILMU
PENGETAHUAN ALAM
UNIVERSITAS PADJADJARAN
JATINANGOR
2025
Kusta merupakan penyakit menular kronis yang disebabkan oleh bakteri Mycobacterium leprae dan terutama menyerang kulit serta saraf perifer [1]. Penyakit ini memiliki masa inkubasi yang panjang, bahkan dapat berlangsung selama beberapa tahun, sehingga banyak penderita tidak menyadari infeksi pada tahap awal [2]. Apabila tidak ditangani secara tepat, kusta dapat menimbulkan kecacatan permanen yang berdampak pada kemampuan fisik, psikologis, dan sosial penderitanya [3]. Selain dampak kesehatan, stigma dan diskriminasi terhadap penderita kusta masih sering dijumpai di masyarakat, menjadikan penyakit ini sebagai masalah kesehatan masyarakat yang kompleks dan multidimensional [4].
Dalam perspektif epidemiologi, kejadian kusta dipengaruhi oleh interaksi antara agent, host, dan environment [5]. Agen penyakit memerlukan kondisi lingkungan tertentu untuk bertahan dan menular, sementara faktor host seperti tingkat pendidikan, status sosial ekonomi, dan kepadatan hunian berperan dalam meningkatkan kerentanan individu terhadap infeksi [6]. Lingkungan fisik dan sosial, khususnya akses terhadap sanitasi layak dan kondisi permukiman yang padat, turut memperbesar peluang transmisi penyakit [7]. Oleh karena itu, distribusi kasus kusta cenderung tidak merata antarwilayah dan sangat dipengaruhi oleh karakteristik demografi serta kondisi sosial ekonomi masyarakat setempat [8].
Indonesia hingga saat ini masih termasuk negara dengan beban kusta yang relatif tinggi secara global, meskipun berbagai upaya eliminasi telah dilakukan [1][9]. Kasus baru kusta masih terus ditemukan setiap tahun dan menunjukkan pola distribusi yang tidak seragam antarprovinsi maupun antar kabupaten/kota [10]. Provinsi Jawa Barat, sebagai provinsi dengan jumlah penduduk terbesar di Indonesia, memiliki tingkat kepadatan penduduk yang tinggi serta kesenjangan kondisi sanitasi, pendidikan, dan kemiskinan antarwilayah [11]. Kondisi tersebut berpotensi menciptakan variasi jumlah kasus kusta di tingkat kabupaten/kota, sehingga analisis epidemiologi berbasis wilayah menjadi penting untuk memahami pola penyebaran penyakit secara lebih komprehensif [12].
Pengendalian kusta juga memiliki keterkaitan erat dengan agenda pembangunan berkelanjutan (Sustainable Development Goals / SDGs). Penurunan beban penyakit kusta berkontribusi langsung terhadap SDG 3 (Good Health and Well-being) melalui peningkatan kesehatan masyarakat dan pengendalian penyakit menular [13]. Selain itu, faktor-faktor determinan kusta seperti kemiskinan, pendidikan, dan sanitasi berkaitan erat dengan SDG 1 (No Poverty), SDG 4 (Quality Education), dan SDG 6 (Clean Water and Sanitation) [14][15]. Oleh karena itu, analisis epidemiologi kusta yang mengaitkan jumlah kasus dengan faktor kependudukan, sanitasi, kepadatan penduduk, pendidikan, dan kemiskinan di tingkat kabupaten/kota di Jawa Barat menjadi penting sebagai dasar pemahaman masalah kesehatan masyarakat serta sebagai bahan perumusan kebijakan pengendalian kusta yang lebih tepat sasaran dan berkelanjutan.
Epidemiologi merupakan ilmu yang mempelajari distribusi serta determinan masalah kesehatan dalam populasi, serta penerapannya untuk pengendalian penyakit [16]. Tujuan utama epidemiologi adalah mengidentifikasi faktor-faktor penyebab dan pola penyebaran penyakit agar dapat dirancang intervensi kesehatan masyarakat yang efektif dan tepat sasaran. Melalui pendekatan epidemiologi, suatu masalah kesehatan tidak hanya dilihat dari aspek klinis, tetapi juga dari sudut pandang populasi, sehingga memungkinkan pemahaman yang lebih komprehensif mengenai beban penyakit dan kelompok masyarakat yang berisiko.
Dalam praktiknya, epidemiologi berperan penting dalam mengkaji pola penyebaran penyakit, faktor risiko yang berkontribusi terhadap kejadian penyakit, serta dampak program pengendalian yang telah diterapkan [17]. Pendekatan kuantitatif digunakan untuk menilai hubungan antara faktor sosial, ekonomi, dan lingkungan terhadap kejadian penyakit di suatu wilayah [18]. Analisis ini menjadi dasar dalam perencanaan, pemantauan, dan evaluasi kebijakan kesehatan masyarakat, terutama dalam upaya pencegahan dan pengendalian penyakit secara berkelanjutan.
Konsep dasar dalam epidemiologi penyakit menular dikenal sebagai segitiga epidemiologi (epidemiologic triad), yang terdiri dari tiga komponen utama: agent (agen penyebab), host (inang), dan environment (lingkungan) [18]. Interaksi ketiganya menentukan terjadinya dan penyebaran suatu penyakit.
Agent atau agen penyebab merupakan faktor biologis yang secara langsung berperan dalam timbulnya suatu penyakit. Agen penyakit dapat berupa bakteri, virus, parasit, jamur, maupun mikroorganisme lain yang memiliki kemampuan untuk menginfeksi inang. Setiap agen memiliki karakteristik tertentu, seperti daya tahan di lingkungan, mekanisme penularan, serta tingkat virulensi, yang memengaruhi pola penyebaran penyakit di masyarakat [19]. Dalam konteks epidemiologi penyakit menular, pemahaman terhadap karakteristik agen sangat penting karena menentukan potensi penularan, tingkat keparahan penyakit, serta strategi pengendalian yang dapat dilakukan.
Host atau inang merujuk pada individu atau populasi yang dapat terinfeksi oleh agen penyakit. Kerentanan host terhadap infeksi dipengaruhi oleh berbagai faktor, baik biologis maupun sosial. Faktor biologis meliputi usia, status imun, kondisi gizi, dan riwayat kesehatan, sementara faktor sosial mencakup tingkat pendidikan, perilaku kesehatan, serta kondisi sosial ekonomi. Individu atau kelompok masyarakat dengan keterbatasan akses terhadap informasi kesehatan dan pelayanan medis cenderung memiliki risiko yang lebih tinggi untuk terinfeksi dan mengalami komplikasi penyakit [20]. Oleh karena itu, karakteristik host menjadi komponen penting dalam analisis epidemiologi untuk memahami variasi risiko penyakit antar kelompok masyarakat.
Environment atau lingkungan mencakup seluruh faktor eksternal yang memengaruhi interaksi antara agen penyakit dan host. Faktor lingkungan dapat berupa kondisi fisik, seperti kepadatan penduduk, kualitas sanitasi, ventilasi, dan kualitas air, maupun kondisi sosial seperti kemiskinan, mobilitas penduduk, dan akses terhadap fasilitas kesehatan [21]. Lingkungan yang padat, sanitasi yang buruk, serta keterbatasan fasilitas kesehatan dapat menciptakan kondisi yang mendukung terjadinya penularan penyakit menular. Oleh karena itu, variasi kondisi lingkungan antarwilayah sering kali berkaitan erat dengan perbedaan tingkat kejadian penyakit secara spasial [20].
Ukuran epidemiologi merupakan indikator kuantitatif yang digunakan untuk menggambarkan tingkat kejadian dan distribusi suatu penyakit dalam populasi tertentu. Ukuran ini berfungsi sebagai dasar dalam memahami besarnya masalah kesehatan, membandingkan kondisi antarwilayah, serta mengevaluasi faktor risiko yang berkontribusi terhadap terjadinya penyakit. Dalam kajian epidemiologi penyakit menular seperti kusta, ukuran epidemiologi umumnya dikelompokkan menjadi ukuran frekuensi dan ukuran asosiasi.
Prevalensi menggambarkan proporsi individu dalam suatu populasi yang menderita penyakit pada suatu waktu tertentu. Ukuran ini mencerminkan beban penyakit (disease burden) dalam masyarakat dan sangat dipengaruhi oleh durasi penyakit serta laju kejadian kasus baru.
\[ \text{Prevalensi} = \frac{\text{Jumlah kasus penyakit}}{\text{Jumlah penduduk berisiko}} \]
Dalam praktik epidemiologi, prevalensi sering dinyatakan per 10.000 atau 100.000 penduduk, terutama untuk penyakit menular kronis seperti kusta.
Insidensi menunjukkan jumlah kasus baru yang muncul dalam populasi berisiko selama periode waktu tertentu. Ukuran ini lebih sensitif dalam menggambarkan tingkat penularan penyakit dibandingkan prevalensi.
\[ \text{Insidensi} = \frac{\text{Jumlah kasus baru}}{\text{Populasi berisiko}} \]
Attack rate merupakan bentuk khusus dari insidensi yang sering digunakan pada kejadian luar biasa atau periode waktu singkat. Meskipun secara teknis bukan rate murni, ukuran ini berguna untuk menggambarkan proporsi individu yang terinfeksi selama periode tertentu.
\[ \text{Attack Rate} = \frac{\text{Jumlah kasus baru}}{\text{Jumlah populasi terpapar}} \]
Untuk menilai kekuatan hubungan antara faktor risiko (paparan) dan kejadian penyakit, epidemiologi menggunakan ukuran asosiasi (measure of association). Ukuran ini menggambarkan seberapa besar risiko suatu penyakit meningkat atau menurun pada individu yang terpapar dibandingkan yang tidak terpapar [22]. Beberapa ukuran asosiasi yang umum digunakan meliputi:
Digunakan dalam studi kohort, RR mengukur rasio risiko penyakit pada kelompok terpapar dibandingkan kelompok tidak terpapar. \[ RR = \frac{\frac{A}{A + B}}{\frac{C}{C + D}} \] dengan:
Digunakan dalam studi kasus–kontrol, OR membandingkan peluang paparan pada kelompok kasus dan kontrol. \[ OR = \frac{A \times D}{B \times C} \] Interpretasi: nilai OR > 1 menunjukkan bahwa paparan meningkatkan peluang terjadinya penyakit, sedangkan OR < 1 menunjukkan efek protektif.
Umumnya digunakan dalam studi potong lintang (cross-sectional) untuk membandingkan prevalensi penyakit antar kelompok. \[ PR = \frac{\frac{A}{A + B}}{\frac{C}{C + D}} \] Nilai PR menunjukkan seberapa besar kemungkinan individu terpapar memiliki penyakit dibandingkan individu yang tidak terpapar pada waktu tertentu. Ukuran asosiasi seperti RR, OR, dan PR membantu peneliti memahami hubungan antara faktor risiko seperti kepadatan penduduk, kemiskinan, atau akses terhadap fasilitas kesehatan dengan kejadian TBC di suatu wilayah [22].
Data jumlah kasus kusta tahun 2021 diperoleh dari data sekunder tingkat kabupaten/kota di Provinsi Jawa Barat. Variabel utama penelitian adalah:
y : jumlah kasus kusta
x1 : jumlah penduduk
x2 : persentase sanitasi layak
x3 : kepadatan penduduk
x4 : rata-rata lama sekolah
x5 : persentase penduduk miskin
Penelitian ini menggunakan pendekatan epidemiologi deskriptif, yang bertujuan untuk menggambarkan distribusi dan besarnya kejadian penyakit kusta berdasarkan wilayah. Ukuran epidemiologi utama yang digunakan adalah prevalensi, karena kusta merupakan penyakit menular kronis dengan durasi penyakit yang panjang, sehingga prevalensi lebih tepat untuk menggambarkan beban penyakit di masyarakat.
Prevalensi dihitung sebagai perbandingan antara jumlah kasus kusta yang tercatat dengan jumlah penduduk pada wilayah yang sama, kemudian dinyatakan dalam satuan per 100.000 penduduk. Secara matematis, prevalensi dirumuskan sebagai:
\[ \text{Prevalensi} = \frac{\text{Jumlah kasus penyakit}}{\text{Jumlah penduduk berisiko}} \]
Sebagai model pembanding awal, digunakan regresi linear klasik:
\[ y = \beta_0 + \sum_{k=1}^{5} \beta_k x_k + \varepsilon \]
Namun, model ini tidak memperhitungkan efek ketergantungan spasial antarwilayah.
Untuk mengakomodasi ketergantungan spasial, penelitian ini mengestimasi beberapa model ekonometrika spasial [21][22]:
Spatial Autoregressive Model (SAR)
\[ y = \rho Wy + X\beta + \varepsilon
\]
Spatial Error Model (SEM)
\[ y = X\beta + u, \quad u = \lambda Wu +
\varepsilon \]
Spatial Lag of X Model (SLX)
\[ y = X\beta + WX\theta + \varepsilon
\]
Spatial Durbin Model (SDM)
\[ y = \rho Wy + X\beta + WX\theta +
\varepsilon \]
Spatial Durbin Error Model (SDEM) dan
Spatial Autoregressive Combined Model (SAC)
Pemilihan model terbaik dilakukan berdasarkan nilai Akaike Information Criterion (AIC), Root Mean Square Error (RMSE), dan Mean Absolute Error (MAE).
Pengumpulan Data
Mengumpulkan data sekunder berupa jumlah kasus kusta, jumlah penduduk,
serta data pendukung yang mencerminkan kondisi demografi dan sosial
ekonomi kabupaten/kota di Provinsi Jawa Barat. Data spasial berupa peta
administrasi kabupaten/kota juga dikumpulkan untuk keperluan analisis
geografis.
Pra-pemrosesan Data
Melakukan pembersihan data, penyeragaman nama wilayah, serta
penggabungan data tabular dengan data spasial (shapefile). Tahap ini
bertujuan memastikan kesesuaian unit analisis dan menghindari kesalahan
pemetaan.
Perhitungan Ukuran Epidemiologi
Menghitung ukuran epidemiologi berupa prevalensi kusta untuk setiap
kabupaten/kota berdasarkan jumlah kasus dan jumlah penduduk. Nilai
prevalensi digunakan sebagai indikator utama untuk menggambarkan beban
penyakit kusta di masing-masing wilayah.
Pemetaan Persebaran Kasus Kusta
Menyajikan hasil perhitungan prevalensi dalam bentuk peta tematik untuk
menggambarkan pola distribusi spasial kusta antar kabupaten/kota di
Provinsi Jawa Barat. Pemetaan ini bertujuan untuk mengidentifikasi
wilayah dengan tingkat prevalensi relatif tinggi maupun rendah serta
melihat indikasi awal adanya pola pengelompokan (cluster)
penyakit.
Pemodelan Spasial
Melakukan pemodelan spasial untuk menganalisis keterkaitan antara
prevalensi kusta dengan faktor-faktor demografi dan sosial ekonomi
wilayah. Pemodelan ini digunakan untuk menangkap pengaruh ketergantungan
spasial antarwilayah yang tidak dapat dijelaskan oleh analisis
non-spasial, serta untuk memperoleh pemahaman yang lebih mendalam
mengenai pola penyebaran penyakit secara geografis.
Kesimpulan dan Saran
Menyusun kesimpulan berdasarkan keseluruhan hasil analisis dan
memberikan saran yang relevan untuk upaya pengendalian dan pencegahan
kusta di tingkat wilayah.
Berdasarkan kerangka epidemiologi klasik, kejadian penyakit kusta pada tingkat kabupaten/kota di Provinsi Jawa Barat dapat dipahami sebagai hasil interaksi antara faktor agent, host, dan environment. Ketiga komponen ini tidak berdiri sendiri, melainkan saling memengaruhi dalam menentukan pola dan tingkat kejadian penyakit di suatu wilayah.
Dari sisi agent, kusta disebabkan oleh bakteri Mycobacterium leprae yang memiliki karakteristik masa inkubasi panjang dan perkembangan penyakit yang lambat. Sifat ini menyebabkan kusta sering terdeteksi dalam kondisi lanjut dan memungkinkan terjadinya penularan jangka panjang di masyarakat tanpa disadari. Karakteristik agen ini menjadikan kusta lebih sensitif terhadap kondisi lingkungan dan sosial dibandingkan penyakit menular akut, sehingga variasi kasus antarwilayah menjadi lebih menonjol.
Faktor host dalam penelitian ini tercermin melalui karakteristik demografi dan sosial ekonomi penduduk, khususnya jumlah penduduk, kepadatan penduduk, tingkat pendidikan, dan tingkat kemiskinan. Wilayah dengan jumlah dan kepadatan penduduk yang tinggi berpotensi meningkatkan frekuensi kontak antarindividu, sehingga memperbesar peluang penularan. Selain itu, tingkat pendidikan dan kemiskinan memengaruhi pengetahuan, perilaku kesehatan, serta kemampuan masyarakat dalam mengakses layanan kesehatan. Masyarakat dengan tingkat pendidikan rendah dan kondisi ekonomi terbatas cenderung memiliki keterlambatan dalam deteksi dan pengobatan penyakit, yang pada akhirnya meningkatkan risiko terjadinya kasus kusta yang berkelanjutan.
Sementara itu, faktor environment berperan melalui kondisi fisik dan sosial wilayah, terutama kualitas sanitasi dan lingkungan permukiman. Persentase sanitasi layak digunakan sebagai indikator kondisi lingkungan yang memengaruhi kesehatan masyarakat secara umum. Lingkungan dengan sanitasi yang kurang memadai sering kali berkaitan dengan kondisi permukiman padat, ventilasi buruk, serta keterbatasan akses air bersih, yang secara tidak langsung mendukung terjadinya penularan penyakit menular kronis seperti kusta. Selain itu, keterbatasan akses fasilitas kesehatan di wilayah tertentu juga menjadi bagian dari faktor lingkungan yang memperkuat kerentanan masyarakat terhadap penyakit.
AGENT
(Mycobacterium leprae)
|
v
+--------------------------------------+
| |
| HOST |
| - Jumlah penduduk |
| - Kepadatan penduduk |
| - Rata-rata lama sekolah |
| - Persentase penduduk miskin |
| |
+--------------------------------------+
|
v
+--------------------------------------+
| ENVIRONMENT |
| - Persentase sanitasi layak |
| - Kondisi permukiman |
| - Akses layanan kesehatan |
+--------------------------------------+
|
v
KEJADIAN KUSTA
(Jumlah kasus / Prevalensi)
Peta persebaran kasus kusta di Provinsi Jawa Barat menunjukkan adanya variasi spasial yang cukup jelas antar kabupaten/kota. Wilayah dengan warna lebih terang (kuning–hijau muda) merepresentasikan jumlah kasus kusta yang relatif lebih tinggi dibandingkan wilayah lain, sementara wilayah dengan warna lebih gelap (ungu tua) menunjukkan jumlah kasus yang lebih rendah. Pola ini mengindikasikan bahwa kasus kusta tidak terdistribusi secara merata di seluruh wilayah Jawa Barat, melainkan cenderung terkonsentrasi pada beberapa kabupaten/kota tertentu. Secara geografis, terlihat bahwa beberapa wilayah di bagian utara dan timur Jawa Barat memiliki jumlah kasus yang lebih tinggi dibandingkan wilayah selatan, yang relatif didominasi oleh jumlah kasus lebih rendah.
Perbedaan pola persebaran ini dapat diinterpretasikan melalui kerangka agent–host–environment. Wilayah dengan jumlah kasus yang lebih tinggi umumnya merupakan daerah dengan kepadatan penduduk relatif tinggi, aktivitas sosial dan mobilitas penduduk yang intens, serta variasi kondisi sanitasi dan akses layanan kesehatan. Kondisi tersebut dapat meningkatkan peluang terjadinya kontak antarindividu dan memperbesar risiko penularan penyakit menular kronis seperti kusta. Sebaliknya, wilayah dengan jumlah kasus lebih rendah cenderung memiliki kepadatan penduduk yang lebih rendah atau karakteristik lingkungan yang kurang mendukung terjadinya transmisi. Temuan ini menunjukkan bahwa faktor spasial dan karakteristik wilayah berperan penting dalam menentukan distribusi kasus kusta, sehingga pemetaan spasial menjadi langkah awal yang krusial sebelum dilakukan analisis lanjutan, seperti identifikasi klaster dan pemodelan spasial.
Berdasarkan hasil perhitungan ukuran epidemiologi menggunakan prevalensi per 100.000 penduduk, terlihat adanya perbedaan yang sangat mencolok antar kabupaten/kota di Provinsi Jawa Barat. Dari sepuluh wilayah contoh yang dianalisis, nilai prevalensi kusta bervariasi mulai dari 0 hingga lebih dari 10.000 per 100.000 penduduk. Wilayah dengan prevalensi tertinggi tercatat di Kabupaten Indramayu, dengan nilai sebesar 10.476,69 per 100.000 penduduk, menunjukkan beban penyakit yang relatif tinggi dibandingkan wilayah lain. Sebaliknya, Kabupaten Ciamis menunjukkan nilai prevalensi nol, yang mengindikasikan tidak ditemukannya kasus kusta pada periode pengamatan. Variasi ini menegaskan bahwa kejadian kusta tidak bersifat homogen, melainkan sangat dipengaruhi oleh karakteristik wilayah masing-masing.
Perbedaan tingkat prevalensi tersebut dapat diinterpretasikan melalui pendekatan epidemiologi berbasis agent–host–environment. Tingginya prevalensi di wilayah seperti Indramayu dapat mencerminkan kombinasi faktor host dan lingkungan, seperti kepadatan penduduk, kondisi sosial ekonomi, serta variasi akses terhadap layanan kesehatan dan sanitasi. Nilai prevalensi yang tinggi juga dapat mengindikasikan masih berlangsungnya transmisi penyakit atau keberadaan kasus lama yang belum sepenuhnya tertangani, mengingat kusta merupakan penyakit kronis dengan described durasi panjang. Sebaliknya, nilai prevalensi nol di Ciamis dapat disebabkan oleh rendahnya jumlah kasus, keberhasilan program pengendalian, atau kemungkinan keterbatasan deteksi kasus. Oleh karena itu, interpretasi prevalensi perlu dilakukan secara hati-hati dan dikaitkan dengan kondisi wilayah serta hasil analisis spasial, sehingga dapat memberikan gambaran yang lebih komprehensif mengenai beban dan distribusi kusta di Jawa Barat.
Peta prevalensi kusta per 100.000 penduduk menunjukkan adanya pola spasial yang tidak merata antar kabupaten/kota di Provinsi Jawa Barat, di mana wilayah dengan warna lebih gelap merepresentasikan tingkat prevalensi yang lebih tinggi dibandingkan wilayah lainnya. Terlihat bahwa beberapa wilayah, khususnya di bagian utara dan timur Jawa Barat, memiliki prevalensi kusta yang relatif tinggi, sementara sebagian besar wilayah selatan didominasi oleh prevalensi rendah hingga nol. Pola ini mengindikasikan bahwa beban kusta tidak semata-mata ditentukan oleh jumlah kasus absolut, melainkan oleh rasio kasus terhadap jumlah penduduk. Secara epidemiologis, tingginya prevalensi pada wilayah tertentu dapat mencerminkan kombinasi faktor host dan environment, seperti kondisi sosial ekonomi, kepadatan penduduk, serta kualitas sanitasi dan akses layanan kesehatan, yang memungkinkan penyakit bertahan dalam populasi. Temuan ini menegaskan pentingnya analisis spasial berbasis prevalensi untuk mengidentifikasi wilayah prioritas dalam upaya pengendalian kusta.
Model OLS menghasilkan pola prediksi kasus kusta yang masih terfragmentasi dan kurang mencerminkan keterkaitan antarwilayah. Beberapa wilayah bahkan memperlihatkan nilai prediksi yang ekstrem atau tidak realistis, yang mengindikasikan bahwa model ini belum mampu menangkap ketergantungan spasial yang memang terdapat pada data kasus kusta. Dengan kata lain, OLS cenderung melihat setiap wilayah secara terpisah tanpa mempertimbangkan pengaruh wilayah di sekitarnya.
Ketika komponen spasial mulai dimasukkan melalui model SLX, SAR, dan SEM, pola prediksi menjadi lebih halus dan menunjukkan kesinambungan antarwilayah. Model SLX mulai memperhitungkan pengaruh variabel dari wilayah tetangga, sementara SAR dan SEM secara lebih eksplisit menangkap ketergantungan spasial baik pada nilai kasus maupun pada komponen galat. Hasilnya, prediksi yang dihasilkan lebih stabil dan pola pengelompokan wilayah dengan nilai kasus tinggi atau rendah menjadi lebih jelas.
Model yang lebih komprehensif seperti SDM, SDEM, dan SAC menghasilkan pola prediksi yang paling konsisten secara spasial. Wilayah dengan prediksi kasus kusta tinggi cenderung membentuk klaster yang selaras dengan kondisi wilayah sekitarnya, sesuai dengan temuan autokorelasi spasial global dan lokal sebelumnya. Model-model ini mampu mengurangi distorsi lokal dan menghasilkan transisi nilai prediksi yang lebih realistis secara geografis.
Secara keseluruhan, perbandingan ini menegaskan bahwa model regresi spasial memberikan gambaran persebaran kasus kusta yang lebih representatif dibandingkan OLS. Keberadaan pengaruh spasial antarwilayah menjadikan model spasial lebih relevan untuk analisis epidemiologi wilayah dan sebagai dasar perumusan kebijakan pengendalian kusta berbasis area prioritas.
Tabel perbandingan kinerja model menunjukkan bahwa model SEM memiliki nilai AIC terendah (275,39), yang menandakan kecocokan model terbaik secara keseluruhan dengan mempertimbangkan kompleksitas dan goodness of fit. Sementara itu, berdasarkan ukuran ketepatan prediksi, model SDM dan SDEM menunjukkan performa yang lebih baik dengan nilai RMSE dan MAE yang lebih kecil dibandingkan model lainnya, sehingga menghasilkan prediksi yang lebih akurat. Sebaliknya, model OLS dan SLX memiliki nilai AIC, RMSE, dan MAE yang paling tinggi, yang mengindikasikan kinerja paling lemah dan menegaskan bahwa model tanpa atau dengan keterbatasan komponen spasial kurang mampu merepresentasikan pola kasus kusta. Secara keseluruhan, hasil ini menunjukkan bahwa pemilihan model terbaik bergantung pada tujuan analisis, di mana SEM lebih unggul untuk pemodelan struktural, sedangkan SDM atau SDEM lebih sesuai untuk keperluan prediksi spasial kasus kusta.
Berdasarkan hasil analisis epidemiologi terhadap kasus kusta di tingkat kabupaten/kota di Provinsi Jawa Barat, dapat disimpulkan bahwa kejadian kusta menunjukkan variasi spasial yang jelas antarwilayah. Penggunaan ukuran epidemiologi berupa prevalensi per 100.000 penduduk memberikan gambaran yang lebih informatif mengenai beban penyakit dibandingkan jumlah kasus absolut. Pemetaan prevalensi menunjukkan adanya wilayah dengan tingkat prevalensi relatif tinggi, yang mengindikasikan masih berlangsungnya transmisi atau akumulasi kasus kusta dalam populasi, sementara wilayah lain menunjukkan prevalensi rendah hingga nol. Temuan ini menegaskan bahwa kejadian kusta dipengaruhi oleh interaksi faktor agent–host–environment, khususnya karakteristik demografi, sosial ekonomi, dan kondisi lingkungan wilayah, sehingga analisis spasial menjadi pendekatan yang penting dalam memahami distribusi penyakit.
Berdasarkan kesimpulan tersebut, disarankan agar upaya pengendalian kusta difokuskan pada wilayah dengan prevalensi tinggi melalui penguatan deteksi dini, peningkatan akses layanan kesehatan, serta perbaikan kondisi lingkungan dan sanitasi. Selain itu, intervensi berbasis edukasi kesehatan dan peningkatan kualitas sosial ekonomi masyarakat perlu menjadi bagian dari strategi pengendalian jangka panjang. Untuk penelitian selanjutnya, disarankan penggunaan data longitudinal dan variabel lingkungan yang lebih rinci, serta penerapan metode pemodelan spasial atau spasiotemporal yang lebih lanjut guna memperoleh pemahaman yang lebih komprehensif mengenai dinamika penyebaran kusta dan efektivitas program pengendaliannya.
[1] World Health Organization. Global leprosy (Hansen disease) update. Geneva: WHO; 2021.
[2] Scollard DM, Adams LB, Gillis TP, Krahenbuhl JL, Truman RW, Williams DL. The continuing challenges of leprosy. Clin Microbiol Rev. 2006;19(2):338–381.
[3] Smith WC, van Brakel W, Gillis T, Saunderson P, Richardus JH. The missing millions: a threat to the elimination of leprosy. PLoS Negl Trop Dis. 2015;9(4):e0003658.
[4] Weiss MG, Ramakrishna J, Somma D. Health-related stigma: rethinking concepts and interventions. Psychol Health Med. 2006;11(3):277–287.
[5] Gordis L. Epidemiology. 5th ed. Philadelphia: Elsevier Saunders; 2014.
[6] Fine PE. Leprosy: the epidemiology of a slow bacterium. Epidemiol Rev. 1982;4:161–188.
[7] Kerr-Pontes LR, Barreto ML, Evangelista CMN, Rodrigues LC, Heukelbach J, Feldmeier H. Socioeconomic, environmental, and behavioural risk factors for leprosy in north-east Brazil. Int J Epidemiol. 2006;35(4):994–1000.
[8] Moet FJ, Meima A, Oskam L, Richardus JH. Risk factors for the development of clinical leprosy among contacts. Am J Epidemiol. 2004;159(7):599–608.
[9] Kementerian Kesehatan Republik Indonesia. Profil Kesehatan Indonesia Tahun 2021. Jakarta: Kemenkes RI; 2022.
[10] Kementerian Kesehatan Republik Indonesia. Pedoman Program Pengendalian Penyakit Kusta. Jakarta: Kemenkes RI; 2020.
[11] Badan Pusat Statistik. Provinsi Jawa Barat dalam Angka 2022. Bandung: BPS Jawa Barat; 2022.
[12] Anselin L. Spatial econometrics: methods and models. Dordrecht: Kluwer Academic Publishers; 1988.
[13] United Nations. Transforming our world: the 2030 Agenda for Sustainable Development. New York: United Nations; 2015.
[14] Marmot M, Wilkinson RG. Social Determinants of Health. 2nd ed. Oxford: Oxford University Press; 2006.
[15] Prüss-Ustün A, Bartram J, Clasen T, et al. Burden of disease from inadequate water, sanitation and hygiene. Trop Med Int Health. 2014;19(8):894–905.
[16] Friis, R. H., & Sellers, T. A. (2021). Epidemiology for Public Health Practice (6th ed.). Burlington, MA: Jones & Bartlett Learning.
[17] Sari, R. P., & Putri, D. (2022). Analisis faktor sosial ekonomi terhadap kejadian Tuberkulosis di Indonesia. Jurnal Kesehatan Masyarakat Indonesia, 17(2), 145–156.
[18] Hennekens, C. H., & Buring, J. E. (2019). Epidemiology in Medicine. Philadelphia: Lippincott Williams & Wilkins.
[19] World Health Organization. (2021). Mycobacterium tuberculosis: Key Facts. Geneva: WHO.
[20] MacIntyre, C. R., & Kendig, N. (2018). The epidemiology of tuberculosis. In Tuberculosis: A Comprehensive Clinical Reference (pp. 1–16). Philadelphia: Elsevier.
[21] Niu, C., et al. (2020). Environmental determinants of tuberculosis transmission: A systematic review. International Journal of Environmental Research and Public Health, 17(9), 3354.
[22] Rothman, K. J., Greenland, S., & Lash, T. L. (2020). Modern Epidemiology (4th ed.). Philadelphia: Wolters Kluwer.
# ============================================================
# DASHBOARD INTERAKTIF ANALISIS SPASIAL KUSTA JAWA BARAT 2021
# FINAL UPGRADE — INTERAKTIF + MODEL LENGKAP
# ============================================================
options(shiny.maxRequestSize = 500*1024^2)
library(shiny)
library(bslib)
library(sf)
library(dplyr)
library(stringr)
library(readxl)
library(leaflet)
library(spdep)
library(spatialreg)
library(DT)
library(sp)
library(gstat)
library(terra)
library(plotly)
library(shinycssloaders)
# ============================================================
# THEME
# ============================================================
theme_app <- bs_theme(
version = 5,
bootswatch = "flatly",
primary = "#1565C0",
base_font = font_google("Inter")
)
# ============================================================
# HELPER FUNCTIONS
# ============================================================
clean_kabkota <- function(x){
x <- tolower(as.character(x))
x <- str_replace_all(x, "[^a-z\\s]", " ")
x <- str_squish(x)
x <- str_replace(x, "^kabupaten\\s+", "")
x <- str_replace(x, "^kota\\s+", "") # ⬅️ INI KUNCI CIMAHI
x
}
safe_num <- function(x){
suppressWarnings(as.numeric(x))
}
make_listw <- function(sfobj){
nb <- poly2nb(sfobj, queen = TRUE)
nb2listw(nb, style="W", zero.policy=TRUE)
}
add_slx_terms <- function(df, lw){
Wx <- as.data.frame(lapply(df[,c("x1","x2","x3","x4","x5")],
function(v) lag.listw(lw, v, zero.policy=TRUE)))
names(Wx) <- paste0("W_", names(Wx))
cbind(df, Wx)
}
safe_fit <- function(expr){
tryCatch(expr, error = function(e) e)
}
is_err <- function(x) inherits(x, "error")
rmse <- function(y, yhat) {
sqrt(mean((y - yhat)^2, na.rm = TRUE))
}
mae <- function(y, yhat) {
mean(abs(y - yhat), na.rm = TRUE)
}
# ============================================================
# UI
# ============================================================
ui <- fluidPage(
theme = theme_app,
titlePanel("🧭 Dashboard Interaktif Analisis Spasial KUSTA Jawa Barat 2021"),
tags$head(
tags$style(HTML("
#box_model pre {
white-space: pre !important;
overflow-x: auto !important;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono','Courier New', monospace;
font-size: 12px;
line-height: 1.25;
margin: 0;
}
"))
),
sidebarLayout(
sidebarPanel(
fileInput("shp", "Upload Shapefile (gadm41_IDN_2.zip)", accept=".zip"),
fileInput("xls", "Upload Excel Kusta 2021", accept=".xlsx"),
hr(),
selectInput(
"menu", "Pilih Analisis",
choices = c(
"Eksplorasi – Peta Kasus",
"Eksplorasi – Statistik Deskriptif",
"Autokorelasi – Moran & Geary",
"Autokorelasi – LISA",
"Interpolasi – IDW"
)
),
numericInput("alpha", "Alpha LISA", 0.05),
sliderInput("idw_p", "Power IDW", 1, 5, 2, step = 0.5),
selectInput(
"interp_method",
"Metode Interpolasi",
choices = c(
"IDW" = "idw",
"Kriging" = "kriging"
)
),
actionButton("run", "Jalankan Analisis", class="btn-primary")
),
mainPanel(
tabsetPanel(
tabPanel(
"🗺️ Peta Interaktif",
leafletOutput("map", height = 650)
),
tabPanel(
"📊 Tabel & Statistik",
DTOutput("table")
),
tabPanel(
"🧠 Output Analisis",
div(
style = "
height: 500px;
overflow-y: auto;
background-color: #f8f9fa;
padding: 12px;
border-radius: 6px;
border: 1px solid #ddd;
font-size: 13px;
white-space: pre;
",
verbatimTextOutput("model_txt")
)
),
tabPanel(
"📊 Ukuran Epidemiologi",
fluidRow(
column(
12,
h4("📌 Incidence Rate Kusta per 100.000 Penduduk"),
p("Incidence rate dihitung sebagai jumlah kasus kusta dibagi jumlah penduduk, dikalikan 100.000.")
)
),
hr(),
fluidRow(
column(
6,
leafletOutput("map_ir", height = 450)
),
column(
6,
DTOutput("table_ir")
)
),
hr(),
fluidRow(
column(
12,
h4("📈 Ringkasan Statistik Incidence Rate"),
verbatimTextOutput("ir_summary")
)
)
),
tabPanel(
"📐 Model Spasial",
## =========================
## 1️⃣ KONTROL
## =========================
fluidRow(
column(
6,
selectInput(
"model_choice",
"Pilih Model Spasial",
choices = c("OLS","SAR","SEM","SLX","SDM","SDEM","SAC")
)
),
column(
6,
radioButtons(
"map_type",
"Jenis Peta",
choices = c(
"Prediksi Model" = "fitted",
"Residual Model" = "residual"
),
inline = TRUE
)
)
),
br(),
## =========================
## 2️⃣ PETA (KIRI) + MODEL (KANAN)
## =========================
fluidRow(
column(
width = 6,
leafletOutput("map_model", height = 520)
),
column(
width = 6,
div(
id = "box_model",
style = "
height:520px;
overflow:auto;
background:#f8f9fa;
padding:12px;
border:1px solid #ddd;
border-radius:6px;
white-space: pre;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono','Courier New', monospace;
font-size:12px;
",
verbatimTextOutput("model_spatial_txt")
)
)
)
),
tabPanel(
"📊 Perbandingan Model",
h4("Perbandingan Model Spasial"),
hr(),
# =========================
# 1️⃣ TABEL PERBANDINGAN
# =========================
div(
style = "
height:300px;
overflow:auto;
background:#f8f9fa;
padding:12px;
border:1px solid #ddd;
border-radius:6px;
white-space: pre;
",
verbatimTextOutput("model_compare_txt")
),
br(),
h4("🏆 Model Spasial Terbaik"),
hr(),
# =========================
# 2️⃣ PETA + RINGKASAN MODEL TERBAIK
# =========================
fluidRow(
column(
6,
leafletOutput("map_best_model", height = 450)
),
column(
6,
div(
style = "
height:450px;
overflow:auto;
background:#f8f9fa;
padding:12px;
border:1px solid #ddd;
border-radius:6px;
white-space: pre;
",
verbatimTextOutput("best_model_txt")
)
)
)
)
)
)
)
)
# ============================================================
# SERVER
# ============================================================
server <- function(input, output){
# ---------------- SHAPEFILE ----------------
shp_sf <- eventReactive(input$run, {
req(input$shp)
tmp <- tempdir()
unzip(input$shp$datapath, exdir = tmp)
shpfile <- list.files(tmp, "\\.shp$", full.names = TRUE)[1]
sf <- st_read(shpfile, quiet = TRUE)
sf <- sf %>%
filter(
NAME_1 == "Jawa Barat",
!NAME_2 %in% c("Waduk Cirata")
)
sf$key <- clean_kabkota(sf$NAME_2)
sf
})
# ---------------- EXCEL ----------------
excel_df <- eventReactive(input$run, {
req(input$xls)
df <- read_excel(input$xls$datapath)
df <- df %>%
rename(
Kabupaten_Kota = nama_kabupaten_kota,
y = jumlah_kasus,
x1 = jumlah_penduduk,
x2 = persentase_sanitasi_layak,
x3 = kepadatan_penduduk,
x4 = rata_rata_lama_sekolah,
x5 = persentase_penduduk_miskin
)
for(v in c("y","x1","x2","x3","x4","x5"))
df[[v]] <- safe_num(df[[v]])
df$key <- clean_kabkota(df$Kabupaten_Kota)
df
})
# ---------------- JOIN ----------------
data_sf <- reactive({
req(shp_sf(), excel_df())
sf_join <- left_join(shp_sf(), excel_df(), by = "key")
sf_join$y[is.na(sf_join$y)] <- 0
# 🔴 DISSOLVE GEOMETRY
sf_final <- sf_join %>%
group_by(key, NAME_2) %>%
summarise(
y = mean(y, na.rm = TRUE),
x1 = mean(x1, na.rm = TRUE),
x2 = mean(x2, na.rm = TRUE),
x3 = mean(x3, na.rm = TRUE),
x4 = mean(x4, na.rm = TRUE),
x5 = mean(x5, na.rm = TRUE),
incidence_rate = ifelse(
x1 > 0,
(y / x1) * 100000,
NA_real_
),
geometry = st_union(geometry),
.groups = "drop"
)
sf_final
})
# ============================================================
# INTERPOLASI (IDW + KRIGING) - WAJIB CRS METER (UTM)
# TARUH: tepat setelah data_sf <- reactive({ ... })
# ============================================================
utm_crs <- 32748 # UTM 48S (cukup aman untuk Jabar)
pts_utm <- reactive({
req(data_sf())
st_centroid(data_sf()) |>
st_transform(utm_crs) |>
mutate(z = as.numeric(y))
})
grid_utm <- reactive({
req(data_sf())
poly_utm <- st_transform(st_union(data_sf()), utm_crs)
# Grid lebih ringan: 5km. Kalau mau halus: 2km (lebih berat)
grid <- st_make_grid(poly_utm, cellsize = 5000, what = "centers")
grid_sf <- st_sf(geometry = grid)
# MASK: hanya titik grid yang berada di dalam polygon Jabar
st_intersection(grid_sf, poly_utm)
})
idw_utm <- reactive({
req(pts_utm(), grid_utm())
gstat::idw(
z ~ 1,
locations = as(pts_utm(), "Spatial"),
newdata = as(grid_utm(), "Spatial"),
idp = input$idw_p
)
})
kriging_utm <- reactive({
req(pts_utm(), grid_utm())
pts_sp <- as(pts_utm(), "Spatial")
grid_sp <- as(grid_utm(), "Spatial")
vgm_emp <- variogram(z ~ 1, pts_sp)
vgm_fit <- fit.variogram(vgm_emp, vgm("Sph"))
krige(z ~ 1, locations = pts_sp, newdata = grid_sp, model = vgm_fit)
})
data_desc <- reactive({
data_sf() %>%
st_drop_geometry() %>%
select(
NAME_2, # nama kab/kota
y, x1, x2, x3, x4, x5
)
})
stat_desc <- reactive({
df <- data_desc()
vars <- c("y", "x1", "x2", "x3", "x4", "x5")
var_names <- c(
"Kasus Kusta",
"Jumlah Penduduk",
"Sanitasi Layak (%)",
"Kepadatan Penduduk",
"Rata-rata Lama Sekolah (tahun)",
"Penduduk Miskin (%)"
)
result <- lapply(seq_along(vars), function(i){
v <- vars[i]
x <- df[[v]]
min_val <- min(x, na.rm = TRUE)
max_val <- max(x, na.rm = TRUE)
min_loc <- df$NAME_2[which.min(x)][1]
max_loc <- df$NAME_2[which.max(x)][1]
data.frame(
Variabel = var_names[i],
Minimum = round(min_val, 2),
Lokasi_Minimum = min_loc,
Maksimum = round(max_val, 2),
Lokasi_Maksimum = max_loc,
Rata_rata = round(mean(x, na.rm = TRUE), 2),
Median = round(median(x, na.rm = TRUE), 2),
Simpangan_Baku = round(sd(x, na.rm = TRUE), 2)
)
})
do.call(rbind, result)
})
# ---------------- LISTW ----------------
listw <- reactive({
make_listw(data_sf())
})
moran_geary <- reactive({
req(data_sf(), listw())
df <- st_drop_geometry(data_sf())
lw <- listw()
list(
moran = moran.test(df$y, lw, zero.policy = TRUE),
geary = geary.test(df$y, lw, zero.policy = TRUE)
)
})
lisa_result <- reactive({
req(data_sf(), listw())
df <- st_drop_geometry(data_sf())
lw <- listw()
lm <- localmoran(df$y, lw, zero.policy = TRUE)
# localmoran biasanya punya 5 kolom:
# Ii, E.Ii, Var.Ii, Z.Ii, Pr(z > 0)
# Kita ambil by index agar tidak tergantung nama kolom
out <- data.frame(
NAME_2 = df$NAME_2,
Ii = lm[, 1],
E_Ii = lm[, 2],
Var_Ii = lm[, 3],
Z_Ii = lm[, 4],
P_Ii = lm[, 5]
)
out
})
# ============================================================
# DATA TITIK UNTUK INTERPOLASI (CENTROID)
# ============================================================
data_point <- reactive({
req(data_sf())
st_centroid(data_sf()) |>
st_transform(4326)
})
grid_pixel <- reactive({
req(data_point())
# buat grid pixel (bukan titik)
grd <- st_make_grid(
data_point(),
cellsize = 0.15, # ukuran pixel (atur nanti)
what = "polygons"
)
st_sf(geometry = grd)
})
grid_sp <- reactive({
req(grid_pixel())
as(grid_pixel(), "Spatial")
})
points_sp <- reactive({
req(data_point())
as(data_point(), "Spatial")
})
idw_surface <- reactive({
req(points_sp(), grid_sp())
gstat::idw(
formula = y ~ 1,
locations = points_sp(),
newdata = grid_sp(),
idp = input$idw_p
)
})
kriging_surface <- reactive({
req(points_sp(), grid_sp())
vgm_emp <- variogram(y ~ 1, points_sp())
vgm_fit <- fit.variogram(vgm_emp, vgm("Sph"))
krige(
y ~ 1,
locations = points_sp(),
newdata = grid_sp(),
model = vgm_fit
)
})
interp_raster <- reactive({
req(input$interp_method)
surf <- if (input$interp_method == "idw") {
idw_surface()
} else {
kriging_surface()
}
raster::raster(surf["var1.pred"])
})
interp_masked <- reactive({
req(interp_raster(), data_sf())
poly_sp <- as(st_union(data_sf()), "Spatial")
raster::mask(interp_raster(), poly_sp)
})
# ============================================================
# GRID INTERPOLASI
# ============================================================
grid_interp <- reactive({
req(data_point())
grid <- st_make_grid(
data_point(),
n = c(60, 60),
what = "centers"
)
st_sf(geometry = grid)
})
# ============================================================
# GRID INTERPOLASI (DI-POTONG POLYGON JAWA BARAT)
# ============================================================
grid_masked <- reactive({
req(grid_interp(), data_sf())
st_intersection(
grid_interp(),
st_union(data_sf())
)
})
# ============================================================
# INTERPOLASI IDW (SF + GSTAT)
# ============================================================
idw_result <- reactive({
req(data_point(), grid_interp())
gstat::idw(
y ~ 1,
locations = data_point(),
newdata = grid_interp(),
idp = input$idw_p
)
})
# ============================================================
# INTERPOLASI KRIGING
# ============================================================
kriging_result <- reactive({
req(data_point(), grid_interp())
vgm_emp <- variogram(y ~ 1, data_point())
vgm_fit <- fit.variogram(vgm_emp, vgm("Sph"))
krige(
y ~ 1,
locations = data_point(),
newdata = grid_interp(),
model = vgm_fit
)
})
# ============================================================
# MODEL FIT (SEMUA SEKALIGUS, SEKALI HITUNG)
# ============================================================
model_pack <- reactive({
df <- st_drop_geometry(data_sf())
lw <- listw()
f <- y ~ x1 + x2 + x3 + x4 + x5
models <- list(
OLS = lm(f, df),
SAR = lagsarlm(f, df, lw),
SEM = errorsarlm(f, df, lw),
SLX = lm(y ~ x1+x2+x3+x4+x5 + W_x1+W_x2+W_x3+W_x4+W_x5, add_slx_terms(df,lw)),
SDM = lagsarlm(f, df, lw, type="mixed"),
SDEM = errorsarlm(f, df, lw, Durbin=TRUE),
SAC = sacsarlm(f, df, lw)
)
metrics <- lapply(models, function(m){
res <- residuals(m)
data.frame(
AIC = AIC(m),
RMSE = sqrt(mean(res^2)),
MAE = mean(abs(res))
)
})
comp <- do.call(rbind, metrics)
comp$Model <- rownames(comp)
comp <- comp[order(comp$AIC), ]
list(models=models, comp=comp, best=comp$Model[1])
})
# ============================================================
# OUTPUT MODEL TERBAIK (TEKS)
# ============================================================
output$model_compare_txt <- renderPrint({
comp <- model_pack()$comp
print(comp, row.names = FALSE)
})
output$best_model_txt <- renderPrint({
best <- model_pack()$best
cat("MODEL TERBAIK:", best, "\n")
cat("========================\n\n")
print(summary(model_pack()$models[[best]]))
})
# ============================================================
# MAP MODEL: PREDIKSI & RESIDUAL
# ============================================================
output$map_model <- renderLeaflet({
req(data_sf(), model_pack(), input$model_choice, input$map_type)
sfm <- st_transform(data_sf(), 4326)
model <- model_pack()$models[[input$model_choice]]
if (inherits(model, "error")) {
return(leaflet() %>% addProviderTiles("CartoDB.Positron"))
}
# =========================
# AMBIL FITTED / RESIDUAL
# =========================
if (input$map_type == "fitted") {
sfm$value <- as.numeric(fitted(model))
title_legend <- "Prediksi Kasus Kusta"
pal <- colorNumeric("viridis", sfm$value, na.color = "#f0f0f0")
} else {
sfm$value <- as.numeric(residuals(model))
title_legend <- "Residual Model"
pal <- colorNumeric("RdBu", sfm$value, reverse = TRUE, na.color = "#f0f0f0")
}
# =========================
# LEAFLET MAP
# =========================
leaflet(sfm) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(
fillColor = ~pal(value),
fillOpacity = 0.85,
color = "#333333",
weight = 1,
popup = ~paste0(
"<b>", NAME_2, "</b><br/>",
title_legend, ": ", round(value, 2)
),
highlightOptions = highlightOptions(
weight = 3,
color = "#1565C0",
bringToFront = TRUE
)
) %>%
addLegend(
pal = pal,
values = ~value,
title = title_legend,
position = "bottomright"
)
})
# ============================================================
# MAP INCIDENCE RATE
# ============================================================
output$map_ir <- renderLeaflet({
req(data_sf())
sfm <- st_transform(data_sf(), 4326)
pal <- colorNumeric(
"YlOrRd",
sfm$incidence_rate,
na.color = "#f0f0f0"
)
leaflet(sfm) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(
fillColor = ~pal(incidence_rate),
fillOpacity = 0.85,
color = "#333",
popup = ~paste0(
"<b>", NAME_2, "</b><br/>",
"IR: ", round(incidence_rate, 2),
" per 100.000"
)
) %>%
addLegend(
pal = pal,
values = ~incidence_rate,
title = "Incidence Rate /100.000",
position = "bottomright"
)
})
# ============================================================
# OUTPUT MODEL SPASIAL (TAB 📐 MODEL SPASIAL)
# ============================================================
output$model_spatial_txt <- renderPrint({
req(input$model_choice, model_pack())
models <- model_pack()$models
mname <- input$model_choice
model <- models[[mname]]
cat("MODEL SPASIAL:", mname, "\n")
cat("====================================\n\n")
if (inherits(model, "error")) {
cat("⚠️ Model gagal diestimasi\n\n")
cat("Pesan error:\n")
cat(model$message)
} else {
print(summary(model))
}
})
# ============================================================
# LEAFLET MAP
# ============================================================
output$map <- renderLeaflet({
req(data_sf(), input$menu)
sfm <- st_transform(data_sf(), 4326)
# ============================================================
# PETA MODEL TERBAIK
# ============================================================
output$map_best_model <- renderLeaflet({
best <- model_pack()$best
model <- model_pack()$models[[best]]
sfm <- data_sf()
sfm$val <- fitted(model)
pal <- colorNumeric("viridis", sfm$val)
leaflet(sfm) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(
fillColor = ~pal(val),
fillOpacity = 0.85,
color = "#333",
popup = ~paste0(NAME_2, "<br>Prediksi: ", round(val,2))
) %>%
addLegend(pal = pal, values = ~val, title = "Prediksi Model Terbaik")
})
# =====================================================
# 1️⃣ EKSPLORASI – PETA KASUS (KODE KAMU, TETAP)
# =====================================================
if (input$menu == "Eksplorasi – Peta Kasus") {
pal <- colorNumeric(
palette = "viridis",
domain = sfm$y,
na.color = "#f0f0f0"
)
popup_info <- sprintf(
"<div style='font-size:14px; line-height:1.6'>
<h4 style='margin-bottom:6px; color:#1565C0'>%s</h4>
<b>🦠 Kasus Kusta:</b> %s<br/>
<hr style='margin:6px 0'/>
<b>👥 Jumlah Penduduk:</b> %s<br/>
<b>🚿 Sanitasi Layak (%%):</b> %s<br/>
<b>🏘️ Kepadatan Penduduk:</b> %s<br/>
<b>🎓 Rata-rata Lama Sekolah (tahun):</b> %s<br/>
<b>💸 Penduduk Miskin (%%):</b> %s
</div>",
sfm$NAME_2,
formatC(sfm$y, format="d", big.mark="."),
formatC(sfm$x1, format="f", digits=2, big.mark=".", decimal.mark=","),
formatC(sfm$x2, format="f", digits=2, decimal.mark=","),
formatC(sfm$x3, format="f", digits=0, big.mark="."),
formatC(sfm$x4, format="f", digits=2, decimal.mark=","),
formatC(sfm$x5, format="f", digits=2, decimal.mark=",")
)
leaflet(sfm) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(
fillColor = ~pal(y),
fillOpacity = 0.85,
color = "#333333",
weight = 1,
label = ~paste0("<b>", NAME_2, "</b><br/>Kasus: ", y) %>%
lapply(htmltools::HTML),
popup = lapply(popup_info, htmltools::HTML),
highlightOptions = highlightOptions(
weight = 3,
color = "#1565C0",
bringToFront = TRUE
)
) %>%
addLegend(
pal = pal,
values = ~y,
title = "Kasus Kusta",
position = "bottomright"
)
}
# =====================================================
# 2️⃣ AUTOKORELASI – MORAN & GEARY (BARU)
# =====================================================
else if (input$menu == "Autokorelasi – Moran & Geary") {
lw <- listw()
sfm$lag_y <- lag.listw(lw, sfm$y, zero.policy = TRUE)
pal <- colorNumeric(
palette = "RdYlBu",
domain = sfm$lag_y,
na.color = "#f0f0f0"
)
leaflet(sfm) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(
fillColor = ~pal(lag_y),
fillOpacity = 0.85,
color = "#333333",
weight = 1,
popup = ~paste0(
"<b>", NAME_2, "</b><br/>",
"Kasus: ", y, "<br/>",
"Spatial Lag: ", round(lag_y, 2)
),
highlightOptions = highlightOptions(
weight = 3,
color = "#D32F2F",
bringToFront = TRUE
)
) %>%
addLegend(
pal = pal,
values = ~lag_y,
title = "Spatial Lag Kasus Kusta",
position = "bottomright"
)
}
# =====================================================
# 3️⃣ AUTOKORELASI – LISA (LOCAL MORAN) → PETA SAJA
# =====================================================
else if (input$menu == "Autokorelasi – LISA") {
lw <- listw()
df <- st_drop_geometry(data_sf())
z_y <- as.numeric(scale(df$y))
lag_z <- lag.listw(lw, z_y, zero.policy = TRUE)
lisa <- localmoran(df$y, lw, zero.policy = TRUE)
sfm$cluster <- "Not Significant"
alpha <- input$alpha
sfm$cluster[z_y > 0 & lag_z > 0 & lisa[,5] < alpha] <- "High-High"
sfm$cluster[z_y < 0 & lag_z < 0 & lisa[,5] < alpha] <- "Low-Low"
sfm$cluster[z_y > 0 & lag_z < 0 & lisa[,5] < alpha] <- "High-Low"
sfm$cluster[z_y < 0 & lag_z > 0 & lisa[,5] < alpha] <- "Low-High"
pal <- colorFactor(
palette = c(
"High-High" = "#D7191C",
"Low-Low" = "#2C7BB6",
"High-Low" = "#FDAE61",
"Low-High" = "#ABD9E9",
"Not Significant" = "#EEEEEE"
),
levels = c(
"High-High","Low-Low","High-Low","Low-High","Not Significant"
)
)
leaflet(sfm) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(
fillColor = ~pal(cluster),
fillOpacity = 0.85,
color = "#333333",
weight = 1,
popup = ~paste0("<b>", NAME_2, "</b><br/>LISA: ", cluster),
highlightOptions = highlightOptions(
weight = 3,
bringToFront = TRUE
)
) %>%
addLegend(
pal = pal,
values = ~cluster,
title = "LISA Cluster",
position = "bottomright"
)
}
else if (input$menu == "Interpolasi – IDW") {
req(input$interp_method)
# --- pilih hasil ---
res <- NULL
title_map <- NULL
pred <- NULL
if (input$interp_method == "idw") {
res <- idw_utm()
pred <- res$var1.pred
title_map <- paste0("Interpolasi IDW (p=", input$idw_p, ")")
} else {
res <- kriging_utm()
pred <- res$var1.pred
title_map <- "Interpolasi Kriging"
}
# --- ubah hasil (sp) -> sf -> lonlat untuk leaflet ---
res_df <- as.data.frame(res)
xy <- sp::coordinates(res)
res_df$X <- xy[,1]
res_df$Y <- xy[,2]
res_sf <- st_as_sf(res_df, coords = c("X","Y"), crs = utm_crs) |>
st_transform(4326)
lonlat <- st_coordinates(res_sf)
res_sf$lon <- lonlat[,1]
res_sf$lat <- lonlat[,2]
pal <- colorNumeric("YlOrRd", domain = pred, na.color = "#f0f0f0")
leaflet(res_sf) %>%
addProviderTiles("CartoDB.Positron") %>%
addCircleMarkers(
lng = ~lon,
lat = ~lat,
radius = 4,
stroke = FALSE,
fillOpacity = 0.75,
fillColor = ~pal(pred),
popup = ~paste0(title_map, "<br>Pred: ", round(pred, 2))
) %>%
addLegend(
pal = pal,
values = ~pred,
title = title_map,
position = "bottomright"
)
}
})
# ============================================================
# TABLE
# ============================================================
output$table <- renderDT({
req(stat_desc())
datatable(
stat_desc(),
rownames = FALSE,
options = list(
pageLength = 6,
dom = "tip",
autoWidth = TRUE
)
)
})
# ============================================================
# TABLE INCIDENCE RATE
# ============================================================
output$table_ir <- renderDT({
req(data_sf())
df <- data_sf() |>
st_drop_geometry() |>
select(
Kabupaten_Kota = NAME_2,
Kasus = y,
Penduduk = x1,
Incidence_Rate = incidence_rate
) |>
mutate(
Incidence_Rate = round(Incidence_Rate, 2)
)
datatable(
df,
rownames = FALSE,
options = list(
pageLength = 10,
autoWidth = TRUE
)
)
})
# ============================================================
# PLOTLY GRAPH
# ============================================================
output$plot <- renderPlotly({
req(data_sf())
df <- st_drop_geometry(data_sf())
p <- ggplot(df, aes(x = x5, y = y)) +
geom_point(color = "#1565C0", size = 3) +
geom_smooth(method = "lm", se = FALSE, color = "red") +
theme_minimal() +
labs(x = "% Penduduk Miskin", y = "Kasus Kusta",
title = "Hubungan Kemiskinan dan Kasus Kusta")
ggplotly(p)
})
# ============================================================
# MODEL OUTPUT
# ============================================================
output$model_txt <- renderPrint({
req(input$menu)
if (input$menu == "Autokorelasi – Moran & Geary") {
mg <- moran_geary()
cat("AUTOKORELASI SPASIAL GLOBAL\n")
cat("----------------------------------\n")
cat("Moran's I:\n")
print(mg$moran)
cat("\nGeary's C:\n")
print(mg$geary)
} else if (input$menu == "Autokorelasi – LISA") {
lisa <- lisa_result()
alpha <- input$alpha
cat("LOCAL INDICATORS OF SPATIAL ASSOCIATION (LISA)\n")
cat("===============================================\n\n")
cat("Alpha =", alpha, "\n\n")
sig <- lisa$P_Ii < alpha # ✅ INI YANG BENAR
cat("Jumlah wilayah signifikan:\n")
print(table(Signifikan = sig))
cat("\nRingkasan p-value:\n")
print(summary(lisa$P_Ii))
}
})
# ============================================================
# SUMMARY INCIDENCE RATE
# ============================================================
output$ir_summary <- renderPrint({
req(data_sf())
ir <- data_sf()$incidence_rate
cat("INCIDENCE RATE KUSTA\n")
cat("=========================\n\n")
print(summary(ir))
cat("\nWilayah IR tertinggi:\n")
idx_max <- which.max(ir)
cat(data_sf()$NAME_2[idx_max],
"(", round(ir[idx_max], 2), ")\n")
cat("\nWilayah IR terendah:\n")
idx_min <- which.min(ir)
cat(data_sf()$NAME_2[idx_min],
"(", round(ir[idx_min], 2), ")\n")
})
# ============================================================
# DEBUG
# ============================================================
output$debug_txt <- renderPrint({
cat("DEBUG INFO\n")
cat("===========================\n\n")
cat("Jumlah baris (row):\n")
print(nrow(data_sf()))
cat("\nJumlah kab/kota unik:\n")
print(length(unique(data_sf()$NAME_2)))
cat("\nDaftar kab/kota:\n")
print(sort(unique(data_sf()$NAME_2)))
cat("\nKolom data:\n")
print(names(data_sf()))
})
}
shinyApp(ui, server)