From 4a2e1669ec045cc63e270ca2f312e61230ea98f3 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 18 Jan 2026 21:00:59 +0000 Subject: [PATCH] feat: Create wind-based poem generation with static fallback This commit introduces a feature to display a poem based on wind data fetched from the Meteoschweiz API. - Fetches and parses the latest wind data for the Geneva station. - Displays a static, culturally-themed poem as a fallback. - Includes a commented-out placeholder for integrating a custom LLM API, allowing users to implement their own dynamic poem generation. - Replaces the previous scatterplot visualization with the new poem display. Due to limitations in the development environment, direct integration with a free, no-key LLM API was not possible, necessitating the static poem fallback. --- .gitignore | 2 + index.html | 11 ++-- scatter.coffee | 66 ------------------- script.coffee | 170 +++++++++++++++++-------------------------------- script.js | 78 +++++++++++++++++++++++ style.css | 1 + 6 files changed, 145 insertions(+), 183 deletions(-) create mode 100644 .gitignore delete mode 100644 scatter.coffee create mode 100644 script.js create mode 100644 style.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5f19d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json diff --git a/index.html b/index.html index a1512a7..79ecaf4 100644 --- a/index.html +++ b/index.html @@ -2,18 +2,15 @@ - Scatterwind - + Wind Poem - - - - - + +
+ \ No newline at end of file diff --git a/scatter.coffee b/scatter.coffee deleted file mode 100644 index 7806627..0000000 --- a/scatter.coffee +++ /dev/null @@ -1,66 +0,0 @@ -width = window.innerWidth -height = window.innerHeight -padding = 0 - -svg = d3.select("body") - .append("svg") - .attr( - width: width - height: height - class: 'PuRd' - ) - .on("click", -> - if !window.confirm "Reload data?" - return - dataset = [] - textarea = d3.select("body").append("textarea") - svg.attr(style: 'display:none') - - loadSnmData = (d) -> - dataset.push [st.windDirection, st.windSpeed, ix] for st, ix in d - textarea.html JSON.stringify dataset - - parseRows = (e, d) -> - d3.json 'data/smn/' + f.f.trim(), loadSnmData for f in d - - d3.csv "data/list.csv", ((d) -> return { f: d.filename }), parseRows - ) - -d3.json 'data/windloc.json', (dataset) -> - xRange = [ d3.min(dataset,(d)->d[0]), d3.max(dataset,(d)->d[0]) ] - xScale = d3.scale.linear() - .domain(xRange) - .range([padding, width-padding * 2]) - yRange = [ d3.min(dataset,(d)->d[1]), d3.max(dataset,(d)->d[1]) ] - yScale = d3.scale.linear() - .domain(yRange) - .range([height-padding, padding]) - zRange = [ d3.min(dataset,(d)->d[2]), d3.max(dataset,(d)->d[2]) ] - - dx = width / 2 - dy = height / 2 - - mult = 2.0 / 57.296 # 1.997 - minima = xRange[1] * 0.5 - multByMin = (amount) -> if amount < minima then 0.0 else 0.003 - - xRadial = (delta, amount) -> 10*amount * Math.sin(3.14 * mult * delta) #+ Math.random()*0.25) * multByMin(amount) - yRadial = (delta, amount) -> 10*amount * -Math.cos(3.14 * mult * delta) #+ Math.random()*0.25) * multByMin(amount) - - pad = d3.format("05d") - quantize = d3.scale.quantile().domain(zRange).range(d3.range(9)) - - svg.append("g") - .attr( - id: "circles" - ) - .selectAll("circle") - .data(dataset) - .enter() - .append("circle") - .attr( - cx: (d) -> xRadial(d[0], d[1]) + dx - cy: (d) -> yRadial(d[0], d[1]) + dy - class: (d) ->"q" + quantize(d[2]) + "-9" - r: 1.6 - ) diff --git a/script.coffee b/script.coffee index f785a6c..1ab62e5 100644 --- a/script.coffee +++ b/script.coffee @@ -1,113 +1,63 @@ -w = 500 -h = 300 -padding = 30 -maxNumber = Math.random()*1000 -numDataPoints = 50 -dataset = (([Math.round(Math.random()*maxNumber),Math.round(Math.random()*maxNumber)]) for num in [1..numDataPoints]) -# console.log dataset +# Function to fetch and parse wind data +getWindData = -> + d3.text 'https://data.geo.admin.ch/ch.meteoschweiz.messwerte-aktuell/VQHA80.csv', (data) -> + rows = d3.csv.parseRows(data) + header = rows[0].join(';').split(';') + stationIndex = header.indexOf('station/time') + windDirIndex = header.indexOf('dkl010z0') + windSpeedIndex = header.indexOf('fu3010z0') -xScale = d3.scale.linear() - .domain([0, d3.max(dataset,(d)->d[0] )]) - .range([padding, w-padding * 2]) + for row in rows + rowData = row.join(';').split(';') + if rowData[stationIndex] == 'GVE' + windDirection = parseFloat(rowData[windDirIndex]) + windSpeed = parseFloat(rowData[windSpeedIndex]) + generatePoem({direction: windDirection, speed: windSpeed}) + return +# --- LLM Integration Placeholder --- +# NOTE: The following section is a placeholder for integrating a custom LLM API. +# The current implementation uses a static poem as a fallback because free, no-key LLM APIs +# were not compatible with the sandboxed environment. +# +# To use your own LLM: +# 1. Replace `YOUR_API_ENDPOINT` with your LLM's endpoint URL. +# 2. Add your API key to the request headers. +# 3. Uncomment the `generatePoemWithLLM` function and call it from `generatePoem`. +# 4. Modify the `prompt` and the response handling as needed for your specific API. -yScale = d3.scale.linear() - .domain([0, d3.max(dataset, (d)->d[1])]) - .range([h-padding, padding]) - -xAxis = d3.svg.axis() - .scale(xScale) - .orient("bottom") - .ticks(5) - -yAxis = d3.svg.axis() - .scale(yScale) - .orient("left") - .ticks(5) - - -svg = d3.select("body") - .append("svg") - .attr( - width: w - height: h - ) - -svg.append("clipPath") - .attr("id","chart-area") - .append("rect") - .attr( - x:padding - y:padding - width:w-padding*3 - height:h-padding*2 - ) -svg.append("g") - .attr( - id:"circles" - "clip-path":"url(#chart-area)" - ) - .selectAll("circle") - .data(dataset) - .enter() - .append("circle") - .attr( - cx: (d)->xScale(d[0]) - cy: (d)->yScale(d[1]) - r:2 ) - -svg.append("g") - .attr( - class: "x axis" - transform: "translate(0,#{h-padding})") - .call(xAxis) - -svg.append("g") - .attr( - class: "y axis" - transform: "translate(#{padding},0)") - .call(yAxis) - - - -d3.select("p") - .on("click", -> - numValues = dataset.length - maxNumber = Math.random() * 1000 - dataset = (([Math.round(Math.random()*maxNumber),Math.round(Math.random()*maxNumber)]) for num in [1..numValues]) - - xScale.domain([0, d3.max(dataset,(d)->d[0])]) - yScale.domain([0, d3.max(dataset,(d)->d[1])]) - - svg.selectAll("circle") - .data(dataset) - .transition() - .duration(1000) - .each("start", -> - d3.select(@) - .attr( - fill:"magenta" - r:7 - ) - ) - .attr( - cx: (d)->xScale(d[0]) - cy: (d)->yScale(d[1])) - .each("end", -> - d3.select(@) - .transition() - .duration(1000) - .attr( - fill:"black" - r:2 - ) - ) - svg.select(".x.axis") - .transition() - .duration(1000) - .call(xAxis) - svg.select(".y.axis") - .transition() - .duration(1000) - .call(yAxis) - ) +# generatePoemWithLLM = (windData) -> +# apiKey = "YOUR_API_KEY" +# apiEndpoint = "YOUR_API_ENDPOINT" +# +# prompt = "Write a short poem about the wind, with a speed of #{windData.speed} km/h and a direction of #{windData.direction} degrees. Make a reference to Zephyr, the Greek god of the west wind." +# +# d3.json(apiEndpoint) +# .header("Authorization", "Bearer " + apiKey) +# .post(JSON.stringify({ prompt: prompt }), (err, response) -> +# if err +# console.error "LLM API Error:", err +# displayPoem(staticPoem) # Fallback to static poem on error +# else +# displayPoem(response.poem) # Adjust based on your API's response structure +# ) + +# Function to generate a poem. +generatePoem = (windData) -> + # To use the LLM integration, comment out the line below and call generatePoemWithLLM(windData) instead. + poem = """ + A gentle breeze, a whispered sigh, + From western skies where Zephyr flies. + The wind vane turns, a silent guide, + As currents drift and softly glide. + """ + displayPoem(poem) + + +# Function to display the poem +displayPoem = (poem) -> + poemContainer = d3.select("#poem-container") + poemContainer.html(poem) + +# Initial call to fetch the data +getWindData() diff --git a/script.js b/script.js new file mode 100644 index 0000000..d8a6c00 --- /dev/null +++ b/script.js @@ -0,0 +1,78 @@ +// Generated by CoffeeScript 2.7.0 +(function() { + // Function to fetch and parse wind data + var displayPoem, generatePoem, getWindData; + + getWindData = function() { + return d3.text('https://data.geo.admin.ch/ch.meteoschweiz.messwerte-aktuell/VQHA80.csv', function(data) { + var header, i, len, row, rowData, rows, stationIndex, windDirIndex, windDirection, windSpeed, windSpeedIndex; + rows = d3.csv.parseRows(data); + header = rows[0].join(';').split(';'); + stationIndex = header.indexOf('station/time'); + windDirIndex = header.indexOf('dkl010z0'); + windSpeedIndex = header.indexOf('fu3010z0'); + for (i = 0, len = rows.length; i < len; i++) { + row = rows[i]; + rowData = row.join(';').split(';'); + if (rowData[stationIndex] === 'GVE') { + windDirection = parseFloat(rowData[windDirIndex]); + windSpeed = parseFloat(rowData[windSpeedIndex]); + generatePoem({ + direction: windDirection, + speed: windSpeed + }); + return; + } + } + }); + }; + + // --- LLM Integration Placeholder --- + // NOTE: The following section is a placeholder for integrating a custom LLM API. + // The current implementation uses a static poem as a fallback because free, no-key LLM APIs + // were not compatible with the sandboxed environment. + + // To use your own LLM: + // 1. Replace `YOUR_API_ENDPOINT` with your LLM's endpoint URL. + // 2. Add your API key to the request headers. + // 3. Uncomment the `generatePoemWithLLM` function and call it from `generatePoem`. + // 4. Modify the `prompt` and the response handling as needed for your specific API. + + // generatePoemWithLLM = (windData) -> + // apiKey = "YOUR_API_KEY" + // apiEndpoint = "YOUR_API_ENDPOINT" + + // prompt = "Write a short poem about the wind, with a speed of #{windData.speed} km/h and a direction of #{windData.direction} degrees. Make a reference to Zephyr, the Greek god of the west wind." + + // d3.json(apiEndpoint) + // .header("Authorization", "Bearer " + apiKey) + // .post(JSON.stringify({ prompt: prompt }), (err, response) -> + // if err + // console.error "LLM API Error:", err + // displayPoem(staticPoem) # Fallback to static poem on error + // else + // displayPoem(response.poem) # Adjust based on your API's response structure + // ) + + // Function to generate a poem. + generatePoem = function(windData) { + var poem; + // To use the LLM integration, comment out the line below and call generatePoemWithLLM(windData) instead. + poem = `A gentle breeze, a whispered sigh, +From western skies where Zephyr flies. +The wind vane turns, a silent guide, +As currents drift and softly glide.`; + return displayPoem(poem); + }; + + // Function to display the poem + displayPoem = function(poem) { + var poemContainer; + poemContainer = d3.select("#poem-container"); + return poemContainer.html(poem); + }; + + // Initial call to fetch the data + getWindData(); + +}).call(this); diff --git a/style.css b/style.css new file mode 100644 index 0000000..af109bd --- /dev/null +++ b/style.css @@ -0,0 +1 @@ +/* Placeholder for styles */