6
Polygon dan Boundary
/paths/{code}.json menyimpan satu polygon boundary untuk satu wilayah, dan endpoint ini berlaku untuk semua level selama berkasnya ada. Bentuk response-nya sama seperti endpoint lain: data berisi id dan path, meta berisi level dan updated_at. Contoh GET /paths/32.json mengembalikan path berupa array ring berisi pasangan [lat, lng]. README.md:447-4731
Bentuk wrapper data/meta dan daftar endpoint tidak diulang di sini; lihat Format Response dan Wrapper data/meta dan Referensi Endpoint.
Cek has_path sebelum fetch
README memberi aturan tegas: cek has_path dulu, karena endpoint ini membalas 404 ketika nilainya false. README.md:447-4731
Field has_path tidak ditulis manual. add-has-path memindai api/paths/*.json untuk membangun set kode yang punya polygon, lalu menempelkan "has_path": true/false ke setiap objek Place/ShortItem pada provinces, regencies, districts, dan villages. tools/add_has_path.go:6-142 Set itu dibangun oleh buildHasPathSet(pDir) dengan pDir default <out>/paths; direktori yang tidak ada dianggap set kosong, bukan error. tools/add_has_path.go:57-613 Penulisan nilainya per id: array data diiterasi item per item, objek tunggal memakai data.id dengan fallback dari nama berkas. tools/add_has_path.go:143-1704
graph TD
item["item dari /districts atau /villages"] --> hasPath{"has_path == true?"}
hasPath -- false --> skip["tanpa polygon: render marker dari lat/lng"]
hasPath -- true --> fetch["GET /paths/{code}.json"]
fetch --> ok{"res.ok?"}
ok -- "false, 404" --> skip
ok -- true --> norm["normalizeRings(data.path)"]
norm --> filter["buang ring dengan length <= 2"]
filter --> lnglat["toLngLat: [lat,lng] jadi [lng,lat]"]
lnglat --> geojson["FeatureCollection: satu Polygon per ring"]
geojson --> layer["source wilayah-polygon + fill/line layer"]
style hasPath fill:#f59e0b,color:#000
style ok fill:#f59e0b,color:#000
style skip fill:#dc2626,color:#fff
style fetch fill:#8b5cf6,color:#fff
style norm fill:#8b5cf6,color:#fff
style filter fill:#8b5cf6,color:#fff
style lnglat fill:#8b5cf6,color:#fff
style geojson fill:#8b5cf6,color:#fff
style layer fill:#2d7d46,color:#fff
%% fig: Alur cek has_path sampai render polygon
%% desc: Nilai has_path dan status response menentukan apakah polygon diambil, lalu ring hasil normalisasi dikonversi ke [lng,lat] dan dipasang sebagai layer MapLibre.Playground sendiri tidak membaca has_path; ia memperlakukan res.ok === false sebagai "no path" lalu mengosongkan polygon, dan memilih kode terdalam yang tersedia lewat villCode || distCode || regCode || provCode. web/src/components/sections/PlaygroundSection.tsx:254-2795 Pola itu setara dengan cek has_path untuk kasus normal, tetapi tidak mendeteksi kode yang punya berkas rusak.
path bertingkat: single, multi, sampai kedalaman 4
Kedalaman data.path tidak seragam, dan kode peta wajib menanganinya. Komentar mapUtils mencatat tiga bentuk: single ring sedalam 2, multi ring sedalam 3, dan deep nesting sedalam 4 seperti Papua Barat 92. web/src/components/playground/mapUtils.ts:1-266 Jumlah ring juga jauh berbeda antar wilayah, sementara has_path tetap true untuk semuanya: Jakarta 2 ring, Aceh 28 ring, Papua Barat 1455 ring. README.md:471-4737
| Kedalaman | Bentuk data.path | Contoh |
|---|---|---|
| 2 | [[lat,lng], ...] | single ring, wilayah pulau tunggal |
| 3 | [[[lat,lng], ...], ...] | Jakarta (2 ring), Aceh (28 ring) |
| 4 | [[[[lat,lng]]]] | Papua Barat 92 (1455 ring) |
normalizeRings mengumpulkan ring secara rekursif. Sebuah node dianggap ring ketika elemen pertamanya array dan dua nilai pertamanya bertipe number; kalau bukan, setiap anak ditelusuri. Hasil akhirnya difilter ring.length > 2 supaya titik ganda atau ring kosong tidak ikut. web/src/components/playground/mapUtils.ts:1-266 README memuat versi ringkas algoritme yang sama: collect menelusuri data.path, lalu rings.filter(r => r.length > 2) diserahkan ke L.polygon. README.md:475-4898
Generator ikut menambal path yang terpotong: bila jumlah [ tiga tetapi jumlah ] hanya dua, satu ] ditambahkan sebelum JSON divalidasi. Aturan itu dipakai generate-boundaries untuk kecamatan dan kelurahan. tools/generate_boundaries.go:229-2529 Aturan yang sama dipakai phase B generate-static-api untuk provinsi dan kab/kota. tools/generate_static_api.go:339-35210
Dua penulis berkas, dua nilai meta.level
Berkas paths/ dihasilkan dua tahap pipeline; keduanya memakai genResponse dengan level berbeda. Kedalaman pipeline lengkap ada di Pipeline Generate dengan Go Tools.
| Penulis | Sumber path | meta.level |
|---|---|---|
generate-static-api phase B | kolom path data/wilayah_level_1_2.csv | genLevelCount(kode)+1, jadi 1 (provinsi) atau 2 (kab/kota) |
generate-boundaries | SQL db/kec/*.sql dan db/kel/*/*.sql | 3 (kecamatan) dan 4 (kelurahan) |
generate-boundaries hanya menulis berkas ketika it.Path tidak kosong dan json.Valid menerima hasil pembulatan; berkas yang ditulis dihitung sebagai totalPathsWritten. tools/generate_boundaries.go:229-2529
Dari [lat,lng] ke layer peta
⚠️ Koordinat API memakai urutan
[lat, lng], sedangkan GeoJSON dan MapLibre menuntut[lng, lat]. Melewatkan konversi ini menaruh polygon di belahan bumi yang salah.
toLngLat melakukan pembalikan per titik. web/src/components/playground/mapUtils.ts:28-3111 ringsToGeoJSON membungkus setiap ring menjadi Feature bertipe Polygon dengan coordinates: [ring] di dalam satu FeatureCollection. web/src/components/playground/mapUtils.ts:33-4512 Komponen MapLibre membangun GeoJSON yang sama secara inline, lalu menambah source wilayah-polygon dengan layer wilayah-polygon-fill (fill-color #10b981, fill-opacity 0.18) dan wilayah-polygon-line (line-color #059669, line-width 2). web/src/components/playground/MapLibreIndonesiaMap.tsx:308-34413
Simplifikasi 6 desimal
roundPathCoords membulatkan setiap float pada string JSON koordinat memakai coordFloatRe (-?\d+\.\d+), lalu math.Round(f*1e6)/1e6 dan strconv.FormatFloat(rounded, 'f', 6, 64). Komentarnya menyebut presisi ~11 cm, cukup untuk render boundary administratif, dan efek utamanya memperkecil ukuran berkas. tools/generate_static_api.go:650-66514 README menyebut hal yang sama untuk sisi pemakaian: 6 desimal, sudah di-simplify, dan pembaca yang butuh resolusi tinggi diminta mengambil langsung dari cahyadsn/wilayah_boundaries. README.md:471-4737
Pembulatan terjadi di dalam generator, bukan saat request. Phase B memanggilnya tepat sebelum validasi JSON dan penulisan berkas. tools/generate_static_api.go:339-35210 generate-boundaries memanggilnya pada tiap it.Path sebelum menulis paths/{kode}.json. tools/generate_boundaries.go:229-2529
⚠️
has_path: falsebukan bug render. Wilayah itu memang belum punya polygon, dan daftar lengkapnya tersedia di Kelengkapan Data danmissings.json. Untuk mencoba interaksi peta secara langsung, lihat Website dan Playground.