# [SKRIP 5.1: Mengevaluasi ekspresi QGIS dari Python, termasuk agregasi]
# Penulis: Badar Mubarok Yogaswara
from qgis.core import (QgsProject, QgsVectorLayer, QgsExpression, QgsExpressionContext,
                       QgsExpressionContextUtils, QgsCoordinateReferenceSystem)

DATA = "D:/Latihan/paket-i4"
HASIL = "D:/Latihan/hasil_i4"

proyek = QgsProject.instance()
batas = QgsVectorLayer(DATA + "/Batas_KPH.gpkg", "Batas_KPH", "ogr")
hotspot = QgsVectorLayer(DATA + "/Hotspot_Nasional.gpkg", "Hotspot_Nasional", "ogr")
survei = QgsVectorLayer(DATA + "/Survei.gpkg", "Titik_Survei", "ogr")
for lyr in (batas, hotspot, survei):
    proyek.addMapLayer(lyr)         # fungsi aggregate() mencari layer lain berdasarkan NAMA di proyek


def konteks(layer, fitur):
    """Membuat 'lingkungan' tempat ekspresi dihitung: variabel proyek, layer, dan fitur saat ini."""
    c = QgsExpressionContext()
    c.appendScopes(QgsExpressionContextUtils.globalProjectLayerScopes(layer))
    c.setFeature(fitur)
    return c


def hitung(teks, layer, fitur):
    e = QgsExpression(teks)
    if e.hasParserError():                      # salah tulis ekspresi
        return "GALAT TULIS: " + e.parserErrorString()
    nilai = e.evaluate(konteks(layer, fitur))
    if e.hasEvalError():                        # salah saat dihitung
        return "GALAT HITUNG: " + e.evalErrorString()
    return nilai


# 1. Ekspresi dasar: rumus, teks, dan pilihan (CASE)
dasar = {
    "luas (ha)": "round($area / 10000, 2)",
    "nama huruf besar": 'upper("NAMA_KPH")',
    "ukuran": "CASE WHEN $area > 50000 THEN 'besar' ELSE 'kecil' END",
}
for fitur in batas.getFeatures():
    print(fitur["NAMA_KPH"], {k: hitung(v, batas, fitur) for k, v in dasar.items()})

# 2. Agregasi: menghitung dari LAYER LAIN. @parent = fitur KPH yang sedang dibaca.
titik_panas = ("aggregate(layer:='Hotspot_Nasional', aggregate:='count', expression:=\"ID_Titik\", "
               "filter:=intersects($geometry, geometry(@parent)))")
tinggi_rata = ("round(aggregate(layer:='Titik_Survei', aggregate:='mean', expression:=\"Tinggi_Phn\", "
               "filter:=intersects($geometry, geometry(@parent))), 1)")
print("--- agregasi per KPH ---")
for fitur in batas.getFeatures():
    print("%-10s titik panas = %s | tinggi pohon rata-rata = %s" % (
        fitur["NAMA_KPH"], hitung(titik_panas, batas, fitur), hitung(tinggi_rata, batas, fitur)))

# 3. Agregasi tanpa filter: satu angka untuk seluruh layer
fitur = next(batas.getFeatures())
total = hitung("aggregate('Hotspot_Nasional', 'count', \"ID_Titik\")", batas, fitur)
print("Total titik panas (semua KPH):", total)

# 4. Ekspresi yang salah dikenali dengan aman
print(hitung("round($area / 10000, ", batas, fitur))
print(hitung("to_int('abc') + 1", batas, fitur))

# 5. Dua cara lain menghitung titik: daftar dan gabungan teks
lain = {
    "jumlah lewat overlay": "array_length(overlay_intersects('Hotspot_Nasional', \"ID_Titik\"))",
    "daftar ID": ("aggregate('Hotspot_Nasional', 'concatenate', \"ID_Titik\", "
                  "filter:=intersects($geometry, geometry(@parent)), concatenator:=', ')"),
}
for fitur in batas.getFeatures():
    print(fitur["NAMA_KPH"], {k: hitung(v, batas, fitur) for k, v in lain.items()})

# 6. Awas: $area mengikuti pengaturan ellipsoid proyek, area($geometry) memakai bidang datar peta
fitur = next(batas.getFeatures())
print("Tanpa ellipsoid  :", hitung("$area", batas, fitur), hitung("area($geometry)", batas, fitur))
proyek.setCrs(QgsCoordinateReferenceSystem("EPSG:32749"))
proyek.setEllipsoid("EPSG:7030")                 # WGS 84, pengaturan umum pada proyek baru
print("Dengan ellipsoid :", round(hitung("$area", batas, fitur), 2), hitung("area($geometry)", batas, fitur))
