geeViz.fsInsights.align

Putting LCMS and FIA side by side, without pretending they agree.

The two answer complementary halves of one question over overlapping geography, and almost nobody joins them because the APIs look nothing alike. That is the opportunity. The hazard is that joining them invites a comparison that is easy to make and easy to get wrong.

Map-derived area is not a design-based area estimate. Comparing them conflates map accuracy with sampling error, and the two can differ substantially without either being wrong — different definitions of “forest”, different minimum mapping units, different reference dates.

So nothing here returns a blended number. Every row carries the source and estimator that produced it, and the comparison helpers report a difference alongside the caveat rather than instead of it. The goal is to make the comparison easy to look at and hard to misread.

Module Attributes

TREE_CLASSES

LCMS land-cover classes that carry tree cover.

COMPARISON_CAVEATS

Why LCMS and FIA can legitimately disagree.

FOREST_AREA_SNUM

FIA attribute 2 — "Area of forest land, in acres".

Functions

compare_area(*, wc[, state, county, year, ...])

Put an LCMS treed area and an FIA forest-land estimate side by side.

fia_forest_area(wc, *[, rselected, snum])

FIA forest-land area with its sampling error.

lcms_tree_area([state, county, region, ...])

LCMS area in tree-bearing land cover classes, by year.

summarize_comparison(result)

One readable paragraph from compare_area(), caveats included.

geeViz.fsInsights.align.TREE_CLASSES = ('Trees', 'Tall Shrubs & Trees Mix (AK Only)', 'Shrubs & Trees Mix', 'Grass/Forb/Herb & Trees Mix', 'Barren & Trees Mix')

LCMS land-cover classes that carry tree cover. Used to build a “treed area” figure that is comparable in spirit to FIA forest land — not equal to it. FIA’s definition is about land use and stocking potential, not present canopy, so a recently harvested stand stays forest land in FIA while LCMS may map it as grass or barren in the same year. That divergence is real signal, and it is exactly what a naive join would hide.

The (AK Only) suffix is part of the real class name, not a comment. Writing it without the suffix looks correct, matches nothing, and silently drops the class — invisible in CONUS where it never occurs, and an understatement of tree area everywhere in Alaska. That is why lcms_tree_area() validates these names against the API’s class list rather than trusting this tuple.

geeViz.fsInsights.align.COMPARISON_CAVEATS = ('LCMS area is map-derived; FIA area is a design-based estimate. The difference mixes map accuracy with sampling error and is not an error term for either.', "FIA 'forest land' is a land-use definition based on stocking and potential; LCMS land cover describes present canopy. A recently harvested stand stays forest land in FIA while LCMS may map it as grass or barren the same year.", 'Reference periods differ: an FIA evaluation spans several years of panels, while an LCMS year is a single annual map.', 'Minimum mapping unit and edge handling differ, which matters most in fragmented landscapes.')

Why LCMS and FIA can legitimately disagree. Module-level on purpose: these are static facts about the two datasets, not properties of any one comparison, so they must be readable even when the FIA half of a comparison could not be fetched. Gating them behind a successful call meant the caveats vanished exactly when a reader had one number and might quote it alone.

geeViz.fsInsights.align.FOREST_AREA_SNUM = 2

FIA attribute 2 — “Area of forest land, in acres”.

geeViz.fsInsights.align.lcms_tree_area(state: str = '', county: str = '', *, region: str = '', forest: str = '', district: str = '', year: int | None = None, tree_classes: tuple | None = None, release: str = '') → Any[source]

LCMS area in tree-bearing land cover classes, by year.

Parameters:

tree_classes – Override which classes count as treed. The default includes the mixed classes, which matters: excluding them understates treed area in exactly the transitional stands where LCMS and FIA are most likely to disagree.

Returns:

pandas.DataFrame with year, acres, classes_used, source, estimator.

geeViz.fsInsights.align.fia_forest_area(wc: int, *, rselected: str = '', snum: int = 2, **kwargs) → Any[source]

FIA forest-land area with its sampling error.

Thin wrapper over estimate() that stamps the estimator label, so a frame from here and a frame from lcms_tree_area() can be concatenated without losing track of which is which.

geeViz.fsInsights.align.compare_area(*, wc: int, state: str = '', county: str = '', year: int | None = None, tree_classes: tuple | None = None, release: str = '') → Dict[str, Any][source]

Put an LCMS treed area and an FIA forest-land estimate side by side.

Returns a dict rather than a single frame, because the two halves are not rows of one table — they are two different estimators of two related-but-distinct quantities, and stacking them would imply a comparability that does not exist.

Keys:

lcms: per-year treed area frame. fia: forest-land estimate with se_pct and plots. comparison: a small dict with both figures, their absolute

and percentage difference, and caveats — a list of the reasons they can legitimately disagree.

The difference is offered as an observation, never as an error term. A 15% gap between these does not mean either is 15% wrong.

geeViz.fsInsights.align.summarize_comparison(result: Dict[str, Any]) → str[source]

One readable paragraph from compare_area(), caveats included.

Written to be pasted into a report. The caveat is part of the sentence rather than a footnote, because a number this easy to quote is a number that travels without its footnotes.