// Import necessary modules
const express = require('express');
const turf = require('@turf/turf');
const app = express();
const PORT = 3000;
// Function to calculate a good bounding box with padding
function calculateBoundingBox(points, padding = 0.02) {
const minLat = Math.min(...points.map(p => p.lat));
const maxLat = Math.max(...points.map(p => p.lat));
const minLon = Math.min(...points.map(p => p.lon));
const maxLon = Math.max(...points.map(p => p.lon));
return [
minLon - padding, // West (min longitude with padding)
minLat - padding, // South (min latitude with padding)
maxLon + padding, // East (max longitude with padding)
maxLat + padding // North (max latitude with padding)
];
}
// Function to calculate the average center of the points
function calculateCenter(points) {
const avgLat = points.reduce((sum, p) => sum + p.lat, 0) / points.length;
const avgLon = points.reduce((sum, p) => sum + p.lon, 0) / points.length;
return [avgLat, avgLon];
}
// Define an endpoint for calculating the optimal point
app.get('/calculate', (req, res) => {
// Parse coordinates from query parameters
const coordinates = req.query.coords;
if (!coordinates) {
return res.status(400).json({ error: 'Please provide coordinates as query parameters.' });
}
// Parse the coordinates into an array of places
try {
const places = JSON.parse(coordinates).map(coord => ({
lat: parseFloat(coord.lat),
lon: parseFloat(coord.lon),
population: parseInt(coord.population, 10)
}));
// Validate that the coordinates have latitude, longitude, and population
if (places.some(place => isNaN(place.lat) || isNaN(place.lon) || isNaN(place.population))) {
return res.status(400).json({ error: 'Invalid coordinate format. Ensure lat, lon, and population are numbers.' });
}
const bbox = calculateBoundingBox(places);
const center = calculateCenter(places);
// Convert place data into GeoJSON points for Turf.js
const points = places.map(place => {
return turf.point([place.lon, place.lat], { population: place.population });
});
// Generate the Voronoi polygons using the bounding box
const voronoiPolygons = turf.voronoi(turf.featureCollection(points), { bbox: bbox });
let optimalPoint = null;
if (voronoiPolygons) {
voronoiPolygons.features.forEach((feature, index) => {
const centroid = turf.centroid(feature);
const lat = centroid.geometry.coordinates[1];
const lon = centroid.geometry.coordinates[0];
const maxPopulation = Math.max(...places.map(p => p.population));
if (places[index] && places[index].population === maxPopulation) {
optimalPoint = { lat: lat, lon: lon };
}
});
}
// Return the results as JSON
res.json({
boundingBox: bbox,
center: center,
optimalPoint: optimalPoint,
voronoiPolygons: voronoiPolygons
});
} catch (error) {
return res.status(400).json({ error: 'Invalid input format. Use a JSON array of coordinates.' });
}
});
// Start the server
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});