我对处理中的代码有疑问。我正在尝试显示过去几年的天气数据并在屏幕上绘制椭圆以创建图表。但是,椭圆并未被绘制。这可能是什么原因?坐标有效且在屏幕区域内。当我尝试在循环外部绘制具有简单坐标的椭圆时,它可以正常工作。这就是为什么我相信它与循环有关。我在这里分享了我的代码。由于 API 密钥无效,它可能无法执行,但也许有人仍然可以帮助我。
import http.requests.*;
import java.util.Calendar;
void setup()
{
fullScreen();
background(0);
noStroke();
Calendar calendar = Calendar.getInstance();
int d;
int m;
int y;
int cols = 8;
int rows = 11;
float[][] temps = new float[cols][rows];
for(int i=0;i<8;i++)
{
calendar.add(Calendar.DAY_OF_YEAR, -1);
m= calendar.get(Calendar.MONTH) + 1;
d= calendar.get(Calendar.DAY_OF_MONTH);
for (int p=0;p<11;p++)
{
calendar.add(Calendar.YEAR, -1);
y = calendar.get(Calendar.YEAR);
String todate = y+"-"+m+"-"+d;
GetRequest get = new GetRequest("https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/Innsbruck/"+todate+"?unitGroup=metric&include=days&key=xxx&contentType=json");
get.send();
//println("Reponse Content: " + get.getContent());
JSONObject response = parseJSONObject(get.getContent());
float tempmax = response.getJSONArray("days").getJSONObject(0).getFloat("tempmax");
//println(d+"."+m+"."+y+": Maxtemp - "+tempmax);
temps[i][p]=tempmax;
float pointx = width-width/10-width/10*i;
float pointy = height/2+tempmax*10;
println(pointx + ", " + pointy);
fill(255);
ellipse(pointx,pointy,5,5);
}
}
}
void draw()
{
}
有几件事同时发生,将程序分解为更简单的步骤并一次解决一个问题会更容易。
例如:
xxx
作为 API 密钥所在位置的占位符。在线共享代码时这完全没问题。您需要将该密钥保密:只需确保在本地运行代码时将 xxx 替换为实际的 Visualcrossing API 密钥即可。i
p
(从 0 到 11)代表月份,所以你会得到一周、一个月内每天的最高温度一次?对 m
存在混淆,因为您总是将其设置为 m= calendar.get(Calendar.MONTH) + 1;
,因此该值永远不会改变:它将是当前月份 + 1(循环 8 次)。也许您的意思是m= calendar.get(Calendar.MONTH) + i;
? (类似地,p
计数器不用于增加日期)。
当我运行您的代码时,我在处理控制台中收到此错误:
WARNING: Authentication error: Unable to respond to any of these challenges: {}
java.lang.RuntimeException: A JSONObject text must begin with '{'
at processing.data.JSONObject.<init>(JSONObject.java:255)
at processing.data.JSONObject.<init>(JSONObject.java:239)
at processing.core.PApplet.parseJSONObject(PApplet.java:5377)
at sketch_231003b.setup(sketch_231003b.java:59)
at processing.core.PApplet.handleDraw(PApplet.java:2051)
at processing.awt.PSurfaceAWT$9.callDraw(PSurfaceAWT.java:1386)
at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:356)
NullPointerException
这仅意味着
xxx
API 不正确,在您的代码中您需要使用实际的 API 密钥。 (请不要在网上公开分享)。
处理此问题的一种典型方法是将 API 密钥设置为环境变量(例如MY_API_KEY=#######
,然后在处理中使用
System.getenv('MY_API_KEY')
来检索值)。如果您不熟悉设置环境变量,请不要担心:现在对密钥进行硬编码就可以了。请小心与谁共享该代码:)缓存数据您已经在此处进行查询并解析响应:
GetRequest get = new GetRequest("https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/Innsbruck/"+todate+"?unitGroup=metric&include=days&key=xxx&contentType=json");
get.send();
JSONObject response = parseJSONObject(get.getContent());
您可以使用
JSONArray
(
https://processing.org/reference/JSONArray.html) 来存储数据。 这是一个基于您的代码的示例:
import http.requests.*;
import java.util.Calendar;
void setup()
{
background(0);
String API_KEY = "PLACE_YOUR_WEATHER_API_KEY_HERE";
// prepare JSONArray to cache Weather API data
JSONArray weatherAPIData = new JSONArray();
// counter for each API call / data entry
int weatherAPICounter = 0;
Calendar calendar = Calendar.getInstance();
int d;
int m;
int y;
int cols = 8;
int rows = 11;
int total = rows * cols;
for(int i = 0; i < cols; i++)
{
calendar.add(Calendar.DAY_OF_YEAR, -1);
m = calendar.get(Calendar.MONTH) + 1;
d = calendar.get(Calendar.DAY_OF_MONTH);
for (int p = 0; p < rows; p++)
{
calendar.add(Calendar.YEAR, -1);
y = calendar.get(Calendar.YEAR);
String todate = y+"-"+m+"-"+d;
println("requesting", todate, weatherAPICounter+1, "of", total);
GetRequest get = new GetRequest("https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/Innsbruck/" + todate + "?unitGroup=metric&include=days&key=" + API_KEY + "&contentType=json");
get.send();
println("Reponse Content: " + get.getContent());
JSONObject response = parseJSONObject(get.getContent());
// cache the API data into RAM
weatherAPIData.setJSONObject(weatherAPICounter, response);
// increment data counter
weatherAPICounter++;
// wait 1s in between calls (to avoid API rate limits)
delay(1000);
}
}
// persist the API data cache to disk
saveJSONArray(weatherAPIData, "data/weather-api-data.json");
println("cached saved to the data folder");
exit();
}
如上所述,日期映射过程尚不清楚。目前它请求 API 似乎不支持的数据年份:
Reponse Content: Bad API Request:Invalid year requested. Years must be between 1950 and 2050
java.lang.RuntimeException: A JSONObject text must begin with '{'
at processing.data.JSONObject.<init>(JSONObject.java:255)
at processing.data.JSONObject.<init>(JSONObject.java:239)
at processing.core.PApplet.parseJSONObject(PApplet.java:5377)
at JSONAPICache.setup(JSONAPICache.java:61)
at processing.core.PApplet.handleDraw(PApplet.java:2051)
at processing.awt.PSurfaceAWT$9.callDraw(PSurfaceAWT.java:1386)
at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:356)
获得此数据后,您可以编写一个单独的草图来加载它并根据映射的
maxtemp
数据渲染椭圆。请记住将weather-api-data.json文件从上面的草图中拖放到新草图中:
void setup()
{
fullScreen();
background(0);
noStroke();
int cols = 8;
int rows = 11;
// load cached data
JSONArray weatherAPIData = loadJSONArray("data/weather-api-data.json");
// counter for each API call / data entry
int weatherAPICounter = 0;
for(int i = 0; i < cols; i++)
{
for (int p=0; p < rows; p++)
{
// try to get each request (and handle errors)
try {
JSONObject response = weatherAPIData.getJSONObject(weatherAPICounter);
float tempmax = response.getJSONArray("days").getJSONObject(0).getFloat("tempmax");
float pointx = width-width/10-width/10*i;
float pointy = height/2+tempmax*10;
println(pointx + ", " + pointy);
fill(255);
ellipse(pointx,pointy,5,5);
} catch(Exception e){
println("encountered null data index", weatherAPICounter);
e.printStackTrace();
}
// increment JSONArray counter
weatherAPICounter++;
}
}
}
(您可能还会发现
map()
(
https://processing.org/reference/map_.html) 函数可以方便地重新映射
maxtemp
数据)。祝调试顺利,并享受可视化数据的乐趣。