CSV to Curve in Blender via Python
This is a simple exercise in data visualisation aided by a vibe-coded script copied below. The weather data can be downloaded from here: https://www.hko.gov.hk/en/cis/dailyElement.htm?ele=TEMP&y=2024 import bpy import csv import math from datetime import date # --- SETTINGS TO CHANGE --- # 1. Full path to your NEW grid-style CSV file. csv_file_path = "/Users/peter/Dropbox/PHD_HK/21_Semester_A_2025/HKBU_Teaching/Object_Technology/Project_Files/Data/HK_Weather/Hong_Kong_Weather - 2024.csv" # 2. The year the data is from (important for leap years!) YEAR_OF_DATA = 2024 # e.g., 2024 for a leap year, 2023 for a normal year # 3. The expected temperature range in your data. TEMP_MIN_EXPECTED = 0 TEMP_MAX_EXPECTED = 40 # 4. The final size of your object in Blender units. RADIUS_MIN = 1.0 # Radius for the lowest possible temperature RADIUS_MAX = 5.0 # Radius for the highest possible temperature # 5. Name for the new object. object_name = "Avg_Temp_Rose" # --- SCRIPT LOGIC (No need to edit below here) --- def map_range(value, from_min, from_max, to_min, to_max): """ Helper function to map a value from one range to another. """ return to_min + (to_max - to_min) * ((value - from_min) / (from_max - from_min)) # Store daily data in a dictionary to ensure correct order daily_data = {} # Read the CSV file try: with open(csv_file_path, 'r') as file: reader = list(csv.reader(file)) # Read all data into a grid # We start from column 1 (February) to 12 (December) for month_idx, month_col in enumerate(reader[0][1:], 1): for day_idx, row in enumerate(reader[1:], 1): # Try to create a valid date to handle leap years & month lengths try: current_date = date(YEAR_OF_DATA, month_idx, day_idx) temp_str = row[month_idx] if temp_str: # Check if the cell is not empty day_of_year = current_date.timetuple().tm_yday avg_temp = float(temp_str) daily_data[day_of_year] = avg_temp except (ValueError, IndexError): # This will catch invalid dates (like Feb 30) or empty rows continue except FileNotFoundError: print(f"ERROR: File not found at '{csv_file_path}'. Please check the path.") raise if not daily_data: print("ERROR: No data was loaded. Check your CSV format and file path.") raise # --- GEOMETRY CREATION --- verts = [] # Sort the data by day of the year to draw the line correctly sorted_days = sorted(daily_data.keys()) total_days_in_year = 366 if YEAR_OF_DATA % 4 == 0 and (YEAR_OF_DATA % 100 != 0 or YEAR_OF_DATA % 400 == 0) else 365 for day_of_year in sorted_days: avg_temp = daily_data[day_of_year] # Convert day of the year to an angle in radians angle = (day_of_year / total_days_in_year) * 2 * math.pi # Map the average temperature to a radius radius = map_range(avg_temp, TEMP_MIN_EXPECTED, TEMP_MAX_EXPECTED, RADIUS_MIN, RADIUS_MAX) # Convert polar coordinates (angle, radius) to cartesian (x, y) x = radius * math.cos(angle) y = radius * math.sin(angle) verts.append((x, y, 0)) # Create edges to connect the vertices in a loop edges = [] for i in range(len(verts)): # Connect current vertex to the next one, wrapping around at the end edge = (i, (i + 1) % len(verts)) edges.append(edge) # Create the mesh and object in Blender mesh = bpy.data.meshes.new(name=object_name + "_Mesh") mesh.from_pydata(verts, edges, []) # Create mesh from verts and edges mesh.update() obj = bpy.data.objects.new(object_name, mesh) bpy.context.collection.objects.link(obj) print(f"Successfully created object: '{object_name}'")
Download
1 formatsVideo Formats
Right-click 'Download' and select 'Save Link As' if the file opens in a new tab.