geeViz.changeDetectionLib

Apply change detection methods using Google Earth Engine.

geeViz.changeDetectionLib is the core module for setting up change detection algorithms in GEE. It facilitates preparation, application, and output formatting/compression/decompression of results from:

Most helpers are lower-level building blocks used by the wrappers above; each is a thin function that operates on ee.Image / ee.ImageCollection inputs. When calling directly, consult the LandTrendr/VERDET/CCDC upstream docs for parameter semantics — this module mostly plumbs their outputs into GEE-friendly shapes for visualization + export.

Palettes exposed at module level (lossYearPalette, lossMagPalette, gainYearPalette, gainMagPalette, changeDurationPalette) match the color conventions used by addLossGainToMap().

Functions

LANDTRENDRFitMagSlopeDiffCollection(ts, ...)

LANDTRENDRVertStack(composites, indexName, ...)

LTExportPrep(rawLT[, multBy])

LTLossGainExportPrep(lossGainDict[, ...])

LT_VT_multBands(img, multBy)

LT_VT_vertStack_multBands(img, ...)

VERDETFitMagSlopeDiffCollection(composites, ...)

VERDETVertStack(ts, indexName[, run_params, ...])

addLossGainToMap(lossGainStack, startYear, ...)

addMillisecondsTimeBand(img)

addToImage(img, howMuch)

annualizeCCDC(ccdcImg, startYear, endYear, ...)

applyDistDir_vertStack(stack, distDir, ...)

applyLinearInterp(composites, nYearsInterpolate)

applyVerdetScaling(ts, indexName, ...)

arrayToTimeSeries(tsArray, yearsArray, ...)

batchFeatherCCDCImgs(ccdcAnnualizedCol1, ...)

Wrapper function to join annualized CCDC images from two different CCDC collections, and iterate across images and apply featherCCDCImgs function The feather years are the overlapping years between the two CCDC collections that are used in weighting

batchSimpleLTFit(ltStacks, startYear, endYear)

ccdcChangeDetection(ccdcImg, bandName[, ...])

Function for getting change years and magnitudes for a specified band from CCDC outputs Only change from the breaks is extracted.

convertStack_To_DurFitMagSlope(...)

convertToLossGain(ltStack[, format, ...])

extractDisturbance(lt, distDir, params, mmu)

featherCCDCImgs(joinedCCDCImg, ccdcBnds, ...)

Function to feather two CCDC collections together based on overlapping data time periods and weights The feather years are the overlapping years between the two CCDC collections that are used in weighting

fitStackToCollection(stack, maxSegments, ...)

getCCDCSegCoeffs(timeImg, ccdcImg, fillGaps)

getFitSlopeCCDC(annualSegCoeffs, startYear, ...)

getLTStack(LTresult, maxVertices, bandNames)

getLTvertStack(LTresult, run_params)

getLinearFit(c[, bandNames])

getRawAndFittedLT(rawTs, lt, startYear, endYear)

getSegmentParamsForYear(ccdc, yearImg)

getTimeImageCollection(startYear, endYear[, ...])

getTimeImageCollectionFromComposites(...[, ...])

landtrendrWrapper(processedComposites, ...)

One-call wrapper that runs LandTrendr end-to-end on a composite collection.

linearInterp(imgcol[, frame, nodata])

multBands(img, distDir[, by])

Multiply every band of img by distDir * by, preserving properties.

multLT(rawLT, multBy)

new_interp_date(dateYr, dateCollection[, ...])

new_interp_date_collection(dateCollection[, ...])

nullFinder(img, countMask)

predictCCDC(ccdcImg, timeImgs[, fillGaps, ...])

Takes one or two raw CCDC ee.Image array outputs, an ee.ImageCollection of time images, and returns a time-series ee.ImageCollection with harmonic coefficients and fitted values

predictModel(c, model[, bandNames])

prepTimeSeriesForLandTrendr(ts, indexName, ...)

prepTimeSeriesForVerdet(ts, indexName, ...)

rawLTToVertices(rawLT[, indexName, multBy, ...])

replace_mask(img, newimg[, nodata])

runLANDTRENDR(ts, bandName[, run_params])

Run LandTrendr on a single band with sensible defaults.

simpleCCDCPrediction(img, timeBandName, ...)

simpleCCDCPredictionAnnualized(img, ...)

simpleCCDCPredictionWrapper(c, timeBandName, ...)

simpleGetTimeImageCollection(startYear, endYear)

Provides a time series of year and decimal days ee.ImageCollection.

simpleLANDTRENDR(ts, startYear, endYear[, ...])

Takes annual time series input data, properly sets it up for LandTrendr, runs LandTrendr, and provides both a compressed vertex-only format output as well as a basic change detection output.

simpleLTFit(ltStack, startYear, endYear[, ...])

simpleRawLTToVertices(rawLT)

thresholdChange(changeCollection, changeThresh)

thresholdZAndTrend(zAndTrendCollection, ...)

toAnnualMedian(images, startYear, endYear)

undoVerdetScaling(fitted, indexName, ...)

updateVerdetMasks(img, linearInterpMasks)

verdetAnnualSlope(tsIndex, indexName, ...[, ...])

yearlySlope(rightYear, fitted)

zAndTrendChangeDetection(allScenes, ...[, ...])

geeViz.changeDetectionLib.multBands(img, distDir, by=1)[source]

Multiply every band of img by distDir * by, preserving properties.

Used to flip the sign of a spectral index when the underlying algorithm expects “up == improvement” but the index is oriented the opposite way (e.g. NBR = negative on loss). Preserves system:time_start and all other image properties.

Parameters:
  • img (ee.Image) – Input image.

  • distDir (int) – Direction multiplier — typically +1 or -1.

  • by (float) – Additional scalar. Default 1 (no extra scaling).

Returns:

Multiplied image with source properties preserved.

Return type:

ee.Image

geeViz.changeDetectionLib.addToImage(img, howMuch)[source]
geeViz.changeDetectionLib.nullFinder(img, countMask)[source]
geeViz.changeDetectionLib.arrayToTimeSeries(tsArray, yearsArray, possibleYears, bandName)[source]
geeViz.changeDetectionLib.extractDisturbance(lt, distDir, params, mmu)[source]
geeViz.changeDetectionLib.getLTvertStack(LTresult, run_params)[source]
geeViz.changeDetectionLib.getLTStack(LTresult, maxVertices, bandNames)[source]
geeViz.changeDetectionLib.rawLTToVertices(rawLT, indexName=None, multBy=10000, vertexNoData=-32768)[source]
geeViz.changeDetectionLib.landtrendrWrapper(processedComposites, startYear, endYear, indexName, distDir, run_params, distParams, mmu)[source]

One-call wrapper that runs LandTrendr end-to-end on a composite collection.

Reorients the target index (via distDir), runs ee.Algorithms.TemporalSegmentation.LandTrendr, extracts the greatest disturbance segment per pixel, filters by distParams, applies MMU cleaning, and converts LT’s array output back to an ImageCollection of fitted values.

Parameters:
  • processedComposites (ee.ImageCollection) – Annual composites (one per year) with indexName present as a band.

  • startYear (int) – First year in the analysis window.

  • endYear (int) – Last year in the analysis window (inclusive).

  • indexName (str) – Band name to segment on (e.g. "NBR", "NDVI").

  • distDir (int) – Orientation of the index — -1 if losses are NEGATIVE in the raw index (NDVI, NBR), +1 if losses are POSITIVE (e.g. brightness, TCA-inverse).

  • run_params (dict) – LandTrendr algorithm params. See default_lt_run_params for defaults / valid keys.

  • distParams (dict) – Disturbance-filter thresholds. Passed through to extractDisturbance.

  • mmu (int) – Minimum mapping unit (pixels). Segments smaller than this are filtered.

Returns:

[lt, distImg, fittedCollection, vertStack]
  • lt — raw LandTrendr array output (ee.Image).

  • distImg — greatest-disturbance image (yod/mag/dur/preval bands).

  • fittedCollection — per-year fitted values as an ImageCollection.

  • vertStack — vertex-stack image (year_1, year_2, …, fit_1, fit_2, …).

Return type:

list

geeViz.changeDetectionLib.getRawAndFittedLT(rawTs, lt, startYear, endYear, indexName='Band', distDir=-1)[source]
geeViz.changeDetectionLib.simpleRawLTToVertices(rawLT)[source]
geeViz.changeDetectionLib.multLT(rawLT, multBy)[source]
geeViz.changeDetectionLib.LTExportPrep(rawLT, multBy=10000)[source]
geeViz.changeDetectionLib.runLANDTRENDR(ts, bandName, run_params=None)[source]

Run LandTrendr on a single band with sensible defaults.

Thin, opinionated wrapper around ee.Algorithms.TemporalSegmentation.LandTrendr — auto-picks the sign flip for the given band (via changeDirDict, falls back to -1), runs LandTrendr, then packages the raw output into export-ready vertex-only form via LTExportPrep.

Parameters:
  • ts (ee.ImageCollection) – Annual time series (one image per year) with bandName present.

  • bandName (str) – Band to segment (e.g. "NBR", "NDVI", "tcWetness").

  • run_params (dict, optional) – Override for LandTrendr params. If None, uses default_lt_run_params.

Returns:

Vertex-stack image with metadata properties band and

run_params set for downstream traceability.

Return type:

ee.Image

geeViz.changeDetectionLib.LTLossGainExportPrep(lossGainDict, indexName='Bn', multBy=10000)[source]
geeViz.changeDetectionLib.addLossGainToMap(lossGainStack, startYear, endYear, lossMagMin=-8000, lossMagMax=-2000, gainMagMin=1000, gainMagMax=8000, indexName=None, howManyToPull=None)[source]
geeViz.changeDetectionLib.simpleLANDTRENDR(ts, startYear, endYear, indexName='NBR', run_params=None, lossMagThresh=-0.15, lossSlopeThresh=-0.1, gainMagThresh=0.1, gainSlopeThresh=0.1, slowLossDurationThresh=3, chooseWhichLoss='largest', chooseWhichGain='largest', addToMap=True, howManyToPull=2, multBy=10000)[source]

Takes annual time series input data, properly sets it up for LandTrendr, runs LandTrendr, and provides both a compressed vertex-only format output as well as a basic change detection output.

geeViz.changeDetectionLib.prepTimeSeriesForLandTrendr(ts, indexName, run_params)[source]
geeViz.changeDetectionLib.LANDTRENDRVertStack(composites, indexName, run_params, startYear, endYear)[source]
geeViz.changeDetectionLib.LANDTRENDRFitMagSlopeDiffCollection(ts, indexName, run_params)[source]
geeViz.changeDetectionLib.LT_VT_multBands(img, multBy)[source]
geeViz.changeDetectionLib.applyDistDir_vertStack(stack, distDir, verdet_or_landtrendr)[source]
geeViz.changeDetectionLib.LT_VT_vertStack_multBands(img, verdet_or_landtrendr, multBy)[source]
geeViz.changeDetectionLib.simpleLTFit(ltStack, startYear, endYear, indexName='bn', arrayMode=True, maxSegs=6, multBy=1)[source]
geeViz.changeDetectionLib.batchSimpleLTFit(ltStacks, startYear, endYear, indexNames=None, bandPropertyName='band', arrayMode=True, maxSegs=6, multBy=1, mosaicReducer=<ee.reducer.Reducer object>)[source]
geeViz.changeDetectionLib.fitStackToCollection(stack, maxSegments, startYear, endYear)[source]
geeViz.changeDetectionLib.convertStack_To_DurFitMagSlope(stackCollection, VTorLT)[source]
geeViz.changeDetectionLib.convertToLossGain(ltStack, format='rawLandTrendr', lossMagThresh=-0.15, lossSlopeThresh=-0.1, gainMagThresh=0.1, gainSlopeThresh=0.1, slowLossDurationThresh=3, chooseWhichLoss='largest', chooseWhichGain='largest', howManyToPull=2)[source]
geeViz.changeDetectionLib.replace_mask(img, newimg, nodata=0)[source]
geeViz.changeDetectionLib.addMillisecondsTimeBand(img)[source]
geeViz.changeDetectionLib.linearInterp(imgcol, frame=32, nodata=0)[source]
geeViz.changeDetectionLib.new_interp_date(dateYr, dateCollection, max_window=10, dummyImage=None, extrapolate=True)[source]
geeViz.changeDetectionLib.new_interp_date_collection(dateCollection, max_window=20, dummyImage=None, extrapolate=True)[source]
geeViz.changeDetectionLib.applyLinearInterp(composites, nYearsInterpolate)[source]
geeViz.changeDetectionLib.applyVerdetScaling(ts, indexName, correctionFactor)[source]
geeViz.changeDetectionLib.undoVerdetScaling(fitted, indexName, correctionFactor)[source]
geeViz.changeDetectionLib.updateVerdetMasks(img, linearInterpMasks)[source]
geeViz.changeDetectionLib.prepTimeSeriesForVerdet(ts, indexName, run_params, correctionFactor)[source]
geeViz.changeDetectionLib.VERDETVertStack(ts, indexName, run_params={'alpha': 0.1, 'tolerance': 0.0001}, maxSegments=10, correctionFactor=1, doLinearInterp='false')[source]
geeViz.changeDetectionLib.VERDETFitMagSlopeDiffCollection(composites, indexName, run_params={'alpha': 0.1, 'tolerance': 0.0001}, maxSegments=10, correctionFactor=1, doLinearInterp='false', masks=None)[source]
geeViz.changeDetectionLib.verdetAnnualSlope(tsIndex, indexName, startYear, endYear, alpha, tolerance=0.0001)[source]
geeViz.changeDetectionLib.getSegmentParamsForYear(ccdc, yearImg)[source]
geeViz.changeDetectionLib.thresholdChange(changeCollection, changeThresh, changeDir=None)[source]
geeViz.changeDetectionLib.toAnnualMedian(images, startYear, endYear)[source]
geeViz.changeDetectionLib.predictModel(c, model, bandNames=None)[source]
geeViz.changeDetectionLib.getLinearFit(c, bandNames=None)[source]
geeViz.changeDetectionLib.zAndTrendChangeDetection(allScenes, indexNames, nDays, startYear, endYear, startJulian, endJulian, baselineLength=5, baselineGap=1, epochLength=5, zReducer=<ee.reducer.Reducer object>, useAnnualMedianForTrend=True, exportImages=False, exportPathRoot='users/iwhousman/test/ChangeCollection', studyArea=None, scale=30, crs=None, transform=None, minBaselineObservationsNeeded=10)[source]
geeViz.changeDetectionLib.thresholdZAndTrend(zAndTrendCollection, zThresh, slopeThresh, startYear, endYear, negativeOrPositiveChange=None)[source]
geeViz.changeDetectionLib.simpleCCDCPrediction(img, timeBandName, whichHarmonics, whichBands)[source]
geeViz.changeDetectionLib.simpleCCDCPredictionWrapper(c, timeBandName, whichHarmonics)[source]
geeViz.changeDetectionLib.getCCDCSegCoeffs(timeImg, ccdcImg, fillGaps)[source]
geeViz.changeDetectionLib.annualizeCCDC(ccdcImg, startYear, endYear, startJulian, endJulian, tEndExtrapolationPeriod, yearStartMonth=9, yearStartDay=1, annualizeWithCompositeDates=False, compositeCollection=None, interpolateCompositeDates=True)[source]
geeViz.changeDetectionLib.getFitSlopeCCDC(annualSegCoeffs, startYear, endYear)[source]
geeViz.changeDetectionLib.yearlySlope(rightYear, fitted)[source]
geeViz.changeDetectionLib.simpleCCDCPredictionAnnualized(img, timeBandName, whichBands)[source]
geeViz.changeDetectionLib.predictCCDC(ccdcImg: list[Image, Image] | Image, timeImgs: ImageCollection, fillGaps: bool = True, whichHarmonics: list[int] = [1, 2, 3], featherStartYr: int = 2015, featherEndYr: int = 2021) ImageCollection[source]

Takes one or two raw CCDC ee.Image array outputs, an ee.ImageCollection of time images, and returns a time-series ee.ImageCollection with harmonic coefficients and fitted values

Parameters:
  • ccdcImg (list[ee.Image, ee.Image] | ee.Image) – A raw CCDC ee.Image array or list of two raw CCDC ee.Image arrays. If a list of 2 images is provided, feathering will automatically be performed. Note that any pixel that is null in either CCDC image will result in a null value in the predicted output.

  • timeImgs (ee.ImageCollection) – An ee.ImageCollection of time images usually from functions such as simpleGetTimeImageCollection.

  • fillGaps (bool, optional) – Whether to fill gaps between segments. If false, outputs can have blank values mid time-series. Defaults to True.

  • whichHarmonics (list[int], optional) – Which harmonics to include in fitted outputs forreturned time-series. Defaults to [1,2,3].

  • featherStartYear (int, optional) – If a list of 2 images is provided as ccdcImg, this is the first year of the window used for feathering the two time-series together. Defaults to 2015.

  • featherEndYear (int, optional) – If a list of 2 images is provided as ccdcImg, this is the last year (inclusive) of the window used for feathering the two time-series together. Defaults to 2021.

Returns:

A collection of CCDC coefficients and fitted values.

Return type:

ee.ImageCollection

>>> import geeViz.changeDetectionLib as cdl
>>> Map = cdl.Map
>>> ee = cdl.ee
>>> ccdcBandNames = ["tStart", "tEnd", "tBreak", "changeProb", "swir1.*", "NDVI.*"]
>>> timeImgs = cdl.simpleGetTimeImageCollection(startYear=1984, endYear=2024, startJulian=1, endJulian=365, step=0.1)
>>> ccdcImg1 = ee.ImageCollection("projects/lcms-292214/assets/CONUS-LCMS/Base-Learners/CCDC-Collection-1984-2022").select(ccdcBandNames).mosaic()
>>> ccdcImg2 = ee.ImageCollection("projects/lcms-292214/assets/CONUS-LCMS/Base-Learners/CCDC-Feathered-Collection").select(ccdcBandNames).mosaic()
>>> fittedFeathered = cdl.predictCCDC(ccdcImg=[ccdcImg1, ccdcImg2], timeImgs=timeImgs, fillGaps=True, whichHarmonics=[1, 2, 3], featherStartYr=2015, featherEndYr=2021)
>>> Map.addLayer(fittedFeathered.select([".*_CCDC_fitted"]), {"reducer": ee.Reducer.mean(), "min": 0.3, "max": 0.8}, "Combined CCDC", True)
>>> Map.turnOnInspector()
>>> Map.setCenter(-88, 36, 12)
>>> Map.view()
geeViz.changeDetectionLib.getTimeImageCollection(startYear, endYear, startJulian=1, endJulian=365, step=0.1, yearStartMonth=1, yearStartDay=1)[source]
geeViz.changeDetectionLib.simpleGetTimeImageCollection(startYear: int, endYear: int, startJulian: int = 1, endJulian: int = 365, step: float = 0.1)[source]

Provides a time series of year and decimal days ee.ImageCollection. This is useful for CCDC predictions

Parameters:
  • startYear (int) – The starting year for returned time-series.

  • endYear (int) – The ending year for the returned time-series.

  • startJulian (int) – The starting Julian day of year for returned time-series (1-365).

  • endJulian (int) – The ending Julian day of year for returned time-series (1-365).

  • step (float, optional) – Fraction of a year for each output in returned time-series (~0.01-1). Defaults to 0.1.

Returns:

A collection of time images.

Return type:

ee.ImageCollection

>>> import geeViz.changeDetectionLib as cdl
>>> Map = cdl.Map
>>> ee = cdl.ee
>>> timeImgs = cdl.simpleGetTimeImageCollection(startYear = 1984, endYear = 2024, startJulian = 1, endJulian = 365, step = 0.1)
>>> Map.addLayer(timeImgs, {}, "Time Images", True)
>>> Map.turnOnInspector()
>>> Map.view()
geeViz.changeDetectionLib.getTimeImageCollectionFromComposites(compositeCollection, startYear=None, endYear=None, interpolate=True, useNewInterpMethod=False)[source]
geeViz.changeDetectionLib.ccdcChangeDetection(ccdcImg: list[Image, Image] | Image, bandName: str, startYear: None | int = None, endYear: None | int = None) dict[source]

Function for getting change years and magnitudes for a specified band from CCDC outputs Only change from the breaks is extracted. As of now, if a segment has a high slope value, this method will not extract that. If combining two CCDC raw outputs provide them as a list of two images for the ccdcImg parameter.

Parameters:
  • ccdcImg (list[ee.Image, ee.Image] | ee.Image) – A raw CCDC ee.Image array or list of two raw CCDC ee.Image arrays. If a list of 2 images is provided, they will automatically be combined.

  • bandName (str) – The band name to use for magnitude of change.

  • startYear (None | int) – The start of the time window. If left as None, all years in the input CCDC images will be included. Defaults to None.

  • endYear (None | int) – The end of the time window (inclusive). If left as None, all years in the input CCDC images will be included. Defaults to None.

Returns:

A dictionary of various CCDC change metrics.

Return type:

dict

>>> import geeViz.changeDetectionLib as cdl
>>> Map = cdl.Map
>>> ee = cdl.ee
>>> changeDetectionBandName = "NDVI"
>>> ccdcChangeBandNames = ["tBreak", "changeProb", f"{changeDetectionBandName}.*"]
>>> sortingMethod = "mostRecent"
>>> ccdcImg1 = ee.ImageCollection("projects/lcms-292214/assets/CONUS-LCMS/Base-Learners/CCDC-Collection-1984-2022").select(ccdcChangeBandNames).mosaic()
>>> ccdcImg2 = ee.ImageCollection("projects/lcms-292214/assets/CONUS-LCMS/Base-Learners/CCDC-Feathered-Collection").select(ccdcChangeBandNames).mosaic()
>>> changeObjCombined = cdl.ccdcChangeDetection([ccdcImg1, ccdcImg2], changeDetectionBandName)
>>> Map.addLayer(changeObjCombined[sortingMethod]["loss"]["year"], {"min": 1984, "max": 2024, "palette": cdl.lossYearPalette}, "Loss Year")
>>> Map.addLayer(changeObjCombined[sortingMethod]["loss"]["mag"], {"min": -0.5, "max": -0.1, "palette": cdl.lossMagPalette}, "Loss Mag", False)
>>> Map.addLayer(changeObjCombined[sortingMethod]["gain"]["year"], {"min": 1984, "max": 2024, "palette": cdl.gainYearPalette}, "Gain Year")
>>> Map.addLayer(changeObjCombined[sortingMethod]["gain"]["mag"], {"min": 0.05, "max": 0.2, "palette": cdl.gainMagPalette}, "Gain Mag", False)
>>> Map.turnOnInspector()
>>> Map.setCenter(-88, 36, 12)
>>> Map.view()
geeViz.changeDetectionLib.featherCCDCImgs(joinedCCDCImg: Image, ccdcBnds: list | List, coeffs1_bns: list | List, coeffs2_bns: list | List, featherStartYr: int, featherEndYr: int) Image[source]

Function to feather two CCDC collections together based on overlapping data time periods and weights The feather years are the overlapping years between the two CCDC collections that are used in weighting

geeViz.changeDetectionLib.batchFeatherCCDCImgs(ccdcAnnualizedCol1: ImageCollection, ccdcAnnualizedCol2: ImageCollection, featherStartYr: int, featherEndYr: int) ImageCollection[source]

Wrapper function to join annualized CCDC images from two different CCDC collections, and iterate across images and apply featherCCDCImgs function The feather years are the overlapping years between the two CCDC collections that are used in weighting