Public
Edited
Feb 19, 2024
Insert cell
Insert cell
data = applyLloydsAlgorithm(sleman)
Insert cell
Insert cell
Insert cell
Insert cell
function applyLloydsAlgorithm(data, maxIter = 20, rows = 10000, cols = 10000) {
// Extract centroid coordinates
let centroids = data.features.map(
(feature) => turf.centroid(feature).geometry.coordinates
);

// Initialize positions (this could be random or based on an initial configuration)
let positions = centroids; // Here we just use the original centroids
let bbox = turf.bbox(turf.points(positions));

// Apply Lloyd's algorithm
for (let iter = 0; iter < maxIter; iter++) {
// Construct Voronoi cells
var options = {
bbox: bbox
};
let voronoi = turf.voronoi(turf.points(positions), options);

// Compute Voronoi centroids and move points
let newPositions = voronoi.features.map(
(feature) => turf.centroid(feature).geometry.coordinates
);

// Check convergence (here we use a simple position change threshold)
let maxPosChange = d3.max(newPositions, (pos, i) =>
turf.distance(pos, positions[i], { units: "meters" })
);
if (maxPosChange < 1e-6) break;

positions = newPositions;
}

// Define the number of rows and columns for your grid
let xExtent = d3.extent(positions, (d) => d[0]);
let yExtent = d3.extent(positions, (d) => d[1]);

// Define the bin size
let xBinSize = (xExtent[1] - xExtent[0]) / cols;
let yBinSize = (yExtent[1] - yExtent[0]) / rows;

// Update positions based on binning
positions = positions.map((pos) => {
let i = Math.floor((pos[0] - xExtent[0]) / xBinSize);
let j = Math.floor((pos[1] - yExtent[0]) / yBinSize);
return [
xExtent[0] + (i + 0.5) * xBinSize,
yExtent[0] + (j + 0.5) * yBinSize
];
});

// Save the updated positions as a new GeoJSON file
let updatedData = {
type: "FeatureCollection",
features: positions.map((pos, i) => ({
type: "Feature",
geometry: {
type: "Point",
coordinates: pos
},
properties: data.features[i].properties // Preserve the original properties
}))
};

return updatedData;
}
Insert cell
Insert cell
import { quickmap } from "@danylaksono/quickmap"
Insert cell
Insert cell
function applyLloydsAlgorithmWithForce(data, maxIter = 10, forceStrength = 1) {
// Extract centroid coordinates
let centroids = data.features.map(
(feature) => turf.centroid(feature).geometry.coordinates
);

// Initialize positions (this could be random or based on an initial configuration)
let positions = centroids; // Here we just use the original centroids
let bbox = turf.bbox(turf.points(positions));

// Initialize force simulation
let simulation = d3
.forceSimulation(positions)
.force("center", d3.forceCenter())
.force("charge", d3.forceManyBody().strength(forceStrength))
.stop();

// Apply Lloyd's algorithm
for (let iter = 0; iter < maxIter; iter++) {
// Construct Voronoi cells
var options = {
bbox: bbox
};
let voronoi = turf.voronoi(turf.points(positions), options);

// Compute Voronoi centroids and move points
let newPositions = voronoi.features.map(
(feature) => turf.centroid(feature).geometry.coordinates
);

// Apply force simulation
simulation.nodes(newPositions);
for (var i = 0; i < 300; ++i) simulation.tick();

// Check convergence (here we use a simple position change threshold)
let maxPosChange = d3.max(newPositions, (pos, i) =>
turf.distance(pos, positions[i], { units: "meters" })
);
if (maxPosChange < 1e-6) break;

positions = newPositions;
}

// Save the updated positions as a new GeoJSON file
let updatedData = {
type: "FeatureCollection",
features: positions.map((pos, i) => ({
type: "Feature",
geometry: {
type: "Point",
coordinates: pos
},
properties: data.features[i].properties // Preserve the original properties
}))
};

return updatedData;
}
Insert cell
Insert cell
kabupaten3 = FileAttachment("kabupaten (3).json").json()
Insert cell
kabupaten3.features.forEach((feature, index) => {
let ring = feature.geometry.coordinates[0];
ring.forEach((coord, i) => {
if (!Array.isArray(coord) || coord.length !== 2) {
console.log(`Invalid coordinates at feature ${index}, ring ${i}:`, coord);
}
});
})
Insert cell
data2 = applyLloydsAlgorithmWithForce(kabupaten3)
Insert cell
{
const container = yield htl.html`<div style="height: 500px;">`;
const map = L.map(container);
const layer = L.geoJSON(data2).addTo(map);
map.fitBounds(layer.getBounds(), {maxZoom: 9});
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© <a href=https://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
}).addTo(map);
}
Insert cell
kabupaten2 = FileAttachment("kabupaten (2).json").json()
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more