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: