Android Question Urgent help - Function for optimal point using Voronoi to control crop disease.

Hi All,
There are 5 villages where there are plant diseases with varying severity of incidents. The agricultural officers need to set crop disease control camp at an optimal point so they can reach the villages efficiently. Given latitude and longitude of the villages and the number of plant disease incidents, the optimal point for camp can be found by Voronoi . Has any one implemented Voronoi in any of their apps. If so can you please share the function?
Thank you in advance.
 
I found that Turf provides advanced geospatial analysis capabilities for browsers and Node.js . Since the requirement was urgent, I created an API (hosted on free tier of render.com) for Voronoi calculation using turf. Please note: I am still interested in b4x stand alone solution .
In case someone is interested, please find below the code to host in node js environment. You pass the latitude and longitude along with population / severity of cases and you will get the optimal Voronoi point back. I just pass on input like this

https://xxxxxxx-2as8.onrender.com/calculate?coords=[{"lat":10.0843179,"lon":77.9610158,"population":34},
{"lat":10.02383,"lon":78.22352,"population":53},
{"lat":10.0699385,"lon":78.003552,"population":42},
{"lat":9.46212,"lon":78.5411,"population":78},
{"lat":9.5694148,"lon":78.4968049,"population":26}]
Vornoi Point API:
// 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}`);
});

This was useful for the webpage map too (added more elaborate data). Disclaimer: All lat long shown are examples and do not correspond to current plant disease location :

voronoi and population weighted points.JPG
 
Upvote 0
Top