improved timezone support
cal celestial can now query the timezone at the place of the observer (use -l)
This commit is contained in:
parent
112fd4cfd6
commit
33aed1302a
4 changed files with 308 additions and 229 deletions
|
@ -44,7 +44,6 @@
|
|||
|
||||
#include "../config.h"
|
||||
#include "objects.h"
|
||||
#include "helpers.h"
|
||||
#include "formatter.h"
|
||||
#include "geonames.h"
|
||||
|
||||
|
@ -59,6 +58,7 @@ static struct option long_options[] = {
|
|||
{"lon", required_argument, 0, 'o'},
|
||||
#ifdef GEONAMES_SUPPORT
|
||||
{"query", required_argument, 0, 'q'},
|
||||
{"local", no_argument, 0, 'l'},
|
||||
#endif
|
||||
{"timezone", required_argument, 0, 'z'},
|
||||
{"universal", no_argument, 0, 'u'},
|
||||
|
@ -70,16 +70,17 @@ static struct option long_options[] = {
|
|||
static const char *long_options_descs[] = {
|
||||
"calc for celestial object: sun, moon, mars, neptune,\n\t\t\t jupiter, mercury, uranus, saturn, venus or pluto",
|
||||
"calc rise/set time with twilight: nautic, civil or astronomical",
|
||||
"calc at given time: YYYY-MM-DD [HH:MM:SS]",
|
||||
"calc at given time: YYYY-MM-DD[_HH:MM:SS]",
|
||||
"calc position at moment of: rise, set, transit",
|
||||
"use rise, set, transit time of tomorrow",
|
||||
"output format: see strftime (3) and calcelestial (1) for more details",
|
||||
"geographical latitude of observer: -90° to 90°",
|
||||
"geographical longitude of oberserver: -180° to 180°",
|
||||
#ifdef GEONAMES_SUPPORT
|
||||
"query geonames.org for geographical coordinates",
|
||||
"query coordinates using the geonames.org geolocation service",
|
||||
"query local timezone using the geonames.org geolocation service",
|
||||
#endif
|
||||
"override system timezone",
|
||||
"override system timezone (TZ environment variable)",
|
||||
"use universial time for parsing and formatting",
|
||||
"show usage help",
|
||||
"show version"
|
||||
|
@ -120,7 +121,7 @@ int main(int argc, char *argv[])
|
|||
int ret;
|
||||
time_t t;
|
||||
double jd;
|
||||
struct tm *date = NULL;
|
||||
struct tm tm;
|
||||
const struct object *obj;
|
||||
|
||||
/* Default options */
|
||||
|
@ -128,12 +129,16 @@ int main(int argc, char *argv[])
|
|||
int tz = INT_MAX;
|
||||
|
||||
char *obj_str = basename(argv[0]);
|
||||
char *format = "time: %Y-%m-%d %H:%M:%S az: §a (§s) alt: §h";
|
||||
char *format = "time: %Y-%m-%d %H:%M:%S (%Z) az: §a (§s) alt: §h";
|
||||
char tzid[32] = "";
|
||||
char *query = NULL;
|
||||
|
||||
bool horizon_set = false;
|
||||
bool utc = false;
|
||||
bool next = false;
|
||||
bool local_tz = false;
|
||||
|
||||
time(&t);
|
||||
localtime_r(&t, &tm);
|
||||
|
||||
enum {
|
||||
MOMENT_NOW,
|
||||
|
@ -147,7 +152,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
/* parse command line arguments */
|
||||
while (1) {
|
||||
char c = getopt_long(argc, argv, "+hvnut:d:f:a:o:q:z:p:m:H:", long_options, NULL);
|
||||
char c = getopt_long(argc, argv, "+hvnult:d:f:a:o:q:z:p:m:H:", long_options, NULL);
|
||||
|
||||
/* detect the end of the options. */
|
||||
if (c == -1)
|
||||
|
@ -173,13 +178,14 @@ int main(int argc, char *argv[])
|
|||
break;
|
||||
|
||||
case 't':
|
||||
date = malloc(sizeof(struct tm));
|
||||
date->tm_isdst = -1; /* update dst */
|
||||
if (strptime(optarg, "%Y-%m-%d %H:%M:%S", date)) { }
|
||||
else if (strptime(optarg, "%Y-%m-%d", date)) { }
|
||||
tm.tm_isdst = -1; /* update dst */
|
||||
if (strchr(optarg, '_')) {
|
||||
if (!strptime(optarg, "%Y-%m-%d_%H:%M:%S", &tm))
|
||||
usage_error("invalid time/date parameter");
|
||||
}
|
||||
else {
|
||||
free(date);
|
||||
usage_error("invalid date parameter");
|
||||
if (!strptime(optarg, "%Y-%m-%d", &tm))
|
||||
usage_error("invalid time/date parameter");
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -213,19 +219,21 @@ int main(int argc, char *argv[])
|
|||
case 'q':
|
||||
query = strdup(optarg);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'l':
|
||||
local_tz = true;
|
||||
break;
|
||||
#endif
|
||||
case 'p':
|
||||
obj_str = optarg;
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
tz = atoi(optarg);
|
||||
strncpy(tzid, optarg, sizeof(tzid));
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
utc = true;
|
||||
tz = 0;
|
||||
strncpy(tzid, "UTC", sizeof(tzid));
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
|
@ -250,33 +258,35 @@ int main(int argc, char *argv[])
|
|||
#ifdef GEONAMES_SUPPORT
|
||||
/* Lookup place at http://geonames.org */
|
||||
if (query) {
|
||||
ret = geonames_lookup(query, &obs, NULL, 0);
|
||||
ret = geonames_lookup_latlng(query, &obs, NULL, 0);
|
||||
if (ret)
|
||||
usage_error("failed to lookup location");
|
||||
}
|
||||
|
||||
if (local_tz) {
|
||||
int gmt_offset;
|
||||
ret = geonames_lookup_tz(obs, &gmt_offset, tzid, sizeof(tzid));
|
||||
if (ret)
|
||||
usage_error("failed to lookup location");
|
||||
}
|
||||
#endif
|
||||
|
||||
setenv("TZ", tzid, 1);
|
||||
tzset();
|
||||
|
||||
/* Validate observer coordinates */
|
||||
if (fabs(obs.lat) > 90)
|
||||
usage_error("invalid latitude, use --lat");
|
||||
if (fabs(obs.lng) > 180)
|
||||
usage_error("invalid longitude, use --lon");
|
||||
|
||||
if (horizon_set && !strcmp(object_name(obj), "sun"))
|
||||
if (horizon_set && strcmp(object_name(obj), "sun"))
|
||||
usage_error("the twilight parameter can only be used for the sun");
|
||||
|
||||
/* Calculate julian date */
|
||||
if (date) {
|
||||
t = (utc) ? mktimeutc(date) : mktime(date);
|
||||
free(date);
|
||||
}
|
||||
else
|
||||
t = time(NULL);
|
||||
|
||||
t = mktime(&tm);
|
||||
jd = ln_get_julian_from_timet(&t);
|
||||
date = localtime(&t);
|
||||
|
||||
result.tz = (tz == INT_MAX) ? date->tm_gmtoff / 3600 : tz;
|
||||
result.obs = obs;
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -285,7 +295,7 @@ int main(int argc, char *argv[])
|
|||
printf("Debug: for position: N %f, E %f\n", obs.lat, obs.lng);
|
||||
printf("Debug: for object: %s\n", object_name(obj));
|
||||
printf("Debug: with horizon: %f\n", horizon);
|
||||
printf("Debug: with timezone: %d\n", result.tz);
|
||||
printf("Debug: with timezone: %s\n", tzid);
|
||||
#endif
|
||||
|
||||
/* calc rst date */
|
||||
|
@ -309,11 +319,12 @@ rst: if (object_rst(obj, jd - .5, horizon, &result.obs, &result.rst) == 1) {
|
|||
goto rst;
|
||||
}
|
||||
}
|
||||
|
||||
ln_get_timet_from_julian(result.jd, &t);
|
||||
localtime_r(&t, &result.tm);
|
||||
|
||||
/* calc position */
|
||||
object_pos(obj, result.jd, &result);
|
||||
object_pos(obj, jd, &result);
|
||||
|
||||
/* format & output */
|
||||
format_result(format, &result);
|
||||
|
||||
return 0;
|
||||
|
|
430
src/geonames.c
430
src/geonames.c
|
@ -39,176 +39,38 @@
|
|||
|
||||
#include "../config.h"
|
||||
#include "geonames.h"
|
||||
#include "formatter.h"
|
||||
|
||||
const char* username = "libastro";
|
||||
const char* request_url_tpl = "http://api.geonames.org/search?name=%s&maxRows=1&username=%s&type=json&orderby=relevance";
|
||||
static const char* url_tpl = "http://api.geonames.org/search?q=%s&maxRows=1&username=libastro&type=json&orderby=relevance";
|
||||
static const char* url_tz_tpl = "http://api.geonames.org/timezoneJSON?lat=%.6f&lng=%.6f&username=libastro";
|
||||
|
||||
static size_t json_parse_callback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
static struct json_tokener *jtok;
|
||||
static struct json_object *jobj;
|
||||
size_t realsize = size * nmemb;
|
||||
struct string {
|
||||
char *ptr;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* initialize tokener */
|
||||
if (jtok == NULL) {
|
||||
jtok = json_tokener_new();
|
||||
jtok->err = json_tokener_continue;
|
||||
}
|
||||
struct ctx_latlng {
|
||||
struct ln_lnlat_posn *coords;
|
||||
char *name;
|
||||
size_t namelen;
|
||||
};
|
||||
|
||||
if (jtok->err == json_tokener_continue) {
|
||||
#ifdef DEBUG
|
||||
printf("Debug: received chunk: %zu * %zu = %zu bytes\r\n", size, nmemb, realsize);
|
||||
printf(" %.*s\r\n", (int) realsize, (char *) contents);
|
||||
#endif
|
||||
struct ctx_tz {
|
||||
int *gmt_offset;
|
||||
char *tzid;
|
||||
size_t tzidlen;
|
||||
};
|
||||
|
||||
jobj = json_tokener_parse_ex(jtok, (char *) contents, realsize);
|
||||
|
||||
if (jtok->err == json_tokener_success) {
|
||||
*(struct json_object **) userp = jobj;
|
||||
json_tokener_free(jtok);
|
||||
}
|
||||
else if (jtok->err != json_tokener_continue) {
|
||||
const char *err = json_tokener_error_desc(json_tokener_get_error(jtok));
|
||||
fprintf(stderr, "json parse error: %s\r\n", err);
|
||||
*(void **) userp = NULL;
|
||||
json_tokener_free(jtok);
|
||||
}
|
||||
}
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
int geonames_lookup(const char *place, struct ln_lnlat_posn *result, char *name, int n)
|
||||
{
|
||||
#ifdef GEONAMES_CACHE_SUPPORT
|
||||
int ret;
|
||||
|
||||
ret = geonames_cache_db(1, place, result);
|
||||
if (ret == 0) {
|
||||
strncpy(name, place, n);
|
||||
#ifdef DEBUG
|
||||
printf("Debug: using cached geonames entry\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
enum cache_op { STORE, LOOKUP, DELETE };
|
||||
|
||||
CURL *ch;
|
||||
CURLcode res;
|
||||
|
||||
struct json_object *jobj;
|
||||
|
||||
/* setup curl */
|
||||
ch = curl_easy_init();
|
||||
if (!ch)
|
||||
return -1;
|
||||
|
||||
/* prepare url */
|
||||
int len = strlen(place) + strlen(request_url_tpl) + 1;
|
||||
char *request_url = malloc(len);
|
||||
if (!request_url)
|
||||
return -2;
|
||||
|
||||
snprintf(request_url, len, request_url_tpl, place, username);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Debug: request url: %s\r\n", request_url);
|
||||
#endif
|
||||
|
||||
curl_easy_setopt(ch, CURLOPT_URL, request_url);
|
||||
curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, json_parse_callback);
|
||||
curl_easy_setopt(ch, CURLOPT_WRITEDATA, (void *) &jobj);
|
||||
curl_easy_setopt(ch, CURLOPT_USERAGENT, "libastro/1.0");
|
||||
|
||||
/* perform request */
|
||||
res = curl_easy_perform(ch);
|
||||
|
||||
/* always cleanup */
|
||||
curl_easy_cleanup(ch);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
fprintf(stderr, "request failed: %s\n", curl_easy_strerror(res));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (jobj) {
|
||||
int ret = geonames_parse(jobj, result, name, n);
|
||||
if (!ret) {
|
||||
#ifdef GEONAMES_CACHE_SUPPORT
|
||||
geonames_cache_db(0, place, result);
|
||||
#ifdef DEBUG
|
||||
printf("Debug: storing cache entry\n");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
json_object_put(jobj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int geonames_parse(struct json_object *jobj, struct ln_lnlat_posn *result, char *name, int n)
|
||||
{
|
||||
json_bool exists;
|
||||
json_object *jobj_count, *jobj_geonames, *jobj_place, *jobj_lat, *jobj_lng, *jobj_name;
|
||||
int results;
|
||||
|
||||
exists = json_object_object_get_ex(jobj, "totalResultsCount", &jobj_count);
|
||||
if (!exists)
|
||||
return -1;
|
||||
|
||||
exists = json_object_object_get_ex(jobj, "geonames", &jobj_geonames);
|
||||
if (!exists)
|
||||
return -1;
|
||||
|
||||
results = json_object_get_int(jobj_count);
|
||||
if (results == 0)
|
||||
return -1;
|
||||
|
||||
jobj_place = json_object_array_get_idx(jobj_geonames, 0);
|
||||
if (!jobj_place)
|
||||
return -1;
|
||||
|
||||
exists = json_object_object_get_ex(jobj_place, "lat", &jobj_lat);
|
||||
if (!exists)
|
||||
return -1;
|
||||
|
||||
exists = json_object_object_get_ex(jobj_place, "lng", &jobj_lng);
|
||||
if (!exists)
|
||||
return -1;
|
||||
|
||||
exists = json_object_object_get_ex(jobj_name, "name", &jobj_name);
|
||||
if (!exists)
|
||||
return -1;
|
||||
|
||||
result->lat = json_object_get_double(jobj_lat);
|
||||
result->lng = json_object_get_double(jobj_lng);
|
||||
|
||||
if (name && n > 0)
|
||||
strncpy(name, json_object_get_string(jobj_name), n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int geonames_cache_db(int lookup, const char *place, struct ln_lnlat_posn *coords)
|
||||
static int cache(enum cache_op op, const char *url, struct string *s)
|
||||
{
|
||||
int ret;
|
||||
DB *dbp;
|
||||
DBT key, data;
|
||||
|
||||
char filename[256];
|
||||
char *place_lower;
|
||||
|
||||
place_lower = strdup(place);
|
||||
if (!place_lower)
|
||||
return -1;
|
||||
|
||||
for (char *p = place_lower; *p; ++p)
|
||||
*p = tolower(*p);
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s", getenv("HOME"), GEONAMES_CACHE_FILE);
|
||||
|
||||
dbp = dbopen(filename, O_RDWR | O_CREAT, 0664, DB_BTREE, NULL);
|
||||
|
@ -220,37 +82,43 @@ int geonames_cache_db(int lookup, const char *place, struct ln_lnlat_posn *coord
|
|||
memset(&key, 0, sizeof(key));
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
key.data = place_lower;
|
||||
key.size = strlen(place_lower) + 1;
|
||||
key.data = (void *) url;
|
||||
key.size = strlen(url) + 1;
|
||||
|
||||
if (lookup) {
|
||||
ret = dbp->get(dbp, &key, &data, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
switch (op) {
|
||||
case LOOKUP:
|
||||
ret = dbp->get(dbp, &key, &data, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Debug: cache key retrieved: %s => %f %f.\n", (char *) key.data,
|
||||
((struct ln_lnlat_posn *) data.data)->lat,
|
||||
((struct ln_lnlat_posn *) data.data)->lng);
|
||||
#endif
|
||||
if (data.size != sizeof(struct ln_lnlat_posn))
|
||||
goto err;
|
||||
printf("Debug: cache key retrieved: %s => %s.\n", (char *) key.data, (char *) data.data);
|
||||
#endif /* DEBUG */
|
||||
s->ptr = malloc(data.size);
|
||||
s->len = data.size;
|
||||
if (!s->ptr) {
|
||||
fprintf(stderr, "malloc() failed: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memcpy(coords, data.data, sizeof(struct ln_lnlat_posn));
|
||||
}
|
||||
else {
|
||||
data.data = coords;
|
||||
data.size = sizeof(struct ln_lnlat_posn);
|
||||
memcpy(s->ptr, data.data, data.size);
|
||||
break;
|
||||
|
||||
ret = dbp->put(dbp, &key, &data, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
case STORE:
|
||||
data.data = s->ptr;
|
||||
data.size = s->len;
|
||||
|
||||
ret = dbp->put(dbp, &key, &data, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
#ifdef DEBUG
|
||||
printf("Debug: cache key stored: %s => %f %f.\n", (char *) key.data,
|
||||
((struct ln_lnlat_posn *) data.data)->lat,
|
||||
((struct ln_lnlat_posn *) data.data)->lng);
|
||||
#endif
|
||||
printf("Debug: cache key stored: %s => %s\n", (char *) key.data, (char *) data.data);
|
||||
#endif /* DEBUG */
|
||||
break;
|
||||
|
||||
case DELETE: {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
|
@ -258,3 +126,201 @@ err:
|
|||
|
||||
return ret;
|
||||
}
|
||||
#endif /* GEONAMES_CACHE_SUPPORT */
|
||||
|
||||
static size_t writefunction(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
struct string *s = userp;
|
||||
|
||||
size_t new_len = s->len + size*nmemb;
|
||||
|
||||
s->ptr = realloc(s->ptr, new_len + 1);
|
||||
if (s->ptr == NULL) {
|
||||
fprintf(stderr, "realloc() failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memcpy(s->ptr + s->len, contents, size*nmemb);
|
||||
|
||||
s->ptr[new_len] = '\0';
|
||||
s->len = new_len;
|
||||
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static int request_json(const char *url, int (*parser)(struct json_object *jobj, void *ctx), void *ctx)
|
||||
{
|
||||
int ret, cached;
|
||||
|
||||
CURL *ch;
|
||||
CURLcode res;
|
||||
|
||||
struct json_object *jobj;
|
||||
enum json_tokener_error error;
|
||||
|
||||
struct string s = { 0 };
|
||||
|
||||
#ifdef GEONAMES_CACHE_SUPPORT
|
||||
cached = cache(LOOKUP, url, &s) == 0;
|
||||
if (cached)
|
||||
goto cached;
|
||||
#endif /* GEONAMES_CACHE_SUPPORT */
|
||||
|
||||
/* Setup curl */
|
||||
ch = curl_easy_init();
|
||||
if (!ch)
|
||||
return -1;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Debug: request url: %s\r\n", url);
|
||||
#endif /* DEBUG */
|
||||
|
||||
curl_easy_setopt(ch, CURLOPT_URL, url);
|
||||
curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, writefunction);
|
||||
curl_easy_setopt(ch, CURLOPT_WRITEDATA, (void *) &s);
|
||||
curl_easy_setopt(ch, CURLOPT_USERAGENT, "libastro/1.0");
|
||||
|
||||
/* perform request */
|
||||
res = curl_easy_perform(ch);
|
||||
|
||||
/* always cleanup */
|
||||
curl_easy_cleanup(ch);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
fprintf(stderr, "Error: request failed: %s\n", curl_easy_strerror(res));
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Debug: request completed: %s\r\n", s.ptr);
|
||||
#endif /* DEBUG */
|
||||
|
||||
cached:
|
||||
jobj = json_tokener_parse_verbose(s.ptr, &error);
|
||||
if (!jobj) {
|
||||
#ifdef DEBUG
|
||||
printf("Debug: failed to parse json: %s\r\n", json_tokener_error_desc(error));
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
|
||||
|
||||
ret = parser(jobj, ctx);
|
||||
if (!ret && !cached) {
|
||||
#ifdef GEONAMES_CACHE_SUPPORT
|
||||
cache(STORE, url, &s);
|
||||
#endif /* GEONAMES_CACHE_SUPPORT */
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else
|
||||
printf("Debug: failed to parse: %d\n", ret);
|
||||
#endif /* DEBUG */
|
||||
|
||||
json_object_put(jobj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parser_tz(struct json_object *jobj, void *userp)
|
||||
{
|
||||
struct ctx_tz *ctx = userp;
|
||||
|
||||
json_bool exists;
|
||||
struct json_object *jobj_offset, *jobj_tzid;
|
||||
|
||||
exists = json_object_object_get_ex(jobj, "gmtOffset", &jobj_offset);
|
||||
if (!exists)
|
||||
return -1;
|
||||
|
||||
exists = json_object_object_get_ex(jobj, "timezoneId", &jobj_tzid);
|
||||
if (!exists)
|
||||
return -1;
|
||||
|
||||
*ctx->gmt_offset = json_object_get_int(jobj_offset);
|
||||
|
||||
if (ctx->tzid)
|
||||
strncpy(ctx->tzid, json_object_get_string(jobj_tzid), ctx->tzidlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parser_latlng(struct json_object *jobj, void *userp)
|
||||
{
|
||||
struct ctx_latlng *ctx = userp;
|
||||
|
||||
json_bool exists;
|
||||
struct json_object *jobj_count, *jobj_geonames, *jobj_place, *jobj_lat, *jobj_lng, *jobj_name;
|
||||
int results;
|
||||
|
||||
exists = json_object_object_get_ex(jobj, "totalResultsCount", &jobj_count);
|
||||
if (!exists)
|
||||
return -1;
|
||||
|
||||
exists = json_object_object_get_ex(jobj, "geonames", &jobj_geonames);
|
||||
if (!exists)
|
||||
return -2;
|
||||
|
||||
results = json_object_get_int(jobj_count);
|
||||
if (results == 0)
|
||||
return -3;
|
||||
|
||||
jobj_place = json_object_array_get_idx(jobj_geonames, 0);
|
||||
if (!jobj_place)
|
||||
return -4;
|
||||
|
||||
exists = json_object_object_get_ex(jobj_place, "lat", &jobj_lat);
|
||||
if (!exists)
|
||||
return -5;
|
||||
|
||||
exists = json_object_object_get_ex(jobj_place, "lng", &jobj_lng);
|
||||
if (!exists)
|
||||
return -6;
|
||||
|
||||
exists = json_object_object_get_ex(jobj_place, "name", &jobj_name);
|
||||
if (!exists)
|
||||
return -7;
|
||||
|
||||
ctx->coords->lat = json_object_get_double(jobj_lat);
|
||||
ctx->coords->lng = json_object_get_double(jobj_lng);
|
||||
|
||||
if (ctx->name)
|
||||
strncpy(ctx->name, json_object_get_string(jobj_name), ctx->namelen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int geonames_lookup_tz(struct ln_lnlat_posn coords, int *gmt_offset, char *tzid, size_t tzidlen)
|
||||
{
|
||||
char url[256];
|
||||
struct ctx_tz ctx = {
|
||||
.gmt_offset = gmt_offset,
|
||||
.tzid = tzid,
|
||||
.tzidlen = tzidlen
|
||||
};
|
||||
|
||||
snprintf(url, sizeof(url), url_tz_tpl, coords.lat, coords.lng);
|
||||
|
||||
return request_json(url, parser_tz, &ctx);
|
||||
}
|
||||
|
||||
int geonames_lookup_latlng(const char *place, struct ln_lnlat_posn *coords, char *name, size_t namelen)
|
||||
{
|
||||
int ret;
|
||||
char url[256];
|
||||
struct ctx_latlng ctx = {
|
||||
.coords = coords,
|
||||
.name = name,
|
||||
.namelen = namelen
|
||||
};
|
||||
|
||||
//char *escaped_place = curl_escape(place, 0);
|
||||
char *escaped_place = strrepl(place, " ", "+");
|
||||
|
||||
snprintf(url, sizeof(url), url_tpl, escaped_place);
|
||||
|
||||
ret = request_json(url, parser_latlng, &ctx);
|
||||
|
||||
//curl_free(escaped_place);
|
||||
free(escaped_place);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -32,10 +32,9 @@
|
|||
struct ln_lnlat_posn;
|
||||
|
||||
#define GEONAMES_CACHE_SUPPORT 1
|
||||
#define GEONAMES_CACHE_FILE ".geonames.cache" /* in users home dir */
|
||||
#define GEONAMES_CACHE_FILE ".geonames.db" /* in users home dir */
|
||||
|
||||
int geonames_lookup(const char *place, struct ln_lnlat_posn *coords, char *name, int n);
|
||||
int geonames_cache_db(int lookup, const char *place, struct ln_lnlat_posn *coords);
|
||||
int geonames_parse(struct json_object *jobj, struct ln_lnlat_posn *result, char *name, int n);
|
||||
int geonames_lookup_latlng(const char *place, struct ln_lnlat_posn *coords, char *name, size_t namelen);
|
||||
int geonames_lookup_tz(struct ln_lnlat_posn coords, int *gmt_offset, char *tzid, size_t tzidlen);
|
||||
|
||||
#endif /* _GEONAMES_H_ */
|
||||
|
|
|
@ -32,21 +32,24 @@
|
|||
#include "geonames.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int ret, gmt_offset;
|
||||
struct ln_lnlat_posn res;
|
||||
char *result_name, *name;
|
||||
|
||||
result_name = malloc(128);
|
||||
if (result_name == NULL)
|
||||
return -1;
|
||||
char name[128], tzid[32];
|
||||
|
||||
if (argc != 2)
|
||||
fprintf(stderr, "Usage: geonames LOCATION\n");
|
||||
|
||||
int ret = geonames_lookup(argv[1], &res, result_name, 32);
|
||||
if (!ret)
|
||||
printf("%s is at (%.4f, %.4f)\r\n", result_name, res.lat, res.lng);
|
||||
|
||||
free(result_name);
|
||||
ret = geonames_lookup_latlng(argv[1], &res, name, sizeof(name));
|
||||
if (ret) {
|
||||
fprintf(stderr, "Error: Failed to lookup coordinates of %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = geonames_lookup_tz(res, &gmt_offset, tzid, sizeof(tzid));
|
||||
if (ret)
|
||||
fprintf(stderr, "Error: Failed to lookup timezone for %.4f %.4f\n", res.lat, res.lng);
|
||||
|
||||
printf("%s is at %.4f, %.4f with timezone %s (GMT%+d)\r\n", name, res.lat, res.lng, tzid, gmt_offset);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue