RosettaCodeData/Task/Distance-and-Bearing/C/distance-and-bearing.c
2026-04-30 12:34:36 -04:00

253 lines
6.2 KiB
C

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define PI (3.14159265358979323846)
#define EARTH_RADIUS_NM (3440.8)
struct airport {
double distance;
double bearing;
int id;
char const *name;
char const *city;
char const *country;
char const *iata;
char const *icao;
double latitude;
double longitude;
double altitude;
double timezone;
char const *dst;
char const *tz;
char const *type;
char const *source;
};
static size_t n_airports = 0;
static struct airport *airport = NULL;
static void print_header(size_t cnlen, size_t anlen) {
static const char dashes[] = "--------------------------------------------------------------------------------";
printf("%.8s | %.4s | %.4s | %-*s | %-*s\n",
"DISTANCE",
"BEAR",
"ICAO",
cnlen, "COUNTRY",
anlen, "AIRPORT"
);
printf("%.8s-+-%.4s-+-%.4s-+-%.*s-+-%.*s\n",
dashes,
dashes,
dashes,
cnlen, dashes,
anlen, dashes
);
}
static void print_airport(struct airport *airp, size_t cnlen, size_t anlen) {
printf("%8.1f | %4.0f | %-4s | %-*s | %-*s\n",
airp->distance,
airp->bearing,
airp->icao,
cnlen, airp->country,
anlen, airp->name
);
}
#define RADIANS * PI / 180
#define DEGREES * 180 / PI
static double sq(double x) {
return x * x;
}
static double distance(double lat1, double lon1, double lat2, double lon2) {
double rlat1 = lat1 RADIANS;
double rlat2 = lat2 RADIANS;
double dlat = (lat2 - lat1) RADIANS;
double dlon = (lon2 - lon1) RADIANS;
double a = sq(sin(dlat/2)) + cos(rlat1) * cos(rlat2) * sq(sin(dlon/2));
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
return EARTH_RADIUS_NM * c;
}
static double bearing(double lat1, double lon1, double lat2, double lon2) {
double rlat1 = lat1 RADIANS;
double rlat2 = lat2 RADIANS;
double dlon = (lon2 - lon1) RADIANS;
double x = cos(rlat1) * sin(rlat2) - sin(rlat1) * cos(rlat2) * cos(dlon);
double y = sin(dlon) * cos(rlat2);
double theta = atan2(y, x);
return fmod(theta DEGREES + 360, 360);
}
static int compare_by_distance(void const *p1, void const *p2) {
struct airport const *a1 = *(struct airport const **)p1;
struct airport const *a2 = *(struct airport const **)p2;
return (a1->distance < a2->distance) ? -1 : (a1->distance > a2->distance) ? +1 : 0;
}
static struct airport **order_by_distance(double latitude, double longitude) {
struct airport **list = malloc(sizeof(*list) * n_airports);
for(int i = 0; i < n_airports; i++) {
airport[i].distance = distance(latitude, longitude, airport[i].latitude, airport[i].longitude);
airport[i].bearing = bearing (latitude, longitude, airport[i].latitude, airport[i].longitude);
list[i] = &airport[i];
}
qsort(list, n_airports, sizeof(*list), compare_by_distance);
return list;
}
static char **split(char *s, int c) {
char **v = malloc(sizeof(*v));
if(!v) {
perror("");
abort();
}
size_t n = 0;
for(char *t; *s; s = t) {
t = strchr(s, c);
if(!t) {
t = s + strlen(s);
} else {
*t++ = '\0';
}
if(*s) {
size_t i = n++;
v = realloc(v, sizeof(*v) * (n + 1));
if(!v) {
perror("");
abort();
}
v[i] = s;
}
}
v[n] = NULL;
return v;
}
static unsigned int_or_zero(char *s) {
return (s && *s && strcmp(s, "\\N")) ? atoi(s) : 0;
}
static double dbl_or_zero(char *s) {
return (s && *s && strcmp(s, "\\N")) ? atof(s) : 0;
}
static char *str_or_null(char *s) {
size_t n = strlen(s);
if((n > 0) && s[n-1] == '"') s[n-1] = '\0';
if(*s == '"') s++;
return (s && *s && strcmp(s, "\\N")) ? s : "NULL";
}
static void load_airports(char *s) {
char **line = split(s, '\n');
if(line) {
for(size_t i = 0; line[i] != NULL; i++) {
char **field = split(line[i], ',');
if(!field) continue;
size_t j = n_airports++;
airport = realloc(airport, sizeof(*airport) * n_airports);
if(!airport) {
perror("");
abort();
}
struct airport *airp = &airport[j];
airp->id = int_or_zero(field[0]);
airp->name = str_or_null(field[1]);
airp->city = str_or_null(field[2]);
airp->country = str_or_null(field[3]);
airp->iata = str_or_null(field[4]);
airp->icao = str_or_null(field[5]);
airp->latitude = dbl_or_zero(field[6]);
airp->longitude = dbl_or_zero(field[7]);
airp->altitude = dbl_or_zero(field[8]);
airp->timezone = dbl_or_zero(field[9]);
airp->dst = str_or_null(field[10]);
airp->tz = str_or_null(field[11]);
airp->type = str_or_null(field[12]);
airp->source = str_or_null(field[13]);
free(field);
}
free(line);
}
}
static char *ingest(FILE *in, char const *filename) {
size_t n = 0;
size_t z = BUFSIZ-1;
char *content = malloc(BUFSIZ);
if(!content) {
perror("");
abort();
}
do {
size_t u = fread(content + n, 1, z - n, in);
if(ferror(in)) {
free(content);
perror(filename);
return NULL;
}
n += u;
if(n == z) {
z += BUFSIZ;
content = realloc(content, z + 1);
if(!content) {
perror(filename);
abort();
}
}
} while(!feof(in))
;
content[n] = '\0';
return content;
}
#define DEFER(DEFER_pre,DEFER_cond,DEFER_post) \
for(int DEFER##__LINE__ = 1; DEFER##__LINE__; DEFER##__LINE__ = 0) \
for(DEFER_pre; DEFER##__LINE__ && (DEFER_cond); (DEFER_post), DEFER##__LINE__ = 0)
int main(int argc, char **argv) {
int failed;
char *airports_dat = NULL;
char const *infile = "airports.dat";
DEFER(FILE *in = fopen(infile, "r"),
!(failed = !in) || (perror(infile), 0),
fclose(in)
) {
airports_dat = ingest(in, infile);
if((failed = !airports_dat)) break;
load_airports(airports_dat);
if((failed = !airport)) break;
}
if(!failed) for(;;) {
double latitude, longitude;
fputs("Enter latitude,longitude> ", stdout);
fflush(stdout);
if(scanf("%lf,%lf", &latitude, &longitude) != 2) break;
putchar('\n');
struct airport **list = order_by_distance(latitude, longitude);
size_t cnlen = 0;
size_t anlen = 0;
for(size_t n, i = 0; i < 20; i++) {
if((n = strlen(list[i]->country)) > cnlen) cnlen = n;
if((n = strlen(list[i]->name)) > anlen) anlen = n;
}
print_header(cnlen, anlen);
for(size_t i = 0; i < 20; i++) {
print_airport(list[i], cnlen, anlen);
}
putchar('\n');
free(list);
}
free(airport);
free(airports_dat);
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
}
//