diff --git a/documentation/changelog.rst b/documentation/changelog.rst index c2c485c92..d5ee7420b 100644 --- a/documentation/changelog.rst +++ b/documentation/changelog.rst @@ -10,6 +10,7 @@ New features ------------- * Allow deleting multiple sensors with a single call to ``flexmeasures delete sensor`` by passing the ``--id`` option multiple times [see `PR #734 `_] +* Make it a lot easier to read off the color legend on the asset page, especially when showing many sensors, as they will now be ordered from top to bottom in the same order as they appear in the chart (as defined in the ``sensors_to_show`` attribute), rather than alphabetically [see `PR #742 `_] Bugfixes ----------- diff --git a/flexmeasures/data/models/charts/belief_charts.py b/flexmeasures/data/models/charts/belief_charts.py index 09782470e..bef9a6a4d 100644 --- a/flexmeasures/data/models/charts/belief_charts.py +++ b/flexmeasures/data/models/charts/belief_charts.py @@ -90,9 +90,10 @@ def chart_for_multiple_sensors( **override_chart_specs: dict, ): # Determine the shared data resolution + all_shown_sensors = flatten_unique(sensors_to_show) condition = list( sensor.event_resolution - for sensor in flatten_unique(sensors_to_show) + for sensor in all_shown_sensors if sensor.event_resolution > timedelta(0) ) minimum_non_zero_resolution = min(condition) if any(condition) else timedelta(0) @@ -112,6 +113,12 @@ def chart_for_multiple_sensors( ] } + # Set up field definition for sensor descriptions + sensor_field_definition = FIELD_DEFINITIONS["sensor_description"].copy() + sensor_field_definition["scale"] = dict( + domain=[sensor.to_dict()["description"] for sensor in all_shown_sensors] + ) + sensors_specs = [] for s in sensors_to_show: # List the sensors that go into one row @@ -164,7 +171,10 @@ def chart_for_multiple_sensors( # Draw a line for each sensor (and each source) layers = [ create_line_layer( - row_sensors, event_start_field_definition, event_value_field_definition + row_sensors, + event_start_field_definition, + event_value_field_definition, + sensor_field_definition, ) ] @@ -186,6 +196,7 @@ def chart_for_multiple_sensors( row_sensors, event_start_field_definition, event_value_field_definition, + sensor_field_definition, shared_tooltip, ) ) @@ -269,6 +280,7 @@ def create_line_layer( sensors: list["Sensor"], # noqa F821 event_start_field_definition: dict, event_value_field_definition: dict, + sensor_field_definition: dict, ): event_resolutions = list(set([sensor.event_resolution for sensor in sensors])) assert ( @@ -286,7 +298,7 @@ def create_line_layer( "encoding": { "x": event_start_field_definition, "y": event_value_field_definition, - "color": FIELD_DEFINITIONS["sensor_description"], + "color": sensor_field_definition, "strokeDash": { "scale": { # Distinguish forecasters and schedulers by line stroke @@ -309,6 +321,7 @@ def create_circle_layer( sensors: list["Sensor"], # noqa F821 event_start_field_definition: dict, event_value_field_definition: dict, + sensor_field_definition: dict, shared_tooltip: list, ): params = [ @@ -348,7 +361,7 @@ def create_circle_layer( "encoding": { "x": event_start_field_definition, "y": event_value_field_definition, - "color": FIELD_DEFINITIONS["sensor_description"], + "color": sensor_field_definition, "size": { "condition": {"value": "200", "test": {"or": or_conditions}}, "value": "0", diff --git a/flexmeasures/utils/coding_utils.py b/flexmeasures/utils/coding_utils.py index 40839ba60..3994a7b72 100644 --- a/flexmeasures/utils/coding_utils.py +++ b/flexmeasures/utils/coding_utils.py @@ -127,9 +127,11 @@ def sort_dict(unsorted_dict: dict) -> dict: def flatten_unique(nested_list_of_objects: list) -> list: """Returns unique objects in a possibly nested (one level) list of objects. + Preserves the original order in which unique objects first occurred. + For example: - >>> flatten_unique([1, [2, 3, 4], 3, 5]) - <<< [1, 2, 3, 4, 5] + >>> flatten_unique([1, [2, 20, 6], 10, [6, 2]]) + <<< [1, 2, 20, 6, 10] """ all_objects = [] for s in nested_list_of_objects: @@ -137,7 +139,7 @@ def flatten_unique(nested_list_of_objects: list) -> list: all_objects.extend(s) else: all_objects.append(s) - return list(set(all_objects)) + return list(dict.fromkeys(all_objects).keys()) def timeit(func):