Spaces:
Runtime error
Runtime error
Commit
·
1d6d0dd
1
Parent(s):
c2a5a36
Add option for scientific/normal numerical representations
Browse files- frontend/src/components/Map.js +36 -4
- frontend/src/utils/mapUtils.js +14 -8
frontend/src/components/Map.js
CHANGED
|
@@ -68,6 +68,8 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 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;
|
|
@@ -434,7 +436,12 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 434 |
boxShadow: 'none',
|
| 435 |
padding: 0
|
| 436 |
}}>
|
| 437 |
-
{geoDistance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
</span>
|
| 439 |
</Tooltip>
|
| 440 |
)}
|
|
@@ -461,7 +468,7 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 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]
|
|
@@ -619,9 +626,23 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 619 |
<option value="mi">Miles</option>
|
| 620 |
</select>
|
| 621 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 622 |
{geoDistance !== null && (
|
| 623 |
<div style={{ fontSize: 20, fontWeight: 600, color: '#1976d2' }}>
|
| 624 |
-
{
|
|
|
|
|
|
|
|
|
|
| 625 |
</div>
|
| 626 |
)}
|
| 627 |
<button
|
|
@@ -668,7 +689,7 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 668 |
</div>
|
| 669 |
{polygonArea !== null && (
|
| 670 |
<div style={{ fontSize: 20, fontWeight: 600, color: '#1976d2' }}>
|
| 671 |
-
{formatArea(polygonArea, areaUnit)}
|
| 672 |
</div>
|
| 673 |
)}
|
| 674 |
<button
|
|
@@ -703,6 +724,17 @@ const Map = ( { onMapClick, searchQuery, contentType } ) => {
|
|
| 703 |
<option value="mi2">mi²</option>
|
| 704 |
</select>
|
| 705 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 706 |
</>
|
| 707 |
)}
|
| 708 |
</div>
|
|
|
|
| 68 |
const [polygonArea, setPolygonArea] = useState(null);
|
| 69 |
const [areaUnit, setAreaUnit] = useState('sqm'); // 'sqm', 'sqkm', 'ha', 'acres', 'sqmi'
|
| 70 |
|
| 71 |
+
const [numberFormat, setNumberFormat] = useState('normal'); // 'normal' | 'scientific'
|
| 72 |
+
|
| 73 |
const handleMouseDown = (e) => {
|
| 74 |
isDragging.current = true;
|
| 75 |
startX.current = e.clientX;
|
|
|
|
| 436 |
boxShadow: 'none',
|
| 437 |
padding: 0
|
| 438 |
}}>
|
| 439 |
+
{geoDistance !== null
|
| 440 |
+
? (numberFormat === 'scientific'
|
| 441 |
+
? geoDistance.toExponential(2)
|
| 442 |
+
: geoDistance.toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 })
|
| 443 |
+
) + ' ' + geoUnit
|
| 444 |
+
: ''}
|
| 445 |
</span>
|
| 446 |
</Tooltip>
|
| 447 |
)}
|
|
|
|
| 468 |
icon={L.divIcon({
|
| 469 |
className: 'area-label',
|
| 470 |
html: polygonArea !== null
|
| 471 |
+
? `<div style="background:rgba(255,255,255,0.8);padding:2px 6px;border-radius:4px;color:#1976d2;font-weight:600;">${formatArea(polygonArea, areaUnit, numberFormat)}</div>`
|
| 472 |
: '',
|
| 473 |
iconSize: [100, 24],
|
| 474 |
iconAnchor: [50, 12]
|
|
|
|
| 626 |
<option value="mi">Miles</option>
|
| 627 |
</select>
|
| 628 |
</div>
|
| 629 |
+
<div>
|
| 630 |
+
<label style={{ fontWeight: 500, marginRight: 8 }}>Number format:</label>
|
| 631 |
+
<select
|
| 632 |
+
value={numberFormat}
|
| 633 |
+
onChange={e => setNumberFormat(e.target.value)}
|
| 634 |
+
style={{ padding: '4px 8px', borderRadius: 4, border: '1px solid #ccc' }}
|
| 635 |
+
>
|
| 636 |
+
<option value="normal">Normal</option>
|
| 637 |
+
<option value="scientific">Scientific</option>
|
| 638 |
+
</select>
|
| 639 |
+
</div>
|
| 640 |
{geoDistance !== null && (
|
| 641 |
<div style={{ fontSize: 20, fontWeight: 600, color: '#1976d2' }}>
|
| 642 |
+
{numberFormat === 'scientific'
|
| 643 |
+
? geoDistance.toExponential(2)
|
| 644 |
+
: geoDistance.toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 })
|
| 645 |
+
}
|
| 646 |
</div>
|
| 647 |
)}
|
| 648 |
<button
|
|
|
|
| 689 |
</div>
|
| 690 |
{polygonArea !== null && (
|
| 691 |
<div style={{ fontSize: 20, fontWeight: 600, color: '#1976d2' }}>
|
| 692 |
+
{formatArea(polygonArea, areaUnit, numberFormat)}
|
| 693 |
</div>
|
| 694 |
)}
|
| 695 |
<button
|
|
|
|
| 724 |
<option value="mi2">mi²</option>
|
| 725 |
</select>
|
| 726 |
</div>
|
| 727 |
+
<div>
|
| 728 |
+
<label style={{ fontWeight: 500, marginRight: 8 }}>Number format:</label>
|
| 729 |
+
<select
|
| 730 |
+
value={numberFormat}
|
| 731 |
+
onChange={e => setNumberFormat(e.target.value)}
|
| 732 |
+
style={{ padding: '4px 8px', borderRadius: 4, border: '1px solid #ccc' }}
|
| 733 |
+
>
|
| 734 |
+
<option value="normal">Normal</option>
|
| 735 |
+
<option value="scientific">Scientific</option>
|
| 736 |
+
</select>
|
| 737 |
+
</div>
|
| 738 |
</>
|
| 739 |
)}
|
| 740 |
</div>
|
frontend/src/utils/mapUtils.js
CHANGED
|
@@ -110,25 +110,31 @@ function getPolygonCentroid(points) {
|
|
| 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 |
-
|
|
|
|
| 122 |
case "ha":
|
| 123 |
-
|
|
|
|
| 124 |
case "sqm":
|
| 125 |
-
|
|
|
|
| 126 |
case "acres":
|
| 127 |
-
|
|
|
|
| 128 |
case "mi2":
|
| 129 |
-
|
|
|
|
| 130 |
default:
|
| 131 |
-
|
|
|
|
| 132 |
}
|
| 133 |
}
|
| 134 |
|
|
|
|
| 110 |
return [x / n, y / n];
|
| 111 |
}
|
| 112 |
|
| 113 |
+
function formatArea(area, unit = 'sqm', format = "normal") {
|
| 114 |
|
| 115 |
if (typeof area !== 'number' || isNaN(area)) {
|
| 116 |
return 'Invalid area';
|
| 117 |
}
|
| 118 |
+
let value;
|
| 119 |
switch (unit) {
|
| 120 |
case "km2":
|
| 121 |
+
value = area / 1e6;
|
| 122 |
+
return (format === "scientific" ? value.toExponential(2) : value.toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 })) + ' km²';
|
| 123 |
case "ha":
|
| 124 |
+
value = area / 1e4;
|
| 125 |
+
return (format === "scientific" ? value.toExponential(2) : value.toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 })) + ' ha';
|
| 126 |
case "sqm":
|
| 127 |
+
value = area;
|
| 128 |
+
return (format === "scientific" ? value.toExponential(2) : value.toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 })) + ' m²';
|
| 129 |
case "acres":
|
| 130 |
+
value = area / 4046.8564224;
|
| 131 |
+
return (format === "scientific" ? value.toExponential(2) : value.toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 })) + ' acres';
|
| 132 |
case "mi2":
|
| 133 |
+
value = area / 2589988.110336;
|
| 134 |
+
return (format === "scientific" ? value.toExponential(2) : value.toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 })) + ' mi²';
|
| 135 |
default:
|
| 136 |
+
value = area;
|
| 137 |
+
return (format === "scientific" ? value.toExponential(2) : value.toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 })) + ' m²';
|
| 138 |
}
|
| 139 |
}
|
| 140 |
|