From d300b3d0bc6895aed1ec253dcb7a64fa61da6be6 Mon Sep 17 00:00:00 2001 From: SwayamBorateIIT <23110066@iitgn.ac.in> Date: Thu, 8 May 2025 23:09:16 +0530 Subject: [PATCH] Fix issue #76: [C4GT Community]: Add Dynamic Color Scaling to GeoPlot Visualizer --- geoplot.py | 62 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/geoplot.py b/geoplot.py index 6f264d1..ed54471 100644 --- a/geoplot.py +++ b/geoplot.py @@ -82,19 +82,28 @@ return result } - function getColor(value, min, max) { - const factor = (value - min) / (max - min) - return interpolateColor( - Cesium.Color.BLUE, - Cesium.Color.RED, - factor - ) + function getColor(value) { + // Use the injected min/max for dynamic color scaling. + // If min == max, factor will be NaN; handle this gracefully. + const min = parseFloat('$colorScaleMin'); + const max = parseFloat('$colorScaleMax'); + const factor = (value - min) / (max - min); + return interpolateColor( + Cesium.Color.BLUE, + Cesium.Color.RED, + Math.max(0, Math.min(1, factor)) + ); } - function getPixelSize(value, min, max) { - const factor = (value - min) / (max - min) - return 100 * (1 + factor) - } + function getPixelSize(value) { + // Use the injected min/max for dynamic size scaling. + // If min == max, factor will be NaN; handle this gracefully. + const min = parseFloat('$colorScaleMin'); + const max = parseFloat('$colorScaleMax'); + const factor = (value - min) / (max - min); + return 100 * (1 + Math.max(0, Math.min(1, factor))); + } + function processTimeSeriesData(geoJsonData) { const timeSeriesMap = new Map() @@ -160,8 +169,8 @@ time, getColor( value, - timeSeriesData.minValue, - timeSeriesData.maxValue + getColor(value) + ) ) @@ -170,8 +179,7 @@ time, getPixelSize( value, - timeSeriesData.minValue, - timeSeriesData.maxValue + getPixelSize(value) ) ) } @@ -233,6 +241,12 @@ def __init__(self, config, options): options["feature"], options["visualization_type"], ) + # Optional: User can manually specify color scale bounds. + # If not provided, these will be computed dynamically from the data. + self.color_scale_min = options.get("color_scale_min", None) + self.color_scale_max = options.get("color_scale_max", None) + + def render(self, state_trajectory): coords, values = [], [] @@ -246,7 +260,14 @@ def render(self, state_trajectory): values.append( np.array(read_var(final_state, self.entity_property)).flatten().tolist() ) - + # Flatten all property values for min/max calculation. + all_values = [v for value_list in values for v in value_list] + # Determine color scale bounds: + # Use user-specified min/max if provided, otherwise compute from data. + min_value = self.color_scale_min if self.color_scale_min is not None else min(all_values) + + max_value = self.color_scale_max if self.color_scale_max is not None else max(all_values) + # Generate timestamps for each step in the simulation. start_time = pd.Timestamp.utcnow() timestamps = [ start_time + pd.Timedelta(seconds=i * self.step_time) @@ -255,7 +276,7 @@ def render(self, state_trajectory): * self.config["simulation_metadata"]["num_steps_per_episode"] ) ] - + # Generate GeoJSON data for each coordinate and its corresponding values. geojsons = [] for i, coord in enumerate(coords): features = [] @@ -274,10 +295,11 @@ def render(self, state_trajectory): } ) geojsons.append({"type": "FeatureCollection", "features": features}) - + # Save GeoJSON data to file. with open(geodata_path, "w", encoding="utf-8") as f: json.dump(geojsons, f, ensure_ascii=False, indent=2) - + # Generate HTML file for visualization. + # Use the template to create the HTML file. tmpl = Template(geoplot_template) with open(geoplot_path, "w", encoding="utf-8") as f: f.write( @@ -288,6 +310,8 @@ def render(self, state_trajectory): "stopTime": timestamps[-1].isoformat(), "data": json.dumps(geojsons), "visualType": self.visualization_type, + "colorScaleMin": min_value, + "colorScaleMax": max_value, } ) )