DebasishDhal99 commited on
Commit
2f322ce
·
1 Parent(s): 1d6d0dd

Fix area using geographiclib-geodesic package

Browse files

- geodesiclib package is deprecated (2025). so i used geographiclib-geodesic package
- did plenty of trials on how to accurately calculated geodesic polygon's area.
- got error whenever I selected points in china or anywhere near that longitude.
- found out that i was actually inverting the (lat, lon) input to (lon, lat) and lat cannon > 90, while lon can be.

frontend/package-lock.json CHANGED
@@ -13,6 +13,7 @@
13
  "@testing-library/react": "^16.3.0",
14
  "@testing-library/user-event": "^13.5.0",
15
  "geodesy": "^2.4.0",
 
16
  "leaflet": "^1.9.4",
17
  "leaflet.geodesic": "^2.7.1",
18
  "react": "^19.0.0-rc.1",
@@ -8509,6 +8510,12 @@
8509
  "node": ">=8.0.0"
8510
  }
8511
  },
 
 
 
 
 
 
8512
  "node_modules/get-caller-file": {
8513
  "version": "2.0.5",
8514
  "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
 
13
  "@testing-library/react": "^16.3.0",
14
  "@testing-library/user-event": "^13.5.0",
15
  "geodesy": "^2.4.0",
16
+ "geographiclib-geodesic": "^2.1.1",
17
  "leaflet": "^1.9.4",
18
  "leaflet.geodesic": "^2.7.1",
19
  "react": "^19.0.0-rc.1",
 
8510
  "node": ">=8.0.0"
8511
  }
8512
  },
8513
+ "node_modules/geographiclib-geodesic": {
8514
+ "version": "2.1.1",
8515
+ "resolved": "https://registry.npmjs.org/geographiclib-geodesic/-/geographiclib-geodesic-2.1.1.tgz",
8516
+ "integrity": "sha512-lkd8EUkPSByobWu9BPMHTdYA5AUZxOa8McmUNtBE9KrvUJEvSADnN6gTDmhXbi6NzdA16LtWLpSxLE/lIIRhyA==",
8517
+ "license": "MIT"
8518
+ },
8519
  "node_modules/get-caller-file": {
8520
  "version": "2.0.5",
8521
  "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
frontend/package.json CHANGED
@@ -8,6 +8,7 @@
8
  "@testing-library/react": "^16.3.0",
9
  "@testing-library/user-event": "^13.5.0",
10
  "geodesy": "^2.4.0",
 
11
  "leaflet": "^1.9.4",
12
  "leaflet.geodesic": "^2.7.1",
13
  "react": "^19.0.0-rc.1",
 
8
  "@testing-library/react": "^16.3.0",
9
  "@testing-library/user-event": "^13.5.0",
10
  "geodesy": "^2.4.0",
11
+ "geographiclib-geodesic": "^2.1.1",
12
  "leaflet": "^1.9.4",
13
  "leaflet.geodesic": "^2.7.1",
14
  "react": "^19.0.0-rc.1",
frontend/src/components/Map.js CHANGED
@@ -276,7 +276,7 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
276
  if (geoToolMode === "area" && areaPoints.length >= 3) {
277
  // Just ensuring that the polygon is closed (first == last)
278
  const closed = [...areaPoints, areaPoints[0]];
279
- const area = calculatePolygonArea(closed.map(([lat, lon]) => [lon, lat])); // [lon, lat] for GeoJSON unfortunately
280
  setPolygonArea(area);
281
  } else {
282
  setPolygonArea(null);
 
276
  if (geoToolMode === "area" && areaPoints.length >= 3) {
277
  // Just ensuring that the polygon is closed (first == last)
278
  const closed = [...areaPoints, areaPoints[0]];
279
+ const area = calculatePolygonArea(closed.map(([lat, lon]) => [lat, lon])); // This took me a while to figure out, it should be just (lat, lon), not (lon, lat)
280
  setPolygonArea(area);
281
  } else {
282
  setPolygonArea(null);
frontend/src/utils/mapUtils.js CHANGED
@@ -1,4 +1,4 @@
1
-
2
  // Haversine-based geodesic interpolator
3
  function generateGeodesicPoints(lat1, lon1, lat2, lon2, numPoints = 512) {
4
  /**
@@ -47,61 +47,24 @@ function generateGeodesicPoints(lat1, lon1, lat2, lon2, numPoints = 512) {
47
 
48
 
49
 
50
- /**
51
- * Calculate the area enclosed by coordinates using simplified Karney method
52
- * @param {Array<Array<number>>} coordinates - Array of [lat, lon] pairs in decimal degrees
53
- * @returns {number} Area in square meters
54
- */
55
- function calculatePolygonArea(coordinates, output_unit = 'sqm') {
56
- if (!coordinates || coordinates.length < 3) {
57
- throw new Error('At least 3 coordinates are required');
58
- }
59
-
60
- // WGS84 ellipsoid parameters
61
- const a = 6378137.0; // Semi-major axis (meters)
62
- const f = 1 / 298.257223563; // Flattening
63
- const e2 = f * (2 - f); // First eccentricity squared
64
-
65
- // Ensure polygon is closed
66
- const coords = [...coordinates];
67
- if (coords[0][0] !== coords[coords.length - 1][0] ||
68
- coords[0][1] !== coords[coords.length - 1][1]) {
69
- coords.push(coords[0]);
70
- }
71
-
72
- let area = 0;
73
- const n = coords.length - 1;
74
-
75
- // Calculate area using simplified geodesic excess method
76
- for (let i = 0; i < n; i++) {
77
- const [lat1, lon1] = coords[i];
78
- const [lat2, lon2] = coords[i + 1];
79
-
80
- // Convert to radians
81
- const phi1 = lat1 * Math.PI / 180;
82
- const phi2 = lat2 * Math.PI / 180;
83
- let dL = (lon2 - lon1) * Math.PI / 180;
84
-
85
- // Normalize longitude difference
86
- while (dL > Math.PI) dL -= 2 * Math.PI;
87
- while (dL < -Math.PI) dL += 2 * Math.PI;
88
-
89
- // Geodesic excess contribution
90
- const E = 2 * Math.atan2(
91
- Math.tan(dL / 2) * (Math.sin(phi1) + Math.sin(phi2)),
92
- 2 + Math.sin(phi1) * Math.sin(phi2) + Math.cos(phi1) * Math.cos(phi2) * Math.cos(dL)
93
- );
94
-
95
- area += E;
96
- }
97
-
98
- // Convert to actual area using ellipsoid parameters
99
- const ellipsoidArea = Math.abs(area) * (a * a / 2) * (1 - e2);
100
-
101
- return ellipsoidArea; // Return area in square meters
102
-
103
- }
104
 
 
 
 
 
 
105
 
106
  function getPolygonCentroid(points) {
107
  // Simple centroid calculation for small polygons
@@ -113,6 +76,7 @@ function getPolygonCentroid(points) {
113
  function formatArea(area, unit = 'sqm', format = "normal") {
114
 
115
  if (typeof area !== 'number' || isNaN(area)) {
 
116
  return 'Invalid area';
117
  }
118
  let value;
 
1
+ import geodesic from 'geographiclib-geodesic';
2
  // Haversine-based geodesic interpolator
3
  function generateGeodesicPoints(lat1, lon1, lat2, lon2, numPoints = 512) {
4
  /**
 
47
 
48
 
49
 
50
+ function calculatePolygonArea(coords) {
51
+ /*** Calculate the geodesic area of a polygon on the WGS84 ellipsoid.
52
+ * @param {Array<Array<number>>} coords - Array of [lat, lon] pairs.
53
+ * @returns {number} Area in square meters.
54
+ */
55
+ // console.log(coords); // Lifesaver
56
+ const geod = geodesic.Geodesic.WGS84;
57
+ const poly = geod.Polygon(false); // false = polygon, not polyline
58
+
59
+ for (const [lat, lon] of coords) {
60
+ poly.AddPoint(lat, lon);
61
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
+ // Closing the polygon is not required as I am already doing it in the frontend.
64
+
65
+ const result = poly.Compute();
66
+ return result.area; // area in square meters, important.
67
+ }
68
 
69
  function getPolygonCentroid(points) {
70
  // Simple centroid calculation for small polygons
 
76
  function formatArea(area, unit = 'sqm', format = "normal") {
77
 
78
  if (typeof area !== 'number' || isNaN(area)) {
79
+ console.log('Invalid area input:', area);
80
  return 'Invalid area';
81
  }
82
  let value;