Grafik batang dan garis mengabaikan satu dimensi yang justru sering menjadi kunci keputusan: lokasi. Penjualan per kecamatan, sebaran kedai pesaing, hingga wilayah yang belum terjangkau armada pengiriman — semuanya paling mudah dibaca ketika digambar sebagai peta. Pada chapter ini kita memvisualisasikan data geospasial yang bentuknya sudah kita kenal di Section Pengumpulan Data : poligon kecamatan dalam berkas GeoJSON, dibaca dengan geopandas, lalu diwarnai menurut nilainya menjadi choropleth — peta wilayah yang arsirannya mewakili angka. Sebagai penutup, kita menumpangkan titik lokasi kedai di atas peta itu.

Choropleth: Peta yang Diwarnai Angka #

Choropleth adalah peta berbatas wilayah (kecamatan, provinsi, negara) yang setiap wilayahnya diwarnai menurut nilai sebuah variabel — makin pekat warnanya, makin tinggi nilainya. Ia menjawab pertanyaan “di mana” yang tidak bisa dijawab grafik batang: bukan sekadar berapa penjualan tiap kecamatan, melainkan wilayah mana yang menonjol dan apakah wilayah-wilayah itu berdempetan membentuk kawasan. Syaratnya satu: variabel yang diwarnai harus bisa dibandingkan adil antar wilayah (nilai absolut seperti total penjualan sudah cukup untuk memantau performa tiap wilayah; untuk membandingkan “kuantitas belanja” antar wilayah berpenduduk berbeda, gunakan nilai per penduduk).

Dataset kita memuat empat kecamatan Bandung — poligon sederhana pada koordinat WGS84 — beserta rekap penjualan Toko Kopi Kita dalam penjualan_kecamatan.csv:

json
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "nama": "Sukajadi"
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [107.6, -6.91],
            [107.62, -6.91],
            [107.62, -6.9],
            [107.6, -6.9],
            [107.6, -6.91]
          ]
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "nama": "Coblong"
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [107.62, -6.91],
            [107.65, -6.91],
            [107.65, -6.9],
            [107.62, -6.9],
            [107.62, -6.91]
          ]
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "nama": "Andir"
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [107.6, -6.9],
            [107.62, -6.9],
            [107.62, -6.89],
            [107.6, -6.89],
            [107.6, -6.9]
          ]
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "nama": "Regol"
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [107.62, -6.9],
            [107.65, -6.9],
            [107.65, -6.89],
            [107.62, -6.89],
            [107.62, -6.9]
          ]
        ]
      }
    }
  ]
}
csv
nama,penjualan_ribu
Sukajadi,8600
Coblong,13200
Andir,5400
Regol,9800

Membaca Peta dan Menempelkan Angka #

Langkah pertama tetap membaca dan memeriksa: pastikan geometri terbaca sebagai poligon dan CRS-nya memang geografis (EPSG:4326) — dua hal yang menentukan apakah peta akan tergambar benar. Setelah itu angka penjualan ditempelkan ke wilayahnya lewat merge pada kolom nama, persis pola DataFrame.merge yang kita kenal:

python
import geopandas as gpd

gdf = gpd.read_file("kecamatan.geojson")
print(gdf)
print()
print("Jenis geometri:", gdf.geom_type.tolist())
print("CRS:", gdf.crs)

Output yang Dihasilkan:

text
       nama                                           geometry
0  Sukajadi  POLYGON ((107.6 -6.91, 107.62 -6.91, 107.62 -6...
1   Coblong  POLYGON ((107.62 -6.91, 107.65 -6.91, 107.65 -...
2     Andir  POLYGON ((107.6 -6.9, 107.62 -6.9, 107.62 -6.8...
3     Regol  POLYGON ((107.62 -6.9, 107.65 -6.9, 107.65 -6....

Jenis geometri: ['Polygon', 'Polygon', 'Polygon', 'Polygon']
CRS: EPSG:4326
python
import geopandas as gpd
import pandas as pd

gdf = gpd.read_file("kecamatan.geojson")
penjualan = pd.read_csv("penjualan_kecamatan.csv")

peta = gdf.merge(penjualan, on="nama", how="left")
tabel = peta.drop(columns="geometry")
print(tabel.to_string(index=False))
print()
print("Total penjualan empat kecamatan:", peta["penjualan_ribu"].sum(), "ribu rupiah")

Output yang Dihasilkan:

text
    nama  penjualan_ribu
Sukajadi            8600
 Coblong           13200
   Andir            5400
   Regol            9800

Total penjualan empat kecamatan: 37000 ribu rupiah

Dua pemeriksaan awal lolos: empat poligon utuh dan CRS geografis siap tampil. Tabel gabungan menegaskan ceritanya — Coblong memimpin dengan 13.200 ribu rupiah, hampir dua setengah lipat Andir yang terkecil (5.400). Angka-angka itu kini siap diwarnai ke dalam peta.

Menggambar Choropleth #

Satu panggilan .plot() menyelesaikan semuanya: column memilih variabel yang diwarnai, cmap memilih palet, legend menampilkan batang warna, dan edgecolor memberi garis batas antar wilayah:

python
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt

gdf = gpd.read_file("kecamatan.geojson")
penjualan = pd.read_csv("penjualan_kecamatan.csv")
peta = gdf.merge(penjualan, on="nama", how="left")

ax = peta.plot(column="penjualan_ribu", cmap="Greens", legend=True,
               edgecolor="white", figsize=(7, 5))
ax.set_title("Penjualan Toko Kopi Kita per Kecamatan (ribu rupiah)")
ax.set_xlabel("Bujur (derajat)")
ax.set_ylabel("Lintang (derajat)")
plt.show()

Peta yang tampil memuat empat kotak wilayah tersusun mozaik dua kali dua. Coblong — kotak kanan atas — paling pekat warnanya karena penjualannya tertinggi (13.200), disusul Regol di kanan bawah (9.800) dan Sukajadi di kiri atas (8.600) dengan gradasi yang nyaris berurutan; Andir di kiri bawah paling pucat (5.400). Batang warna di sisi kanan memetakan gelap-terang ke rentang rupiah, dan garis putih memisahkan kecamatan satu sama lain. Sekali pandang, pola yang tidak kelihatan di tabel menjadi jelas: dua wilayah dengan penjualan terbaik sama-sama berada di sisi timur (bujur lebih besar) peta.

Choropleth menjawab “wilayah mana yang menonjol”; lapisan titik menjawab “di tepat mana sesuatu berada”. Kedai Toko Kopi Kita yang dua gerainya berdiri di wilayah berbeda kita bangun sebagai GeoDataFrame titik memakai points_from_xy — urutannya [bujur, lintang], bukan sebaliknya — lalu digambar di atas peta yang sama lewat parameter ax:

python
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt

gdf = gpd.read_file("kecamatan.geojson")
penjualan = pd.read_csv("penjualan_kecamatan.csv")
peta = gdf.merge(penjualan, on="nama", how="left")

kedai = gpd.GeoDataFrame({
    "kedai": ["Toko Kopi Kita - Cendana", "Toko Kopi Kita - Dipatiukur"],
    "bujur": [107.61, 107.635],
    "lintang": [-6.905, -6.895],
}, geometry=gpd.points_from_xy([107.61, 107.635], [-6.905, -6.895]), crs="EPSG:4326")

ax = peta.plot(column="penjualan_ribu", cmap="Greens", edgecolor="white",
               figsize=(7, 5), legend=True, legend_kwds={"label": "penjualan (ribu rupiah)"})
kedai.plot(ax=ax, color="red", marker="x", markersize=90)
for _, baris in kedai.iterrows():
    ax.annotate(baris["kedai"], xy=(baris["bujur"], baris["lintang"]),
                xytext=(3, 5), textcoords="offset points", fontsize=8)
ax.set_title("Peta penjualan dan lokasi kedai")
plt.show()

Peta gabungan ini menampilkan mozaik choropleth yang sama dengan dua tanda silang merah di atasnya: gerai Cendana di tengah wilayah Sukajai-Andir (kiri), dan gerai Dipatiukur di antara Coblong dan Regol (kanan). Label nama kedai mengambang sedikit di kanan-atas tiap tanda. Kombinasi ini langsung memunculkan pertanyaan bisnis yang relevan: penjualan tinggi di sisi timur bertepatan dengan lokasi gerai Dipatiukur — apakah timur memang pasar yang lebih kuat, atau gerai itulah yang menarik pasarnya? Peta tidak menjawabnya; ia menunjukkan di mana mencari jawabannya.

Rangkuman #

  • Choropleth mewarnai wilayah menurut nilai variabel; ia menjawab pertanyaan “di mana”, bukan hanya “berapa” — dan paling tepat dipakai ketika nilainya sebanding antar wilayah.
  • Alurnya tiga langkah: gpd.read_file() membaca GeoJSON, merge() menempelkan angka pada wilayahnya, lalu .plot(column=..., cmap=..., legend=True, edgecolor=...) menggambar peta.
  • Palet satu arah (Greens, Blues) untuk nilai bergradasi tunggal; simpan palet dua arah untuk data yang berkisar di kedua sisi nol.
  • Lapisan titik lewat gpd.points_from_xy(bujur, lintang) dan kedai.plot(ax=ax) menambahkan konteks lokasi persis di atas choropleth — dengan ax yang sama, keduanya berbagi satu kanvas dan satu CRS.
  • EPSG:4326 cukup untuk menampilkan peta; hitung luas/jarak hanya setelah proyeksi ke CRS berbentuk meter.

Peta choropleth memakai satu kekuatan besar — warna yang makin pekat makin tinggi — dan kekuatan itu bisa disalahgunakan: skala warna yang dipilih sewenang-wenang, palet yang menyilaukan, atau rentang yang dipangkas bisa membuat peta (dan grafik apa pun) berbicara lebih banyak atau lebih sedikit daripada angkanya. Chapter penutup Section ini mengaudit persis masalah itu: Menghindari Visualisasi Menyesatkan .

Referensi #

  1. GeoPandas — Dokumentasi Resmi
  2. pandas — API Reference: DataFrame.merge
  3. Matplotlib — Penggunaan Pyplot