diff --git a/backend/app/server.js b/backend/app/server.js index 41c5b04..af1ed10 100644 --- a/backend/app/server.js +++ b/backend/app/server.js @@ -1,66 +1,130 @@ const express = require('express'); const cors = require('cors'); +const { Pool } = require('pg'); require('dotenv').config(); const app = express(); const PORT = process.env.PORT || 3001; +// Database connection pool +const pool = new Pool({ + connectionString: process.env.DATABASE_URL || 'postgresql://line_of_sight:line_of_sight_pass@postgres:5432/line_of_sight' +}); + app.use(cors()); app.use(express.json()); -// Mock conurbation data for MVP -const MOCK_CONURBATIONS = [ - { id: 1, name: "London", population: 9000000, distance_km: 0, lat: 51.5074, lon: -0.1278 }, - { id: 2, name: "Paris", population: 2161000, distance_km: 344, lat: 48.8566, lon: 2.3522 }, - { id: 3, name: "Berlin", population: 3644000, distance_km: 878, lat: 52.5200, lon: 13.4050 }, - { id: 4, name: "Warsaw", population: 1793000, distance_km: 1200, lat: 52.2297, lon: 21.0122 }, - { id: 5, name: "Moscow", population: 12506000, distance_km: 2063, lat: 55.7558, lon: 37.6173 }, - { id: 6, name: "Kazan", population: 1257000, distance_km: 2850, lat: 55.7897, lon: 49.1219 }, - { id: 7, name: "Almaty", population: 2000000, distance_km: 3900, lat: 43.2220, lon: 76.8512 }, - { id: 8, name: "Urumqi", population: 3500000, distance_km: 4500, lat: 43.8256, lon: 87.6168 }, - { id: 9, name: "Lahore", population: 11126000, distance_km: 5400, lat: 31.5204, lon: 74.3587 }, - { id: 10, name: "New Delhi", population: 29399000, distance_km: 5800, lat: 28.6139, lon: 77.2090 }, - { id: 11, name: "Dhaka", population: 21006000, distance_km: 6200, lat: 23.8103, lon: 90.4125 }, - { id: 12, name: "Chennai", population: 10971000, distance_km: 6500, lat: 13.0827, lon: 80.2707 }, - { id: 13, name: "Bangkok", population: 10539000, distance_km: 7200, lat: 13.7563, lon: 100.5018 }, - { id: 14, name: "Jakarta", population: 10562000, distance_km: 8100, lat: -6.2088, lon: 106.8456 }, - { id: 15, name: "Singapore", population: 5686000, distance_km: 8300, lat: 1.3521, lon: 103.8198 }, - { id: 16, name: "Manila", population: 17801000, distance_km: 8700, lat: 14.5995, lon: 120.9842 }, - { id: 17, name: "Tokyo", population: 37400000, distance_km: 9500, lat: 35.6762, lon: 139.6503 }, - { id: 18, name: "Seoul", population: 9720000, distance_km: 9200, lat: 37.5665, lon: 126.9780 }, - { id: 19, name: "Beijing", population: 21540000, distance_km: 8900, lat: 39.9042, lon: 116.4074 }, - { id: 20, name: "Shanghai", population: 27058000, distance_km: 9000, lat: 31.2304, lon: 121.4737 } -]; +// Helper to calculate destination point given start, bearing, and distance (km) +const calculateDestination = (lat, lon, bearing, distance) => { + const R = 6371; + const brng = (bearing * Math.PI) / 180; + const φ1 = (lat * Math.PI) / 180; + const λ1 = (lon * Math.PI) / 180; + const δ = distance / R; -// Mock API endpoint - returns dummy conurbations based on input coordinates -app.get('/api/line-of-sight', (req, res) => { + const φ2 = Math.asin( + Math.sin(φ1) * Math.cos(δ) + + Math.cos(φ1) * Math.sin(δ) * Math.cos(brng) + ); + const λ2 = + λ1 + + Math.atan2( + Math.sin(brng) * Math.sin(δ) * Math.cos(φ1), + Math.cos(δ) - Math.sin(φ1) * Math.sin(φ2) + ); + + return { + lat: (φ2 * 180) / Math.PI, + lon: (((λ2 * 180) / Math.PI + 540) % 360) - 180 + }; +}; + +// Real API endpoint - uses PostGIS for spatial queries +app.get('/api/line-of-sight', async (req, res) => { const { lat, lon, direction, tolerance } = req.query; - console.log(`Received request: lat=${lat}, lon=${lon}, direction=${direction}, tolerance=${tolerance}`); - - // Return mock data for MVP - res.json({ - success: true, - data: { - start_point: { lat: parseFloat(lat) || 51.5074, lon: parseFloat(lon) || -0.1278 }, - direction: parseInt(direction) || 45, - tolerance_km: parseInt(tolerance) || 50, - conurbations: MOCK_CONURBATIONS.slice(0, 20), - line_coordinates: [ - { lat: 51.5074, lon: -0.1278 }, - { lat: 48.8566, lon: 2.3522 }, - { lat: 52.5200, lon: 13.4050 }, - { lat: 55.7558, lon: 37.6173 }, - { lat: 43.2220, lon: 76.8512 }, - { lat: 28.6139, lon: 77.2090 }, - { lat: 13.7563, lon: 100.5018 }, - { lat: -6.2088, lon: 106.8456 }, - { lat: 35.6762, lon: 139.6503 }, - { lat: 51.5074, lon: -0.1278 } // Complete the circle - ] - }, - message: "Mock data returned for MVP - Real geospatial calculations coming soon" - }); + const startLat = parseFloat(lat) || 51.5074; + const startLon = parseFloat(lon) || -0.1278; + const bearing = parseInt(direction) || 0; + const toleranceKm = parseInt(tolerance) || 50; + + console.log(`Processing real request: lat=${startLat}, lon=${startLon}, bearing=${bearing}, tolerance=${toleranceKm}`); + + try { + // Generate path points for visualization and spatial query + const pathPoints = []; + const totalDistance = 20000; + const steps = 80; // More steps for smoother speed transition + + for (let i = 0; i <= steps; i++) { + const dist = (totalDistance * i) / steps; + pathPoints.push(calculateDestination(startLat, startLon, bearing, dist)); + } + + // Batch check for 'over water' status for all path points + // We'll consider a point 'over water' if no city is within 500km + const waterChecks = await Promise.all(pathPoints.map(async (p) => { + const checkQuery = ` + SELECT EXISTS ( + SELECT 1 FROM cities + WHERE ST_DWithin(geom, ST_SetSRID(ST_MakePoint($1, $2), 4326)::geography, 500000) + LIMIT 1 + ) as has_land; + `; + const res = await pool.query(checkQuery, [p.lon, p.lat]); + return !res.rows[0].has_land; + })); + + const pathPointsWithWater = pathPoints.map((p, i) => ({ + ...p, + is_over_water: waterChecks[i] + })); + + const lineWKT = `LINESTRING(${pathPoints.map(p => `${p.lon} ${p.lat}`).join(',')})`; + + const query = ` + WITH path AS ( + SELECT ST_GeogFromText($1) as route, + ST_MakePoint($3, $4)::geography as start_node + ) + SELECT + id, + name, + population, + country, + ST_Y(geom::geometry) as lat, + ST_X(geom::geometry) as lon, + ST_Distance(geom, (SELECT route FROM path)) / 1000 as distance_off_line_km, + ST_Distance(geom, (SELECT start_node FROM path)) / 1000 as distance_from_start_km, + ST_LineLocatePoint((SELECT route FROM path)::geometry, geom::geometry) as pos_on_line + FROM cities + WHERE ST_DWithin(geom, (SELECT route FROM path), $2 * 1000) + ORDER BY pos_on_line ASC + LIMIT 200; + `; + + const result = await pool.query(query, [lineWKT, toleranceKm, startLon, startLat]); + + res.json({ + success: true, + data: { + start_point: { lat: startLat, lon: startLon }, + direction: bearing, + tolerance_km: toleranceKm, + conurbations: result.rows.map(row => ({ + ...row, + name: row.name || 'Unknown', + country: row.country || 'Unknown', + distance_km: Math.round(row.distance_from_start_km), + off_line_km: Math.round(row.distance_off_line_km) + })), + line_coordinates: pathPointsWithWater + } + }); + } catch (err) { + console.error('Database query error:', err); + res.status(500).json({ success: false, error: 'Database query failed' }); + } }); // Health check endpoint diff --git a/backend/package-lock.json b/backend/package-lock.json index 1fbc974..d32c9a4 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -8,6 +8,7 @@ "name": "line-of-sight-backend", "version": "1.0.0", "dependencies": { + "axios": "^1.13.6", "cors": "^2.8.6", "dotenv": "^17.3.1", "express": "^5.2.1", @@ -1426,6 +1427,23 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-jest": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.3.0.tgz", @@ -1891,6 +1909,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2008,6 +2038,15 @@ "node": ">=0.10.0" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2128,6 +2167,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2329,6 +2383,26 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -2345,6 +2419,43 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2533,6 +2644,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -4158,6 +4284,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", diff --git a/backend/package.json b/backend/package.json index 5dd16ad..1769387 100644 --- a/backend/package.json +++ b/backend/package.json @@ -6,16 +6,18 @@ "scripts": { "start": "node app/server.js", "dev": "nodemon app/server.js", - "test": "jest" + "test": "jest", + "seed-data": "node scripts/import_cities.js" }, "dependencies": { - "express": "^5.2.1", + "axios": "^1.13.6", "cors": "^2.8.6", - "pg": "^8.20.0", - "dotenv": "^17.3.1" + "dotenv": "^17.3.1", + "express": "^5.2.1", + "pg": "^8.20.0" }, "devDependencies": { - "nodemon": "^3.1.14", - "jest": "^30.3.0" + "jest": "^30.3.0", + "nodemon": "^3.1.14" } } diff --git a/backend/scripts/import_cities.js b/backend/scripts/import_cities.js new file mode 100644 index 0000000..7cf53ae --- /dev/null +++ b/backend/scripts/import_cities.js @@ -0,0 +1,95 @@ +const { Pool } = require('pg'); +const { execSync } = require('child_process'); +const fs = require('fs'); +const readline = require('readline'); +require('dotenv').config(); + +const DATA_URL = 'https://download.geonames.org/export/dump/cities5000.zip'; +const ZIP_FILE = '/tmp/cities.zip'; +const TXT_FILE = '/tmp/cities5000.txt'; + +async function importGeoNames() { + const pool = new Pool({ + connectionString: process.env.DATABASE_URL || 'postgresql://line_of_sight:line_of_sight_pass@postgres:5432/line_of_sight' + }); + + try { + console.log('Downloading GeoNames cities5000 (Pop > 5000)...'); + execSync(`wget -q ${DATA_URL} -O ${ZIP_FILE}`); + + console.log('Extracting data...'); + execSync(`unzip -o ${ZIP_FILE} -d /tmp`); + + console.log('Connecting to database...'); + const client = await pool.connect(); + + // Ensure table is clean + await client.query('TRUNCATE TABLE cities'); + + console.log('Starting stream import...'); + + const fileStream = fs.createReadStream(TXT_FILE); + const rl = readline.createInterface({ + input: fileStream, + crlfDelay: Infinity + }); + + let batch = []; + const batchSize = 500; + let count = 0; + + for await (const line of rl) { + const parts = line.split('\t'); + if (parts.length < 15) continue; + + const name = parts[1]; // name + const lat = parseFloat(parts[4]); + const lon = parseFloat(parts[5]); + const country = parts[8]; // country code + const population = parseInt(parts[14]) || 0; + + batch.push({ name, lat, lon, country, population }); + + if (batch.length >= batchSize) { + await insertBatch(client, batch); + count += batch.length; + if (count % 5000 === 0) console.log(`Imported ${count} cities...`); + batch = []; + } + } + + if (batch.length > 0) { + await insertBatch(client, batch); + count += batch.length; + } + + console.log(`SUCCESS: Imported ${count} cities and towns.`); + client.release(); + + } catch (err) { + console.error('ERROR during import:', err); + } finally { + // Cleanup + if (fs.existsSync(ZIP_FILE)) fs.unlinkSync(ZIP_FILE); + if (fs.existsSync(TXT_FILE)) fs.unlinkSync(TXT_FILE); + await pool.end(); + } +} + +async function insertBatch(client, batch) { + const queryParts = []; + const values = []; + + batch.forEach((city, index) => { + const base = index * 5; + queryParts.push(`($${base + 1}, $${base + 2}, $${base + 3}, ST_SetSRID(ST_MakePoint($${base + 4}, $${base + 5}), 4326)::geography)`); + values.push(city.name, city.population, city.country, city.lon, city.lat); + }); + + await client.query( + `INSERT INTO cities (name, population, country, geom) VALUES ${queryParts.join(',')}`, + values + ); +} + +importGeoNames(); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index dcade21..4c991a5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "line-of-sight-frontend", "version": "1.0.0", "dependencies": { + "@turf/turf": "^7.3.4", "axios": "^1.7.9", "maplibre-gl": "^5.20.1", "react": "^19.2.1", @@ -1478,6 +1479,1937 @@ } } }, + "node_modules/@turf/along": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-7.3.4.tgz", + "integrity": "sha512-PvIoXin0I1t3nRwJz7uqR6fsxDMqdGwJq90qGOeqkNwlZqlF+5o2wKHPwYwi0RXZhLvxRP5qlbNIvV8ADdbWxw==", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/angle": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/angle/-/angle-7.3.4.tgz", + "integrity": "sha512-235JAfbrNMjHQXQfd/p+fYnlfCHsQsKHda5Eeyc+/jIY0s5mKvhcxgFaOEnigA2q1n+PrVOExs3BViGTKnWhAg==", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/area": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-7.3.4.tgz", + "integrity": "sha512-UEQQFw2XwHpozSBAMEtZI3jDsAad4NnHL/poF7/S6zeDCjEBCkt3MYd6DSGH/cvgcOozxH/ky3/rIVSMZdx4vA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.3.4.tgz", + "integrity": "sha512-D5ErVWtfQbEPh11yzI69uxqrcJmbPU/9Y59f1uTapgwAwQHQztDWgsYpnL3ns8r1GmPWLP8sGJLVTIk2TZSiYA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-clip": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-7.3.4.tgz", + "integrity": "sha512-HCn0q/WPVEE9Dztg7tCvClOPrrh9MoxNUk73byHvcZLBcvziN6F84f/ZbFcbQSh8hgOeVMs/keeqWMqsICcNLg==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-7.3.4.tgz", + "integrity": "sha512-XCDYQwCA41Bum3R1xX0Na1nR4ozoe/pCYy5bxqrzyMs87kPJUIfBrD5IWxjnZyLqFpfEpolMHJz5ed1uA2PanQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bearing": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-7.3.4.tgz", + "integrity": "sha512-zvFjapyFaOrM8nBtAND7f4yb0BJV0jyj6cyoXyTYqLY+3Hn0eHgL0M8lwxDLbTom5KfqYDHDVDQC3+VSfypoEA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bezier-spline": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-7.3.4.tgz", + "integrity": "sha512-+iDUeiBKByIs/6K5WW8pG6IDxrRLJHFLM80zSpzk2xBtgy3mq36NZwwt67Pu7EJAkc9GUXKIm9SkspoKue9aYQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-clockwise": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-7.3.4.tgz", + "integrity": "sha512-X/O+u/OsoJ99mujhlqviuB7HX0tdJ5931TBjNSseps43XtROVuB5PwBDgwKfu5lY1B4DSGAxbbxJ795RmPnguQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-concave": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-concave/-/boolean-concave-7.3.4.tgz", + "integrity": "sha512-SHuAzjqaAes6ELDZcN/FKZWCQZsqwYv3gMosoLRFWTwKyBQe8i29e4y6XnXakDr1uklVUeRRcdhZ5oKtX9ABPQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-contains": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-7.3.4.tgz", + "integrity": "sha512-AJMGbtC6HiXgHvq0RNlTfsDB58Qf9Js45MP/APbhGTH4AiLZ8VMDISywVFNd7qN6oppNlDd3xApVR28+ti8bNg==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-split": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-crosses": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-7.3.4.tgz", + "integrity": "sha512-v/U3SuGdkexfLTMhho6Vj0OjqPUeYdThxp8zggGJ1VHow27fvLLez0DjUR3AftHjjHM6bRzZoNsu2qUlEe5hjw==", + "dependencies": { + "@turf/boolean-equal": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/polygon-to-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-disjoint": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-7.3.4.tgz", + "integrity": "sha512-Dl4O27ygi2NqskGQuvSlDLJYlJ2SPkHb3A9T/v6eAudjlMiKdEY6bMxKUfU5y+Px1WiCZxd+9rXGXJgGC3WiQg==", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/polygon-to-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-equal": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-7.3.4.tgz", + "integrity": "sha512-AhWqe7D1o0wp3d3QQRSqgWDI8s1JfTFKFe9rU5mrSxYPGlmaQsJC07RCaYfFiGym9lACd1lxBJiPidCbLaPOfw==", + "dependencies": { + "@turf/clean-coords": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "geojson-equality-ts": "^1.0.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-intersects": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-intersects/-/boolean-intersects-7.3.4.tgz", + "integrity": "sha512-sxi41NXkb5hrJgOvpm32hyBLhW8fem0vn2XxR4+jyRg1rM/v3ziF10/VqC9KDZuDNZkt9JjL9B0825Cf7AN6Lg==", + "dependencies": { + "@turf/boolean-disjoint": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-overlap": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-7.3.4.tgz", + "integrity": "sha512-Q3dlswIuqffSiMfln7xa36YDnN1TWtERMF/155rzjglm4NTUG/6S+gNsb8s6qpLjc+hN6btCq1ZjxAWurPf8Vg==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/line-overlap": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "geojson-equality-ts": "^1.0.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-parallel": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-7.3.4.tgz", + "integrity": "sha512-sTNMqsUkLPnSJEqc2IZ5ig3nHRoubyOH2HW1LILqOybCJI630FEM9UoYP1pZniF5nwTyCjQWnXA1FxusVILuFQ==", + "dependencies": { + "@turf/clean-coords": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/line-segment": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-point-in-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-7.3.4.tgz", + "integrity": "sha512-v/4hfyY90Vz9cDgs2GwjQf+Lft8o7mNCLJOTz/iv8SHAIgMMX0czEoIaNVOJr7tBqPqwin1CGwsncrkf5C9n8Q==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "point-in-polygon-hao": "^1.1.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-point-on-line": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-7.3.4.tgz", + "integrity": "sha512-70gm5x6YQOZKcw0b/O4jjMwVWnFj+Zb6TXozLgZFDZShc8pgTQtZku7K+HKZ7Eya+7usHIB4IimZauomOMa+iw==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-touches": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-touches/-/boolean-touches-7.3.4.tgz", + "integrity": "sha512-XOwhjc0oCWhnBUB+l4drpXcg7mkNXPX3SuSz/Xv7gvLH/yRrBwzVGllzK1AHlGU9BVkGVBJIZGYX7jgTM681NQ==", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-valid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-valid/-/boolean-valid-7.3.4.tgz", + "integrity": "sha512-P6M9BtRvzFF2N5g+1/DTIbYGpEbwQ2sv/Pw+uj11P3NYAA9VE8mvrxFYf+CowFdSfY6bY4ejhuqKhrTmAMv7wA==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-crosses": "7.3.4", + "@turf/boolean-disjoint": "7.3.4", + "@turf/boolean-overlap": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@types/geojson": "^7946.0.10", + "geojson-polygon-self-intersections": "^1.2.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-within": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-7.3.4.tgz", + "integrity": "sha512-eLgi803gz0KcYkyxnnqnz9Vd6tw2/0eAExe/Rq8sO0dqypaSiomSumxjqu89d/yo24Qz8gW7c0kJ6YihNbMYxA==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-split": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/buffer": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-7.3.4.tgz", + "integrity": "sha512-MVOCBDuOl3KGDsh2stW12RmiFaFeSkVjeUbZ+ADUtIVnv+jlFsmjBpFtsEw8s9YQn5g0667QppOshm0FBHA57Q==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/center": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/jsts": "^2.7.1", + "@turf/meta": "7.3.4", + "@turf/projection": "7.3.4", + "@types/geojson": "^7946.0.10", + "d3-geo": "1.7.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-7.3.4.tgz", + "integrity": "sha512-4SsLMDHWthXbyIHsczgFCo4fx+8tC8w2+B5HdEuY+P+cSOOL4T+6QQzd7WWjuN/Y3ndowFssUmwRrvXuwVRxQA==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-mean": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-7.3.4.tgz", + "integrity": "sha512-6foVk5HLjlSPr48EI686Eis6/bYrJiHjKQlwY/7YlJc1uDitsIjPw2LjUCGIUZDEd6PdNUgg1+LgI7klXYvW3A==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-median": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-7.3.4.tgz", + "integrity": "sha512-Bz6rDr0plQOGSXgT3X3t941pYd44a5vIY8OEt4Y11H1BsgpmzFc6g7L5mr7FXW/uiYGxOewAfNcVUYUdJf9kMg==", + "dependencies": { + "@turf/center-mean": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-of-mass": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-7.3.4.tgz", + "integrity": "sha512-mOSupDF5qxQTA/kOWYletHcBJQ3S2gVl/IRgrBH/YY9yiFq6UGRpZ0sNcIML4H06u/1DY/jqqG+d1nc/1yIA6Q==", + "dependencies": { + "@turf/centroid": "7.3.4", + "@turf/convex": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/centroid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-7.3.4.tgz", + "integrity": "sha512-6c3kyTSKBrmiPMe75UkHw6MgedroZ6eR5usEvdlDhXgA3MudFPXIZkMFmMd1h9XeJ9xFfkmq+HPCdF0cOzvztA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/circle": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-7.3.4.tgz", + "integrity": "sha512-6ccr5iT51/XONF+pbpkqoRxKX4ZVWLubXb1frGCnClv2suo1UIY9SIlINNctVDupXd2P9PpqZCbrXATrcrokPg==", + "dependencies": { + "@turf/destination": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clean-coords": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-7.3.4.tgz", + "integrity": "sha512-S61aJXLvPN/uZHtjzmJbLv7xhi28Sq3PshCIZSvno4Mo45bvl79Vg4aZskrG05AaSSbipplqfH+MZrkW9Xboeg==", + "dependencies": { + "@turf/boolean-point-on-line": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clone": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-7.3.4.tgz", + "integrity": "sha512-pwQ+RyQw986uu7IulY/18NRAebwZZScb084bvVqVkTrllwLSv4oVBqUxmUMiwtp+PNdiRGRFOvNyZqtRsiD+Jw==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-7.3.4.tgz", + "integrity": "sha512-+zoSyiF0LilXy4Tr0/lC7IgqbTMZZ2wwP3iSrqre58b61pUtdhCnBcjA2r8FkcW7z3GMbGf5XkIWhO+b+vDSsw==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-dbscan": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-7.3.4.tgz", + "integrity": "sha512-RkuXf767Shk0AfY+fh0PASVw8YR4H8zYR7XQrCgWd/bCuh6CXs7rWZ6UTLu/PiA6y6WsIhyAQv4LhNH5kCzpbA==", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "@types/geokdbush": "^1.1.5", + "geokdbush": "^2.0.1", + "kdbush": "^4.0.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-kmeans": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-7.3.4.tgz", + "integrity": "sha512-89mlwhcb+vyZAKX0eBa3LQ8VyIKLayrzJpKGb90sEkIu0hDua9JCE+zlbaPoUAvAqflEiX+poFFuh7pngtsBMg==", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "skmeans": "0.9.7", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/collect": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-7.3.4.tgz", + "integrity": "sha512-fG28oDZK4HCXC/AhF0pmHKLtI9DWwdJr/ktuWolrqzA5b1G7eawrXwDu8B5I3sXhdWonNRMcuLbIuz+XQscHKw==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/combine": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-7.3.4.tgz", + "integrity": "sha512-wNp9ar4FfpTfQXLZWXQ/jUBBoUFOwRN/mmlv5xrhoYFpP/F5SNy7GVDMZXaBfHdUUplfJUPF5hIKQlCUR8+k3A==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/concave": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-7.3.4.tgz", + "integrity": "sha512-HZa1CV2pv4Xpcoe3t5S3ZW6j9jVbc27exzKwZWF7MlFxSz4BKRirWiME8Fku8nvQcGafpfLc+Lwpma+nGvg06w==", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/tin": "7.3.4", + "@types/geojson": "^7946.0.10", + "topojson-client": "3.x", + "topojson-server": "3.x", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/convex": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-7.3.4.tgz", + "integrity": "sha512-zeNv0fFdOoHuOQB7nl6OLb0DyjvzDvm0e3zlFkph50GF9pEKOmkCSmlniw681aWL2aRBdWZBnON3rRzOS+9C7Q==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "concaveman": "^1.2.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/destination": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-7.3.4.tgz", + "integrity": "sha512-YxoUJwkKmTHiRFQxMQOP0tz8Vy+ga5EXl+C+F/WubjDLwT1AJu5y8CNIjLvWyjPWckj/vZG4u/1js5bx6MLADA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/difference": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-7.3.4.tgz", + "integrity": "sha512-kIxizNQrYLO2rtqUIeed0tPycicrXoipy/g9d4mjv91kzBEbwpyojz9zi8U9G1ISBfCEgA7wsViQD0r+8qzxXw==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/dissolve": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-7.3.4.tgz", + "integrity": "sha512-xjGY1gQ4icWhDgsW0YfU2KQtij1+ru34AfvtkVMQEgI86O9EwjW2r9Jq5DJY2PMKPbor3kz9yM/RTOiDP7f3Jg==", + "dependencies": { + "@turf/flatten": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-7.3.4.tgz", + "integrity": "sha512-9drWgd46uHPPyzgrcRQLgSvdS/SjVlQ6ZIBoRQagS5P2kSjUbcOXHIMeOSPwfxwlKhEtobLyr+IiR2ns1TfF8w==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance-weight": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/distance-weight/-/distance-weight-7.3.4.tgz", + "integrity": "sha512-dVMNEmIluKgn7iQTmzJJOe0UASRNmmSdFX1boAev5MISaW3AvPiURCCOV+lTIeoaQbWRpEAESbAp6JIimXFr8Q==", + "dependencies": { + "@turf/centroid": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/ellipse": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-7.3.4.tgz", + "integrity": "sha512-SMgbERZl12j7H8YaIofmnf0NwAvdF5Wly4tjI/eUhj/sFOKrKXOS1lvCSBJ6uSV9tFijl3ecGOVOlTpURdZ30g==", + "dependencies": { + "@turf/destination": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/transform-rotate": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/envelope": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-7.3.4.tgz", + "integrity": "sha512-anXSjYMXGAyXT7rpO74VyRI0q/rPAbKE/MYvou+QvG0U/Oa7el0yF4JNNi9wKEAxXg/10aWm9kHp8s2caeLg6A==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/explode": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-7.3.4.tgz", + "integrity": "sha512-7QWhp3f8jhrWjvArhJ74hXBFHMaiJr/2Y1PzHCWue2/pC5MbbTV0o7peehwrrrJC/1uD6CVb3hlcb77IxtMQkw==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flatten": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-7.3.4.tgz", + "integrity": "sha512-Yt3HCh/qeNaXS4LYhXczFhBfTeaKlTBoxEw1OICb9RT3SiGU0XCxuK7H0W26OLo7XxB0qP7GPs2L3FZbiri6wQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flip": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-7.3.4.tgz", + "integrity": "sha512-HME+kVMTyvcsYVY6dC6DTvuzq8vvDpw+C7PviEqpuT3KcVlBCoGPAqlWRdyWYOb9MDciOqNxvvJF/okpb/GQcg==", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/geojson-rbush": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/geojson-rbush/-/geojson-rbush-7.3.4.tgz", + "integrity": "sha512-aDG/5mMCgKduqBwZ3XpLOdlE2hizV3fM+5dHCWyrBepCQLeM/QRvvpBDCdQKDWKpoIBmrGGYDNiOofnf3QmGhg==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/great-circle": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-7.3.4.tgz", + "integrity": "sha512-JvfzWFL9efP+xKtOnKzGvwEIXfaN0CLZoPPxNnWa/cVisLs9FVMlC9PWnuL3/3aqH5VhBHPddmU8ipzNE6KIIA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "arc": "^0.2.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.3.4.tgz", + "integrity": "sha512-U/S5qyqgx3WTvg4twaH0WxF3EixoTCfDsmk98g1E3/5e2YKp7JKYZdz0vivsS5/UZLJeZDEElOSFH4pUgp+l7g==", + "dependencies": { + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/hex-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-7.3.4.tgz", + "integrity": "sha512-TDCgBykFdsrP3IOOfToiiLpYkbUb3eEEhM9riIqWht0ubKUY61LN7qVs9bxZD83hG6XaDB6uY7SWkxK1zIEopQ==", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/intersect": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/interpolate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-7.3.4.tgz", + "integrity": "sha512-lwYSMbHxsXYEWObv0tyBCjwTLXyfsTvOLn/NFhlsGrNCYEXn8I1VPtLGwuxbSdF3hVRgurn8qftkB1npHrNs6Q==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/hex-grid": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/point-grid": "7.3.4", + "@turf/square-grid": "7.3.4", + "@turf/triangle-grid": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/intersect": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-7.3.4.tgz", + "integrity": "sha512-VsqMEMeRWWs2mjwI7sTlUgH1cEfugTGhQ0nF8ncHG7YKd9HUUTzIKpn9FJeoguPWIYITcy1ar4yJEOU/hteBVw==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/invariant": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.3.4.tgz", + "integrity": "sha512-88Eo4va4rce9sNZs6XiMJowWkikM3cS2TBhaCKlU+GFHdNf8PFEpiU42VDU8q5tOF6/fu21Rvlke5odgOGW4AQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isobands": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-7.3.4.tgz", + "integrity": "sha512-SFYefwjQdQfF0MV0zfaSwNg9J1wD7mfPP8scGcScKGM3admbwS2A3V8rqPADBfYLD2eCPBDFnySxcl9SHbPung==", + "dependencies": { + "@turf/area": "7.3.4", + "@turf/bbox": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/explode": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isolines": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-7.3.4.tgz", + "integrity": "sha512-UFRIULkIgkZOmrhLxExWvguixbzfoCgVcXIqo2Cp68do4v+nwc3pTM7MTt4DBVFloIdX0Usrn4K44LQ/V05gxg==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/jsts": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@turf/jsts/-/jsts-2.7.2.tgz", + "integrity": "sha512-zAezGlwWHPyU0zxwcX2wQY3RkRpwuoBmhhNE9HY9kWhFDkCxZ3aWK5URKwa/SWKJbj9aztO+8vtdiBA28KVJFg==", + "dependencies": { + "jsts": "2.7.1" + } + }, + "node_modules/@turf/kinks": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-7.3.4.tgz", + "integrity": "sha512-LZTKELWxvXl0vc9ZxVgi0v07fO9+2FrZOam2B10fz/eGjy3oKNazU5gjggbnc499wEIcJS4hN+VyjQZrmsJAdQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/length": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-7.3.4.tgz", + "integrity": "sha512-Dg1GnQ/B2go5NIWXt91N4L7XTjIgIWCftBSYIXkrpIM7QGjItzglek0Z5caytvb8ZRWXzZOGs8//+Q5we91WuQ==", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-arc": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-7.3.4.tgz", + "integrity": "sha512-nqZ+JKjDVIrvREFHgtJIP9Ps4WbWw3eStqdIzAPolrzoXyAZnpIKquyfRTxpJFYUUjDmf+uQ/SFWsPP4SOWAqQ==", + "dependencies": { + "@turf/circle": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-chunk": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-7.3.4.tgz", + "integrity": "sha512-xWEHR99EpUO5ZPEZhMfa0QvnFZC0W+QLxB1GcJcSeJAQ5ZMXUXY8doKF1Nztk0eppawMprEEO3nQWLvQoR4z2g==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/length": "7.3.4", + "@turf/line-slice-along": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-intersect": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-7.3.4.tgz", + "integrity": "sha512-XygbTvHa6A+v6l2ZKYtS8AAWxwmrPxKxfBbdH75uED1JvdytSLWYTKGlcU3soxd9sYb4x/g9sDvRIVyU6Lucrg==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "sweepline-intersections": "^1.5.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-offset": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-7.3.4.tgz", + "integrity": "sha512-CSrg3njde9Tx+C0oL+BHUpZYpgD+PEmzp0ldDNis5ZQiTe5tUrwiIyG7A/QXf9eDnGhtV1WhCAycX0Wjged4pg==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-overlap": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-7.3.4.tgz", + "integrity": "sha512-3GBECiwNAQ2MmSwiqAHMweIl+EiePK0Jx4fXxF1KFE+NGCDv/MbGcEYfAbmsTg8mg6oRI9D8fJZzrT44DHpHXA==", + "dependencies": { + "@turf/boolean-point-on-line": "7.3.4", + "@turf/geojson-rbush": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-segment": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "fast-deep-equal": "^3.1.3", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-segment": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-7.3.4.tgz", + "integrity": "sha512-UeISzf/JHoWEY5yeoyvKwA5epWcvJMCpCwbIMolvfTC5pp+IVozjHPVCRvRWuzmbmAvetcW0unL5bjqi0ADmuQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-slice": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-7.3.4.tgz", + "integrity": "sha512-6Vt4Eptdr2C5T+jtpbo8D4v8b6X7KqYonPPyMB6huv+Kcg3nz4JRI9OQCDCaon9rWvU3ffWwjsjcbJCQS9o0sA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-slice-along": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-7.3.4.tgz", + "integrity": "sha512-RT5HydNy8+m9Y3u39USeYZauG2EyMqCYoLnTpWcAxbZGdq9WjIwdzAwYir3d8eJkOzjlR6Khz071VM4Ufqs0Kg==", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-split": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-7.3.4.tgz", + "integrity": "sha512-l1zmCSUnGsiN4gf22Aw91a2VnYs5DZS67FdkYqKgr+wPEAL/gpQgIBBWSTmhwY8zb3NEqty+f/gMEe8EJAWYng==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/geojson-rbush": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/line-segment": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@turf/truncate": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-to-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-7.3.4.tgz", + "integrity": "sha512-vRnDHjzwOroC74/fsJEU+dUeGhiR/B2bG0/HeEWRBplAjmwVPptRBmDGtXKTz8sbA6or17/XtOITp3zTU0lBZw==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/mask": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-7.3.4.tgz", + "integrity": "sha512-FJIlSk8m0AiqzNoLSMdYuhDRif6aeOYVdW/WxjEjpUoMalwy2w5MMlZqJB9zxt/xSrMq6lvTWJgZfZfGL2s4ZQ==", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/meta": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.3.4.tgz", + "integrity": "sha512-tlmw9/Hs1p2n0uoHVm1w3ugw1I6L8jv9YZrcdQa4SH5FX5UY0ATrKeIvfA55FlL//PGuYppJp+eyg/0eb4goqw==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/midpoint": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-7.3.4.tgz", + "integrity": "sha512-/XAeGvsz8l5HaqcP7TUlexzGfibqXozQgBZ8rH7az6op2Dfm3pL/Z7bKLHoVavM0ccBg0Pt7g6j9NM54kZWdKA==", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/moran-index": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/moran-index/-/moran-index-7.3.4.tgz", + "integrity": "sha512-SNb16szwEG0OiyNn3z9zvSnk3M3tfwvvN8i//9UIC32APEApI+MRXCl93H/qZkKMhhh/cHA0pF0pjYZwl5z8Ow==", + "dependencies": { + "@turf/distance-weight": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-neighbor-analysis": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/nearest-neighbor-analysis/-/nearest-neighbor-analysis-7.3.4.tgz", + "integrity": "sha512-8EZlDy5poU0t7BDy8KTzOmfiGsAs2kWuB3/kgI4sMdbThKVk2P4hHKuToCSGvqAzwSy3B2qKYM1N6JeVWytu+w==", + "dependencies": { + "@turf/area": "7.3.4", + "@turf/bbox": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/nearest-point": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-7.3.4.tgz", + "integrity": "sha512-WfI09f2bX0nKx/jkO7zCt3tUrJulyAlUYQtZHP7lWYMCOmZ6Pq26D6lKWjpfs2it0OHbhlx1XF/UupEUaz830w==", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-on-line": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-7.3.4.tgz", + "integrity": "sha512-DQrP3lRju83rIXFN68tUEpc7ki/eRwdwBkK2CTT4RAcyCxbcH2NGJPQv8dYiww/Ar77u1WLVn+aINXZH904dWw==", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-to-line": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-7.3.4.tgz", + "integrity": "sha512-Nzp3ojQt0gDACNYG+oNWymRXAUCey0LzdiSezYtRwdA0/+FQCtuxP8Lbc8FftV10JL8D78/CRlmt7omaXLLXCg==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/point-to-line-distance": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/planepoint": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-7.3.4.tgz", + "integrity": "sha512-KAhMAnddbuWIEZuk2bK//g+xTeKn8aV9N2AaE27x6JMJyV/wqvatIuVVqEIXI3SkAFbhiVBpVuarvPYhrJ+fhg==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-7.3.4.tgz", + "integrity": "sha512-9CL3OJ4dEt266+fxYlOQeRFqAY3XtsAuak2Gpk+K8k+Y3yGv8pvyn3QaAQ6P2npbiKt0zfG8Md/+HBAPOMPQ0A==", + "dependencies": { + "@turf/boolean-within": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-on-feature": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-7.3.4.tgz", + "integrity": "sha512-tQfIxsJUxZqyO7OeJC25y3DqN9i4fmrAt4TBrPvZcIIwymgN7aMrElJKlg/dfi7JDihKp3h/CkWMjtMQA14Vwg==", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/center": "7.3.4", + "@turf/explode": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/nearest-point": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-to-line-distance": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-7.3.4.tgz", + "integrity": "sha512-IdPAxlAQZj7FCZg+ObyVHlNdqwLL/oxYoQjpxMNJ511gNxokCtEv0aeRZQjYOYIxr9Ss97v3yo3ILJaF9V2kPw==", + "dependencies": { + "@turf/bearing": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@turf/projection": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@turf/rhumb-distance": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-to-polygon-distance": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/point-to-polygon-distance/-/point-to-polygon-distance-7.3.4.tgz", + "integrity": "sha512-VxbkgHyzCkYWSxirqSUqw+lzbYmTf2qFhVZ/T5dprhwyXWcgalpupvgRzmZmjKkgsoJ017vrvCNKZRaCCn+Z7Q==", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/point-to-line-distance": "7.3.4", + "@turf/polygon-to-line": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/points-within-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-7.3.4.tgz", + "integrity": "sha512-HfT83Iw99zywDfCp+nJwS+JDzH+GdNug0sut9WDjGEznHKoZyAcOk+hGKL/ja8TeCLx9VsZHOiVCQFm+NTgvgA==", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-smooth": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/polygon-smooth/-/polygon-smooth-7.3.4.tgz", + "integrity": "sha512-AnpaGgNYVvP/dfz10id3AotDrUh9O+4unXCk3es1ff51VrpUhVgH3H+zyTSbVL4zAXN/ejPb8UnKCxDvNOQs4g==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-tangents": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-7.3.4.tgz", + "integrity": "sha512-D1IFocXJYF8PUMZ+BmnOstyRrzklqC86FgakYVk9O61F9Ki8LhMGaRfF+6reKMD473KvHvEf1M2EgmGt+OHDRw==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/boolean-within": "7.3.4", + "@turf/explode": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/nearest-point": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-to-line": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-7.3.4.tgz", + "integrity": "sha512-xhmOZ5rHZAKLUDLeYKWMsX84ip8CCGOcGLBHtPPYOjdIDHddMV6Sxt5kVgkmlZpK6NEWEmOD6lYR4obxHcHlGA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygonize": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-7.3.4.tgz", + "integrity": "sha512-kmj05rkJ4tE8LvbQ4GVsL5GOrRiX/F5W4RIdxo8gPGTw1Y5oLG/1vFk6Hg6x63L1WcdNtF0sq6AdEI0G9BXWXA==", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/envelope": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/projection": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-7.3.4.tgz", + "integrity": "sha512-p91zOaLmzoBHzU/2H6Ot1tOhTmAom85n1P7I4Oo0V9xU8hmJXWfNnomLFf/6rnkKDIFZkncLQIBz4iIecZ61sA==", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/quadrat-analysis": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/quadrat-analysis/-/quadrat-analysis-7.3.4.tgz", + "integrity": "sha512-Yxqq8wgrDiXIX+s0uOZ2exmYfRwTIcUX8J7j4P+sbyLVbyN8W3AjN2s5ZX21P0aFf3v24FBd2fNWlm5VmMUAdg==", + "dependencies": { + "@turf/area": "7.3.4", + "@turf/bbox": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/point-grid": "7.3.4", + "@turf/random": "7.3.4", + "@turf/square-grid": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/random": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/random/-/random-7.3.4.tgz", + "integrity": "sha512-CXMS5XDoI5x0zc1aCYbn3t603k8hjaFHNsSOvGBW20z68cwP0UwMQQr0KLqFPqI4J1O7dMX+urn8IHH27RXFYg==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rectangle-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rectangle-grid/-/rectangle-grid-7.3.4.tgz", + "integrity": "sha512-qM7vujJ4wndB4MKZlEcnUSawgvs5wXpSEFf4f+LWRIfmGhtv6serzDqFzWcmy8kF8hg5J465PMktRmAFWq/a+w==", + "dependencies": { + "@turf/boolean-intersects": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rewind": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-7.3.4.tgz", + "integrity": "sha512-4BZ8MHMujl4NAT7XnIs7JoOuDhpR96oDTB0RtqTeIP4onioIedVnw1ZA3Uq08sILGpR0qKLuDsvdz4x9jtbptg==", + "dependencies": { + "@turf/boolean-clockwise": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-bearing": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-7.3.4.tgz", + "integrity": "sha512-tvX1toSo80q0iL0cUMMXpSKsCCfOjRqDGCmOdR6B9shhk6xP1ZM2PLQDr+MFPBFeGyQuyY4CNFkV2+3DF49vYw==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-destination": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-7.3.4.tgz", + "integrity": "sha512-6HikEb5nm2A18FQWk6vVLMQkc099I/7c69j47RYM27xQK8J8uBCNk1zLYyMPcZTh24xcNSbZ1iPHDsDOqw6wWQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-distance": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-7.3.4.tgz", + "integrity": "sha512-phwskeijdgYMsR3qDQmytfsg2iZcp3uWK7UFc76wKTEpxozbDGFI4enX5gXvZPpyI1iD7gsktGqHsO33AjnFDA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sample": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-7.3.4.tgz", + "integrity": "sha512-XzAATg09c2XYAXkIBbg8lktSrU1tXNjJYXtbVwF6jLp1q2wTRpwb+mZpTEPAwzZwVF81uR5c0CsdQyr5UHINVw==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sector": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-7.3.4.tgz", + "integrity": "sha512-x2tNAXl21HRcF302ghU5ohE/vmmfDcXpQKgoWHyi7o5Q9kDRBwy7kbvr5YxbT3vwW/kAWUDYM7FoXNH42bXgCw==", + "dependencies": { + "@turf/circle": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/line-arc": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/shortest-path": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-7.3.4.tgz", + "integrity": "sha512-xbK/oM+JRL+lJCHkAdZ3QPgoivT40J9WKJ0d1Ddt8LXTpzX2YeJVgcwOZaBPG9ncZUzHfHIWS1rUjc54clnZcg==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/clean-coords": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/transform-scale": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/simplify": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-7.3.4.tgz", + "integrity": "sha512-OoSwu3vI0H9P+GzLDaOJIL9v0V8ubeP8wQjM8GeMEZrq6U2uh9JWQnAU+jviT3ODcKF5H+88snpiMik585L0wA==", + "dependencies": { + "@turf/clean-coords": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-7.3.4.tgz", + "integrity": "sha512-vJ+NeiEaOVsb8YiUExtyIgvH+ZybthHszl2TASZn5q340ioKHPb2JeHGlbgrB2x8pEMh3MVhoqxAbXDuND/cnw==", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-7.3.4.tgz", + "integrity": "sha512-MgjlVRklQYFfQm9yJNha9kXothLPliVdeycNdmn4lWLH3SOZe1rqJPB5Z9+dhmJELT3BJraDq3W5ik5taEpKyQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/rectangle-grid": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/standard-deviational-ellipse": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-7.3.4.tgz", + "integrity": "sha512-+BaetOKN8zA2mQCVTcRWMcfidNR3JkjmYj0r5iGRncK0J+pdxIjX2q6sF6yBMOOxMoEMy393P7j07HdBIPbibw==", + "dependencies": { + "@turf/center-mean": "7.3.4", + "@turf/ellipse": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/points-within-polygon": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tag": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-7.3.4.tgz", + "integrity": "sha512-ienLhLzBLeChtKhbJMmU3/vGg0hWzi6Wh/q0n39W4CmdNb+yAoGQhlYjcCbPOJT4IcdFlWE3OhbP9EmH/xPgfg==", + "dependencies": { + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tesselate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-7.3.4.tgz", + "integrity": "sha512-NnDgVb5ZchJEhEpq1je2hktS5UhnHMfeeumxZQgnIoMeGILpJtcOL//b/1biBBUVSJ0ZZg5zxiHdQc1PgK2gxA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "earcut": "^2.2.4", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tesselate/node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, + "node_modules/@turf/tin": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-7.3.4.tgz", + "integrity": "sha512-tuegrGlbKPp6Dm8r5SuYDtQ2EVzdXVVxelqI1agnzj9N+l8oTBIKLRxRbBkLsizeVIDnlmVHCQB6cRc3v+u8JQ==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-rotate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-7.3.4.tgz", + "integrity": "sha512-pbUG6QLwyJvvitq4aAq4IQH79X8T0NmEPUGDUEEP69yW7t4+UZjDBAVbCKwpOc8gtsK0K5yvxlZ0e2CdtpNmEw==", + "dependencies": { + "@turf/centroid": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@turf/rhumb-destination": "7.3.4", + "@turf/rhumb-distance": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-scale": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-7.3.4.tgz", + "integrity": "sha512-7gUIFFHaU3Ewj3rCzIu5Yo7Zjfv4R2ypjh6UWiMJnDavb7RQ8fn0AKKcNMA/vF/yxuncp2l3zoa2gygv4AKM8A==", + "dependencies": { + "@turf/bbox": "7.3.4", + "@turf/center": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@turf/rhumb-destination": "7.3.4", + "@turf/rhumb-distance": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-translate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-7.3.4.tgz", + "integrity": "sha512-qbSIEueOR8mNB7p4EB88vHvUAyuSBM8zxP68UiiTNV3Gh+OZF2VXTFiu3EFYMTaD9sE6Lxmzvv3fjW8N2q82pw==", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/rhumb-destination": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/triangle-grid": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-7.3.4.tgz", + "integrity": "sha512-0bki10XwYvNcPzDcSs5kUh3niOogdVeFtawJEz5FdlyTAUohbNlC+Vb40K//OqEyTrGII+q1/dE4q+1J6ZCmDA==", + "dependencies": { + "@turf/distance": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/intersect": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/truncate": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-7.3.4.tgz", + "integrity": "sha512-VPXdae9+RLLM19FMrJgt7QANBikm7DxPbfp/dXgzE4Ca7v+mJ4T1fYc7gCZDaqOrWMccHKbvv4iSuW7YZWdIIA==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/turf": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-7.3.4.tgz", + "integrity": "sha512-uMAKLYt2tWJj8xIepq4vExF1r8fzJviP/5l/elDHuRyauI2mASy/Gox6kSFlrN0t0p8AT4Cs8o//4GuJTXyC+Q==", + "dependencies": { + "@turf/along": "7.3.4", + "@turf/angle": "7.3.4", + "@turf/area": "7.3.4", + "@turf/bbox": "7.3.4", + "@turf/bbox-clip": "7.3.4", + "@turf/bbox-polygon": "7.3.4", + "@turf/bearing": "7.3.4", + "@turf/bezier-spline": "7.3.4", + "@turf/boolean-clockwise": "7.3.4", + "@turf/boolean-concave": "7.3.4", + "@turf/boolean-contains": "7.3.4", + "@turf/boolean-crosses": "7.3.4", + "@turf/boolean-disjoint": "7.3.4", + "@turf/boolean-equal": "7.3.4", + "@turf/boolean-intersects": "7.3.4", + "@turf/boolean-overlap": "7.3.4", + "@turf/boolean-parallel": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/boolean-point-on-line": "7.3.4", + "@turf/boolean-touches": "7.3.4", + "@turf/boolean-valid": "7.3.4", + "@turf/boolean-within": "7.3.4", + "@turf/buffer": "7.3.4", + "@turf/center": "7.3.4", + "@turf/center-mean": "7.3.4", + "@turf/center-median": "7.3.4", + "@turf/center-of-mass": "7.3.4", + "@turf/centroid": "7.3.4", + "@turf/circle": "7.3.4", + "@turf/clean-coords": "7.3.4", + "@turf/clone": "7.3.4", + "@turf/clusters": "7.3.4", + "@turf/clusters-dbscan": "7.3.4", + "@turf/clusters-kmeans": "7.3.4", + "@turf/collect": "7.3.4", + "@turf/combine": "7.3.4", + "@turf/concave": "7.3.4", + "@turf/convex": "7.3.4", + "@turf/destination": "7.3.4", + "@turf/difference": "7.3.4", + "@turf/dissolve": "7.3.4", + "@turf/distance": "7.3.4", + "@turf/distance-weight": "7.3.4", + "@turf/ellipse": "7.3.4", + "@turf/envelope": "7.3.4", + "@turf/explode": "7.3.4", + "@turf/flatten": "7.3.4", + "@turf/flip": "7.3.4", + "@turf/geojson-rbush": "7.3.4", + "@turf/great-circle": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/hex-grid": "7.3.4", + "@turf/interpolate": "7.3.4", + "@turf/intersect": "7.3.4", + "@turf/invariant": "7.3.4", + "@turf/isobands": "7.3.4", + "@turf/isolines": "7.3.4", + "@turf/kinks": "7.3.4", + "@turf/length": "7.3.4", + "@turf/line-arc": "7.3.4", + "@turf/line-chunk": "7.3.4", + "@turf/line-intersect": "7.3.4", + "@turf/line-offset": "7.3.4", + "@turf/line-overlap": "7.3.4", + "@turf/line-segment": "7.3.4", + "@turf/line-slice": "7.3.4", + "@turf/line-slice-along": "7.3.4", + "@turf/line-split": "7.3.4", + "@turf/line-to-polygon": "7.3.4", + "@turf/mask": "7.3.4", + "@turf/meta": "7.3.4", + "@turf/midpoint": "7.3.4", + "@turf/moran-index": "7.3.4", + "@turf/nearest-neighbor-analysis": "7.3.4", + "@turf/nearest-point": "7.3.4", + "@turf/nearest-point-on-line": "7.3.4", + "@turf/nearest-point-to-line": "7.3.4", + "@turf/planepoint": "7.3.4", + "@turf/point-grid": "7.3.4", + "@turf/point-on-feature": "7.3.4", + "@turf/point-to-line-distance": "7.3.4", + "@turf/point-to-polygon-distance": "7.3.4", + "@turf/points-within-polygon": "7.3.4", + "@turf/polygon-smooth": "7.3.4", + "@turf/polygon-tangents": "7.3.4", + "@turf/polygon-to-line": "7.3.4", + "@turf/polygonize": "7.3.4", + "@turf/projection": "7.3.4", + "@turf/quadrat-analysis": "7.3.4", + "@turf/random": "7.3.4", + "@turf/rectangle-grid": "7.3.4", + "@turf/rewind": "7.3.4", + "@turf/rhumb-bearing": "7.3.4", + "@turf/rhumb-destination": "7.3.4", + "@turf/rhumb-distance": "7.3.4", + "@turf/sample": "7.3.4", + "@turf/sector": "7.3.4", + "@turf/shortest-path": "7.3.4", + "@turf/simplify": "7.3.4", + "@turf/square": "7.3.4", + "@turf/square-grid": "7.3.4", + "@turf/standard-deviational-ellipse": "7.3.4", + "@turf/tag": "7.3.4", + "@turf/tesselate": "7.3.4", + "@turf/tin": "7.3.4", + "@turf/transform-rotate": "7.3.4", + "@turf/transform-scale": "7.3.4", + "@turf/transform-translate": "7.3.4", + "@turf/triangle-grid": "7.3.4", + "@turf/truncate": "7.3.4", + "@turf/union": "7.3.4", + "@turf/unkink-polygon": "7.3.4", + "@turf/voronoi": "7.3.4", + "@types/geojson": "^7946.0.10", + "@types/kdbush": "^3.0.5", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/union": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/union/-/union-7.3.4.tgz", + "integrity": "sha512-JJYyPMmGcrTa9sPv2ief2QU9Hb//cEAU1zgKu/OfoCMa9a8Imp5QVm9UTAkhGlc+4qm/N/X16iJ+cvVWaxPjkg==", + "dependencies": { + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "polyclip-ts": "^0.16.8", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/unkink-polygon": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-7.3.4.tgz", + "integrity": "sha512-dFIqTLAnLL5D3OANPJtRb5OvmOM81GlNCjwgjlLQy0xdpYgKwGdE+gNXjygDrPUUXNc22xnaj3EfAfC3Pq7W4Q==", + "dependencies": { + "@turf/area": "7.3.4", + "@turf/boolean-point-in-polygon": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/meta": "7.3.4", + "@types/geojson": "^7946.0.10", + "rbush": "^3.0.1", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/voronoi": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-7.3.4.tgz", + "integrity": "sha512-cwKSiDzDHRnA7yafQ1zOhWxRuMzp+fYFFzadCdByBAG1jAD7UlFwKhS1fjNPBNs67Fl5X3LL5ahCLW5gEdFgmg==", + "dependencies": { + "@turf/clone": "7.3.4", + "@turf/helpers": "7.3.4", + "@turf/invariant": "7.3.4", + "@types/d3-voronoi": "^1.1.12", + "@types/geojson": "^7946.0.10", + "d3-voronoi": "1.1.2", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -1536,6 +3468,11 @@ "assertion-error": "^2.0.1" } }, + "node_modules/@types/d3-voronoi": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.12.tgz", + "integrity": "sha512-DauBl25PKZZ0WVJr42a6CNvI6efsdzofl9sajqZr2Gf5Gu733WkDdUGiPkUHXiUvYGzNNlFQde2wdZdfQPG+yw==" + }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -1553,6 +3490,24 @@ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==" }, + "node_modules/@types/geokdbush": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/geokdbush/-/geokdbush-1.1.5.tgz", + "integrity": "sha512-jIsYnXY+RQ/YCyBqeEHxYN9mh+7PqKJUJUp84wLfZ7T2kqyVPNaXwZuvf1A2uQUkrvVqEbsG94ff8jH32AlLvA==", + "dependencies": { + "@types/kdbush": "^1" + } + }, + "node_modules/@types/geokdbush/node_modules/@types/kdbush": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/kdbush/-/kdbush-1.0.7.tgz", + "integrity": "sha512-QM5iB8m/0mnGOjUKshErIZQ0LseyTieRSYc3yaOpmrRM0xbWiOuJUWlduJx+TPNK7/VFMWphUGwx3nus7eT1Wg==" + }, + "node_modules/@types/kdbush": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/kdbush/-/kdbush-3.0.5.tgz", + "integrity": "sha512-tdJz7jaWFu4nR+8b2B+CdPZ6811ighYylWsu2hpsivapzW058yP0KdfZuNY89IiRe5jbKvBGXN3LQdN2KPXVdQ==" + }, "node_modules/@types/supercluster": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", @@ -1712,6 +3667,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/arc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/arc/-/arc-0.2.0.tgz", + "integrity": "sha512-8NFOo126uYKQJyXNSLY/jSklgfLQL+XWAcPXGo876JwEQ8nSOPXWNI3TV2jLZMN8QEw8uksJ1ZwS4npjBca8MA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -1766,6 +3729,14 @@ "require-from-string": "^2.0.2" } }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "engines": { + "node": "*" + } + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -1876,6 +3847,27 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/concaveman": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/concaveman/-/concaveman-1.2.1.tgz", + "integrity": "sha512-PwZYKaM/ckQSa8peP5JpVr7IMJ4Nn/MHIaWUjP4be+KoZ7Botgs8seAZGpmaOM+UZXawcdYRao/px9ycrCihHw==", + "dependencies": { + "point-in-polygon": "^1.1.0", + "rbush": "^3.0.1", + "robust-predicates": "^2.0.4", + "tinyqueue": "^2.0.3" + } + }, + "node_modules/concaveman/node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1901,6 +3893,24 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true }, + "node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "node_modules/d3-geo": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.7.1.tgz", + "integrity": "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw==", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/d3-voronoi": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", + "integrity": "sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw==" + }, "node_modules/data-urls": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", @@ -2121,6 +4131,11 @@ "node": ">=12.0.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -2203,6 +4218,48 @@ "node": ">=6.9.0" } }, + "node_modules/geojson-equality-ts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/geojson-equality-ts/-/geojson-equality-ts-1.0.2.tgz", + "integrity": "sha512-h3Ryq+0mCSN/7yLs0eDgrZhvc9af23o/QuC4aTiuuzP/MRCtd6mf5rLsLRY44jX0RPUfM8c4GqERQmlUxPGPoQ==", + "dependencies": { + "@types/geojson": "^7946.0.14" + } + }, + "node_modules/geojson-polygon-self-intersections": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/geojson-polygon-self-intersections/-/geojson-polygon-self-intersections-1.2.2.tgz", + "integrity": "sha512-6XRNF4CsRHYmR9z5YuIk5f/aOototnDf0dgMqYGcS7y1l57ttt6MAIAxl3rXyas6lq1HEbTuLMh4PgvO+OV42w==", + "dependencies": { + "rbush": "^2.0.1" + } + }, + "node_modules/geojson-polygon-self-intersections/node_modules/quickselect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==" + }, + "node_modules/geojson-polygon-self-intersections/node_modules/rbush": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz", + "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==", + "dependencies": { + "quickselect": "^1.0.1" + } + }, + "node_modules/geokdbush": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/geokdbush/-/geokdbush-2.0.1.tgz", + "integrity": "sha512-0M8so1Qx6+jJ1xpirpCNrgUsWAzIcQ3LrLmh0KJPBYI3gH7vy70nY5zEEjSp9Tn0nBt6Q2Fh922oL08lfib4Zg==", + "dependencies": { + "tinyqueue": "^2.0.3" + } + }, + "node_modules/geokdbush/node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2401,6 +4458,14 @@ "node": ">=6" } }, + "node_modules/jsts": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/jsts/-/jsts-2.7.1.tgz", + "integrity": "sha512-x2wSZHEBK20CY+Wy+BPE7MrFQHW6sIsdaGUMEqmGAio+3gFzQaBYPwLRonUfQf9Ak8pBieqj9tUofX1+WtAEIg==", + "engines": { + "node": ">= 12" + } + }, "node_modules/kdbush": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", @@ -2614,6 +4679,33 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==" + }, + "node_modules/point-in-polygon-hao": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/point-in-polygon-hao/-/point-in-polygon-hao-1.2.4.tgz", + "integrity": "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/point-in-polygon-hao/node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, + "node_modules/polyclip-ts": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/polyclip-ts/-/polyclip-ts-0.16.8.tgz", + "integrity": "sha512-JPtKbDRuPEuAjuTdhR62Gph7Is2BS1Szx69CFOO3g71lpJDFo78k4tFyi+qFOMVPePEzdSKkpGU3NBXPHHjvKQ==", + "dependencies": { + "bignumber.js": "^9.1.0", + "splaytree-ts": "^1.0.2" + } + }, "node_modules/postcss": { "version": "8.5.8", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", @@ -2686,6 +4778,19 @@ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==" }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dependencies": { + "quickselect": "^2.0.0" + } + }, + "node_modules/rbush/node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "node_modules/react": { "version": "19.2.4", "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", @@ -2751,6 +4856,11 @@ "protocol-buffers-schema": "^3.3.1" } }, + "node_modules/robust-predicates": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz", + "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==" + }, "node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", @@ -2832,6 +4942,11 @@ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true }, + "node_modules/skmeans": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", + "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg==" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2841,6 +4956,11 @@ "node": ">=0.10.0" } }, + "node_modules/splaytree-ts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/splaytree-ts/-/splaytree-ts-1.0.2.tgz", + "integrity": "sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA==" + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -2891,6 +5011,19 @@ "kdbush": "^4.0.2" } }, + "node_modules/sweepline-intersections": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sweepline-intersections/-/sweepline-intersections-1.5.0.tgz", + "integrity": "sha512-AoVmx72QHpKtItPu72TzFL+kcYjd67BPLDoR0LarIk+xyaRg+pDTMFXndIEvZf9xEKnJv6JdhgRMnocoG0D3AQ==", + "dependencies": { + "tinyqueue": "^2.0.0" + } + }, + "node_modules/sweepline-intersections/node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -2975,6 +5108,30 @@ "integrity": "sha512-5WJ2SqFsv4G2Dwi7ZFVRnz6b2H1od39QME1lc2y5Ew3eWiZMAeqOAfWpRP9jHvhUl881406QtZTODvjttJs+ew==", "dev": true }, + "node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/topojson-server": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.1.tgz", + "integrity": "sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw==", + "dependencies": { + "commander": "2" + }, + "bin": { + "geo2topo": "bin/geo2topo" + } + }, "node_modules/tough-cookie": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", @@ -2999,6 +5156,11 @@ "node": ">=20" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/undici": { "version": "7.24.4", "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 7d67f60..af076f1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { + "@turf/turf": "^7.3.4", "axios": "^1.7.9", "maplibre-gl": "^5.20.1", "react": "^19.2.1", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 23a4049..18ec4dc 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useRef } from 'react'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; +import * as turf from '@turf/turf'; import apiService from './services/api'; import './styles/App.css'; @@ -8,33 +9,77 @@ const APP = () => { const mapContainerRef = useRef(null); const mapRef = useRef(null); const startMarkerRef = useRef(null); + const cityMarkersRef = useRef([]); + const animationRef = useRef(null); + const popupRef = useRef(null); const [selectedPoint, setSelectedPoint] = useState({ lat: 51.5074, lon: -0.1278 }); const [direction, setDirection] = useState(45); const [lineOfSightData, setLineOfSightData] = useState(null); const [loading, setLoading] = useState(false); + const [isPlaying, setIsPlaying] = useState(false); + const [flightSpeed, setFlightSpeed] = useState(1.0); const [mapStyle, setMapStyle] = useState('light'); // 'light' or 'dark' const [tolerance, setTolerance] = useState(50); + const [selectedCity, setSelectedCity] = useState(null); + const [isLocked, setIsLocked] = useState(false); useEffect(() => { - // Initialize MapLibre map + // Initialize MapLibre map - ONLY ONCE mapRef.current = new maplibregl.Map({ container: mapContainerRef.current, style: getMapStyle(mapStyle), center: [-0.1278, 51.5074], // London - zoom: 3, - pitch: 0 + zoom: 2, + pitch: 0, + projection: 'globe' // Enable 3D Globe }); - mapRef.current.on('load', () => { - console.log('Map loaded successfully'); - - // Initialize start marker - startMarkerRef.current = new maplibregl.Marker({ color: '#FF6B6B' }) - .setLngLat([selectedPoint.lon, selectedPoint.lat]) - .addTo(mapRef.current); + mapRef.current.on('style.load', () => { + console.log('Map style loaded or changed'); + setupMapLayers(); + }); - // Initialize preview line source and layer - mapRef.current.addSource('preview-line', { + return () => { + if (animationRef.current) cancelAnimationFrame(animationRef.current); + if (mapRef.current) mapRef.current.remove(); + }; + }, []); // Run only once + + const setupMapLayers = () => { + const map = mapRef.current; + if (!map) return; + + // 1. Add atmosphere/sky effects + map.setSky({ + 'sky-color': '#199EF3', + 'sky-horizon-blend': 0.5, + 'horizon-color': '#ffffff', + 'horizon-fog-blend': 0.5, + 'fog-color': '#add8e6', + 'fog-ground-blend': 0.5 + }); + + // 2. Add terrain source for 3D effect + if (!map.getSource('terrain')) { + map.addSource('terrain', { + type: 'raster-dem', + tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'], + encoding: 'terrarium', + tileSize: 256, + maxzoom: 15 + }); + } + map.setTerrain({ source: 'terrain', exaggeration: 1.5 }); + + // 3. Initialize start marker + if (startMarkerRef.current) startMarkerRef.current.remove(); + startMarkerRef.current = new maplibregl.Marker({ color: '#FF6B6B' }) + .setLngLat([selectedPoint.lon, selectedPoint.lat]) + .addTo(map); + + // 4. Initialize preview line source and layer + if (!map.getSource('preview-line')) { + map.addSource('preview-line', { type: 'geojson', data: { type: 'Feature', @@ -45,7 +90,7 @@ const APP = () => { } }); - mapRef.current.addLayer({ + map.addLayer({ id: 'preview-line', type: 'line', source: 'preview-line', @@ -55,11 +100,28 @@ const APP = () => { 'line-dasharray': [2, 2] } }); + } + // 5. Restore results line and city markers if they existed + if (lineOfSightData) { + renderLineOnMap(lineOfSightData); + // Hide preview if results are shown + if (map.getLayer('preview-line')) { + map.setLayoutProperty('preview-line', 'visibility', 'none'); + } + } else { updatePreviewLine(selectedPoint, direction); - }); + } + }; + + // Separate effect for the click listener that respects isLocked + useEffect(() => { + if (!mapRef.current) return; + + const handleClick = (e) => { + // Don't allow moving start point if locked + if (isLocked) return; - mapRef.current.on('click', (e) => { const { lng, lat } = e.lngLat; setSelectedPoint({ lat, lon: lng }); @@ -67,33 +129,329 @@ const APP = () => { startMarkerRef.current.setLngLat([lng, lat]); } - // Clear previous final line when moving start point + // Clear previous final results when moving start point + clearCityMarkers(); if (mapRef.current.getSource('line-of-sight')) { mapRef.current.removeLayer('line-of-sight'); mapRef.current.removeSource('line-of-sight'); } + }; + + mapRef.current.on('click', handleClick); + return () => { + if (mapRef.current) mapRef.current.off('click', handleClick); + }; + }, [isLocked]); + + const handleStartAgain = () => { + if (animationRef.current) { + cancelAnimationFrame(animationRef.current); + setIsPlaying(false); + setFlightSpeed(1.0); + } + + if (popupRef.current) { + popupRef.current.remove(); + popupRef.current = null; + } + + setIsLocked(false); + setLineOfSightData(null); + setSelectedCity(null); + clearCityMarkers(); + + if (mapRef.current) { + if (mapRef.current.getSource('line-of-sight')) { + mapRef.current.removeLayer('line-of-sight'); + mapRef.current.removeSource('line-of-sight'); + } + // Re-enable preview line if it was hidden + if (mapRef.current.getLayer('preview-line')) { + mapRef.current.setLayoutProperty('preview-line', 'visibility', 'visible'); + } + + // Reset map pitch and zoom + mapRef.current.flyTo({ + center: [selectedPoint.lon, selectedPoint.lat], + zoom: 3, + pitch: 0, + bearing: 0 + }); + } + }; + + const clearCityMarkers = () => { + if (cityMarkersRef.current) { + cityMarkersRef.current.forEach(marker => marker.remove()); + cityMarkersRef.current = []; + } + }; + + const getCountryName = (code) => { + try { + const regionNames = new Intl.DisplayNames(['en'], { type: 'region' }); + return regionNames.of(code.toUpperCase()) || code; + } catch (e) { + return code; + } + }; + + const showCityPopup = (city) => { + if (!mapRef.current) return; + + if (popupRef.current) popupRef.current.remove(); + + const popupNode = document.createElement('div'); + popupNode.className = 'map-info-popup'; + const countryName = getCountryName(city.country); + + popupNode.innerHTML = ` + + + `; + + popupRef.current = new maplibregl.Popup({ + closeButton: true, + closeOnClick: false, + maxWidth: '300px', + offset: [0, -20] + }) + .setLngLat([city.lon, city.lat]) + .setDOMContent(popupNode) + .addTo(mapRef.current); + + popupRef.current.on('close', () => { + setSelectedCity(null); + }); + }; + + const startFlyOver = () => { + if (!lineOfSightData || !mapRef.current) return; + + if (isPlaying) { + if (animationRef.current) cancelAnimationFrame(animationRef.current); + setIsPlaying(false); + setFlightSpeed(1.0); + return; + } + + setIsPlaying(true); + setSelectedCity(null); + if (popupRef.current) popupRef.current.remove(); + + const coordinates = lineOfSightData.line_coordinates.map(c => [c.lon, c.lat]); + const route = turf.lineString(coordinates); + const routeDistance = turf.length(route); + const startTime = performance.now(); + + let lastNearestCityId = null; + let lastFetchedDist = 0; + let currentProgress = 0; + let lastTimestamp = performance.now(); + let currentSpeedMultiplier = 1.0; + let frameCount = 0; + + async function animate(currentTime) { + if (!mapRef.current) return; + + const deltaTime = currentTime - lastTimestamp; + lastTimestamp = currentTime; + + const baseKmPerMs = 0.1; // 100km per second + + const pathPoints = lineOfSightData.line_coordinates; + const pointIndex = Math.min( + Math.floor((currentProgress / routeDistance) * pathPoints.length), + pathPoints.length - 1 + ); + const isOverWater = pathPoints[pointIndex]?.is_over_water; + + // Predictive land detection (look ahead 2000km) + let futureIsOverWater = true; + const lookAheadDistance = 2000; + const lookAheadSteps = 10; + for (let i = 1; i <= lookAheadSteps; i++) { + const checkDist = currentProgress + (lookAheadDistance * i) / lookAheadSteps; + const checkIndex = Math.min( + Math.floor((checkDist / routeDistance) * pathPoints.length), + pathPoints.length - 1 + ); + if (pathPoints[checkIndex] && !pathPoints[checkIndex].is_over_water) { + futureIsOverWater = false; + break; + } + } + + const targetMultiplier = (isOverWater && futureIsOverWater) ? 20.0 : 1.0; + const acceleration = 0.005; + + if (currentSpeedMultiplier < targetMultiplier) { + currentSpeedMultiplier = Math.min(currentSpeedMultiplier + acceleration * deltaTime, targetMultiplier); + } else if (currentSpeedMultiplier > targetMultiplier) { + currentSpeedMultiplier = Math.max(currentSpeedMultiplier - acceleration * deltaTime, targetMultiplier); + } + + frameCount++; + if (frameCount % 10 === 0) { + setFlightSpeed(currentSpeedMultiplier); + } + + currentProgress += baseKmPerMs * deltaTime * currentSpeedMultiplier; + + const phase = Math.min(currentProgress / routeDistance, 1); + + if (phase >= 1) { + setIsPlaying(false); + setFlightSpeed(1.0); + return; + } + + const eye = turf.along(route, currentProgress).geometry.coordinates; + const target = turf.along(route, Math.min(currentProgress + 10, routeDistance)).geometry.coordinates; + + const bearing = turf.bearing(turf.point(eye), turf.point(target)); + + mapRef.current.jumpTo({ + center: eye, + zoom: 6, + pitch: 65, + bearing: bearing + }); + + if (currentProgress - lastFetchedDist > 2000) { + lastFetchedDist = currentProgress; + try { + const response = await apiService.getLineOfSight( + eye[1], eye[0], bearing, tolerance + ); + + if (response.data.success) { + const newCities = response.data.data.conurbations; + setLineOfSightData(prev => { + const existingIds = new Set(prev.conurbations.map(c => c.id)); + const uniqueNewCities = newCities.filter(c => !existingIds.has(c.id)); + const adjustedCities = uniqueNewCities.map(c => ({ + ...c, + distance_km: Math.round(c.distance_km + currentProgress) + })); + const updatedData = { + ...prev, + conurbations: [...prev.conurbations, ...adjustedCities].sort((a, b) => a.distance_km - b.distance_km) + }; + addMarkersToMap(adjustedCities, prev.conurbations.length); + return updatedData; + }); + } + } catch (e) { + console.error('Error fetching more cities during flight:', e); + } + } + + const nearestCity = lineOfSightData.conurbations.find(city => + Math.abs(city.distance_km - currentProgress) < 100 + ); + + if (nearestCity && nearestCity.id !== lastNearestCityId) { + lastNearestCityId = nearestCity.id; + setSelectedCity(nearestCity); + showCityPopup(nearestCity); + } + + animationRef.current = requestAnimationFrame(animate); + } + + animationRef.current = requestAnimationFrame(animate); + }; + + const addMarkersToMap = (cities, startIndex = 0) => { + const map = mapRef.current; + if (!map) return; + + cities.forEach((city, index) => { + const displayIndex = startIndex + index + 1; + + const el = document.createElement('div'); + el.className = 'city-marker'; + el.innerHTML = ` +
${displayIndex}
+
+
${city.name}
+
${(city.population / 1000).toFixed(0)}k
+ `; + + const marker = new maplibregl.Marker(el) + .setLngLat([city.lon, city.lat]) + .addTo(map); + + marker.getElement().addEventListener('click', (e) => { + e.stopPropagation(); + setSelectedCity(city); + showCityPopup(city); + }); + + cityMarkersRef.current.push(marker); + }); + }; + + const renderLineOnMap = (data) => { + const map = mapRef.current; + if (!map) return; + + clearCityMarkers(); + if (map.getSource('line-of-sight')) { + map.removeLayer('line-of-sight'); + map.removeSource('line-of-sight'); + } + + map.addSource('line-of-sight', { + type: 'geojson', + data: { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: data.line_coordinates.map(coord => [coord.lon, coord.lat]) + } + } }); - return () => { - mapRef.current.remove(); - }; - }, []); + map.addLayer({ + id: 'line-of-sight', + type: 'line', + source: 'line-of-sight', + layout: { + 'line-join': 'round', + 'line-cap': 'round' + }, + paint: { + 'line-color': '#FF6B6B', + 'line-width': 4, + 'line-opacity': 0.8 + } + }); - useEffect(() => { - // Update preview whenever point or direction changes - if (mapRef.current && mapRef.current.isStyleLoaded()) { - updatePreviewLine(selectedPoint, direction); - } - }, [selectedPoint, direction]); + addMarkersToMap(data.conurbations); + + const bounds = new maplibregl.LngLatBounds(); + data.line_coordinates.forEach(coord => { + bounds.extend([coord.lon, coord.lat]); + }); + map.fitBounds(bounds, { padding: 50 }); + }; const updatePreviewLine = (point, bearing) => { const map = mapRef.current; if (!map || !map.getSource('preview-line')) return; - // Generate 50 points along a 5000km great circle path for a smooth curve const path = []; const steps = 50; - const totalDistance = 5000; + const totalDistance = 20000; for (let i = 0; i <= steps; i++) { const dist = (totalDistance * i) / steps; @@ -109,9 +467,8 @@ const APP = () => { }); }; - // Helper to calculate destination point given start, bearing, and distance (km) const calculateDestination = (lat, lon, bearing, distance) => { - const R = 6371; // Earth's radius in km + const R = 6371; const brng = (bearing * Math.PI) / 180; const φ1 = (lat * Math.PI) / 180; const λ1 = (lon * Math.PI) / 180; @@ -135,20 +492,42 @@ const APP = () => { }; useEffect(() => { - // Update map style when toggle changes if (mapRef.current) { mapRef.current.setStyle(getMapStyle(mapStyle)); } }, [mapStyle]); const getMapStyle = (style) => { + if (style === 'satellite') { + return { + version: 8, + sources: { + 'esri-satellite': { + type: 'raster', + tiles: ['https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'], + tileSize: 256, + attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community' + } + }, + layers: [ + { + id: 'satellite-layer', + type: 'raster', + source: 'esri-satellite', + minzoom: 0, + maxzoom: 20 + } + ] + }; + } return style === 'dark' ? 'https://demotiles.maplibre.org/style.json' - : 'https://demotiles.maplibre.org/style.json'; // Using same for now, can be customized + : 'https://demotiles.maplibre.org/style.json'; }; const handleShowLineOfSight = async () => { setLoading(true); + setSelectedCity(null); try { const response = await apiService.getLineOfSight( selectedPoint.lat, @@ -157,8 +536,14 @@ const APP = () => { tolerance ); - setLineOfSightData(response.data); - renderLineOnMap(response.data); + setLineOfSightData(response.data.data); + setIsLocked(true); + + if (mapRef.current && mapRef.current.getLayer('preview-line')) { + mapRef.current.setLayoutProperty('preview-line', 'visibility', 'none'); + } + + renderLineOnMap(response.data.data); } catch (error) { console.error('Error fetching line of sight:', error); } finally { @@ -166,81 +551,13 @@ const APP = () => { } }; - const renderLineOnMap = (data) => { - const map = mapRef.current; - if (!map) return; - - // Clear existing line - if (map.getSource('line-of-sight')) { - map.removeLayer('line-of-sight'); - map.removeSource('line-of-sight'); - } - - // Add line source - map.addSource('line-of-sight', { - type: 'geojson', - data: { - type: 'Feature', - properties: {}, - geometry: { - type: 'LineString', - coordinates: data.line_coordinates.map(coord => [coord.lon, coord.lat]) - } - } - }); - - // Add line layer - map.addLayer({ - id: 'line-of-sight', - type: 'line', - source: 'line-of-sight', - layout: { - 'line-join': 'round', - 'line-cap': 'round' - }, - paint: { - 'line-color': '#FF6B6B', - 'line-width': 4, - 'line-opacity': 0.8 - } - }); - - // Add city markers - data.conurbations.forEach((city, index) => { - const markerId = `city-${index}`; - - // Create marker element - const el = document.createElement('div'); - el.className = 'city-marker'; - el.innerHTML = ` -
-
${city.name}
-
${(city.population / 1000000).toFixed(1)}M
- `; - - // Add marker to map - new maplibregl.Marker(el) - .setLngLat([city.lon, city.lat]) - .setPopup(new maplibregl.Popup().setHTML( - `${city.name}
Population: ${city.population.toLocaleString()}
Distance: ${city.distance_km} km` - )) - .addTo(map); - }); - - // Fit bounds to show line - const bounds = new maplibregl.LngLatBounds(); - data.line_coordinates.forEach(coord => { - bounds.extend([coord.lon, coord.lat]); - }); - map.fitBounds(bounds, { padding: 50 }); - }; - return (
-
+
+
-
+

Line of Sight Settings

@@ -255,6 +572,7 @@ const APP = () => { min="0" max="360" value={direction} + disabled={isLocked} onChange={(e) => setDirection(parseInt(e.target.value))} /> {direction}° @@ -265,6 +583,7 @@ const APP = () => { setTolerance(parseInt(e.target.value))} min="10" max="200" @@ -274,45 +593,79 @@ const APP = () => {
- - + + +
- + {!isLocked ? ( + + ) : ( + <> + + + + )}
{lineOfSightData && (

Conurbations Found ({lineOfSightData.conurbations.length})

+

Click a city for details

- + - {lineOfSightData.conurbations.slice(0, 10).map((city, index) => ( - + {lineOfSightData.conurbations.map((city, index) => ( + { + setSelectedCity(city); + showCityPopup(city); + mapRef.current.flyTo({ center: [city.lon, city.lat], zoom: 8 }); + }} + className={selectedCity?.id === city.id ? 'selected-row' : ''} + > - - + + ))}
# City PopulationDistanceDist.
{index + 1} {city.name}{(city.population / 1000000).toFixed(1)}M{city.distance_km} km{(city.population / 1000).toFixed(0)}k{city.distance_km}km
- {lineOfSightData.conurbations.length > 10 && ( -

... and {lineOfSightData.conurbations.length - 10} more cities

- )}
)} diff --git a/frontend/src/styles/App.css b/frontend/src/styles/App.css index 8a8bac3..4d635e2 100644 --- a/frontend/src/styles/App.css +++ b/frontend/src/styles/App.css @@ -3,6 +3,7 @@ height: 100vh; width: 100vw; overflow: hidden; + position: relative; } .map-container { @@ -19,6 +20,7 @@ box-shadow: -2px 0 10px rgba(0,0,0,0.1); overflow-y: auto; z-index: 2; + position: relative; } .control-group { @@ -62,6 +64,7 @@ .setting-row span { font-weight: bold; color: #2c3e50; + min-width: 40px; } .setting-row button { @@ -78,6 +81,12 @@ background: #e9ecef; } +.setting-row button.active-style { + background: #34495e; + color: white; + border-color: #2c3e50; +} + .action-btn { width: 100%; padding: 12px; @@ -101,18 +110,66 @@ cursor: not-allowed; } +.action-btn-secondary { + width: 100%; + padding: 12px; + background: #34495e; + color: white; + border: none; + border-radius: 6px; + font-size: 16px; + font-weight: bold; + cursor: pointer; + transition: background 0.3s; + margin-top: 10px; +} + +.action-btn-secondary:hover { + background: #2c3e50; +} + +.disabled-controls { + opacity: 0.8; +} + +.disabled-controls input { + pointer-events: none; + cursor: not-allowed; +} + +/* Allow action buttons and settings buttons to work even when locked */ +.action-btn, .action-btn-secondary, .setting-row button { + pointer-events: auto !important; +} + +.results-panel table thead { + position: sticky; + top: 0; + z-index: 1; +} + .results-panel { background: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 20px; + max-height: 450px; + overflow-y: auto; + border: 1px solid #eee; } .results-panel h3 { margin-top: 0; color: #2c3e50; font-size: 16px; - margin-bottom: 15px; + margin-bottom: 5px; +} + +.results-panel .hint { + font-size: 11px; + color: #7f8c8d; + margin-bottom: 10px; + font-style: italic; } .results-panel table { @@ -124,21 +181,114 @@ .results-panel th { background: #34495e; color: white; - padding: 10px; + padding: 8px 4px; text-align: left; } .results-panel td { - padding: 8px; + padding: 8px 4px; border-bottom: 1px solid #ddd; + cursor: pointer; } .results-panel tr:nth-child(even) { - background: #f8f9fa; + background: #fdfdfd; } .results-panel tr:hover { - background: #e9ecef; + background: #ecf0f1; +} + +.results-panel tr.selected-row { + background: #d5e8d4 !important; + font-weight: bold; +} + +.info-panel-modal { + position: absolute; + top: 20px; + left: 20px; + width: 280px; + background: white; + border-radius: 8px; + box-shadow: 0 4px 15px rgba(0,0,0,0.3); + z-index: 1000; + animation: slideIn 0.3s ease-out; +} + +@keyframes slideIn { + from { opacity: 0; transform: translateX(-20px); } + to { opacity: 1; transform: translateX(0); } +} + +.info-panel-content { + padding: 20px; + position: relative; +} + +.info-panel-content h2 { + margin-top: 0; + margin-bottom: 15px; + font-size: 18px; + color: #2c3e50; + border-bottom: 2px solid #4CAF50; + padding-bottom: 8px; +} + +.close-btn { + position: absolute; + top: 10px; + right: 10px; + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: #95a5a6; +} + +.close-btn:hover { + color: #34495e; +} + +.info-grid { + display: flex; + flex-direction: column; + gap: 12px; + margin-bottom: 20px; +} + +.info-item { + display: flex; + flex-direction: column; +} + +.info-item label { + font-size: 11px; + color: #7f8c8d; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.info-item span { + font-size: 14px; + color: #2c3e50; + font-weight: 500; +} + +.action-btn-small { + width: 100%; + padding: 8px; + background: #3498db; + color: white; + border: none; + border-radius: 4px; + font-size: 14px; + cursor: pointer; + transition: background 0.2s; +} + +.action-btn-small:hover { + background: #2980b9; } .more-info { @@ -177,6 +327,25 @@ flex-direction: column; align-items: center; font-family: sans-serif; + z-index: 100; + cursor: pointer; + pointer-events: auto !important; +} + +.marker-number { + background: #2c3e50; + color: white; + width: 18px; + height: 18px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 10px; + font-weight: bold; + margin-bottom: -4px; + border: 1px solid white; + z-index: 2; } .marker-dot { @@ -186,6 +355,7 @@ border: 2px solid white; border-radius: 50%; box-shadow: 0 2px 4px rgba(0,0,0,0.3); + z-index: 1; } .marker-label { @@ -209,6 +379,38 @@ white-space: nowrap; } +.map-info-popup { + padding: 5px; + font-family: sans-serif; +} + +.popup-header { + border-bottom: 1px solid #eee; + padding-bottom: 5px; + margin-bottom: 5px; + color: #2c3e50; + font-size: 14px; +} + +.popup-body { + font-size: 12px; + color: #666; +} + +.popup-body p { + margin: 3px 0; +} + +.maplibregl-popup { + z-index: 2000 !important; +} + +.maplibregl-popup-content { + border-radius: 8px !important; + box-shadow: 0 4px 15px rgba(0,0,0,0.3) !important; + padding: 15px !important; +} + @media (max-width: 768px) { .app-container { flex-direction: column; @@ -223,4 +425,12 @@ .map-container { height: 60vh; } + + .info-panel-modal { + left: 10px; + right: 10px; + width: auto; + bottom: 20px; + top: auto; + } }