1 #include "menu.h" 2 3 #include <iostream> 4 #include <fstream> 5 #include <string> 6 #include <sstream> 7 #include <cstdlib> 8 #include <vector> 9 #include <cmath> 10 #include <iomanip> 11 #include <cctype> 12 13 void Menu::imprimirCantidadDeParadasPorBarrio() { 14 Barrio *barrio; 15 16 std::cout << std::endl; 17 std::cout << std::setw(10); 18 std::cout.fill(' '); 19 20 std::cout << std::left << std::setw(18) << std::left << "Barrio"; 21 std::cout << std::right << std::setw(20) << "Cantidad de Paradas" << std::endl; 22 std::cout.fill('='); 23 std::cout << std::left << std::setw(38) << std::left << "=" << std::endl; 24 25 std::cout.fill('.'); 26 this->barrios->startCursor(); 27 while(this->barrios->forwardCursor()) { 28 barrio = this->barrios->getCursorData(); 29 std::cout << std::left << std::setw(18) << barrio->getNombre() 30 << std::right << std::setw(20) << barrio->getSizeListaDeParadas() 31 << std::endl; 32 } 33 34 std::cout << std::endl; 35 } 36 37 Parada *Menu::paradaMasCercanaPorCoordenada(double coordX, double coordY) { 38 double distanciaMin; 39 double distancia; 40 41 Parada *res, *parada; 42 Barrio *barrio; 43 44 res = NULL; 45 this->barrios->startCursor(); 46 while(this->barrios->forwardCursor()) { 47 barrio = this->barrios->getCursorData(); 48 49 if(res == NULL) { 50 res = barrio->paradaMasCercana(coordX, coordY); 51 distanciaMin = getDistancia(coordX, coordY, res->getCoordX(), res->getCoordY()); 52 } 53 else { 54 parada = barrio->paradaMasCercana(coordX, coordY); 55 distancia = getDistancia(coordX, coordY, parada->getCoordX(), parada->getCoordY()); 56 57 if(distanciaMin > distancia) { 58 distanciaMin = distancia; 59 res = parada; 60 } 61 } 62 } 63 64 return res; 65 } 66 67 void Menu::agregarElementoSinRepetir(std::vector<int> *lineas, std::vector<int> *vectorLineasAux) { 68 size_t lineaTope = vectorLineasAux->size(); 69 70 for(size_t i = 0; i < lineaTope; i++) { 71 bool seRepite = false; 72 for(size_t j = 0; j < lineas->size(); j++) { 73 if((*vectorLineasAux)[i] == (*lineas)[j]) { 74 seRepite = true; 75 } 76 } 77 if(seRepite == false) { 78 lineas->push_back((*vectorLineasAux)[i]); 79 } 80 } 81 } 82 83 void Menu::lineasPorParada(List<Parada*> *paradas) { 84 Parada *paradaAux; 85 std::vector<int> *vectorLineasAux; 86 87 paradas->startCursor(); 88 while(paradas->forwardCursor()){ 89 paradaAux = paradas->getCursorData(); 90 91 vectorLineasAux = paradaAux->getLineas(); 92 agregarElementoSinRepetir(this->lineas, vectorLineasAux); 93 } 94 } 95 96 void Menu::cantidadDeParadasPorLinea() { 97 Barrio *barrio; 98 99 this->lineas->clear(); 100 this->barrios->startCursor(); 101 102 while(this->barrios->forwardCursor()){ 103 barrio = this->barrios->getCursorData(); 104 105 lineasPorParada(barrio->getParadas()); 106 } 107 } 108 109 Menu::Menu() { 110 this->barrios = new List<Barrio*>; 111 this->lineas = new std::vector<int>; 112 } 113 114 std::string strToLowercase(std::string str) { 115 std::string res; 116 for(size_t i = 0; i < str.size(); ++i) { 117 res += std::tolower(str[i]); 118 } 119 120 return res; 121 } 122 123 void delSurroundingChar(std::string &str, char c) { 124 str.erase(0, 1); 125 str[str.size() - 1] = '\0'; 126 } 127 128 size_t getTokens(std::string line, std::vector<std::string> &tokens) { 129 std::string token; 130 std::stringstream lineStream; 131 size_t field; 132 133 lineStream.clear(); 134 lineStream.str(line); 135 token.clear(); 136 137 for(field = CALLE; std::getline(lineStream, token, CSV_DELIM); ++field) { 138 // solucion a `token` es una cadena que contiene CSV_DELIM 139 while((token[0] == '"') && (token[token.size() - 1] != '"')) { 140 std::string nextToken; 141 std::getline(lineStream, nextToken, CSV_DELIM); 142 token += CSV_DELIM + nextToken; 143 } 144 if(token[0] == '"' && token[token.size() - 1] == '"') { 145 delSurroundingChar(token, '"'); 146 } 147 tokens.push_back(token); 148 } 149 150 return field; 151 } 152 153 List<Parada*> *Menu::buscarParadas(List<Barrio*> *barrios, int linea) { 154 List<Parada*> *resultado, *paradasPorBarrio; 155 Barrio *barrio; 156 157 resultado = new List<Parada*>; 158 159 barrios->startCursor(); 160 while(barrios->forwardCursor()) { 161 barrio = barrios->getCursorData(); 162 paradasPorBarrio = barrio->listaDeParadasPorLinea(linea); 163 if(paradasPorBarrio == NULL) { 164 // no hay paradas de la linea 165 continue; 166 } 167 168 paradasPorBarrio->startCursor(); 169 while(paradasPorBarrio->forwardCursor()) { 170 resultado->insert(paradasPorBarrio->getCursorData()); 171 } 172 173 delete paradasPorBarrio; 174 } 175 176 if(resultado->getSize() == 0) { 177 delete resultado; 178 return NULL; 179 } 180 181 return resultado; 182 } 183 184 // busca el barrio en la lista de barrios, si no lo encuentra devuelve NULL 185 Barrio *Menu::getBarrioPorNombre(std::string nombre) { 186 Barrio *res, *barrio; 187 188 res = NULL; 189 this->barrios->startCursor(); 190 191 while(this->barrios->forwardCursor()) { 192 barrio = this->barrios->getCursorData(); 193 if(strToLowercase(barrio->getNombre()) == strToLowercase(nombre)) { 194 res = this->barrios->getCursorData(); 195 break; 196 } 197 } 198 199 return res; 200 } 201 202 double gradosARadianes(double grados) { 203 return grados * M_PI / 180.0; 204 } 205 206 // lat -> y; lon -> x; 207 // calcula la distancia en km entre dos coordenadas usando la formula de Lambert 208 double getDistanciaEnKilometros(double x1, double y1, double x2, double y2) { 209 const double R = 6371.0; // radio de la tierra 210 211 double x1Rad = gradosARadianes(x1); 212 double y1Rad = gradosARadianes(y1); 213 double x2Rad = gradosARadianes(x2); 214 double y2Rad = gradosARadianes(y2); 215 216 double dLon = x2Rad - x1Rad; 217 double dLat = y2Rad - y1Rad; 218 219 double a = sin(dLat / 2) * sin(dLat / 2) + cos(y1Rad) * cos(y2Rad) * sin(dLon / 2) * sin(dLon / 2); 220 double c = 2 * atan2(sqrt(a), sqrt(1 - a)); 221 double distancia = R * c; 222 223 return distancia; 224 } 225 226 double Menu::getDistancia(double x1, double y1, double x2, double y2) { 227 double dX = (x2 - x1); 228 double dY = (y2 - y1); 229 230 return std::sqrt(dX*dX + dY*dY); 231 } 232 233 void ordenarParadasPorDistanciaACoordenada(List<Parada*> *paradas, double x, double y) { 234 Node<Parada*> *actual, *min, *sig; 235 Parada *tmp; 236 237 double distMin, distSig; 238 239 paradas->startCursor(); 240 while(paradas->forwardCursor()) { 241 actual = paradas->getCursor(); 242 min = actual; 243 244 distMin = getDistanciaEnKilometros(min->getData()->getCoordX(), min->getData()->getCoordY(), x, y); 245 246 sig = actual->getNext(); 247 while (sig != NULL) { 248 distSig = getDistanciaEnKilometros(sig->getData()->getCoordX(), sig->getData()->getCoordY(), x, y); 249 250 if (distSig < distMin) { 251 min = sig; 252 distMin = distSig; 253 } 254 sig = sig->getNext(); 255 } 256 257 if (min != actual) { 258 tmp = actual->getData(); 259 actual->setData(min->getData()); 260 min->setData(tmp); 261 } 262 } 263 } 264 265 int Menu::cargarDatos() { 266 std::ifstream inputFile; 267 std::string token, line; 268 std::vector<std::string> tokens; 269 std::vector<int> lineasEnParada; 270 float coordX, coordY; 271 Barrio *barrio; 272 273 const char *inputFilePath = "paradas-de-colectivo.csv"; 274 275 int fields, linea; 276 std::string barrioNombre, calle, direccion; 277 278 inputFile.open(inputFilePath); 279 if(!inputFile.is_open()) { 280 std::cout << "No se pudo abrir el archivo `" << inputFilePath << "`\n"; 281 return -1; 282 } 283 284 coordX = coordY = 0.0f; 285 286 std::getline(inputFile, line); // saltea la primer linea 287 288 while(std::getline(inputFile, line)) { 289 tokens.clear(); 290 lineasEnParada.clear(); 291 fields = getTokens(line, tokens); 292 293 for(int field = CALLE; field <= fields; ++field) { 294 token = tokens[field]; 295 switch(field) { 296 case CALLE: 297 calle = token; 298 break; 299 case ALT_PLANO: 300 break; 301 case DIRECCION: 302 if(token == "") { 303 break; 304 } 305 direccion = token; 306 break; 307 case COORD_X: 308 coordX = std::atof(token.c_str()); 309 break; 310 case COORD_Y: 311 coordY = std::atof(token.c_str()); 312 break; 313 case COMUNA: 314 break; 315 case BARRIO: 316 barrioNombre = token; 317 if(barrioNombre == "") { 318 barrioNombre = "NOMBRE SIN DEFINIR"; 319 } 320 break; 321 case LINEA_1: 322 case LINEA_2: 323 case LINEA_3: 324 case LINEA_4: 325 case LINEA_5: 326 case LINEA_6: 327 if(token == "") { 328 break; 329 } 330 linea = std::atoi(token.c_str()); 331 lineasEnParada.push_back(linea); 332 break; 333 case LINEA_1_SENTIDO: 334 case LINEA_2_SENTIDO: 335 case LINEA_3_SENTIDO: 336 case LINEA_4_SENTIDO: 337 case LINEA_5_SENTIDO: 338 case LINEA_6_SENTIDO: 339 break; 340 default: 341 break; 342 } 343 } 344 345 barrio = getBarrioPorNombre(barrioNombre); 346 if(barrio == NULL) { 347 barrio = new Barrio(barrioNombre); 348 barrio->addParada(calle, direccion, coordX, coordY, lineasEnParada); 349 barrios->insert(barrio); 350 } else { 351 // barrio ya existe 352 barrio->addParada(calle, direccion, coordX, coordY, lineasEnParada); 353 } 354 } 355 356 inputFile.close(); 357 return 0; 358 } 359 360 void Menu::mostrarMenu() { 361 bool terminarPrograma = false; 362 int linea; 363 double lat, lon; // lat -> Y; lon -> X; 364 double distanciaEnKm; 365 366 std::string opcion, barrioNombre, lineaAImprimir, distanciaUnidad; 367 List<Parada*> *paradas, *paradasDeLaLinea; 368 Parada *actual; 369 Barrio *barrio; 370 371 while(!terminarPrograma) { 372 std::cout << "`1` Cantidad de paradas por barrio\n"; 373 std::cout << "`2` Parada mas cercana a una coordenada\n"; 374 std::cout << "`3` Listado de paradas de una linea de colectivo\n"; 375 std::cout << "`4` Listado de cantidad de paradas por linea de colectivo\n"; 376 std::cout << "`5` Listado de paradas de una linea mas cercano a una coordenada\n"; 377 std::cout << "`b` Imprimir barrios\n"; 378 std::cout << "`q` Salir\n"; 379 std::cout << "opcion> "; 380 381 opcion.clear(); 382 std::cin >> opcion; 383 384 if(opcion[0] == '\n') { 385 continue; 386 } 387 switch(opcion[0]) { 388 case 'b': 389 this->barrios->startCursor(); 390 while(this->barrios->forwardCursor()) { 391 std::cout << "`" << this->barrios->getCursorData()->getNombre() << "`\n"; 392 } 393 std::cout << std::endl; 394 break; 395 case '1': 396 imprimirCantidadDeParadasPorBarrio(); 397 break; 398 case '2': 399 // TODO: verificar que `latitud` y `longitud` sean validas 400 std::cin.clear(); 401 std::cout << "latitud> "; 402 std::cin >> lat; 403 404 std::cin.clear(); 405 std::cout << "longitud> "; 406 std::cin >> lon; 407 408 // std::cout << "latitud: " << lat << "; longitud: " << lon << "\n"; 409 410 Parada *parada; 411 parada = paradaMasCercanaPorCoordenada(lon, lat); 412 if(parada == NULL) { 413 std::cout << "Parada inaccessible"; 414 break; 415 } 416 417 distanciaEnKm = getDistanciaEnKilometros(lon, lat, parada->getCoordX(), parada->getCoordY()); 418 419 if(distanciaEnKm < 1) { 420 distanciaEnKm *= 1000.0f; 421 distanciaUnidad.assign("metros"); 422 } else { 423 distanciaUnidad.assign("kilometros"); 424 } 425 426 std::cout << "Parada mas cercana: `" << parada->getDireccion() 427 << "` (" << parada->getCoordX() << ", " << parada->getCoordY() 428 << ") a " << std::fixed << std::setprecision(2) << distanciaEnKm 429 << " " << distanciaUnidad << "\n"; 430 break; 431 case '3': 432 // TODO: verificar que `linea` sea valida 433 434 std::cin.clear(); 435 std::cout << "linea> "; 436 std::cin >> linea; 437 438 paradas = buscarParadas(barrios, linea); 439 if(paradas == NULL) { 440 std::cout << "no hay paradas de la linea indicada\n"; 441 break; 442 } 443 std::cout << "La linea `" << linea << "` tiene `" 444 << paradas->getSize() << "` paradas en CABA\n"; 445 446 paradas->startCursor(); 447 while(paradas->forwardCursor()) { 448 std::cout << " ยท " << paradas->getCursorData()->getDireccion() << "\n"; 449 } 450 delete paradas; 451 break; 452 case '4': 453 cantidadDeParadasPorLinea(); 454 455 std::cout << std::endl; 456 std::cout << std::setw(10); 457 std::cout.fill(' '); 458 459 std::cout << std::left << std::setw(18) << std::left << "Linea"; 460 std::cout << std::right << std::setw(20) << "Cantidad de Paradas" << std::endl; 461 std::cout.fill('='); 462 463 std::cout << std::left << std::setw(38) << std::left << "=" << std::endl; 464 std::cout.fill('.'); 465 466 paradas = NULL; 467 for(size_t i = 0; i < this->lineas->size(); ++i) { 468 paradas = buscarParadas(barrios, (*this->lineas)[i]); 469 if(paradas == NULL) { 470 std::cout << "no hay paradas de la linea indicada\n"; 471 continue; 472 } 473 474 std::ostringstream sstream; 475 sstream << "Linea " << (*this->lineas)[i]; 476 lineaAImprimir = sstream.str(); 477 478 std::cout << std::left << std::setw(18) << lineaAImprimir 479 << std::right << std::setw(20) << paradas->getSize() 480 << std::endl; 481 } 482 483 std::cout << std::endl; 484 if(paradas != NULL) { 485 delete paradas; 486 } 487 break; 488 case '5': 489 std::cout << "barrio> "; 490 std::cin.ignore(1); 491 std::getline(std::cin, barrioNombre, '\n'); 492 493 std::cin.clear(); 494 std::cout << "linea> "; 495 std::cin >> linea; 496 497 // TODO: verificar que `latitud` y `longitud` sean validas 498 std::cin.clear(); 499 std::cout << "latitud> "; 500 std::cin >> lat; 501 502 std::cin.clear(); 503 std::cout << "longitud> "; 504 std::cin >> lon; 505 506 if((barrio = getBarrioPorNombre(barrioNombre)) == NULL) { 507 std::cout << "No se encontro el barrio `" << barrioNombre << "`\n"; 508 break; 509 } 510 511 paradasDeLaLinea = barrio->listaDeParadasPorLinea(linea); 512 if(paradasDeLaLinea == NULL) { 513 std::cout << "La linea `" << linea 514 << "` no tiene paradas en el barrio `" << barrioNombre << "`\n"; 515 break; 516 } 517 518 // ordenar de mayor a menor distancia a `lat, lon` la lista `paradasDeLaLinea` 519 ordenarParadasPorDistanciaACoordenada(paradasDeLaLinea, lon, lat); 520 521 std::cout << std::endl; 522 std::cout.fill(' '); 523 std::cout << std::left << std::setw(18) << std::left << "Direccion" 524 << std::right << std::setw(35) << "Distancia en Kilometros" 525 << std::right << std::setw(30) << "Coordenadas (lat, lon)" << std::endl; 526 std::cout.fill('='); 527 std::cout << std::left << std::setw(83) << std::left << "=" << std::endl; 528 529 paradasDeLaLinea->startCursor(); 530 while(paradasDeLaLinea->forwardCursor()) { 531 actual = paradasDeLaLinea->getCursorData(); 532 distanciaEnKm = getDistanciaEnKilometros(lon, lat, actual->getCoordY(), actual->getCoordX()); 533 534 std::string coord; 535 coord += actual->getCoordX(); 536 537 std::cout.fill('.'); 538 std::cout << std::left << std::setw(33) << actual->getDireccion() 539 << std::right << std::setw(20) 540 << std::fixed << std::setprecision(2) 541 << distanciaEnKm; 542 543 std::cout.fill('.'); 544 std::cout << std::right << std::setw(10) << std::setprecision(4) 545 << "(" << actual->getCoordX(); 546 547 std::cout.fill(' '); 548 std::cout << "," << std::right << std::setw(10) << std::setprecision(4) 549 << actual->getCoordY() << ")" << std::endl; 550 } 551 std::cout << std::endl; 552 553 delete paradasDeLaLinea; 554 break; 555 case 'q': 556 terminarPrograma = true; 557 break; 558 default: 559 std::cout << "opcion `" << opcion[0] << "` no definida\n"; 560 break; 561 } 562 } 563 } 564 565 Menu::~Menu() { 566 Barrio *delBarrio; 567 568 this->barrios->startCursor(); 569 while(this->barrios->forwardCursor()){ 570 delBarrio = this->barrios->getCursorData(); 571 delete delBarrio; 572 } 573 574 delete this->barrios; 575 delete this->lineas; 576 }
