-
Notifications
You must be signed in to change notification settings - Fork 75
Split emissions layer categories #3917
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
base: master
Are you sure you want to change the base?
Changes from 2 commits
f5692d5
ce3e934
4e42ee2
83933bd
77a0903
e8852de
f69569d
4b00f27
3dccbd9
25260e0
84e9577
6ee383a
203cc1c
905afd2
238eaff
22aa587
079ec7f
2fcd82b
382177c
8d3745c
25ecb08
63ae267
12206d7
ba964d1
00aee77
6769526
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,17 +9,14 @@ | |
from cea.plots.colors import color_to_hex | ||
|
||
|
||
class EmissionsMapLayer(MapLayer): | ||
class EmbodiedEmissionsMapLayer(MapLayer): | ||
category = LifeCycleAnalysisCategory | ||
name = "life-cycle-analysis" | ||
label = "GHG Emissions" | ||
description = "GHG Emissions" | ||
name = "embodied-emissions" | ||
label = "Embodied Emissions" | ||
description = "" | ||
Comment on lines
+13
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major ❓ Verification inconclusivePublic rename may break imports—add a back‑compat alias Renaming EmissionsMapLayer to EmbodiedEmissionsMapLayer is a breaking change. Provide a temporary alias to avoid downstream breakage. Run this to find existing references: Add after class definitions: # Backward compatibility alias (remove in a future major release)
EmissionsMapLayer = EmbodiedEmissionsMapLayer 🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -nP -C2 '\bEmissionsMapLayer\b' Length of output: 0 Add backward-compat alias for EmissionsMapLayer # Backward compatibility alias (remove in a future major release)
EmissionsMapLayer = EmbodiedEmissionsMapLayer 🤖 Prompt for AI Agents
|
||
|
||
def _get_emissions_types(self): | ||
return ["embodied", "operational"] | ||
|
||
def _get_data_columns(self, parameters: dict) -> Optional[list]: | ||
results_path = self._get_results_files(parameters) | ||
def _get_data_columns(self) -> Optional[list]: | ||
results_path = self.locator.get_lca_embodied() | ||
|
||
try: | ||
emissions_df = pd.read_csv(results_path) | ||
|
@@ -29,34 +26,121 @@ def _get_data_columns(self, parameters: dict) -> Optional[list]: | |
|
||
return sorted(list(columns - {"name", "GFA_m2"})) | ||
|
||
def _get_results_files(self, parameters: dict) -> str: | ||
emissions_type = parameters['emissions-type'] | ||
if emissions_type == "embodied": | ||
return self.locator.get_lca_embodied() | ||
elif emissions_type == "operational": | ||
return self.locator.get_lca_operation() | ||
else: | ||
raise ValueError(f"Invalid emissions type: {emissions_type}") | ||
|
||
@classmethod | ||
def expected_parameters(cls): | ||
return { | ||
'emissions-type': | ||
'data-column': | ||
ParameterDefinition( | ||
"Emissions Type", | ||
"Data Column", | ||
"string", | ||
description="Type of emissions", | ||
options_generator="_get_emissions_types", | ||
description="Data column to use", | ||
options_generator="_get_data_columns", | ||
selector="choice", | ||
), | ||
'radius': | ||
ParameterDefinition( | ||
"Radius", | ||
"number", | ||
default=5, | ||
description="Radius of hexagon bin in meters", | ||
selector="input", | ||
range=[0, 100], | ||
filter="radius", | ||
), | ||
'scale': | ||
ParameterDefinition( | ||
"Scale", | ||
"number", | ||
default=1, | ||
description="Scale of hexagon bin height", | ||
selector="input", | ||
range=[0.1, 10], | ||
filter="scale", | ||
), | ||
} | ||
|
||
@classmethod | ||
def file_requirements(cls): | ||
return [ | ||
FileRequirement( | ||
"Zone Buildings Geometry", | ||
file_locator="locator:get_zone_geometry", | ||
), | ||
FileRequirement( | ||
"Embodied Emissions Results", | ||
file_locator="locator:get_lca_embodied", | ||
), | ||
] | ||
|
||
Comment on lines
+85
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major 🧩 Analysis chainAlign file_requirements with actual per‑building files used. EmbodiedEmissionsMapLayer.file_requirements():
- FileRequirement(
- "Embodied Emissions Results",
- file_locator="locator:get_lca_embodied",
- ),
+ FileRequirement(
+ "Embodied Emissions Timeline Files",
+ file_locator="layer:_get_results_files",
+ ),
OperationalEmissionsMapLayer.file_requirements():
- FileRequirement(
- "Operational Emissions Results",
- file_locator="locator:get_lca_operation",
- ),
+ FileRequirement(
+ "Operational Emissions Hourly Files",
+ file_locator="layer:_get_results_files",
+ ), Add the missing embodied helper: def _get_results_files(self, _):
buildings = self.locator.get_zone_building_names()
return [self.locator.get_lca_timeline_building(b) for b in buildings] To confirm the old locators exist, run: Also applies to: 225-236 🏁 Script executed: #!/bin/bash
rg -nP 'def\s+get_lca_(embodied|operation)\b' cea/inputlocator.py Length of output: 74 🏁 Script executed: #!/bin/bash
rg -nP 'def\s+get_lca_timeline_building' cea/inputlocator.py
rg -nP 'def\s+get_zone_building_names' cea/inputlocator.py Length of output: 103 Use per-building timeline files via EmbodiedEmissionsMapLayer.file_requirements():
- FileRequirement("Embodied Emissions Results", file_locator="locator:get_lca_embodied")
+ FileRequirement("Embodied Emissions Timeline Files", file_locator="layer:_get_results_files")
OperationalEmissionsMapLayer.file_requirements():
- FileRequirement("Operational Emissions Results", file_locator="locator:get_lca_operation")
+ FileRequirement("Operational Emissions Hourly Files", file_locator="layer:_get_results_files") Then add this method to each map-layer class: def _get_results_files(self, _):
buildings = self.locator.get_zone_building_names()
return [self.locator.get_lca_timeline_building(b) for b in buildings] This ensures missing-file checks and caching cover every per-building output. 🤖 Prompt for AI Agents
|
||
@cache_output | ||
def generate_data(self, parameters): | ||
"""Generates the output for this layer""" | ||
|
||
# FIXME: Hardcoded to zone buildings for now | ||
buildings = self.locator.get_zone_building_names() | ||
|
||
data_column = parameters['data-column'] | ||
|
||
output = { | ||
"data": [], | ||
"properties": { | ||
"name": self.name, | ||
"label": "Embodied Emissions", | ||
"description": self.description, | ||
"colours": { | ||
"colour_array": [color_to_hex("grey_lighter"), color_to_hex("black")], | ||
"points": 12 | ||
} | ||
Comment on lines
+115
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainVerify colours payload shape: 'colour_array' vs 'color_array' ColourGradient.to_dict() uses 'color_array' (US). Here it's 'colour_array' (UK). Confirm which the frontend expects to avoid broken colour gradients. 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Occurrences of colour/color array keys:"
rg -nP -C1 '\b(colo[u]?r_array)\b'
echo "Occurrences of the 'colours' property structure:"
rg -nP -C2 '\bcolou?rs\b' Length of output: 28889 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "---- ColourGradient definition ----"
rg -nP 'class ColourGradient' -C10 cea/interfaces/dashboard/map_layers/base.py
echo "---- ColourGradient.to_dict ----"
rg -nP 'def to_dict' -C5 cea/interfaces/dashboard/map_layers/base.py
echo "---- MapLayer.colours usage in to_dict ----"
rg -nP 'colours' -C5 cea/interfaces/dashboard/map_layers/base.py Length of output: 1541 Replace ‘colour_array’ with ‘color_array’ in cea/interfaces/dashboard/map_layers/life_cycle_analysis/layers.py:90-93; ColourGradient.to_dict() emits “color_array”, so this must match to avoid broken gradients. 🤖 Prompt for AI Agents
|
||
} | ||
} | ||
|
||
df = gpd.read_file(self.locator.get_zone_geometry()).set_index("name").loc[buildings] | ||
building_centroids = df.geometry.centroid.to_crs(CRS.from_epsg(4326)) | ||
|
||
results_path = self.locator.get_lca_embodied() | ||
emissions_df = pd.read_csv(results_path, usecols=["name", data_column], index_col="name")[data_column].loc[ | ||
buildings] | ||
|
||
output['data'] = [{"position": [centroid.x, centroid.y], "value": emissions} for centroid, emissions in | ||
zip(building_centroids, emissions_df)] | ||
output['properties']['range'] = { | ||
'total': { | ||
'label': 'Total Range', | ||
'min': float(min(emissions_df)), | ||
'max': float(max(emissions_df)) | ||
}, | ||
} | ||
|
||
return output | ||
|
||
|
||
class OperationalEmissionsMapLayer(MapLayer): | ||
category = LifeCycleAnalysisCategory | ||
name = "operational-emissions" | ||
label = "Operational Emissions" | ||
description = "" | ||
|
||
def _get_data_columns(self) -> Optional[list]: | ||
results_path = self.locator.get_lca_operation() | ||
|
||
try: | ||
emissions_df = pd.read_csv(results_path) | ||
columns = set(emissions_df.columns) | ||
except (pd.errors.EmptyDataError, FileNotFoundError): | ||
return | ||
|
||
return sorted(list(columns - {"name", "GFA_m2"})) | ||
|
||
@classmethod | ||
def expected_parameters(cls): | ||
return { | ||
'data-column': | ||
ParameterDefinition( | ||
"Data Column", | ||
"string", | ||
description="Data column to use", | ||
options_generator="_get_data_columns", | ||
selector="choice", | ||
depends_on=["emissions-type"], | ||
), | ||
'radius': | ||
ParameterDefinition( | ||
|
@@ -88,9 +172,8 @@ def file_requirements(cls): | |
file_locator="locator:get_zone_geometry", | ||
), | ||
FileRequirement( | ||
"Demand Results", | ||
file_locator="layer:_get_results_files", | ||
depends_on=["emissions-type"], | ||
"Operational Emissions Results", | ||
file_locator="locator:get_lca_operation", | ||
), | ||
] | ||
|
||
|
@@ -101,14 +184,13 @@ def generate_data(self, parameters): | |
# FIXME: Hardcoded to zone buildings for now | ||
buildings = self.locator.get_zone_building_names() | ||
|
||
emissions_type = parameters['emissions-type'] | ||
data_column = parameters['data-column'] | ||
|
||
output = { | ||
"data": [], | ||
"properties": { | ||
"name": self.name, | ||
"label": f"GHG Emissions: {emissions_type}", | ||
"label": "Operational Emissions", | ||
"description": self.description, | ||
"colours": { | ||
"colour_array": [color_to_hex("grey_lighter"), color_to_hex("black")], | ||
|
@@ -119,14 +201,8 @@ def generate_data(self, parameters): | |
|
||
df = gpd.read_file(self.locator.get_zone_geometry()).set_index("name").loc[buildings] | ||
building_centroids = df.geometry.centroid.to_crs(CRS.from_epsg(4326)) | ||
|
||
if emissions_type == "embodied": | ||
results_path = self.locator.get_lca_embodied() | ||
elif emissions_type == "operational": | ||
results_path = self.locator.get_lca_operation() | ||
else: | ||
raise ValueError(f"Invalid emissions type: {emissions_type}") | ||
|
||
|
||
results_path = self.locator.get_lca_operation() | ||
emissions_df = pd.read_csv(results_path, usecols=["name", data_column], index_col="name")[data_column].loc[ | ||
buildings] | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against None parameters and log more context
Membership test on current_params can raise if None. Also, include locator name and provided params for easier debugging.
Apply:
📝 Committable suggestion
🤖 Prompt for AI Agents