156 lines
4.2 KiB
JavaScript
156 lines
4.2 KiB
JavaScript
(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;
|
|
};
|
|
}());
|