(function () { 'use strict'; const DEFAULT_SOURCE = 'assets/data/Kecamatan.json'; const DEFAULT_ANCHOR = { lat: -0.06054796552220232, lng: 109.34490231930592 }; const DEFAULT_GEO_BOUNDS = { south: -0.0981948, west: 109.2741676, north: 0.0381168, east: 109.3853475 }; const DEFAULT_CALIBRATION = { scaleX: 1, scaleY: 1, offsetLat: 0, offsetLng: 0 }; function walkCoordinates(coordinates, callback) { if (!Array.isArray(coordinates)) { return; } if (coordinates.length >= 2 && typeof coordinates[0] === 'number' && typeof coordinates[1] === 'number') { callback(coordinates[0], coordinates[1]); return; } coordinates.forEach((child) => walkCoordinates(child, callback)); } function getCoordinateBounds(data) { const bounds = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }; function addGeometry(geometry) { if (!geometry) { return; } if (geometry.type === 'GeometryCollection') { (geometry.geometries || []).forEach(addGeometry); return; } walkCoordinates(geometry.coordinates, (x, y) => { bounds.minX = Math.min(bounds.minX, x); bounds.minY = Math.min(bounds.minY, y); bounds.maxX = Math.max(bounds.maxX, x); bounds.maxY = Math.max(bounds.maxY, y); }); } if (data.type === 'FeatureCollection') { (data.features || []).forEach((feature) => addGeometry(feature.geometry)); } else if (data.type === 'Feature') { addGeometry(data.geometry); } else { addGeometry(data); } if (!Number.isFinite(bounds.minX) || !Number.isFinite(bounds.minY)) { return null; } return bounds; } function createBoundingBoxTransform(sourceBounds, targetBounds, calibration) { const sourceWidth = sourceBounds.maxX - sourceBounds.minX; const sourceHeight = sourceBounds.maxY - sourceBounds.minY; const targetCenterLng = (targetBounds.west + targetBounds.east) / 2 + calibration.offsetLng; const targetCenterLat = (targetBounds.south + targetBounds.north) / 2 + calibration.offsetLat; const targetWidth = (targetBounds.east - targetBounds.west) * calibration.scaleX; const targetHeight = (targetBounds.north - targetBounds.south) * calibration.scaleY; return function coordsToLatLng(coordinates) { const xRatio = ((coordinates[0] - sourceBounds.minX) / sourceWidth) - 0.5; const yRatio = ((coordinates[1] - sourceBounds.minY) / sourceHeight) - 0.5; const lng = targetCenterLng + (xRatio * targetWidth); const lat = targetCenterLat + (yRatio * targetHeight); return L.latLng(lat, lng); }; } window.addKecamatanLayer = async function addKecamatanLayer(map, options) { const settings = Object.assign({ source: DEFAULT_SOURCE, anchor: DEFAULT_ANCHOR, targetBounds: DEFAULT_GEO_BOUNDS, calibration: DEFAULT_CALIBRATION, fitBounds: false }, options || {}); if (!map || typeof L === 'undefined') { return null; } if (!map.getPane('kecamatanPane')) { map.createPane('kecamatanPane'); map.getPane('kecamatanPane').style.zIndex = 350; } const response = await fetch(settings.source, { headers: { 'Accept': 'application/json' } }); if (!response.ok) { throw new Error('Gagal memuat data batas kecamatan.'); } const data = await response.json(); const coordinateBounds = getCoordinateBounds(data); if (!coordinateBounds) { throw new Error('Data batas kecamatan tidak memiliki koordinat valid.'); } const layer = L.geoJSON(data, { coordsToLatLng: createBoundingBoxTransform(coordinateBounds, settings.targetBounds, settings.calibration), pane: 'kecamatanPane', style: { color: '#0f766e', weight: 2, opacity: 0.9, fillColor: '#14b8a6', fillOpacity: 0.08, dashArray: '6 4' }, onEachFeature: function (feature, featureLayer) { const name = feature && feature.properties && ( feature.properties.nama || feature.properties.NAMA || feature.properties.KECAMATAN || feature.properties.kecamatan ); featureLayer.bindPopup(name ? `Kecamatan: ${name}` : 'Batas Kecamatan'); } }).addTo(map); if (settings.fitBounds && layer.getBounds().isValid()) { map.fitBounds(layer.getBounds().pad(0.12), { maxZoom: 15 }); } return layer; }; }());