docs

Dokumentasi Modul map.js

map.js menampilkan peta interaktif dan menggambar data GeoJSON di atasnya — ditulis dari nol, tanpa Leaflet atau library peta lain. Peta dasarnya berupa tile raster (bawaan: OpenStreetMap), datanya digambar sebagai SVG, dan semuanya bisa digeser, di-zoom, dan diklik, baik dengan mouse maupun jari.

CSS-nya di-inject otomatis oleh modul ini sendiri (seperti toast.js), jadi tidak ada file CSS yang perlu ditautkan. Yang diunduh browser sekitar 11 KB (gzip): map.min.js beserta geo.js yang dipakainya. Sebagai pembanding, Leaflet sekitar 46 KB (gzip) untuk JS dan CSS-nya.

Marker ikon, skala, “lokasi saya”, live tracking, pengelompokan titik, pengukuran, dan rute ada di modul tambahan yang terpisah, masing-masing 1–3 KB, supaya halaman hanya mengunduh yang dipakainya.

Cara Penggunaan (Import)

import { createMap, addGeoJSON, fitMap, getGeoJSON } from "https://cdn.jsdelivr.net/gh/crootjs/lib@0.0.16/map.js";

Semua fungsi geo.js (getGeoJSON, readGeoJSONFile, parseGeoJSON, getBBox, dan lainnya) ikut diekspor dari map.js. Halaman yang menampilkan peta cukup mengimpor dari map.js saja.

Contoh Minimal

Elemen tempat peta wajib punya tinggi dari CSS. Peta mengisi elemen itu sepenuhnya; elemen tanpa tinggi berarti peta setinggi 0 piksel (modul ini menulis peringatan di console kalau itu terjadi).

<!-- index.html -->
<link rel="stylesheet" href="style.css">
<div id="peta" class="peta"></div>
<script type="module" src="index.js"></script>
/* style.css */
.peta {
    height: 360px;
}
// index.js
import { createMap, addGeoJSON, fitMap, getGeoJSON } from "https://cdn.jsdelivr.net/gh/crootjs/lib@0.0.16/map.js";

createMap("peta");

getGeoJSON("https://github.com/petapedia/geojson/blob/main/contoh.geojson", (response) => {
    if (!response.data) {
        console.log("gagal membaca GeoJSON, status:", response.status);
        return;
    }
    addGeoJSON("peta", response.data); // gambar datanya
    fitMap("peta", response.data);     // paskan tampilan ke datanya
});

Hasilnya: peta dengan dua polygon gedung dan satu titik; klik salah satunya untuk melihat properties-nya di popup.

Semua fungsi map.js menerima ID elemen sebagai parameter pertama, sama seperti fungsi-fungsi element.js. Tidak ada object peta yang perlu disimpan.

createMap(id, options)

Membuat peta di dalam elemen ber-ID id. Memanggilnya lagi untuk ID yang sama mengganti peta yang lama.

options (semuanya opsional):

Opsi Bawaan Keterangan
center [118, -2.5] (Indonesia) Pusat tampilan, [lng, lat]
zoom 4 Level zoom awal
minZoom 1 Zoom terjauh yang diizinkan
maxZoom 19 Zoom terdekat yang diizinkan
tiles tile OpenStreetMap Pola URL tile raster dengan {z}, {x}, {y}. false = tanpa peta dasar
attribution — Teks atribusi untuk tiles buatan sendiri
zoomControl true false = tanpa tombol + / −
scrollZoom true false = roda mouse tetap menggulung halaman, tidak men-zoom peta
createMap("peta", { center: [107.5759, -6.8736], zoom: 17 });

addGeoJSON(id, geojson, options)

Menggambar GeoJSON di atas peta. geojson boleh berupa apa pun yang diterima parseGeoJSON: FeatureCollection, satu Feature, satu geometri polos, array-nya, atau teks JSON. Semua tipe geometri didukung (Point, LineString, Polygon, versi Multi-nya, dan GeometryCollection).

Mengembalikan function remove() untuk menghapus lapisan itu lagi — pola yang sama dengan dismiss() di toast.js.

const hapusGedung = addGeoJSON("peta", dataGedung);
// ...
hapusGedung(); // lapisan ini saja yang hilang

options (semuanya opsional):

style — tampilan fitur. Berupa object, atau function (feature) => object untuk membedakan tiap fitur:

Kunci Berlaku untuk Bawaan
stroke garis, tepi polygon, tepi titik biru #2563eb (titik: putih)
strokeWidth garis, tepi polygon, tepi titik garis 3, polygon 2, titik 2
strokeOpacity garis, tepi polygon, tepi titik 1
fill polygon, titik biru #2563eb
fillOpacity polygon, titik polygon 0.25, titik 1
radius titik 7 (piksel)
// semua fitur merah
addGeoJSON("peta", data, { style: { stroke: "#dc2626", fill: "#dc2626" } });

// warna tergantung properti; null = pakai tampilan bawaan
addGeoJSON("peta", data, {
    style: (feature) => feature.properties.Gedung === "Rektorat" ? { fill: "#16a34a", stroke: "#16a34a" } : null,
});

Ketebalan garis dan ukuran titik tetap sama di layar pada zoom berapa pun.

popup — isi popup saat sebuah fitur diklik:

Nilai Hasil
tidak diisi daftar nama: nilai dari properties fitur itu (fitur tanpa properti tidak punya popup)
function (feature) => teks teks itu, ditampilkan apa adanya
function (feature) => elemen DOM elemen itu
function yang mengembalikan null tanpa popup untuk fitur itu
false tanpa popup sama sekali
addGeoJSON("peta", data, { popup: (feature) => "Gedung " + feature.properties.Gedung });

Teks popup dipasang sebagai teks, bukan HTML — tag di dalam data atau di teks yang kamu kembalikan tampil apa adanya dan tidak dieksekusi. Kalau butuh tautan atau format, kembalikan elemen DOM:

addGeoJSON("peta", data, {
    popup: (feature) => {
        const link = document.createElement("a");
        link.href = "/gedung?nama=" + encodeURIComponent(feature.properties.Gedung);
        link.textContent = "Detail " + feature.properties.Gedung;
        return link;
    },
});

fitMap(id, geojson, padding = 24)

Memaskan tampilan ke seluruh data. geojson sama fleksibelnya dengan di addGeoJSON, atau langsung sebuah kotak pembatas [minLng, minLat, maxLng, maxLat]. padding adalah jarak kosong (piksel) antara data dan tepi peta.

fitMap("peta", data);
fitMap("peta", [95, -11, 141, 6]); // seluruh Indonesia

setMapView(id, center, zoom) dan getMapView(id)

setMapView memindahkan tampilan ke center ([lng, lat]). zoom opsional; kalau tidak diisi, level zoom sekarang dipertahankan.

getMapView mengembalikan tampilan sekarang: { center: [lng, lat], zoom }.

setMapView("peta", [106.8272, -6.1754], 12); // Jakarta
setMapView("peta", [107.6191, -6.9175]);     // pindah ke Bandung, zoom tetap 12

getMapView("peta"); // { center: [107.6191, -6.917499999999983], zoom: 12 }

Nilai center yang dikembalikan bisa berbeda di digit-digit terakhir dari yang kamu masukkan (pembulatan bilangan pecahan saat proyeksi); selisihnya jauh di bawah satu milimeter di lapangan.

zoom selalu dibatasi ke minZoom..maxZoom. Saat sebuah gestur (roda mouse, pinch) sedang berlangsung, zoom bisa berupa pecahan.

onMapClick(id, runFunction)

runFunction(position, feature) dipanggil setiap kali peta diklik atau di-tap (menggeser peta tidak dihitung klik):

Kegunaan paling umum: memilih lokasi untuk sebuah form.

import { createMap, addGeoJSON, onMapClick } from "https://cdn.jsdelivr.net/gh/crootjs/lib@0.0.16/map.js";
import { setValue } from "https://cdn.jsdelivr.net/gh/crootjs/lib@0.0.16/element.js";

createMap("peta", { center: [107.5759, -6.8736], zoom: 17 });

let hapusPenanda = () => {};
onMapClick("peta", (position) => {
    setValue("longitude", position[0]);
    setValue("latitude", position[1]);

    // penanda lama diganti dengan yang baru di titik yang diklik
    hapusPenanda();
    hapusPenanda = addGeoJSON("peta", { type: "Point", coordinates: position });
});

onMapMove(id, runFunction) dan getMapBounds(id)

runFunction({ center, zoom }) dipanggil setiap kali tampilan berubah: digeser, di-zoom, atau ukuran petanya berubah. Selama sebuah gestur berlangsung ia dipanggil di setiap frame. Mengembalikan function stop() untuk berhenti mendengarkan.

getMapBounds(id) mengembalikan bagian dunia yang sedang terlihat sebagai kotak pembatas [minLng, minLat, maxLng, maxLat].

Keduanya biasa dipakai bersama untuk meminta ke backend hanya data di area yang tampak. Karena onMapMove terpanggil sangat sering, bungkus dengan debounce supaya request baru dikirim setelah peta berhenti bergerak:

import { createMap, addGeoJSON, clearMap, onMapMove, getMapBounds, getGeoJSON } from "https://cdn.jsdelivr.net/gh/crootjs/lib@0.0.16/map.js";
import { debounce } from "https://cdn.jsdelivr.net/gh/crootjs/lib@0.0.16/debounce.js";

createMap("peta", { center: [107.5759, -6.8736], zoom: 16 });

onMapMove("peta", debounce(() => {
    const bbox = getMapBounds("peta");
    getGeoJSON("https://api.example.com/lokasi?bbox=" + bbox.join(","), (response) => {
        if (!response.data) return;
        clearMap("peta");
        addGeoJSON("peta", response.data);
    });
}, 400));

mapToPoint(id, position) dan pointToPosition(id, point)

Mengubah posisi [lng, lat] menjadi titik [x, y] dalam piksel (dihitung dari pojok kiri-atas peta), dan sebaliknya. Hasil mapToPoint bisa berada di luar ukuran peta kalau posisinya sedang tidak terlihat.

mapToPoint("peta", [107.575914, -6.8732098]); // misalnya [412.6, 180.3]
pointToPosition("peta", [0, 0]);              // [lng, lat] di pojok kiri-atas peta

Bersama onMapMove, kedua fungsi ini cukup untuk membuat elemen HTML sendiri yang mengikuti peta. Untuk marker, pakai mapmarker.js yang sudah mengurus itu.

addMapControl(id, element, corner = "top-right")

Menaruh elemen buatanmu — tombol, legenda, kotak info — di pojok peta: "top-left", "top-right", "bottom-left", atau "bottom-right". Beberapa kontrol di pojok yang sama ditumpuk rapi, dan klik di dalamnya tidak ikut menggeser atau mengklik peta. Mengembalikan function remove().

const legenda = document.createElement("div");
legenda.className = "legenda"; // tampilannya dari CSS halamanmu
legenda.textContent = "Biru: gedung kampus";
addMapControl("peta", legenda, "bottom-right");

showPopup(id, position, content) dan closePopup(id)

Menampilkan popup di position ([lng, lat]) tanpa harus ada fitur di sana. content berupa teks (ditampilkan apa adanya, bukan HTML) atau elemen DOM. Hanya ada satu popup per peta; popup baru menggantikan yang lama.

showPopup("peta", [107.575914, -6.8732098], "Kampus ULBI");
closePopup("peta");

Kalau popup terpotong tepi peta, petanya digeser sedikit supaya popup terlihat utuh. Ini hanya berlaku kalau position-nya sedang terlihat; showPopup di tempat yang jauh tidak membuat peta melompat ke sana (pakai setMapView lebih dulu).

clearMap(id) dan removeMap(id)

Interaksi Bawaan

Aksi Hasil
Geser dengan mouse atau satu jari menggeser peta
Roda mouse / pinch di trackpad zoom di posisi kursor
Pinch dua jari zoom di antara kedua jari
Klik ganda zoom satu level di posisi kursor
Tombol + / − zoom satu level di tengah peta
Tombol panah (saat peta terfokus) menggeser peta
Tombol + / - (saat peta terfokus) zoom
Klik / tap sebuah fitur membuka popup-nya

Pengguna yang mengaktifkan reduce motion di sistemnya mendapat zoom tanpa animasi.

Peta Dasar

OpenStreetMap (bawaan). Tile diambil dari tile.openstreetmap.org, dan atribusi “© OpenStreetMap contributors” otomatis ditampilkan di pojok kanan bawah — lisensi datanya mewajibkan itu, jadi jangan disembunyikan. Server tile OpenStreetMap gratis tetapi dijalankan relawan dan punya kebijakan pemakaian: cocok untuk belajar dan situs dengan trafik kecil. Untuk aplikasi dengan trafik besar, pakai penyedia tile sendiri.

Penyedia tile lain. Isi tiles dengan pola URL-nya dan attribution dengan teks atribusi yang diminta penyedianya:

createMap("peta", {
    tiles: "https://tiles.example.com/{z}/{x}/{y}.png",
    attribution: "© Example Maps",
});

Tanpa peta dasar. Dengan tiles: false hanya datamu yang digambar, di atas latar polos — tidak ada request ke server tile sama sekali:

createMap("peta", { tiles: false });

Modul Tambahan

Fitur yang tidak dibutuhkan semua peta dipisahkan ke modul sendiri. Impor hanya yang kamu pakai:

Modul Isi
mapmarker.js Marker pin, ikon gambar, atau elemen sendiri — dengan label, popup, dan bisa digeser (addMarker)
mapcontrol.js Garis skala, “lokasi saya”, dan live tracking (addScaleBar, addLocateButton, locateUser, addTrackButton, trackUser)
mapcluster.js Pengelompokan titik untuk data titik yang sangat banyak (addCluster)
geomeasure.js Jarak, panjang, luas, titik-di-dalam-area, titik terdekat, lingkaran radius
georoute.js Rute terpendek di sepanjang garis-garis di dalam data GeoJSON (findRoute)
import { createMap } from "https://cdn.jsdelivr.net/gh/crootjs/lib@0.0.16/map.js";
import { addMarker } from "https://cdn.jsdelivr.net/gh/crootjs/lib@0.0.16/mapmarker.js";
import { addScaleBar, addLocateButton } from "https://cdn.jsdelivr.net/gh/crootjs/lib@0.0.16/mapcontrol.js";

createMap("peta", { center: [107.5759, -6.8736], zoom: 17 });
addMarker("peta", [107.575914, -6.8732098], { label: "ULBI", popup: "Kampus ULBI" });
addScaleBar("peta");
addLocateButton("peta");

Membuat Modul Tambahan Sendiri

createMap menitipkan sebuah antarmuka kecil di elemen petanya, di properti crootjsMap. Lewat itulah mapmarker.js dan mapcontrol.js bekerja, dan modul buatanmu bisa memakai jalan yang sama:

// penanda.js - modul tambahan sederhana: sebuah teks yang menempel di satu posisi
export function addText(id, position, text) {
    const map = document.getElementById(id).crootjsMap; // undefined kalau petanya belum dibuat

    const element = document.createElement("div");
    element.className = "penanda"; // butuh position: absolute dari CSS-mu
    element.textContent = text;
    map.overlay.appendChild(element);

    const place = () => {
        const point = map.toPoint(position);
        element.style.transform = `translate(${point[0]}px, ${point[1]}px)`;
    };
    place();
    map.onMove(place); // dipanggil setiap kali peta bergerak
}
Anggota crootjsMap Kegunaan
overlay Elemen tempat menaruh elemen HTML-mu: di atas data, di bawah popup, terpotong di tepi peta
toPoint(position) / toPosition(point) [lng, lat] ke piksel dan sebaliknya
getView() { center, zoom }
setView(center, zoom) Memindahkan tampilan
panBy(dx, dy) Menggeser peta sejauh sekian piksel
onMove(runFunction) Mendengarkan perubahan tampilan; mengembalikan stop()
onTap(element, runFunction) Menangani tap pada sebuah elemen di overlay: peta tidak menganggapnya klik peta (onMapClick tidak dipanggil), sedangkan geseran yang berawal di elemen itu tetap menggeser peta
addControl(element, corner) Menaruh elemen di pojok peta; mengembalikan remove()
showPopup(position, content, offset) / closePopup() Popup; offset = jarak ke atas dari posisinya, dalam piksel

Modul tambahan jangan mengimpor map.js. Halaman biasanya memuat map.min.js, sedangkan import "./map.js" dari modul lain memuat salinan kedua dengan daftar petanya sendiri — dan peta yang dibuat halaman tidak akan ditemukan. Antarmuka di elemen ini menghindari itu: modulmu bekerja dengan salinan map.js mana pun yang dipakai halaman.

Catatan

Batasan