Lewati ke isi
Profil penulisSeri Buku GIS Kehutanan dan Pertanian/ PY

BAB 32: Dari QGIS ke ArcPy dan Kembali: Pola, Padanan, dan Cara Menerjemahkan

Studi kasus: "Skrip ini jalan di QGIS. Bisakah dibuat versi ArcGIS-nya?" #

Anda sudah punya skrip PyQGIS yang menghitung luas, sempadan sungai, dan titik panas per KPH. Kantor cabang meminta versi yang jalan di ArcGIS. Anda tidak perlu menulis dari nol. Pekerjaannya sama, hanya "bahasa alat"-nya yang berbeda. Kabar baiknya: setelah Bab 30 dan 31, Anda sudah punya kamus kecil di kepala. Bab ini merapikan kamus itu menjadi tabel, lalu mengajarkan cara menerjemahkan dengan langkah yang bisa diulang.

Konsep: menerjemahkan skrip dalam tiga kalimat #

Menerjemahkan skrip itu seperti menerjemahkan resep dari bahasa lain. Bahan dan urutan masaknya sama, tetapi nama bumbu dan alat dapurnya berbeda. Ada tiga lapis yang perlu Anda terjemahkan: gaya Python (Pro lawan ArcMap), nama alat (QGIS lawan ArcPy), dan cara menunjuk data (layer dalam proyek lawan jalur teks).

Ilustrasi 32.1: Tiga lapis beda saat menerjemahkan
Skema tiga lapis beda: gaya Python antara Pro dan ArcMap, nama alat antara QGIS dan ArcPy, dan cara menunjuk data antara layer dalam proyek dan jalur teks

Empat istilah baru yang perlu Anda kenal sekarang:

  • Padanan: dua penulisan di dua alat yang mengerjakan hal yang sama.
  • Alat (tool): satu pekerjaan siap pakai, misalnya penyangga. Di QGIS dipanggil lewat processing.run, di ArcGIS lewat arcpy.<kotak alat>.<Alat>.
  • Keluaran sementara: hasil antara yang tidak ditulis ke berkas. Di QGIS "TEMPORARY_OUTPUT", di ArcGIS memory\nama.
  • Angka acuan: hasil yang Anda ketahui benar dari data contoh, misalnya luas KPH Alpha 4,0 hektare. Angka ini dipakai untuk menguji terjemahan Anda.

Tabel padanan: membaca data dan atribut #

Tabel ini diambil dari Skrip 31.1 dan skrip PyQGIS b1_01 dan b1_02 (Bab 14). Kolom ArcPy memakai penulisan ArcGIS Pro.

Ilustrasi 32.2: Lima pasangan konsep QGIS dan ArcPy
Skema lima pasangan: layer vektor dan jalur teks, getFeatures dan SearchCursor, processing.run dan arcpy.analysis, TEMPORARY_OUTPUT dan memory, QgsProject dan ArcGISProject
PekerjaanPyQGISArcPy (Pro)
Membuka layer vektorQgsVectorLayer(DATA + "/Batas_KPH.gpkg", "Batas_KPH", "ogr")nama teks "Batas_KPH.shp" di folder kerja arcpy.env.workspace
Memeriksa data terbukalayer.isValid()arcpy.Exists(jalur)
Jumlah fiturlayer.featureCount()int(arcpy.management.GetCount(batas)[0])
Nama kolom[f.name() for f in layer.fields()][f.name for f in arcpy.ListFields(batas)]
Sistem koordinatlayer.crs().authid()arcpy.Describe(batas).spatialReference.factoryCode
Jenis geometriQgsWkbTypes.displayString(layer.wkbType())arcpy.Describe(batas).shapeType
Membaca tiap barisfor fitur in batas.getFeatures():with arcpy.da.SearchCursor(batas, [...]) as kursor:
Isi satu kolomfitur["NAMA_KPH"]satu isian di baris kursor
Luas satu fiturfitur.geometry().area()kolom khusus "SHAPE@AREA"

Dua hal yang mudah luput. Pertama, authid() mengembalikan teks EPSG:32749, sedangkan factoryCode mengembalikan angka 32749. Perbandingan == "EPSG:32749" di PyQGIS menjadi == 32749 di ArcPy. Kedua, kursor ArcPy mengembalikan isian menurut urutan kolom yang Anda daftarkan, sedangkan fitur QGIS dibaca dengan nama kolom.

Tabel padanan: raster #

Tabel ini diambil dari Skrip 31.1, 31.2, dan 31.3, serta skrip PyQGIS b2_03 dan b3_02 (Bab 21).

PekerjaanPyQGISArcPy (Pro)
Membuka rasterQgsRasterLayer(jalur, nama, "gdal")arcpy.Raster(jalur)
Lebar dan tinggir.width() dan r.height()r.width dan r.height
Jumlah bandr.bandCount()r.bandCount
Ukuran pikselr.rasterUnitsPerPixelX()r.meanCellWidth
Satu band"Citra_KPH@4" di dalam ekspresiRaster(DATA + r"\Citra_KPH.tif\Band_4")
NDVIekspresi ("Citra_KPH@4" - "Citra_KPH@1") / (...) lewat native:rastercalc(nir - merah) / (nir + merah) dengan Float(...)
Tinggi tajuk (CHM)ekspresi "DSM@1" - "DTM@1"Raster(DSM) - Raster(DTM)
Rata-rata per poligonnative:zonalstatisticsfb, kolom hasil ndvi_meanZonalStatisticsAsTable(..., "DATA", "MEAN"), kolom hasil MEAN
Memotong rastergdal:cliprasterbymasklayerExtractByMask(raster, batas).save(...)

Perhatikan tanda kurung pada baris kedua sampai keempat. Di PyQGIS, lebar raster adalah fungsi: r.width(). Di ArcPy, ia sifat: r.width tanpa kurung. Salah menulis kurung memberi galat. Dokumentasi Esri mencatat bahwa ExtractByMask mengembalikan objek raster yang disimpan lewat .save(), sedangkan hasil native:rastercalc di skrip Bab 21 berupa teks jalur yang kemudian dibungkus jadi layer. [CEK]

Tabel padanan: alat vektor #

Tabel ini diambil dari Skrip 31.2, 31.3, dan 31.4, serta skrip PyQGIS b2_02, b3_02, dan b4_02 (Bab 21 dan 22).

PekerjaanPyQGISArcPy (Pro)
Memanggil alatprocessing.run("native:buffer", {...})["OUTPUT"]arcpy.analysis.Buffer(...)
Cara memberi parameterkamus: nama parameter lalu nilaiisian berurutan atau kata kunci
Penyangga 30 m"DISTANCE": 30"30 Meters"
Gabungkan semua penyangga"DISSOLVE": Truedissolve_option="ALL"
Irisannative:intersection dengan INPUT dan OVERLAYarcpy.analysis.Intersect([a, b], keluaran)
Jumlah titik per poligonnative:countpointsinpolygonarcpy.analysis.SpatialJoin(...) lalu kolom Join_Count
Memilih satu KPHnative:extractbyexpressionarcpy.analysis.Select(batas, keluaran, "NAMA_KPH = '%s'" % nama)
Mengubah sistem koordinatnative:reprojectlayerarcpy.management.Project(...)
Keluaran sementara"TEMPORARY_OUTPUT"memory\nama

Dua hal lain yang mudah luput. Pertama, hasil alat QGIS berupa kamus, jadi Anda mengambil ["OUTPUT"]. Di ArcPy tidak ada kamus: Anda sendiri yang menyebut nama keluaran, lalu memakai nama itu di langkah berikutnya. Kedua, "DISTANCE": 30 di QGIS mengikuti satuan sistem koordinat layer. Skrip PyQGIS Anda mencatatnya dalam komentar: satuannya meter karena sistem koordinatnya UTM. Di ArcPy, satuan ikut ditulis di dalam teks jarak.

Tabel padanan: peta, galat, dan berkas #

Tabel ini diambil dari Skrip 31.1, 31.4, dan 31.5, skrip M1 di Bab 30, serta skrip PyQGIS b1_03, b4_02, dan b7_01 (Bab 14, 22, dan 23).

PekerjaanPyQGISArcPy (Pro)ArcPy (ArcMap)
Proyek yang terbukaQgsProject.instance()arcpy.mp.ArcGISProject("CURRENT")arcpy.mapping.MapDocument("CURRENT")
Menambah layerproyek.addMapLayer(layer)peta.addDataFromPath(jalur)arcpy.mapping.AddLayer(df, arcpy.mapping.Layer(jalur))
Mengambil layerproyek.mapLayersByName("Batas_KPH")[0]peta.listLayers("Batas_KPH")[0]arcpy.mapping.ListLayers(mxd, "Batas_KPH", df)[0]
Menyimpanproyek.write(berkas)aprx.save()mxd.save()
Menangkap galat alatexcept Exception as e:except arcpy.ExecuteError:except arcpy.ExecuteError:
Pesan galat alatstr(e)arcpy.GetMessages(2)arcpy.GetMessages(2)
Menulis CSVopen(..., "w", newline="", encoding="utf-8")open(..., "w", newline="")open(..., "wb")
Membuat folderos.makedirs(HASIL, exist_ok=True)os.makedirs(..., exist_ok=True)if not os.path.exists(...): os.makedirs(...)

Bedanya penting pada baris "Menyimpan". proyek.write(berkas) menulis ke berkas yang Anda sebut. Perintah aprx.save() menyimpan perubahan ke proyek yang sedang terbuka, yaitu menimpa. Dokumentasi Esri menyediakan saveACopy(nama_berkas) untuk menyimpan salinan. [CEK]

Cara menerjemahkan skrip: enam langkah #

Ilustrasi 32.3: Enam langkah menerjemahkan skrip
Skema enam langkah: membaca pola skrip, menandai bagian Python murni, mencari padanan alat, mengganti cara menunjuk data, menyesuaikan gaya Python, menguji kecil dan mencocokkan angka
  1. Baca pola skrip dulu. Hampir semua skrip di buku ini berbentuk empat tahap: tetapkan lokasi data, cek sifat data, ulangi tiap baris, lalu simpan atau tampilkan. Tulis keempat tahap itu di kertas.
  2. Tandai bagian Python murni. Perulangan for, if, kamus, daftar, round, dan print tidak perlu diterjemahkan. Cukup pindahkan.
  3. Cari padanan tiap alat. Pakai tabel di bab ini. Bila alat tidak ada di tabel, buka dokumentasi alat itu. Cek urutan isiannya, jangan menebak.
  4. Ganti cara menunjuk data. QGIS memakai layer yang dimuat, ArcPy memakai jalur teks atau nama di folder kerja. Karena itu tentukan arcpy.env.workspace dan nama keluaran sementara.
  5. Sesuaikan gaya Python. Bila tujuannya ArcMap, terapkan beda Python 2.7: print dengan %, 10000.0, .format, dan arcpy.mapping.
  6. Uji kecil dan cocokkan angka acuan. Jalankan dengan data contoh. Bandingkan hasil dengan angka yang sudah Anda ketahui.

Contoh terjemahan 1: membaca luas tiap KPH #

Bagian ini dari skrip PyQGIS b1_02_baca_atribut.py, baris 16 sampai 20:

PYTHONUnduh berkas
for fitur in batas.getFeatures():
    nama = fitur["NAMA_KPH"]
    luas_ha = fitur.geometry().area() / 10000      # m2 menjadi hektare (CRS harus berupa meter)
    baris.append((nama, round(luas_ha, 2)))
    print(nama, round(luas_ha, 2), "ha")

Bagian Python murni: for, print, round. Bagian yang diterjemahkan: cara membaca baris dan cara mendapat luas. getFeatures() menjadi kursor, fitur["NAMA_KPH"] menjadi isian kursor, dan fitur.geometry().area() menjadi kolom "SHAPE@AREA". Padanan ArcPy-nya ada di Skrip 31.1, baris 18 sampai 20:

PYTHONUnduh berkas
with arcpy.da.SearchCursor(batas, ["NAMA_KPH", "SHAPE@AREA"]) as kursor:
    for nama, luas in kursor:
        print(nama, round(luas / 10000, 2), "ha")          # luas dalam satuan peta (m2) dibagi 10.000

Dua baris PyQGIS tidak punya baris kembaran. Baris nama = fitur["NAMA_KPH"] tidak perlu, sebab kursor sudah menaruh nama langsung ke penampung nama. Baris baris.append(...) hanya dipakai skrip QGIS untuk menulis CSV nanti. Angka acuan untuk menguji terjemahan: luas KPH Alpha 4,0 hektare.

Contoh terjemahan 2: penyangga sungai #

Bagian ini dari skrip PyQGIS b2_02_rantai_vektor.py, baris 14 sampai 16:

PYTHONUnduh berkas
sempadan = processing.run("native:buffer", {
    "INPUT": sungai, "DISTANCE": 30, "SEGMENTS": 8, "END_CAP_STYLE": 0, "JOIN_STYLE": 0,
    "MITER_LIMIT": 2, "DISSOLVE": True, "OUTPUT": "TEMPORARY_OUTPUT"})["OUTPUT"]

Semua alat QGIS memakai kamus parameter. Padanan ArcPy-nya di Skrip 31.2, baris 14:

PYTHON
arcpy.analysis.Buffer("Sungai.shp", r"memory\sempadan", "30 Meters", dissolve_option="ALL")

Empat parameter QGIS tidak muncul di versi ArcPy: SEGMENTS, END_CAP_STYLE, JOIN_STYLE, dan MITER_LIMIT. Artinya versi ArcPy memakai nilai bawaan alat untuk hal-hal itu. Dokumentasi Esri menyebut parameter pilihan line_side dan line_end_type pada Buffer bernilai bawaan FULL dan ROUND. Karena pengaturan ujung dan sambungan tidak sama persis, sempadan di dua alat bisa selisih sedikit di desimal terakhir. Itu bukan galat. [CEK]

Contoh terjemahan 3: dari ArcGIS Pro ke ArcMap #

Terjemahan Pro ke ArcMap lebih mekanis. Ia hanya soal lapis pertama dan kedua. Ambil dua baris dari Skrip 31.2. Versi Pro, baris 15:

PYTHON
arcpy.analysis.Intersect(["Batas_KPH.shp", r"memory\sempadan"], r"memory\irisan")

Versi ArcMap, baris 16:

PYTHON
arcpy.Intersect_analysis(["Batas_KPH.shp", r"in_memory\sempadan"], r"in_memory\irisan")

Dua penggantian: arcpy.analysis.Intersect(...) menjadi arcpy.Intersect_analysis(...), dan memory\ menjadi in_memory\. Pelajari polanya: nama kotak alat pindah ke belakang dan disambung dengan garis bawah.

Jebakan lintas alat #

Hal-hal ini tidak muncul bila Anda hanya memakai satu alat. Baru terasa ketika Anda berpindah.

JebakanYang terjadiCara aman
Urutan kolom di kursorIsian dibaca menurut urutan daftar kolom. Menukar urutan daftar tanpa menukar nama penampung membuat nilai tertukar diam-diam.Tulis daftar kolom dan nama penampung berdampingan, dan cetak satu baris untuk memeriksa.
Jarak dan satuanQGIS mengikuti satuan sistem koordinat layer. ArcPy membaca satuan dari teks "30 Meters".Periksa sistem koordinat data, dan tulis satuan di ArcPy.
Hasil rasterHasil native:rastercalc berupa teks jalur. Hasil operasi raster ArcPy berupa objek raster yang harus .save().Ingat: satu bungkus jadi layer, satu lagi disimpan dengan .save().
Kutip dalam klausa pilihQGIS: '"NAMA_KPH" = \'%s\''. Pro: "NAMA_KPH = '%s'". ArcMap: "\"NAMA_KPH\" = '%s'".Uji klausa dengan satu data kecil dulu. [CEK]
Nama kolom bawaanDaftar kolom Shapefile di ArcPy memuat kolom bantu seperti FID dan Shape, sedangkan GeoPackage di QGIS memuat fid.Jangan mengandalkan urutan atau jumlah kolom. Sebut nama kolom. [CEK]
Angka antaralatDua alat bisa beda di desimal terakhir. Di skrip PyQGIS, persen sempadan KPH Beta tercetak 34.6, sedangkan hitungan dengan OGR memberi 34.7.Bandingkan dengan toleransi, bukan dengan tanda ==.
Ekstensi ArcGISAlat raster seperti ZonalStatisticsAsTable perlu ekstensi Spatial Analyst atau Image Analyst. QGIS tidak memakai ekstensi berbayar untuk itu.Cek lisensi sebelum menyerahkan skrip ke orang lain.
Tempat menjalankanSkrip PyQGIS jalan di Windows, macOS, dan Ubuntu. Skrip ArcPy hanya di Windows. Kata "CURRENT" hanya berarti di dalam aplikasi ArcGIS.Putuskan lebih dulu di komputer mana skrip akan dipakai.

Satu contoh nyata untuk baris pertama. Anggap Anda menulis kursor dengan urutan ["SHAPE@AREA", "NAMA_KPH"], tetapi tetap membuka dengan for nama, luas in kursor:. Penampung nama kini berisi luas, dan luas berisi nama. Baris round(luas / 10000, 2) gagal dengan TypeError: unsupported operand type(s) for /: 'str' and 'int'. Pesan itu sudah diuji pada pasangan nilai yang dibalik, di Python 3.

Latihan menerjemahkan #

Kerjakan di kertas atau di berkas teks. Tidak ada yang perlu dijalankan di ArcGIS. Jawaban memakai skrip yang sudah ada di buku ini.

  1. PyQGIS ke ArcPy. Terjemahkan dua baris PyQGIS ini ke ArcGIS Pro: print("Jumlah fitur:", layer.featureCount()) dan print("Nama field:", [f.name() for f in layer.fields()]). Anggap data bernama batas di folder kerja.
  2. ArcPy ke PyQGIS. Terjemahkan satu baris ArcPy Pro ini ke PyQGIS: arcpy.analysis.Select(batas, r"memory\satu_kph", "NAMA_KPH = '%s'" % nama).
  3. Pro ke ArcMap. Terjemahkan dua baris ArcGIS Pro ini ke ArcMap 10.8: for i, nama in enumerate(daftar, start=1): dan print(f"{i}/{total} selesai: {nama}").
  4. Menyusun sendiri. Tambahkan satu baris ke tabel padanan Anda: pekerjaan "menghitung jumlah fitur" untuk ArcMap. Gunakan Skrip 31.1.

Contoh jawaban.

  1. Dari Skrip 31.1, baris 14 dan 15:
PYTHONUnduh berkas
print("Jumlah fitur:", int(arcpy.management.GetCount(batas)[0]))
print("Field:", [f.name for f in arcpy.ListFields(batas)])
  1. Dari skrip PyQGIS b3_02_batch_per_kph.py, baris 24 sampai 26:
PYTHONUnduh berkas
    satu_kph = processing.run("native:extractbyexpression", {
        "INPUT": batas, "EXPRESSION": '"NAMA_KPH" = \'%s\'' % nama,
        "OUTPUT": "TEMPORARY_OUTPUT"})["OUTPUT"]
  1. Dari skrip M1 versi ArcMap, baris 20 dan 26 (nomor baris ArcMap bergeser satu karena baris from __future__):
PYTHON
for i, nama in enumerate(daftar, 1):
PYTHON
    print("{}/{} selesai: {}".format(i, total, nama))
  1. Dari Skrip 31.1 versi ArcMap, baris 15: int(arcpy.GetCount_management(batas).getOutput(0)). Di Pro, penulisannya int(arcpy.management.GetCount(batas)[0]).

Semua jawaban disalin langsung dari berkas sumbernya. Hasil menjalankannya di ArcGIS belum teruji. [CEK]

Cek paham #

  1. Sebutkan tiga lapis beda saat menerjemahkan skrip QGIS ke ArcPy ArcMap.
  2. Mengapa r.width() di PyQGIS menjadi r.width di ArcPy?
  3. Mengapa sempadan di dua alat bisa selisih di desimal terakhir?
  4. Mengapa hasil alat QGIS diambil dengan ["OUTPUT"], sedangkan di ArcPy tidak?

Jawaban.

  1. Gaya Python (Pro lawan ArcMap), nama alat (QGIS lawan ArcPy), dan cara menunjuk data (layer dalam proyek lawan jalur teks).
  2. Di PyQGIS, lebar raster adalah fungsi, jadi dipanggil dengan kurung. Di ArcPy, ia sifat objek raster, jadi ditulis tanpa kurung.
  3. Parameter ujung dan sambungan penyangga tidak persis sama. Itu wajar, dan bukan galat.
  4. Alat QGIS mengembalikan kamus berisi semua keluaran. Alat ArcPy menulis ke nama keluaran yang Anda tentukan sendiri, jadi Anda memakai nama itu langsung.

Kesalahan umum #

  • Menerjemahkan seluruh skrip sekaligus. Hasilnya sulit diperiksa. Perbaikan: terjemahkan satu tahap, uji dengan angka acuan, lalu lanjut.
  • Menyalin nama alat dari ingatan. Nama dan urutan isian alat bisa keliru. Perbaikan: cek dokumentasi alat.
  • Lupa mengganti cara menunjuk data. Di ArcPy, layer tidak "terbuka" di proyek. Perbaikan: tentukan folder kerja dan nama keluaran.
  • Menganggap angka dua alat harus persis sama. Perbaikan: bandingkan dengan toleransi kecil.

Ringkasan dan latihan #

  • Menerjemahkan skrip berarti menerjemahkan tiga lapis: gaya Python, nama alat, dan cara menunjuk data.
  • Python murni (for, if, kamus, daftar) dipindahkan tanpa diubah.
  • Tabel padanan membantu mencari nama alat. Dokumentasi alat membantu memeriksa urutan isian.
  • Enam langkah: baca pola, tandai Python murni, cari padanan, ganti cara menunjuk data, sesuaikan gaya Python, uji dengan angka acuan.
  • Semua skrip ArcPy di buku ini belum dijalankan di ArcGIS dan bertanda [CEK].

Latihan. Ambil skrip PyQGIS dari Bab 21 yang menghitung NDVI per KPH. Tulis ulang rencana terjemahannya ke ArcPy dengan enam langkah di atas, tanpa menjalankan apa pun. Mana yang paling banyak berubah: bagian Python murni, nama alat, atau cara menunjuk data?

Tabel perbandingan: tiga lapis beda #

LapisYang berbedaContoh
Gaya PythonPro lawan ArcMapprint(nama, luas) lawan print("%s %s" % (nama, luas))
Nama alatQGIS lawan ArcPynative:buffer lawan arcpy.analysis.Buffer
Cara menunjuk datalayer dalam proyek lawan jalur teksQgsVectorLayer(...) lawan "Batas_KPH.shp"