001: #include<stdio.h>
002: #include<stdlib.h>
003: #include<string.h>
004: #include<math.h>
005:
006: // struct per memorizzazione dati singola citta'
007: struct city{
008: char *name;
009: char country[3];
010: double lon;
011: double lat;
012: };
013:
014:
015: // prototipi funzioni usate nella main()
016: struct city *readfile(const char *, int *); // legge file e restituisce array di "citta'" e relativa numerosita'
017: void stampasessagimale(double); // stampa angolo in formato sessagesimale
018: double calcoladistanza(struct city *, struct city *); // calcolo distanza tra due citta'
019: int compareb(const void *, const void *); // callback per bsearch()
020: int compare(const void *, const void *); // callback per qsort()
021: void dumpdata(struct city *, int, struct city *); // crea file di output
022: struct city *findcity(struct city *, int); // trova citta' in base al nome
023:
024: int main(int argc, char **argv){
025:
026: struct city *mioarr; // array in cui memorizzero' quanto letto dal CSV
027: int narr; // numero di elementi nell'array
028:
029: mioarr = readfile("europa.csv", &narr); // invoco funzione che legge file e riempe array
030:
031: struct city *sel = findcity(mioarr, narr);
032:
033:
034: if(!sel) // verifico di averla trovata, in caso negativo stampo messaggio di errore ed esco
035: {
036: printf("La citta' richiesta non e' presente...\n");
037: return 1;
038: }
039:
040: printf("Ho trovato %s %s %g %g\n", sel->name, sel->country, sel->lat, sel->lon);
041:
042: printf("Le sue coordinare in formato sessagesimale sono: ");
043: stampasessagimale(sel->lat);
044: stampasessagimale(sel->lon);
045:
046: dumpdata(mioarr, narr, sel); // invoco funzione che crea il file di output. Passso 1. array, 2. numero elementi dell'array, 3. elemento da usarsi per le distanze
047:
048: return 0;
049: }
050:
051: // questa funzione legge il file CSV di cui viene passato il nome (a rigore non necessario)
052: // restituisce l'indirizzo di un array che contiene le varie "struct city" con tutti i dati
053: // in *n pone la dimensione dell'array
054: // prima di restituire l'array lo ordina usando qsort()
055: struct city *readfile(const char *filename, int *n)
056: {
057: struct city *list = NULL; // array di struct city
058: int ncities = 0; // inizialmente ne ho 0
059:
060: FILE *t = fopen(filename, "r");
061: if(!t)
062: {
063: perror("");
064: exit(EXIT_FAILURE);
065: }
066:
067: char tmp[1000]; // temporaneo per lettura nome citta' (lo uso anche per leggere e ignorare la prima riga)
068: char tmpc[3]; // temporaneo per lettura codice nazione (2 caratteri + 1 per fine stringa)
069: double lat, lon; // temporanei per lettura latitudine e longitudine
070:
071: // dobbiamo saltare la prima riga
072: fscanf(t, "%[^\n]", tmp);
073:
074: // fino a che riesco a leggere 4 campi continuo
075: while(fscanf(t, " %[^;];%[^;];%lg;%lg", tmp, tmpc, &lat, &lon) == 4) // i %[^;] permettono di leggere una stringa fino al primo ";" escluso
076: {
077: ++ncities; // ho letto quello che diventera' il prossimo elemento dell'array, incremento quindi il numero di elementi e
078: list = realloc(list, sizeof(*list) * ncities); // allargo l'array, l'ultimo elemento aggiunto (quello vuoto) avra' come indice ncities - 1
079: list[ncities - 1].lat = lat; // copio latitudine
080: list[ncities - 1].lon = lon; // copio longitudine
081: strcpy(list[ncities - 1].country, tmpc); // copio codice nazione
082: list[ncities - 1].name = malloc(sizeof(char) * (strlen(tmp) + 1)); // devo allocare spazio per il nome della citta'
083: strcpy(list[ncities - 1].name, tmp); // copio nome citta'
084: }
085: fclose(t); // chiudo file
086:
087: // ordino array per nome citta'
088: qsort(list, ncities, sizeof(struct city), compare);
089:
090: // copio dimensione array in indirizzo passato
091: *n = ncities;
092:
093: // restituisco indirizzo array
094: return list;
095: }
096:
097: // funzione di appoggio per la bsearch(), la chiave e' una semplice
098: // stringa (nome citta', primo argomento) devo confrontare con il
099: // nome della citta' contenuto nel secondo argomento che pero' e'
100: // l'indirizzo di una struct city
101: int compareb(const void *a, const void *b)
102: {
103: char *x = (char *)a;
104: struct city *y = (struct city *)b;
105:
106: return strcmp(x, y->name);
107: }
108:
109: // funzione di appoggio per la qsort()
110: // verranno passati due indirizzi di due
111: // elementi di un array di "struct city"
112: // il confronto lo faccio sull'elemento
113: // "name" di quegli elementi
114: int compare(const void *a, const void *b)
115: {
116: struct city *x = (struct city *)a;
117: struct city *y = (struct city *)b;
118:
119: return strcmp(x->name, y->name);
120: }
121:
122: // stampa in formato sessagesimale
123: void stampasessagimale(double a)
124: {
125: // stampiamo parte intera angolo (ci vorrebbe il relativo simbolo ma lo omettiamo in quanto non ASCII < 128)
126: printf("%d ", (int)a);
127:
128: double primi = fabs(a - (int)a) * 60;
129: // la parte frazionaria va riportata su scala 60 ovvero moltiplico per 60/100 e poi mi interessano le due cifre piu' significative, ovvero moltiplico per 100
130: // uso fabs() in quanto il segno l'ho stampato al giro precedente
131: printf("%d'", (int)primi);
132: double secondi = (primi - (int)primi)*60; // come sopra
133: printf("%d''\n", (int)(secondi + .5)); // nello stampare arrotondo
134:
135: }
136:
137: // funzione d'appoggio per convertire da gradi a radianti
138: double deg2rad(const double a)
139: {
140: return a*M_PI/180;
141: }
142:
143: // applica la formula presente nel testo (dopo aver convertito le coordinate geografiche in radianti)
144: double calcoladistanza(struct city *a, struct city *b) // passo per indirizzo per efficienza
145: {
146: double lat1 = deg2rad(a->lat);
147: double lat2 = deg2rad(b->lat);
148: double lon1 = deg2rad(a->lon);
149: double lon2 = deg2rad(b->lon);
150: return acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon2-lon1))*6371;
151: }
152:
153:
154: // salva dati in CSV come da testo, prende in ingresso:
155: // *list array di tutte le citta
156: // *ncities dimensione array
157: // *selected elemento da usare per il calcolo delle distanze
158: void dumpdata(struct city *list, int ncities, struct city *selected)
159: {
160:
161: FILE *t = fopen("dist.csv", "w");
162: if(!t)
163: {
164: perror("");
165: exit(EXIT_FAILURE);
166: }
167:
168: for(int i = 0; i < ncities; ++i)
169: fprintf(t, "%s;%g\n", list[i].name, calcoladistanza(&list[i], selected));
170:
171: fclose(t);
172:
173: }
174:
175: // chiede all'utente un nome citta' e lo ricerca nell'array
176: // che passo insieme alla sua dimensione
177: struct city *findcity(struct city *list, int nelem)
178: {
179: char tmp[1000];
180: printf("Inserire il nome di una citta': ");
181: scanf("%[^\n]", tmp);
182:
183: // mi appoggio banalmente alla bsearch() visto che l'array e' stato ordinato
184: return bsearch(tmp, list, nelem, sizeof(struct city), compareb);
185: }
186:
187:
188: