Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GTC-2734 Add JRC/SBTN, confidence levels, and total_ha to integrated alerts #230

Merged
merged 3 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/main/scala/org/globalforestwatch/layers/Layer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,12 @@ trait DateConfLayer extends ILayer {

// Encoding for IntegratedAlerts. Confidence value of 2 means "highest", 1 means
// "high", and 0 means "nominal".
object DateConfLevelsLayer {
val ConfLo = 0
val ConfMed = 1
val ConfHi = 2
}

trait DateConfLevelsLayer extends ILayer {
type B = Option[(LocalDate, Int)]

Expand All @@ -448,7 +454,9 @@ trait DateConfLevelsLayer extends ILayer {
val baseDate = LocalDate.of(2014,12,31)

override def lookup(value: Int): Option[(LocalDate, Int)] = {
val confidence = if (value >= 40000) 2 else if (value >= 30000) 1 else 0
val confidence = if (value >= 40000) DateConfLevelsLayer.ConfHi
else if (value >= 30000) DateConfLevelsLayer.ConfMed
else DateConfLevelsLayer.ConfLo
val days: Int = if (value >= 40000) value - 40000
else if (value >= 30000) value - 30000
else value - 20000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ object GfwProDashboardAnalysis extends SummaryAnalysis {
// extract year from acq_date column is YYYY-MM-DD
val acqDate = point.getUserData.asInstanceOf[String].split("\t")(2)
val alertDate = LocalDate.parse(acqDate)
z.merge(GfwProDashboardDataDateCount.fillDaily(Some(alertDate), 1))
z.merge(GfwProDashboardDataDateCount.fillDaily(Some(alertDate), true, 1))
}
(fid, data)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,25 @@ case class GfwProDashboardData(

/** Location intersects Integrated Alert tiles, integrated alerts are possible */
glad_alerts_coverage: Boolean,
/** Total hectares in location geometry */
total_ha: ForestChangeDiagnosticDataDouble,
/** How many hectares of location geometry had tree cover extent > 30% in 2000 */
tree_cover_extent_total: ForestChangeDiagnosticDataDouble,
/** Integrated alert count within location geometry grouped by day */
/** Integrated alert count within location geometry grouped by day, over the whole
* area and including all confidences */
glad_alerts_daily: GfwProDashboardDataDateCount,
/** Integrated alert count within location geometry grouped by day, and further
* subdivided by cross-product of cover area (all, SBTN area only, JRC area only)
* x alert confidence (lo, med, hi) */
glad_alerts_daily_all_lo: GfwProDashboardDataDateCount,
danscales marked this conversation as resolved.
Show resolved Hide resolved
glad_alerts_daily_all_med: GfwProDashboardDataDateCount,
glad_alerts_daily_all_hi: GfwProDashboardDataDateCount,
glad_alerts_daily_sbtn_lo: GfwProDashboardDataDateCount,
glad_alerts_daily_sbtn_med: GfwProDashboardDataDateCount,
glad_alerts_daily_sbtn_hi: GfwProDashboardDataDateCount,
glad_alerts_daily_jrc_lo: GfwProDashboardDataDateCount,
glad_alerts_daily_jrc_med: GfwProDashboardDataDateCount,
glad_alerts_daily_jrc_hi: GfwProDashboardDataDateCount,
/** Integrated alert count within location geometry grouped by ISO year-week */
glad_alerts_weekly: GfwProDashboardDataDateCount,
/** Integrated alert count within location geometry grouped by year-month */
Expand All @@ -30,8 +45,18 @@ case class GfwProDashboardData(
def merge(other: GfwProDashboardData): GfwProDashboardData = {
GfwProDashboardData(
glad_alerts_coverage || other.glad_alerts_coverage,
total_ha.merge(other.total_ha),
tree_cover_extent_total.merge(other.tree_cover_extent_total),
glad_alerts_daily.merge(other.glad_alerts_daily),
glad_alerts_daily_all_lo.merge(other.glad_alerts_daily_all_lo),
glad_alerts_daily_all_med.merge(other.glad_alerts_daily_all_med),
glad_alerts_daily_all_hi.merge(other.glad_alerts_daily_all_hi),
glad_alerts_daily_sbtn_lo.merge(other.glad_alerts_daily_sbtn_lo),
glad_alerts_daily_sbtn_med.merge(other.glad_alerts_daily_sbtn_med),
glad_alerts_daily_sbtn_hi.merge(other.glad_alerts_daily_sbtn_hi),
glad_alerts_daily_jrc_lo.merge(other.glad_alerts_daily_jrc_lo),
glad_alerts_daily_jrc_med.merge(other.glad_alerts_daily_jrc_med),
glad_alerts_daily_jrc_hi.merge(other.glad_alerts_daily_jrc_hi),
glad_alerts_weekly.merge(other.glad_alerts_weekly),
glad_alerts_monthly.merge(other.glad_alerts_monthly),
viirs_alerts_daily.merge(other.viirs_alerts_daily)
Expand All @@ -44,10 +69,20 @@ object GfwProDashboardData {
def empty: GfwProDashboardData =
GfwProDashboardData(
glad_alerts_coverage = false,
total_ha = ForestChangeDiagnosticDataDouble.empty,
tree_cover_extent_total = ForestChangeDiagnosticDataDouble.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty,
GfwProDashboardDataDateCount.empty
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,19 @@ object GfwProDashboardDataDateCount {

def empty: GfwProDashboardDataDateCount = GfwProDashboardDataDateCount(SortedMap())

def fillDaily(alertDate: Option[LocalDate], alertCount: Int): GfwProDashboardDataDateCount =
fill(alertDate, alertCount, _.format(DateTimeFormatter.ISO_DATE))

/** Record alerts as a GfwProDashboardDataDateCount for the specified day if
* alertDate is not None and include is true, else return
* GfwProDashboardDataDateCount.empty. */
def fillDaily(alertDate: Option[LocalDate], include: Boolean, alertCount: Int): GfwProDashboardDataDateCount =
fill(if (include) alertDate else None, alertCount, _.format(DateTimeFormatter.ISO_DATE))

/** Record alerts as a GfwProDashboardDataDateCount for the appropriate week if
* alertDate is not None, else return GfwProDashboardDataDateCount.empty. */
def fillWeekly(alertDate: Option[LocalDate], alertCount: Int): GfwProDashboardDataDateCount =
fill(alertDate, alertCount, _.format(WeekOfYear))

/** Record alerts as a GfwProDashboardDataDateCount for the appropriate month if
* alertDate is not None, else return GfwProDashboardDataDateCount.empty. */
def fillMonthly(alertDate: Option[LocalDate], alertCount: Int): GfwProDashboardDataDateCount =
fill(alertDate, alertCount, _.format(MonthOfYear))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import org.globalforestwatch.layers._
case class GfwProDashboardGridSources(gridTile: GridTile, kwargs: Map[String, Any]) extends GridSources {
val integratedAlerts = IntegratedAlerts(gridTile, kwargs)
val treeCoverDensity2000 = TreeCoverDensityPercent2000(gridTile, kwargs)
val sbtnNaturalForest: SBTNNaturalForests = SBTNNaturalForests(gridTile, kwargs)
val jrcForestCover: JRCForestCover = JRCForestCover(gridTile, kwargs)

def readWindow(
windowKey: SpatialKey,
Expand All @@ -26,8 +28,14 @@ case class GfwProDashboardGridSources(gridTile: GridTile, kwargs: Map[String, An
tcd2000Tile <- Either
.catchNonFatal(treeCoverDensity2000.fetchWindow(windowKey, windowLayout))
.right
sbtnNaturalForestTile <- Either
.catchNonFatal(sbtnNaturalForest.fetchWindow(windowKey, windowLayout))
.right
jrcForestCoverTile <- Either
.catchNonFatal(jrcForestCover.fetchWindow(windowKey, windowLayout))
.right
} yield {
val tile = GfwProDashboardTile(integratedAlertsTile, tcd2000Tile)
val tile = GfwProDashboardTile(integratedAlertsTile, tcd2000Tile, sbtnNaturalForestTile, jrcForestCoverTile)
Raster(tile, windowKey.extent(windowLayout))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,54 @@
package org.globalforestwatch.summarystats.gfwpro_dashboard

import org.globalforestwatch.summarystats.forest_change_diagnostic.ForestChangeDiagnosticDataDouble
import org.globalforestwatch.layers.DateConfLevelsLayer
import java.time.LocalDate


case class GfwProDashboardRawDataGroup(
alertDate: Option[LocalDate],
integratedAlertsCoverage: Boolean
alertDateAndConf: Option[(LocalDate, Int)],
integratedAlertsCoverage: Boolean,
isNaturalForest: Boolean,
jrcForestCover: Boolean,
isTreeCoverExtent30: Boolean
) {
def toGfwProDashboardData(alertCount: Int, totalArea: Double): GfwProDashboardData = {

val (alertDate, alertConf) = alertDateAndConf match {
case Some((date, conf)) => (Some(date), conf)
case _ => (None, DateConfLevelsLayer.ConfLo)
}

GfwProDashboardData(
glad_alerts_coverage = integratedAlertsCoverage,
glad_alerts_daily = GfwProDashboardDataDateCount.fillDaily(alertDate, alertCount),
glad_alerts_daily = GfwProDashboardDataDateCount.fillDaily(alertDate, true, alertCount),
glad_alerts_daily_all_lo = GfwProDashboardDataDateCount.fillDaily(alertDate,
alertConf == DateConfLevelsLayer.ConfLo, alertCount),
glad_alerts_daily_all_med = GfwProDashboardDataDateCount.fillDaily(alertDate,
alertConf == DateConfLevelsLayer.ConfMed, alertCount),
glad_alerts_daily_all_hi = GfwProDashboardDataDateCount.fillDaily(alertDate,
alertConf == DateConfLevelsLayer.ConfHi, alertCount),
glad_alerts_daily_sbtn_lo = GfwProDashboardDataDateCount.fillDaily(alertDate,
isNaturalForest && alertConf == DateConfLevelsLayer.ConfLo, alertCount),
glad_alerts_daily_sbtn_med = GfwProDashboardDataDateCount.fillDaily(alertDate,
isNaturalForest && alertConf == DateConfLevelsLayer.ConfMed, alertCount),
glad_alerts_daily_sbtn_hi = GfwProDashboardDataDateCount.fillDaily(alertDate,
isNaturalForest && alertConf == DateConfLevelsLayer.ConfHi, alertCount),
glad_alerts_daily_jrc_lo = GfwProDashboardDataDateCount.fillDaily(alertDate,
jrcForestCover && alertConf == DateConfLevelsLayer.ConfLo, alertCount),
glad_alerts_daily_jrc_med = GfwProDashboardDataDateCount.fillDaily(alertDate,
jrcForestCover && alertConf == DateConfLevelsLayer.ConfMed, alertCount),
glad_alerts_daily_jrc_hi = GfwProDashboardDataDateCount.fillDaily(alertDate,
jrcForestCover && alertConf == DateConfLevelsLayer.ConfHi, alertCount),
glad_alerts_weekly = GfwProDashboardDataDateCount.fillWeekly(alertDate, alertCount),
glad_alerts_monthly = GfwProDashboardDataDateCount.fillMonthly(alertDate, alertCount),
viirs_alerts_daily = GfwProDashboardDataDateCount.empty,
tree_cover_extent_total = ForestChangeDiagnosticDataDouble.fill(totalArea))
tree_cover_extent_total = if (isTreeCoverExtent30) {
ForestChangeDiagnosticDataDouble.fill(totalArea)
} else {
ForestChangeDiagnosticDataDouble.empty
},
total_ha = ForestChangeDiagnosticDataDouble.fill(totalArea)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,24 @@ object GfwProDashboardSummary {

def visit(raster: Raster[GfwProDashboardTile], col: Int, row: Int): Unit = {
val tcd2000: Integer = raster.tile.tcd2000.getData(col, row)
val integratedAlertDate: Option[LocalDate] = raster.tile.integratedAlerts.getData(col, row).map { case (date, _) => date }
val integratedAlertDateAndConf: Option[(LocalDate, Int)] = raster.tile.integratedAlerts.getData(col, row)
val integratedAlertCoverage = raster.tile.integratedAlerts.t.isDefined
val isTreeCoverExtent30: Boolean = tcd2000 > 30
val naturalForestCategory: String = raster.tile.sbtnNaturalForest.getData(col, row)
val jrcForestCover: Boolean = raster.tile.jrcForestCover.getData(col, row)

val groupKey = GfwProDashboardRawDataGroup(integratedAlertDate, integratedAlertsCoverage = integratedAlertCoverage)
val groupKey = GfwProDashboardRawDataGroup(integratedAlertDateAndConf,
integratedAlertCoverage,
naturalForestCategory == "Natural Forest",
jrcForestCover,
isTreeCoverExtent30)
val summaryData = acc.stats.getOrElse(groupKey, GfwProDashboardRawData(treeCoverExtentArea = 0.0, alertCount = 0))

if (isTreeCoverExtent30) {
val re: RasterExtent = raster.rasterExtent
val areaHa = Geodesy.pixelArea(lat = re.gridRowToMap(row), re.cellSize) / 10000.0
summaryData.treeCoverExtentArea += areaHa
}
val re: RasterExtent = raster.rasterExtent
val areaHa = Geodesy.pixelArea(lat = re.gridRowToMap(row), re.cellSize) / 10000.0
summaryData.treeCoverExtentArea += areaHa

if (integratedAlertDate.isDefined) {
if (integratedAlertDateAndConf.isDefined) {
summaryData.alertCount += 1
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import org.globalforestwatch.layers._
*/
case class GfwProDashboardTile(
integratedAlerts: IntegratedAlerts#OptionalITile,
tcd2000: TreeCoverDensityPercent2000#ITile
tcd2000: TreeCoverDensityPercent2000#ITile,
sbtnNaturalForest: SBTNNaturalForests#OptionalITile,
jrcForestCover: JRCForestCover#OptionalITile
) extends CellGrid[Int] {

def cellType: CellType = integratedAlerts.cellType.getOrElse(IntCellType)
Expand Down
Loading
Loading