How to find size of rectangular boundary in the Explore tab

I have several rectangular boundaries in the explore tab that I am analyzing. Each area is of a different size. I can view the latitude and longitude of the northeast and southwest corners of the rectangular boundaries in the URL bar. I need to find the area of each boundary.

I know how to calculate this by hand but was wondering if the area of the boundaries is accessible or visible in iNat. I’d prefer to not calculate by hand if there is an easier route.

If you could use the circular bounding box the url provides the radius of that circle, from which area could be calculated.

Thank you. That’s smart.

However, for my purposes, I’m stuck with rectangular boundaries. The boundaries are roughly adjacent to each other, leaving little area between them. If I were to use circles, the areas between adjacent boundaries would be too large.

Someone with more math/spherical trigonometry/surveying experience please correct me

It seems like the API does not automatically calculate or return the area of the polygon, and it has to be calculated externally

The url of the explore tab lists the ne and sw bounding coordinates for the area selected, from those to you can extrapolate to all 4 coordinates of the polygon.
take this example url:
https://www.inaturalist.org/observations?nelat=48.11&nelng=-121.77&subview=map&swlat=46.91&swlng=-123.66”
the 4 corners of this polygon in counterclockwise rotation are:
p1(48.11, -123.66) p2(46.91, -123.66) p3(46.91, -121.77) p4(48.11, -121.77)

Something to do with the Spherical Excess…

Area = R^2 × (Σ [ (λᵢ₋₁ - λᵢ₊₁) × sin(φᵢ) ] ) ÷ 2

  • R: Earth’s radius ( 6,378,137 for WGS84)

  • φᵢ: Latitude of vertex i in radians

  • λᵢ: Longitude of vertex i in radians

  • n: Number of vertices

  • i: Current vertex index (1 to n)

(λᵢ₋₁ - λᵢ₊₁) is a Cycling Index

When i=1: λᵢ₋₁ = λₙ (last vertex’s longitude)

When i=n: λᵢ₊₁ = λ₁ (first vertex’s longitude)

points need to be ordered counter-clockwise around the polygon

p1(48.11, -123.66) NW corner
p4(48.11, -121.77) NE corner
p3(46.91, -121.77) SE corner
p2(46.91, -123.66) SW corner

First step is to convert coordinate degrees to radians, Radians = Degrees × π / 180

p1: Lat/Long = 48.11°, -123.66° = 0.8396, 2.1582 rad

p2: Lat/Long= 46.91°, -123.66° = 0.8187, 2.1582 rad

p3: Lat/Long= 46.91°, -121.77° = 0.8187, 2.1251 rad

p4: Lat/Long= 48.11°, 121.77° = 0.8396, 2.1251 rad

p1: φ₁ = 0.8396, λ₁ = -2.1582
p4: φ₄ = 0.8396, λ₄ = -2.1251
p3: φ₃ = 0.8187, λ₃ = -2.1251
p2: φ₂ = 0.8187, λ₂ = -2.158

Calculate Each Term

Term 1 = (λ₄ - λ₂) × sin(φ₁)
= (-2.1251 - (-2.1582)) × sin(0.8396)
= (0.0331) × 0.7441 = 0.02463
Term 2 = (λ₁ - λ₃) × sin(φ₂)
= (-2.1582 - (-2.1251)) × sin(0.8187)
= (-0.0331) × 0.7293 = -0.02414
Term 3 = (λ₂ - λ₄) × sin(φ₃)
= (-2.1582 - (-2.1251)) × sin(0.8187)
= (-0.0331) × 0.7293 = -0.02414
Term 4 = (λ₃ - λ₁) × sin(φ₄)
= (-2.1251 - (-2.1582)) × sin(0.8396)
= (0.0331) × 0.7441 = 0.02463

Since this polygon is a rectangle the value of terms repeat due to symmetry

Sum of terms (Σ,sum) = 0.02463 - 0.02414 - 0.02414 + 0.02463 = 0.00098

Area = R² × Sum ÷ 2
= (6378137)² × 0.00098 ÷ 2
= 1.993 × 10^10 m² = 19,930 km²

As far as I understand the formula calculates each latitude slices contribution to the total area: sin(φᵢ) gives the radius of the latitude circle at that point, (λᵢ₋₁ - λᵢ₊₁) represents the angular width that this latitude contributes, The cyclic indexing ensures all edges are properly accounted for

Since you don’t want to calculate by hand, vibe coded a Java Script to run in your browser console on an open explore tab:

// Automatically extract and calculate area from iNaturalist explore URL
function calculateINatAreaFromURL() {
    // Get current URL parameters
    const urlParams = new URLSearchParams(window.location.search);
    
    // Extract bounding box coordinates
    const nelat = parseFloat(urlParams.get('nelat'));
    const nelng = parseFloat(urlParams.get('nelng'));
    const swlat = parseFloat(urlParams.get('swlat'));
    const swlng = parseFloat(urlParams.get('swlng'));
    
    // Check if we have all required coordinates
    if (!nelat || !nelng || !swlat || !swlng) {
        console.log('❌ No bounding box coordinates found in URL');
        console.log('Make sure you have nelat, nelng, swlat, swlng parameters');
        return null;
    }
    
    console.log('📍 Extracted coordinates:');
    console.log(`  NE: (${nelat}, ${nelng})`);
    console.log(`  SW: (${swlat}, ${swlng})`);
    
    // Calculate area
    const R = 6378137; // Earth radius in meters
    const toRad = deg => deg * Math.PI / 180;
    
    const dLng = Math.abs(toRad(nelng) - toRad(swlng));
    const dSinLat = Math.sin(toRad(nelat)) - Math.sin(toRad(swlat));
    
    const areaM2 = R * R * dLng * Math.abs(dSinLat);
    const areaKm2 = areaM2 / 1000000;
    const areaMi2 = areaKm2 * 0.386102;
    
    return {
        km2: areaKm2,
        mi2: areaMi2,
        coordinates: { nelat, nelng, swlat, swlng }
    };
}

// Run the calculation and display results
function displayAreaResults() {
    const result = calculateINatAreaFromURL();
    
    if (result) {
        console.log('📐 CALCULATED AREA:');
        console.log(`  ${result.km2.toLocaleString('en-US', {maximumFractionDigits: 0})} km²`);
        console.log(`  ${result.mi2.toLocaleString('en-US', {maximumFractionDigits: 0})} mi²`);
        console.log('---');
        console.log('ℹ️  This is the approximate surface area of your search polygon');
        
        // Optional: Create a visual alert
        alert(`Search Area: ${result.km2.toLocaleString('en-US', {maximumFractionDigits: 0})} km²\n(${result.mi2.toLocaleString('en-US', {maximumFractionDigits: 0})} mi²)`);
    }
}

// One-click function to run everything
function iNatArea() {
    console.clear();
    console.log('🌍 iNaturalist Area Calculator');
    console.log('===============================');
    displayAreaResults();
}

// Run it automatically when pasted
iNatArea();

// Also make it available as a function you can call later
console.log('\n💡 Type iNatArea() to run this again');
8 Likes

Checked the script and it works like a charm! Thank you.

2 Likes

You can use the measure tool on Google maps or Google Earth and it will give you the area, but may not be as precise as you need, Will be more accurate if you put in the coordinates for the corners, but will take a little more time.

Worth noting the Earth isn’t a sphere–would the small 0.3% difference between equatorial and polar radius cause any issues for OP’s use case?

1 Like

Yes, but barely enough to matter over small areas at mid latitudes

rectangles that span more latitudes than longitudes (tall skinny rectangles if globe oriented north up) and rectangles at the extremes of latitudes will have more of a percent difference in area in the different calculating methods

try this to see the percent difference

function calculateINatAreaEllipsoidvsSphere() {
    const urlParams = new URLSearchParams(window.location.search);
    const nelat = parseFloat(urlParams.get('nelat'));
    const nelng = parseFloat(urlParams.get('nelng'));
    const swlat = parseFloat(urlParams.get('swlat'));
    const swlng = parseFloat(urlParams.get('swlng'));
    
    if (!nelat || !nelng || !swlat || !swlng) {
        console.log('No bounding box coordinates found');
        return null;
    }

    // WGS84 parameters
    const a = 6378137.0;        // equatorial radius
    const b = 6356752.314245;   // polar radius  
    const e2 = 1 - (b * b) / (a * a); // eccentricity squared
    const e = Math.sqrt(e2);    // eccentricity
    const R_mean = 6371008.8;   // mean radius
    
    const toRad = deg => deg * Math.PI / 180;
    
    // Convert coordinates to radians
    const φ1 = toRad(swlat);
    const φ2 = toRad(nelat);
    const λ1 = toRad(swlng);
    const λ2 = toRad(nelng);
    
    // Calculate spherical area
    function sphericalArea(φ1, φ2, λ1, λ2, R) {
        const Δλ = Math.abs(λ2 - λ1);
        const Δsinφ = Math.sin(φ2) - Math.sin(φ1);
        return R * R * Δλ * Math.abs(Δsinφ);
    }
    
    // Calculate ellipsoidal area using corrected authalic approximation
    function ellipsoidalAreaApprox(φ1, φ2, λ1, λ2) {
        // Correct authalic radius calculation
        const R_authalic = Math.sqrt((a * a + b * b * Math.atanh(e) / e) / 2);
        
        const mean_φ = (φ1 + φ2) / 2;
        const sinφ = Math.sin(mean_φ);
        const cosφ = Math.cos(mean_φ);
        
        // Correct scaling factor - accounts for ellipsoidal distortion
        const scale = Math.sqrt(1 - e2 * sinφ * sinφ) * 
                     Math.sqrt(1 + (e2 * cosφ * cosφ) / (1 - e2));
        
        const spherical_area = sphericalArea(φ1, φ2, λ1, λ2, R_authalic);
        return spherical_area * scale;
    }
    
    // Calculate ellipsoidal area using numerical integration
    function ellipsoidalAreaPrecise(φ1, φ2, λ1, λ2) {
        const Δλ = Math.abs(λ2 - λ1);
        
        function N(φ) {
            const sinφ = Math.sin(φ);
            return a / Math.sqrt(1 - e2 * sinφ * sinφ);
        }
        
        function M(φ) {
            const sinφ = Math.sin(φ);
            return a * (1 - e2) / Math.pow(1 - e2 * sinφ * sinφ, 1.5);
        }
        
        // Simpson's rule integration
        const steps = 1000;
        const Δφ = (φ2 - φ1) / steps;
        let area = 0;
        
        for (let i = 0; i <= steps; i++) {
            const φ = φ1 + i * Δφ;
            const weight = (i === 0 || i === steps) ? 1 : (i % 2 === 0) ? 2 : 4;
            const dA = N(φ) * Math.cos(φ) * M(φ) * Δλ;
            area += weight * dA;
        }
        
        return Math.abs(area * Δφ / 3);
    }
    
    // Calculate all three areas
    const areaM2_sphere = sphericalArea(φ1, φ2, λ1, λ2, R_mean);
    const areaM2_ellipsoid_approx = ellipsoidalAreaApprox(φ1, φ2, λ1, λ2);
    const areaM2_ellipsoid_precise = ellipsoidalAreaPrecise(φ1, φ2, λ1, λ2);
    
    // Convert to km²
    const areaKm2_sphere = areaM2_sphere / 1e6;
    const areaKm2_ellipsoid_approx = areaM2_ellipsoid_approx / 1e6;
    const areaKm2_ellipsoid_precise = areaM2_ellipsoid_precise / 1e6;
    
    // Calculate differences
    const diff_sphere_vs_precise = ((areaKm2_ellipsoid_precise - areaKm2_sphere) / areaKm2_ellipsoid_precise * 100);
    const diff_approx_vs_precise = ((areaKm2_ellipsoid_precise - areaKm2_ellipsoid_approx) / areaKm2_ellipsoid_precise * 100);
    
    console.log('AREA COMPARISON');
    console.log('==================');
    console.log('Coordinates: ' + swlat.toFixed(4) + ' to ' + nelat.toFixed(4) + 'N');
    console.log('            ' + swlng.toFixed(4) + ' to ' + nelng.toFixed(4) + 'W');
    console.log('Spherical: ' + areaKm2_sphere.toLocaleString('en-US', {maximumFractionDigits: 1}) + ' km²');
    console.log('Ellipsoid (approx): ' + areaKm2_ellipsoid_approx.toLocaleString('en-US', {maximumFractionDigits: 1}) + ' km²');
    console.log('Ellipsoid (precise): ' + areaKm2_ellipsoid_precise.toLocaleString('en-US', {maximumFractionDigits: 1}) + ' km²');
    console.log('Sphere vs Precise: ' + diff_sphere_vs_precise.toFixed(3) + '%');
    console.log('Approx vs Precise: ' + Math.abs(diff_approx_vs_precise).toFixed(3) + '%');
    console.log('==================');
    
    return {
        spherical_km2: areaKm2_sphere,
        ellipsoid_approx_km2: areaKm2_ellipsoid_approx,
        ellipsoid_precise_km2: areaKm2_ellipsoid_precise,
        difference_sphere_vs_precise: diff_sphere_vs_precise,
        difference_approx_vs_precise: diff_approx_vs_precise
    };
}

// Execute
calculateINatAreaEllipsoidvsSphere();
2 Likes