Spaces:
Runtime error
Runtime error
Commit
·
c2a5a36
1
Parent(s):
a5e71eb
Add options for 5 different units for area feature
Browse files- frontend/src/components/Map.js +17 -3
- frontend/src/utils/mapUtils.js +23 -6
frontend/src/components/Map.js
CHANGED
|
@@ -66,7 +66,8 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 66 |
|
| 67 |
const [areaPoints, setAreaPoints] = useState([]);
|
| 68 |
const [polygonArea, setPolygonArea] = useState(null);
|
| 69 |
-
|
|
|
|
| 70 |
const handleMouseDown = (e) => {
|
| 71 |
isDragging.current = true;
|
| 72 |
startX.current = e.clientX;
|
|
@@ -460,7 +461,7 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 460 |
icon={L.divIcon({
|
| 461 |
className: 'area-label',
|
| 462 |
html: polygonArea !== null
|
| 463 |
-
? `<div style="background:rgba(255,255,255,0.8);padding:2px 6px;border-radius:4px;color:#1976d2;font-weight:600;">${formatArea(polygonArea)}</div>`
|
| 464 |
: '',
|
| 465 |
iconSize: [100, 24],
|
| 466 |
iconAnchor: [50, 12]
|
|
@@ -667,7 +668,7 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 667 |
</div>
|
| 668 |
{polygonArea !== null && (
|
| 669 |
<div style={{ fontSize: 20, fontWeight: 600, color: '#1976d2' }}>
|
| 670 |
-
{formatArea(polygonArea)}
|
| 671 |
</div>
|
| 672 |
)}
|
| 673 |
<button
|
|
@@ -689,6 +690,19 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 689 |
>
|
| 690 |
Clear & Back
|
| 691 |
</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 692 |
</>
|
| 693 |
)}
|
| 694 |
</div>
|
|
|
|
| 66 |
|
| 67 |
const [areaPoints, setAreaPoints] = useState([]);
|
| 68 |
const [polygonArea, setPolygonArea] = useState(null);
|
| 69 |
+
const [areaUnit, setAreaUnit] = useState('sqm'); // 'sqm', 'sqkm', 'ha', 'acres', 'sqmi'
|
| 70 |
+
|
| 71 |
const handleMouseDown = (e) => {
|
| 72 |
isDragging.current = true;
|
| 73 |
startX.current = e.clientX;
|
|
|
|
| 461 |
icon={L.divIcon({
|
| 462 |
className: 'area-label',
|
| 463 |
html: polygonArea !== null
|
| 464 |
+
? `<div style="background:rgba(255,255,255,0.8);padding:2px 6px;border-radius:4px;color:#1976d2;font-weight:600;">${formatArea(polygonArea, areaUnit)}</div>`
|
| 465 |
: '',
|
| 466 |
iconSize: [100, 24],
|
| 467 |
iconAnchor: [50, 12]
|
|
|
|
| 668 |
</div>
|
| 669 |
{polygonArea !== null && (
|
| 670 |
<div style={{ fontSize: 20, fontWeight: 600, color: '#1976d2' }}>
|
| 671 |
+
{formatArea(polygonArea, areaUnit)}
|
| 672 |
</div>
|
| 673 |
)}
|
| 674 |
<button
|
|
|
|
| 690 |
>
|
| 691 |
Clear & Back
|
| 692 |
</button>
|
| 693 |
+
<div>
|
| 694 |
+
<label style={{ fontWeight: 500, marginRight: 8 }}>Unit:</label>
|
| 695 |
+
<select
|
| 696 |
+
value={areaUnit}
|
| 697 |
+
onChange={e => setAreaUnit(e.target.value)}
|
| 698 |
+
style={{ padding: '4px 8px', borderRadius: 4, border: '1px solid #ccc' }}
|
| 699 |
+
>
|
| 700 |
+
<option value="m2">m²</option>
|
| 701 |
+
<option value="km2">km²</option>
|
| 702 |
+
<option value="ha">ha</option>
|
| 703 |
+
<option value="mi2">mi²</option>
|
| 704 |
+
</select>
|
| 705 |
+
</div>
|
| 706 |
</>
|
| 707 |
)}
|
| 708 |
</div>
|
frontend/src/utils/mapUtils.js
CHANGED
|
@@ -52,7 +52,7 @@ function generateGeodesicPoints(lat1, lon1, lat2, lon2, numPoints = 512) {
|
|
| 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) {
|
| 56 |
if (!coordinates || coordinates.length < 3) {
|
| 57 |
throw new Error('At least 3 coordinates are required');
|
| 58 |
}
|
|
@@ -98,7 +98,8 @@ function calculatePolygonArea(coordinates) {
|
|
| 98 |
// Convert to actual area using ellipsoid parameters
|
| 99 |
const ellipsoidArea = Math.abs(area) * (a * a / 2) * (1 - e2);
|
| 100 |
|
| 101 |
-
return ellipsoidArea;
|
|
|
|
| 102 |
}
|
| 103 |
|
| 104 |
|
|
@@ -109,10 +110,26 @@ function getPolygonCentroid(points) {
|
|
| 109 |
return [x / n, y / n];
|
| 110 |
}
|
| 111 |
|
| 112 |
-
function formatArea(area) {
|
| 113 |
-
|
| 114 |
-
if (area
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
}
|
| 117 |
|
| 118 |
export {generateGeodesicPoints, calculatePolygonArea, getPolygonCentroid, formatArea};
|
|
|
|
| 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 |
}
|
|
|
|
| 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 |
|
|
|
|
| 110 |
return [x / n, y / n];
|
| 111 |
}
|
| 112 |
|
| 113 |
+
function formatArea(area, unit = 'sqm') {
|
| 114 |
+
|
| 115 |
+
if (typeof area !== 'number' || isNaN(area)) {
|
| 116 |
+
return 'Invalid area';
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
switch (unit) {
|
| 120 |
+
case "km2":
|
| 121 |
+
return (area / 1e6).toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 }) + ' km²';
|
| 122 |
+
case "ha":
|
| 123 |
+
return (area / 1e4).toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 }) + ' ha';
|
| 124 |
+
case "sqm":
|
| 125 |
+
return area.toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 }) + ' m²';
|
| 126 |
+
case "acres":
|
| 127 |
+
return (area / 4046.8564224).toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 }) + ' acres';
|
| 128 |
+
case "mi2":
|
| 129 |
+
return (area / 2589988.110336).toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 }) + ' sq mi';
|
| 130 |
+
default:
|
| 131 |
+
return area.toFixed(2).toLocaleString() + ' m²'; // Default
|
| 132 |
+
}
|
| 133 |
}
|
| 134 |
|
| 135 |
export {generateGeodesicPoints, calculatePolygonArea, getPolygonCentroid, formatArea};
|