summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorketsuban_152 <webmaster@dangofacotry.net>2026-08-09 11:52:20 +0900
committerketsuban_152 <webmaster@dangofacotry.net>2026-08-09 11:52:20 +0900
commit3d0a7e4adce1e050e309d90d1ee73ff784b704df (patch)
treefea2e81b8fd2921e7bbf9aadbbd40a7e364eef83
parent48f7b7c060f5a867fc8bac5a29202ffc6de9d4c4 (diff)
downloaddwgo-3d0a7e4adce1e050e309d90d1ee73ff784b704df.tar.gz
dwgo-3d0a7e4adce1e050e309d90d1ee73ff784b704df.tar.bz2
dwgo-3d0a7e4adce1e050e309d90d1ee73ff784b704df.zip
modified: dwgo.conf
modified: src/MySock.cpp modified: src/MySock.h modified: src/localtemp.cpp modified: src/localtemp.h
-rw-r--r--dwgo.conf12
-rw-r--r--src/MySock.cpp104
-rw-r--r--src/MySock.h5
-rw-r--r--src/localtemp.cpp340
-rw-r--r--src/localtemp.h41
5 files changed, 299 insertions, 203 deletions
diff --git a/dwgo.conf b/dwgo.conf
index af2f1cd..56613b5 100644
--- a/dwgo.conf
+++ b/dwgo.conf
@@ -1,9 +1,11 @@
#[Stations]
-AddStation LEMG:Malaga
-AddStation LEVD:Valladolid
-AddStation LEGR:Granada
-AddStation LEBA:Cprdoba
-AddStation LEMD:Madrid
+# Station id is the 5-digit JMA station code.
+# You can found this on the JMA Jmedas map.
+AddStation 44132:Tokyo
+AddStation 62078:Osaka
+AddStation 51106:Nagoya
+AddStation 14163:Sapporo
+AddStation 82182:Fukuoka
#[General configuration]
update_interval=100
diff --git a/src/MySock.cpp b/src/MySock.cpp
index 79e407d..6f73517 100644
--- a/src/MySock.cpp
+++ b/src/MySock.cpp
@@ -1,7 +1,7 @@
/*******************************************************************************
* File: MySock.cpp *
- * Version: 0.2 *
- * Date: 20081002 *
+ * Version: 0.4 *
+ * Date: 20260809 *
* Author: Gaspar Fernández (helyo@totaki.com) *
* *
* Copyright (C) 2008 Gaspar Fernández *
@@ -29,9 +29,12 @@
* Date Author Modification
* 10.02.2008 Gaspar Fernández Initial release
* 31.10.2010 Gaspar Fernández Bug Corrections
+ * 09.08.2026 Koudai Kudo fetch(1) for FreeBSD
********************************************************************************/
#include "MySock.h"
#include <unistd.h>
+#include <cstdio>
+#include <cctype>
/*************************************************************
* Function: extract_key_value *
*************************************************************
@@ -86,6 +89,7 @@ MySock::MySock()
{
this->error=NO_ERROR;
this->connected=false;
+ this->use_external_fetch=false;
}
MySock::MySock(string uri)
@@ -96,6 +100,7 @@ MySock::MySock(string uri)
string protostr;
string req;
this->error=NO_ERROR;
+ this->use_external_fetch=false;
string::size_type pos = uri.find("//", 0);
string::size_type pos2;
@@ -119,6 +124,15 @@ MySock::MySock(string uri)
if (this->connected)
this->SendData(req);
}
+ else if (protostr=="https")
+ {
+ if(this->safe_url(uri))
+ {
+ this->https_url=url;
+ this->use_external_fetch=true;
+ this->connection;
+ }
+ }
else
{
this->error=NO_VALID_PROTOCOL;
@@ -146,6 +160,85 @@ MySock::MySock(string uri)
MySock::MySock(string server, int port)
{
this->MakeConnection(server, port);
+ this->use_external_fetch=false;
+}
+
+/*************************************************************
+ * Method: is_safe_url
+**************************************************************
+ * Description: Since https:// URIs end up inside a popen() *
+ * command, nad only allow a conservation character set. *
+ *
+ * Input:
+ * string url
+ *************************************************************/
+bool MySock::is_safe_url(const string &url)
+{
+ for (string::size_type i=0; i<url.length(); i++)
+ {
+ char c=url[i];
+ if (c=='\'')
+ return false;
+ if (!(isalnum((unsigned char)c) || strchr(":/._-?=&%", c)!=NULL))
+ return false;
+ }
+ return (url.length()>0) && (url.length()<2048);
+}
+
+/*************************************************************
+ * Method: GetHTTPDataViaExternalFetch() *
+ *************************************************************
+ * Description: *
+ * Retrieves an https:// URL by shelling out to fetch(1) *
+ * (FreeBSD base system) via popen(), since this class has *
+ * no TLS implementation of its own. fetch -o - writes the *
+ * response body to stdout, so unlike GetHTTPData()'s raw *
+ * socket path there are no HTTP headers to parse here - we *
+ * only know success/failure from fetch(1)'s exit status. *
+ * *
+ * Input: *
+ * Nothing (uses this->https_uri) *
+ * *
+ * Output: *
+ * HTTP_Request* - status is 200 on success, 502 if *
+ * fetch(1) reported an error (bad URL, network failure, *
+ * HTTP error status, etc); data holds the response body. *
+ * *
+ * Change History: *
+ * Date Author Modification *
+ * *
+ *************************************************************/
+HTTP_Request *MySock::GetHTTPDataViaExternalFetch()
+{
+ HTTP_Request *http;
+ string cmd;
+ FILE *pipe;
+ char buffer[4096];
+ size_t n;
+ cmd=(string)HTTPS_FETCH_CMD+"'"+this->https_uri+"'"+" 2>/dev/null";
+ pipe=popen(cmd.data(), "r");
+ if (pipe==NULL)
+ {
+ this->error=CANT_RUN_FETCH;
+ return NULL;
+ }
+ http=new HTTP_Request;
+ http->data="";
+ while ((n=fread(buffer, 1, sizeof(buffer), pipe))>0)
+ http->data.append(buffer, n);
+ int rc=pclose(pipe);
+ if ((rc==0) && (!http->data.empty()))
+ {
+ http->status=200;
+ http->statusstr="HTTP/1.1 200 OK (via fetch)";
+ }
+ else
+ {
+ http->status=502;
+ http->statusstr="HTTP/1.1 502 Bad Gateway (fetch failed)";
+ }
+ this->connected=false; // "Connection" (the popen pipe) is closed now
+ return http;
}
/*************************************************************
@@ -293,6 +386,9 @@ HTTP_Request *MySock::GetHTTPData()
char buf[255], buf2[255];
TKey_Value hdata;
+ if (this->use_external_fetch)
+ return this->GetHTTPDataViaExternalFetch();
+
if (this->error==NO_ERROR)
{
txtData=this->GetTextData();
@@ -354,11 +450,11 @@ HTTP_Request *MySock::GetHTTPData()
void MySock::closeConnection()
{
- if (connected)
+ if (connected && !use_external_fetch)
{
close(sockd);
- connected=false;
}
+ connected=false;
}
MySock::~MySock()
diff --git a/src/MySock.h b/src/MySock.h
index 132978c..4001429 100644
--- a/src/MySock.h
+++ b/src/MySock.h
@@ -20,7 +20,9 @@ using namespace std;
#define CANT_READ_DATA 45
#define NO_VALID_PROTOCOL 50
#define NO_VALID_HEADERS 52
+#define CANT_RUN_FETCH 60
+#define HTTPS_FETCH_CMD "fetch -q -o - "
#define CRLF "\r\n"
struct HTTP_Request
{
@@ -55,8 +57,11 @@ public:
private:
int sockd, puerto;
struct hostent *server;
+ bool use_external_fetch;
+ string https_uri;
void MakeConnection (string server, int port);
+ HTTP_Request *GetHTTPDataViaExternalFetch();
};
TKey_Value extract_key_value(string str);
diff --git a/src/localtemp.cpp b/src/localtemp.cpp
index e8314bb..e6a5c2b 100644
--- a/src/localtemp.cpp
+++ b/src/localtemp.cpp
@@ -1,7 +1,7 @@
/*******************************************************************************
* File: localtemp.cpp *
- * Version: 0.3 *
- * Date: 20080508 *
+ * Version: 0.4 *
+ * Date: 20260809 *
* Author: Gaspar Fernández (helyo@totaki.com) *
* *
* Copyright (C) 2008 Gaspar Fernández *
@@ -31,6 +31,7 @@
* Date (D.M.Y) Author Modification
* 08.05.2008 Gaspar Fernández Initial release
* 31.10.2010 Gaspar Fernández Bug Corrections
+ * 09.08.2026 Koudai Kudo Ported to JMA
*
********************************************************************************/
@@ -48,7 +49,7 @@ using namespace std;
* *
* Input: *
* char *metar - ICAO name locator (4 chars) *
- * char location_name - Name of this location *
+ * char location_name - Amedas station code (5 digits) *
* *
* Change History: *
* Date Author Modification *
@@ -79,13 +80,10 @@ localtemp::localtemp(char *metar, char *location_name)
* Date Author Modification *
* *
*************************************************************/
-void localtemp::set_temp(string tmp_str)
+void localtemp::set_temp(double temp_c)
{
- char buf[8],buf3[8];
- // Format xx F (xx C)
- sscanf(tmp_str.data(), "%s %s (%s", buf, buf3, buf3); // We don't want to store the middle F
- this->celsius=atoi(buf3);
- this->fahrenheit=atoi(buf);
+ this->celsius=(int)(temp_c+0.5);
+ this->fahrenheit=(int)(temp_c*9.0/5.0+32.0+0.5);
}
/*************************************************************
@@ -95,143 +93,53 @@ void localtemp::set_temp(string tmp_str)
* Extract humidity information. *
* *
* Input: *
- * string hum - Temporary string containing humidity info. *
+ * int hum - Relative humidity, from the "humidity" field *
+ * of the Amedas JSON *
* *
* Change History: *
* Date Author Modification *
* *
*************************************************************/
-void localtemp::set_humidity(string hum)
+void localtemp::set_humidity(int hum)
{
- char buf[5]; // Be able to store a 100%\0
- // Format (xxx%)
- sscanf(hum.data(), "%s", buf);
- this->humidity=atoi(buf);
+ this->humidity=hum;
}
/*************************************************************
* Method: get_ob_info *
*************************************************************
* Description: *
- * Extracts METAR encoded information. We find it in *
- * the line starting with "ob: ". We parse this information *
- * and fill in variables. Used for time and sky conditions. *
+ * Works out which theme to use from the 10 minute *
+ * precipitation figure and current temperature, since *
+ * Amedas doesn't report general sky conditions the way *
+ * METAR does. *
* *
* Input: *
- * Nothing *
+* double precip10m - 10 minute precipitation, in mm, as *
+ * read from the "precipitation10m" field of the Amedas *
+ * JSON (0 or negative means no precipitation reported) *
* *
* Change History: *
* Date Author Modification *
* *
*************************************************************/
-void localtemp::get_ob_info() // ob: line stores METAR information. This info. is useful to know more exactly sky conditions.
-{ // It stores also temperature, time and other info extracted by the decoded text
-
- size_t pos;
- struct tm *localtm;
- struct tm *remotetm;
- remotetm =new struct tm;
- string timeinfo;
-try
- {
- timeinfo=this->ob.substr(6,this->ob.find(' ',6)-6); // DDHHMM[Z] Day Hour Minute Z (Zulu, Aviation Time Reference)
- } catch (exception &e)
- {
- cout<<"fallo en 1"<<ob<<"***"<<endl;
- }
- localtm=localtime(&get_time); // tm of the fetch time
-
- // We need month and year of the time given. So let's calculate.
-try
- {
-
- remotetm->tm_mday=atoi(timeinfo.substr(0,2).data());
- remotetm->tm_hour=atoi(timeinfo.substr(2,2).data());
- remotetm->tm_min=atoi(timeinfo.substr(4,2).data());
- } catch (exception &e)
+void localtemp::get_ob_info(double precip10m)
+{
+ mInfo.CB=false;
+ mInfo.fog=undef_fog; // Not reported by Amedas
+ if (precip10m>0.0)
{
- cout<<"fallo en 2"<<timeinfo<<"*"<<endl;
+ if (this->celsius<=2)
+ mInfo.rain=snow;
+ else
+ mInfo.rain=rain;
+ mInfo.sky=undef_sky;
}
- remotetm->tm_mon=localtm->tm_mon;
- remotetm->tm_year=localtm->tm_year;
- remotetm->tm_sec=0;
-
- if (remotetm->tm_mday==1) // Month of the data
- {
- if (remotetm->tm_mday!=localtm->tm_mday)
- remotetm->tm_mon++;
- if (remotetm->tm_mon==12) // It can't be 12!!
- {
- remotetm->tm_mon=0;
- remotetm->tm_year++;
- }
- }
- info_time=mktime(remotetm);
-
-try
- {
- this->ob=this->ob.substr(ob.find(' ',6)); // Extract ICAO station name and TIME INFO.
- // It will prevent some errors. We could find any of these strings in that name.
-
- //this->ob=this->ob.substr(this->ob.find(this->metar)+5);
- this->ob=this->ob.substr(0,this->ob.rfind('/')-2); // The info. we want is located betweet the last
-
- pos=this->ob.rfind('/'); // 2 slashes. The las one indicates temperature and dew point
- if (pos!=string::npos) // The other one, may not exists but sometimes it does.
- this->ob=this->ob.substr(this->ob.rfind('/'));
-
- } catch (exception &e)
+ else
{
- cout <<"Fallo en 3"<<ob<<"***"<<endl;
- }
- //Sky
- if (this->ob.find("TCU")!=string::npos)
- mInfo.sky=tcu;
- else if ((this->ob.find("BKN")!=string::npos) ||
- (this->ob.find("OVC")!=string::npos))
- mInfo.sky=brkovc;
- else if ((this->ob.find("FEW")!=string::npos) ||
- (this->ob.find("SCT")!=string::npos))
- mInfo.sky=cloudy;
- else if ((this->ob.find("CAVOK")!=string::npos) ||
- (this->ob.find("SKC")!=string::npos) ||
- (this->ob.find("CLR")!=string::npos))
- mInfo.sky=clear;
- else
- mInfo.sky=undef_sky;
-
- mInfo.CB=(this->ob.find("CB")!=string::npos);
-
-
- //Rain
- if ((this->ob.find("DZ")!=string::npos) ||
- (this->ob.find("RA")!=string::npos))
- mInfo.rain=rain;
- else if ((this->ob.find("SN")!=string::npos) ||
- (this->ob.find("SG")!=string::npos))
- mInfo.rain=snow;
- else if ((this->ob.find("GS")!=string::npos) ||
- (this->ob.find("GR")!=string::npos))
- mInfo.rain=hail;
- else
mInfo.rain=undef_rain;
-
- //Fog
- if ((this->ob.find("BR")!=string::npos) ||
- (this->ob.find("FG")!=string::npos))
- mInfo.fog=fog;
- else if ((this->ob.find("DU")!=string::npos) ||
- (this->ob.find("DS")!=string::npos))
- mInfo.fog=dust;
- else if ((this->ob.find("FC")!=string::npos) ||
- (this->ob.find("FU")!=string::npos) ||
- (this->ob.find("HZ")!=string::npos) ||
- (this->ob.find("SA")!=string::npos) ||
- (this->ob.find("SS")!=string::npos) ||
- (this->ob.find("VA")!=string::npos))
- mInfo.fog=other;
- else
- mInfo.fog=undef_fog;
+ mInfo.sky=clear;
+ }
// The most important for the display theme is rain, then fog and then sky conditions
if (mInfo.rain!=undef_rain)
@@ -240,17 +148,6 @@ try
{
case rain: theme=RAINY_THEME; break;
case snow: theme=SNOWY_THEME; break;
- case hail: theme=HAIL_THEME; break;
- default: break;
- }
- }
- else if (mInfo.fog!=undef_fog)
- {
- switch (mInfo.fog)
- {
- case fog : theme=FOG_THEME; break;
- case dust : theme=DUST_THEME; break;
- case other: theme=PARTS_THEME; break;
default: break;
}
}
@@ -259,9 +156,6 @@ try
switch (mInfo.sky)
{
case clear : theme=CLEAR_THEME; break;
- case cloudy : theme=CLOUDY_THEME; break;
- case brkovc : theme=BROKEN_THEME; break;
- case tcu : theme=TCU_THEME; break;
default: break;
}
}
@@ -270,6 +164,99 @@ try
}
/*************************************************************
+ * Method: extract_json_number *
+ *************************************************************
+ * Description: *
+ * Very small, purpose built JSON scraper. It looks for *
+ * "key":[ inside the given text and parses the first number *
+ * after it. Amedas values always look like "temp":[26.3,0] *
+ * (value, quality flag), so we only need the number before *
+ * the first comma or closing bracket. *
+ * *
+ * Input: *
+ * string json - Text to search in (one HHMM observation *
+ * block) *
+ * string key - Field name, e.g. "temp" *
+ * *
+ * Output: *
+ * double &value - Parsed number *
+ * bool - true if the key was found and a number was read *
+ * *
+ * Change History: *
+ * Date Author Modification *
+ * *
+ *************************************************************/
+bool localtemp::extract_json_number(const string &json, const string &key, double &value)
+{
+ string needle="\""+key+"\":[";
+ size_t pos=json.find(needle);
+ if (pos==string::npos)
+ return false;
+ pos+=needle.length();
+ size_t endpos=json.find_first_of(",]", pos);
+ if (endpos==string::npos)
+ return false;
+ string numstr=json.substr(pos, endpos-pos);
+ if (numstr.empty() || numstr=="null")
+ return false;
+ value=atof(numstr.data());
+ return true;
+}
+/*************************************************************
+ * Method: fetch_latest_time *
+ *************************************************************
+ * Description: *
+ * Downloads AMEDAS_LATEST_URL, which returns a single *
+ * ISO-8601 JST timestamp such as *
+ * "2024-08-09T15:40:00+09:00", telling us which day's file *
+ * to request and which HHMM block inside it is the most *
+ * recent observation. *
+ * It also converts that JST timestamp into a real UTC *
+ * epoch (obs_utc), because dwgo.cpp's displaytemp() expects *
+ * info_time to be a plain UTC instant: it later does *
+ * "info_time + tm_diff" and feeds that into localtime() to *
+ * show it in the viewer's own local time zone. JMA gives us *
+ * JST (UTC+9) directly, so we build the calendar fields with *
+ * timegm() (which treats a struct tm as UTC, unlike *
+ * mktime() which would apply the *host*'s time zone) and *
+ * then subtract 9 hours to land back on true UTC. *
+ * *
+ * Output: *
+ * string &yyyymmdd - Date, e.g. "20240809" *
+ * string &hhmm - Time, e.g. "1540" *
+ * time_t &obs_utc - Observation instant as a UTC epoch *
+ * bool - true if we could fetch and parse the timestamp *
+ * *
+ * Change History: *
+ * Date Author Modification *
+ * *
+ *************************************************************/
+bool localtemp::fetch_latest_time(string &yyyymmdd, string &hhmm, time_t &obs_utc)
+{
+ MySock *skt;
+ HTTP_Request *http;
+ struct tm jst;
+ skt = new MySock((char*)AMEDAS_LATEST_URL);
+ http=skt->GetHTTPData();
+ delete skt;
+ if (http==NULL || http->status!=200 || http->data.length()<19)
+ return false;
+ // Expected format: YYYY-MM-DDTHH:MM:SS+09:00
+ string d=http->data;
+ yyyymmdd=d.substr(0,4)+d.substr(5,2)+d.substr(8,2);
+ hhmm=d.substr(11,2)+d.substr(14,2);
+ memset(&jst, 0, sizeof(jst));
+ jst.tm_year=atoi(d.substr(0,4).data())-1900;
+ jst.tm_mon =atoi(d.substr(5,2).data())-1;
+ jst.tm_mday=atoi(d.substr(8,2).data());
+ jst.tm_hour=atoi(d.substr(11,2).data());
+ jst.tm_min =atoi(d.substr(14,2).data());
+ jst.tm_sec =atoi(d.substr(17,2).data());
+ obs_utc=timegm(&jst)-9*3600; // JST -> UTC (JMA has no DST, always UTC+9)
+ return true;
+}
+
+/*************************************************************
* Method: getInfo *
*************************************************************
* Description: *
@@ -284,65 +271,68 @@ try
*************************************************************/
bool localtemp::getInfo()
{
- char *metar_fetch; // Get Metar data URL
+ char *point_fetch; // Amedas point data URL
MySock *skt;
HTTP_Request *http;
- string::size_type pos, pos2;
- TKey_Value datarl;
+ string yyyymmdd, hhmm;
+ time_t obs_utc;
+ double temp_c, hum, precip10m;
this->error=0; // No error
this->loaded=false;
- metar_fetch= (char*) malloc(80);
- string tmp;
- sprintf(metar_fetch, METAR_URL, this->metar.data());
+ if (!fetch_latest_time(yyyymmdd, hhmm, obs_utc))
+ return false;
- skt = new MySock(metar_fetch);
+ point_fetch= (char*) malloc(strlen(AMEDAS_POINT_URL)+this->metar.length()+yyyymmdd.length()+4);
+ sprintf(point_fetch, AMEDAS_POINT_URL, this->metar.data(), yyyymmdd.data());
+
+ skt = new MySock(point_fetch);
verbsth(VERB_ASTTO, "Open connection: ");
http=skt->GetHTTPData();
delete skt; // We don't need this anymore
+ free(point_fetch);
if (http!=NULL)
{
if (http->status==200)
{
time(&get_time); // We got the file at this moment
- pos=0;
verbsth(VERB_ASTTO, "Get Data: ");
- do
+ this->long_location=this->location_name;
+ // Locate the "HHMM": { ... } block matching the latest
+ // reported observation time and keep it (and everything
+ // after it, it doesn't matter) in this->ob, mirroring the
+ // way the old METAR "ob:" line used to be stored.
+ size_t blockpos=http->data.find("\""+hhmm+"\":");
+ if (blockpos==string::npos)
{
- pos2=http->data.find("\n",pos+1);
- if (pos==0) // Extracts the first line (Name of the Station)
- {
- this->long_location=http->data.substr(0, http->data.find("(",0)-1);
- verbsth(VERB_ASTTO, "Station name: "+this->long_location);
- }
- if (pos2!=string::npos)
- {
- tmp = http->data.substr(pos,pos2-pos);
- datarl.key=tmp.substr(0, tmp.find(":"));
- datarl.value=tmp.substr(tmp.find(":")+1);
- if (datarl.key=="Temperature")
- this->set_temp(datarl.value);
- else if (datarl.key=="Relative Humidity")
- this->set_humidity(datarl.value);
- else if (datarl.key=="Sky conditions")
- this->sky=datarl.value;
- else if (datarl.key=="ob")
- this->ob=datarl.value;
- // Search interesting data
- pos=pos2+1;
- }
- } while (pos2!=string::npos);
- verbsth(VERB_ASTTO, "Get ob info: ");
-
- get_ob_info(); // Get info from the METAR string
- this->loaded=true;
- verbsth(VERB_ASTTO, "Close connection: ");
-
+ blockpos=http->data.rfind("\":{");
+ if (blockpos!=string::npos)
+ blockpos=http->data.rfind("\"", blockpos-1);
+ }
+ if (blockpos!=string::npos)
+ {
+ this->ob=http->data.substr(blockpos);
+ if (extract_json_number(this->ob, "temp", temp_c))
+ this->set_temp(temp_c);
+ if (extract_json_number(this->ob, "humidity", hum))
+ this->set_humidity((int)hum);
+ if (!extract_json_number(this->ob, "precipitation10m", precip10m))
+ precip10m=0.0;
+ this->info_time=obs_utc; // Actual observation instant, in UTC
+ get_ob_info(precip10m); // Work out theme from precipitation/temperature
+ this->loaded=true;
+ verbsth(VERB_ASTTO, "Close connection: ");
+ }
+ else
+ {
+ this->error=1;
+ verbsth(VERB_WARNING, "Couldn't find observation data in Amedas response.");
+ }
}
else
{
diff --git a/src/localtemp.h b/src/localtemp.h
index 8bdb189..b4bbe0e 100644
--- a/src/localtemp.h
+++ b/src/localtemp.h
@@ -1,49 +1,50 @@
#include <string.h>
#include <strings.h>
#include "errors.h"
+#include <time.h>
// Old URL
-//#define METAR_URL "http://weather.noaa.gov/pub/data/observations/metar/decoded/%s.TXT"
-
-// New URL
#define METAR_URL "http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT"
+// JMA Amedas data.
+#define AMEDAS_LATEST_URL "https://www.jma.go.jp/bosai/amedas/data/latest_time.txt"
+#define AMEDAS_POINT_URL "https://www.jma.go.jp/bosai/amedas/data/point/%s/%s_00.json"
+
class localtemp {
public:
enum ESky
{
- undef_sky,
- clear, // CAVOK, SKC, CLR
- cloudy, // FEW, SCT
- brkovc, // Broken, Overcast (BKN, OVC)
- tcu // TCU (Towering CUmulus)
+ clear, // Fine weather (no precipitation reported)
+ cloudy, // Reserved (not reported by Amedas)
+ brkovc, // Reserved (not reported by Amedas)
+ tcu // Reserved (not reported by Amedas)
};
enum ERain
{
undef_rain,
- rain, // Rainy (DZ, RA)
- snow, // Snowy (SN, SG)
- hail // Hail (GS, GR)
+ rain, // precipitation10m > 0, temperature > 2C
+ snow, // precipitation10m > 0, temperature <= 2C
+ hail // Reserved (not reported by Amedas)
};
enum EFog
{
undef_fog,
- fog, // (BR, FG)
- dust, // (DU, DS)
- other // (FC, FU, HZ, SA, SS, VA)
+ fog, // Reserved (not reported by Amedas)
+ dust, // Reserved (not reported by Amedas)
+ other // Reserved (not reported by Amedas)
};
struct TMetar
{
ESky sky;
- bool CB; // Cumulonimbus
+ bool CB; // Cumulonimbus (unused, kept for compatibility)
ERain rain;
EFog fog;
};
std::string metar;
std::string location_name;
std::string long_location;
- std::string ob; // ob: line, METAR info.
+ std::string ob; // Raw "HHMM" observation block extracted from the JSON
int error, celsius, fahrenheit;
int theme; // º theme to use
short humidity;
@@ -55,7 +56,9 @@ public:
localtemp(char* metar, char* location_name);
bool getInfo();
private:
- void set_temp(std::string temp);
- void set_humidity(std::string hum);
- void get_ob_info();
+ void set_temp(double temp_c);
+ void set_humidity(int hum);
+ void get_ob_info(double precip10m);
+ bool fetch_latest_time(std::string &yyyymmdd, std::string &hhmm, time_t &obs_utc);
+ bool extract_json_number(const std::string &json, const std::string &key, double &value);
};