From 2c05412dfb4be731e27f46e008abd3bf6f78486a Mon Sep 17 00:00:00 2001 From: Floke Date: Wed, 4 Feb 2026 13:22:47 +0000 Subject: [PATCH] feat([2fd88f42]): display attributes in point tooltip --- heatmap-tool/backend/main.py | 56 +++++++++++++++++-- heatmap-tool/frontend/src/App.tsx | 1 + .../frontend/src/components/MapDisplay.tsx | 5 ++ 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/heatmap-tool/backend/main.py b/heatmap-tool/backend/main.py index 47d0c17a..f6fdbe18 100644 --- a/heatmap-tool/backend/main.py +++ b/heatmap-tool/backend/main.py @@ -116,8 +116,24 @@ async def get_heatmap_data(request: FilterRequest): if filtered_df.empty: return [] - # Aggregate data by PLZ - plz_counts = filtered_df.groupby(plz_column_name).size().reset_index(name='count') + # Aggregate data by PLZ, and also collect attribute summaries + plz_grouped = filtered_df.groupby(plz_column_name) + plz_counts = plz_grouped.size().reset_index(name='count') + + # Collect unique attributes for each PLZ + attribute_summaries = {} + for plz_val, group in plz_grouped: + summary = {} + for col in filtered_df.columns: + if col != plz_column_name and col != 'lat' and col != 'lon': # Exclude lat/lon if they somehow exist + unique_attrs = group[col].unique().tolist() + # Limit to top 3 unique values for readability + summary[col] = unique_attrs[:3] + attribute_summaries[plz_val] = summary + + # Convert summaries to a DataFrame for merging + summary_df = pd.DataFrame.from_dict(attribute_summaries, orient='index') + summary_df.index.name = plz_column_name # --- Geocoding Step --- # Merge the aggregated counts with the geocoding dataframe @@ -129,17 +145,45 @@ async def get_heatmap_data(request: FilterRequest): how='inner' ) + # Merge with attribute summaries + merged_df = pd.merge( + merged_df, + summary_df, + left_on=plz_column_name, + right_index=True, + how='left' + ) + # Rename columns to match frontend expectations ('lon' and 'lat') merged_df.rename(columns={'x': 'lon', 'y': 'lat'}, inplace=True) # Also rename the original PLZ column to the consistent name 'plz' merged_df.rename(columns={plz_column_name: 'plz'}, inplace=True) - # Convert to the required JSON format - heatmap_data = merged_df[['plz', 'lat', 'lon', 'count']].to_dict(orient='records') + # Convert to the required JSON format, including all remaining columns (which are the attributes) + # We'll dynamically collect attribute columns for output + output_columns = ['plz', 'lat', 'lon', 'count'] + for col in merged_df.columns: + if col not in output_columns and col != plz_column_name: # Ensure we don't duplicate PLZ or coords + output_columns.append(col) + + heatmap_data = merged_df[output_columns].to_dict(orient='records') - print(f"Generated heatmap data with {len(heatmap_data)} PLZ points.") - return heatmap_data + # The frontend expects 'attributes_summary' as a single field, so let's restructure for that + # For each record, pick out the attributes that are not 'plz', 'lat', 'lon', 'count' + final_heatmap_data = [] + for record in heatmap_data: + attrs = {k: v for k, v in record.items() if k not in ['plz', 'lat', 'lon', 'count']} + final_heatmap_data.append({ + "plz": record['plz'], + "lat": record['lat'], + "lon": record['lon'], + "count": record['count'], + "attributes_summary": attrs + }) + + print(f"Generated heatmap data with {len(final_heatmap_data)} PLZ points.") + return final_heatmap_data except Exception as e: print(f"ERROR generating heatmap: {e}") diff --git a/heatmap-tool/frontend/src/App.tsx b/heatmap-tool/frontend/src/App.tsx index a1ec5838..a21e4807 100644 --- a/heatmap-tool/frontend/src/App.tsx +++ b/heatmap-tool/frontend/src/App.tsx @@ -16,6 +16,7 @@ export interface HeatmapPoint { lat: number; lon: number; count: number; + attributes_summary?: Record; } export type MapMode = 'points' | 'heatmap'; diff --git a/heatmap-tool/frontend/src/components/MapDisplay.tsx b/heatmap-tool/frontend/src/components/MapDisplay.tsx index 71a7c7d1..42d535c3 100644 --- a/heatmap-tool/frontend/src/components/MapDisplay.tsx +++ b/heatmap-tool/frontend/src/components/MapDisplay.tsx @@ -43,6 +43,11 @@ const MapDisplay: React.FC = ({ heatmapData, radiusMultiplier, PLZ: {point.plz}
Count: {point.count} + {point.attributes_summary && Object.entries(point.attributes_summary).map(([attr, values]) => ( +
+ {attr}: {values.join(', ')} +
+ ))}
))