No Description

README.md 20KB

English

Arreglos de Objetos - PRMap

Los arreglos te permiten guardar y trabajar con varios datos del mismo tipo. Los datos se guardan en espacios de memoria consecutivos a los que se puede acceder utilizando el nombre del arreglo e índices o suscritos que indican la posición en que se encuentra el dato. Es fácil acceder a los elementos de un arreglo utilizando ciclos.

Una tarea bien común en programación usando C++ lo es el trabajar con arreglos de objetos. En la experiencia de laboratorio de hoy estarás trabajando con datos “georeferenciados” de pueblos en Puerto Rico en donde tendrás atributos como el nombre y la latitud y longitud de su localización que utilizarás para ilustrar propiedades en un mapa.

Objetivos:

  1. Crear dinámicamente y manipular un arreglo de objetos.
  2. Codificar funciones para procesar arreglos de objetos.
  3. Practicar el pasar arreglos de objetos como parámetros de una función.
  4. Practicar la lectura secuencial de datos en un archivo.
  5. Usar programación modular.
  6. Usar estructuras de repetición y control.

Pre-Lab:

Antes de llegar al laboratorio debes:

  1. haber repasado los conceptos relacionados a arreglos de objetos
  2. haber repasado los conceptos relacionados funciones que utilizan arreglos de objetos.

  3. haber repasado como leer datos de un archivo.

  4. haber estudiado los conceptos e instrucciones de la sesión de laboratorio.

  5. haber tomado el quiz Pre-Lab 12 (recuerda que el quiz Pre-Lab puede tener conceptos explicados en las instrucciones del laboratorio).



Datos “georeferenciados”

Trabajar con arreglos de objetos es una tarea bien común en la programación usando C++. Una vez has leido la información de los objetos en un archivo o provenientes de un usuario, debes depender de tus destrezas algorítmicas y conocimiento sobre C++ para invocar los métodos y funciones adecuadas para procesar los datos correctamente.

En esta experiencia de laboratorio estarás trabajando con datos “georeferenciados” sobre las ciudades en Puerto Rico. El que un dato sea “georeferenciados” quiere decir que el dato tiene una localización física asociada. Típicamente, esta localización son coordenadas de longitud y latitud. Por ejemplo, lo que sigue es parte de un archivo que contiene datos georeferenciados de pueblos en Puerto Rico:

Arroyo 17.9658 -66.0614
Bayamon 18.3833 -66.15
Caguas 18.2342 -66.0486
Dorado 18.4589 -66.2678
Fajardo 18.3258 -65.6525

Como viste en la clase, la manera más común para los programadores en C++ encapsular datos asociados a un ente es utilizando classes. Por ejemplo, en el caso de datos georeferenciados, una manera práctica de encapsular la información para cada pueblo sería implementando una clase GeoreferencedPointOfInterest que contenga al menos datos (o atributos) para: el nombre del pueblo, su longitud y su latitud. La clase GOPI también necesitará implementar métodos para acceder, modificar, y hacer operaciones en sus atributos.

La clase GPOI

En esta experiencia de laboratorio te proveemos una clase GPOI con los siguientes métodos de interfase:

  • GISPOI(): constructor por defecto

  • GISPOI(QString s, double latitude, double longitude): constructor que recibe nombre, longitud y latitud

  • double getLat(), double getLon(): “getters” para la latitud y longitud

  • QString getName(): “getter” para el nombre

  • void setAll(string s, double a, double b): “setter” para todas las propiedades (a la vez)

  • double odDistance(const GISPOI &B) const: dado otro objeto B de la clase GPOI, devuelve la distancia ortodrómica (orthodromic) (la distancia más corta) entre el GPOI que invoca y B.

Sesión de laboratorio:

Ejercicio 0 - Baja y entiende el código

Instrucciones

  1. Abre un terminal y escribe el comando git clone https://bitbucket.org/eip-uprrp/lab12-objecstarrays-prmap.git para descargar la carpeta Lab12-ObjectsArrays-prMap a tu computadora.

  2. Haz doble “click” en el archivo prMap.pro para cargar este proyecto a Qt y correrlo. En su estado actual, el programa solo despliega un mapa de Puerto Rico.

    Cuando el programa esté terminado necesitará que el usuario entre texto. Por lo tanto, debemos especificar en Qt Creator que este programa debe correr en un terminal, no en la ventana QtCreator. Para hacer esto, marca el botón de Projects en la parte de la izquierda de la ventana de QtCreator. Luego selecciona Run en la pestaña cerca de la parte de arriba de la ventana. Asegúrate de que la caja Run in terminal esté seleccionada.

  3. Haz “Build” del programa para que determines si quedan errores. Quizás ves algunas advertencias (“warnings”) debido a que hay algunas funciones que estean incompletas. Estarás completando estas funciones durante el laboratorio.

  4. Abre el archivo main.cpp. En este archivo es que estarás escribiendo tu código. El archivo contiene las siguientes funciones:

    1. void printArrayOfCities(GISPOI A[], int size): Dado un arreglo A de objetos de la clase GISPOI y su tamaño, imprime todos los pueblos en el arreglo. Puedes usar esta función como parte de tu proceso de depuración (“debugging”).

    2. int countLinesInFile(ifstream &file): Dada una referencia a un objeto que representa un archivo, esta función cuenta y devuelve el número de filas del archivo.

    3. void readFileToArray(ifstream &file, GISPOI A[], int numOfCities): Dado el objeto ifstream de un archivo, un arreglo de pueblos, y el número de registros para leer de un archivo, esta función lee los valores de un archivo y llena el arreglo con objetos. Esta es una función que tú implementarás.

    4. void maxDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB) : Dado un arreglo A de pueblos, determina los dos pueblos que quedan más lejos. La función devuelve (por referencia) los índices de estas ciudades en el arreglo. Esta es una función que tú implementarás.

    5. void minDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB): Dado un arreglo A de pueblos, determina los dos pueblos que quedan más cerca. La función devuelve (por referencia) los índices de estas ciudades en el arreglo. Esta es una función que tú implementarás.

    6. double cycleDistance(GISPOI A[], int size, int P[]): Dado un arreglo A de pueblos, el tamaño de este arreglo, y un arreglo P con una permutación de los enteros en [0,size-1], computa y devuelve la distancia de viajar el ciclo de pueblos A[P[0]] $\rightarrow$ A[P[1]] $\rightarrow \cdots \rightarrow$ A[P[size-1]].

      Por ejemplo, si los pueblos que se leen del archivo fueran Mayaguez, Ponce, Yauco y San Juan (en ese orden) y la permutación P es $\left(3, 1, 0, 2\right)$, la función debe computar la distancia del ciclo San Juan $\rightarrow$ Ponce $\rightarrow$ Mayaguez $\rightarrow$ Yauco $\rightarrow$ San Juan.

      Esta es una función que tú implementarás.

Hay otras dos funciones que debes conocer:

  1. void MainWindow::drawLine(const GISPOI &city01, const GISPOI &city02): Dada una referencia a dos objetos GISPOI, la función pinta una línea entre ellos.

  2. void drawPoints(GISPOI* gisLocations, unsigned int size);: Dado un arreglo de objetos GISPOI y su tamaño, despliega sus localizaciones como puntos en el mapa.

Ejercicio 1 - lee los puntos georeferenciados a un arreglo

Recuerda que solo estarás cambiando código en el archivo main.cpp. Tu primera tarea será añadir código para leer todo el contenido de un archivo a un arreglo de objetos GISPOI.

  1. En la función main(), añade las instrucciones necesarias para abrir el archivo que contiene la información del pueblo georeferenciado. El archivo está en el directorio data y es pr10.txt. Necesitas dar el path completo del archivo como parámetro del método open() de tu objeto ifstream. Como siempre, cuando uses archivos debes verificar si el nombre del archivo que pusiste se puede abrir para leer exitosamente.

  2. Invoca la función int countLinesInFile(ifstream &inFile) para obtener el número de líneas en el archivo. Puedes imprimir el número de líneas obtenido de modo que valides que tu programa está funcionando correctamente.

  3. Crea un arreglo dinámicamente tan grande como el número de líneas en tu programa.

  4. Modifica la función void readFileToArray(ifstream &file, GISPOI A[], int numOfCities) de modo que lea todas las líneas en el archivo a objetos en el arreglo.

  5. En la función main(), invoca la función readFileToArray, pasando la referencia al archivo, el arreglo que creaste en el paso 3, y su tamaño.

  6. Luego de invocar la función readFileToArray puedes invocar la función void printArrayOfCities(GISPOI A[], int size) para imprimir los nombres y georeferencias de los puntos leidos del archivo.

  7. Pídele al usuario el nombre del archivo de texto que contiene los datos con el siguiente formato: CityName Latitude Longitude. Algunos ejemplos de archivos son: cities.txt, calif.txt, y pr.txt.

  8. Invoca el método drawPoints(GISPOI* gisLocations, unsigned int size) en el objeto w de modo que se muestre un punto en el mapa para cada pueblo. La invocación debe hacerse de esta manera: w.drawPoints(A, size) (asumiendo que A es el nombre de tu arreglo). Debes obtener algo parecido a la siguiente figura:

Ejercicio 2 - las funciones max y min

Una vez que tengas la información de los pueblos georeferenciados en el arreglo de objetos, puedes comenzar a procesarlos de muchas formas interesantes. Comenzaremos con algunas operaciones básicas.

  1. Lee la documentación e implementa la la función void maxDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB). Invoca la función desde main().

  2. Usa el método void drawLine(const GISPOI &city01, const GISPOI &city02) del objeto w para dibujar una línea que conecte los pueblos más lejanos. Nota que el segundo y tercer parámetro de este método son referencias a los objetos que representan los pueblos (no sus índices en el arreglo).

  3. Lee la documentación e implementa la función void minDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB). Invoca la función desde main().

  4. Usa el método void drawLine(const GISPOI &city01, const GISPOI &city02) del objeto w para dibujar una línea que conecte los pueblos más cercanos.

Ejercicio 3 - computa la distancia del ciclo

  1. Lee la documentación e implementa la función double cycleDistance(GISPOI A[], int size, int P[]). Invoca la función desde main() como se indica en los comentarios dentro de la función main():

    1. Primero con $P = \left(0, 2, 4, 6, 8, 1, 3, 5, 7, 9\right)$,
    2. luego con = \left(0, 3, 6, 9, 1, 4, 7, 2, 5, 8\right)$.

Ejercicio 3 - ¡más diversión!

Cambia tu código de modo que ahora abra el archivo pr.txt. Valida tus resultados y ¡maravíllate de tu gran logro!

Entregas:

  1. Entrega el archivo main.cpp utilizando este enlace a Moodle. Recuerda comentar adecuadamente las funciones, usar indentación adecuada y buenas prácticas para darle nombre a las variables.

English

Arrays of Objects - PRMap

Objectives

Throughout this exercise the students will practice:

  • reading a text file sequentially
  • repetition and control structures
  • creating and manipulating an array of objects
  • writing functions to process arrays of objects
  • passing arrays of objects as parameters
  • modular programming

Concepts

Working with arrays of objects is a very common task in C++ programs. Once you have read information about the objects from a file and/or the user, you must relly on your algorithmic and C++ skills to invoke the proper methods and functions in order to correctly process the data.

In this lab, we will be working with georeferenced data about the cities in Puerto Rico. When a data is georeferenced* it simply means that it has an associated location in pyhsical space. Typically this location means latitude and longitude coordinates. For example, the following is part of file that contains georeferenced data for some Puertorrican cities:

Arroyo 17.9658 -66.0614
Bayamon 18.3833 -66.15
Caguas 18.2342 -66.0486
Dorado 18.4589 -66.2678
Fajardo 18.3258 -65.6525

As you saw in class, classes are the most common way that C++ programmers encapsulate data that is associated to an entity. For example, in the case of the georeferenced points, a practical way to encapsulate the information about each city would be to implement a GeoreferencedPointOfInterest class that contains at least data members for: the name of the city, its latitude and longitude. The GPOI class would also need to implement methods to access, modify, and perform computations on its properties.

The GPOI class

In this lab, you are provided a GPOI class with the following interface:

  • GISPOI(): the default constructor

  • GISPOI(QString s, double latitude, double longitude): constructor that accepts name, longitude and latitude.

  • double getLat(), double getLon(): getters for the latutide, longitude.

  • QString getName(): getter for the name.

  • void setAll(string s, double a, double b): setter for all the properties (at once)

  • double odDistance(const GISPOI &B) const: given B, another GPOI object, returns the orthodromic distance (or the closest distance) between the invoking GPOI and B.

Exercise 0 - Download and understand the code

  1. Download _______.

  2. Open the .pro file. Build the program and run it. In its current state, the program simply displays a map of Puerto Rico. This map is provided so that you can viualize the results of your program.

    When you complete this program, it will need some text input from the user. Thus, we need to specify in Qt Creator that we expect this program to run in a terminal, i.e. not on the QtCreator window. To do this, please click of the Projects button on the left part of the QtCreator window. Then choose Run on the tab near the top of the window. Then make sure that the checkbox Run in terminal is checked.

  3. Build the program just to determine if there are any outstanding errors. You may see some warnings which are due to the fact that some of the functions are incomplete (you will complete throughout this lab.)

  4. Open the main.cpp file. This is the file where you will be writing your code. This file contains the following functions:

    1. void printArrayOfCities(GISPOI A[], int size): Given A, an array of GISPOI objects and its size, prints all the cities in the array. You may use this function as part of your debugging.

    2. int countLinesInFile(ifstream &file): Given a reference to the object that represents a file, this function counts and returns the number of lines in the file.

    3. void readFileToArray(ifstream &file, GISPOI A[], int numOfCities): Given the ifstream object of a file, an array of cities and the number of records to read from the file, this function reads the values from the file and populates the array with objects. THIS IS A FUNCTION THAT YOU WILL IMPLEMENT

    4. void maxDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB) : Given A, the array of cities, determines the farthest two cities. Returns (by reference) the indices of the two cities in the array. THIS IS A FUNCTION THAT YOU WILL IMPLEMENT

    5. void minDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB): Given A, the array of cities, determines the closest two cities. Returns (by reference) the indices of the two cities in the array. THIS IS A FUNCTION THAT YOU WILL IMPLEMENT

    6. double cycleDistance(GISPOI A[], int size, int P[]): Given the array of cities, the size of the array and a array (P) with a permutation of the integers [0, size-1] computes and returns the distance to travel the cycle of cities A[P[0]] $\rightarrow$ A[P[1]] $\rightarrow$ …. $\rightarrow$ A[P[size-1]].

      For example, if the cities read from the file where Mayaguez, Ponce, Yauco and San Juan (in that order) and the permutation $P$ is $left(3, 1, 0, 2\right)$, the function should compute the distance of a cycle from San Juan $\rightarrow$ Ponce $\rightarrow$ Mayaguez $\rightarrow$ Yauco $\rightarrow$ San Juan.

      THIS IS A FUNCTION THAT YOU WILL IMPLEMENT

There is one additional function that you need to know:

  1. void MainWindow::drawLine(const GISPOI &city01, const GISPOI &city02): Given (a reference to) two GISPOI objects, paints a line between them.

  2. void drawPoints(GISPOI* gisLocations, unsigned int size);: Given an array of GISPOI objects and their size, displays their locations as points in the map.

Exercise 1 - read the georeferenced points into an array

Remember that you will only be changing code in the main.cpp file. Your first task will be to add code to read the entire contents of a file into an array of GISPOI objects.

  1. In the main function, add the necessary instructions to open the file that contains the georeferenced city information. The file is pr10.txt in the data directory. You need to provide the complete path to the file as a parameter to the open() method of your ifstream object. As always, when using files you should verify if the entered name is a file that can be succefully opened for reading.

  2. Invoke the int countLinesInFile(ifstream &inFile) function to obtain the number of lines in the file. You may print out the number obtained so that you can validate is your program is working correctly.

  3. Dynamically create an array as big as the number of lines in the file.

  4. Modify the void readFileToArray(ifstream &file, GISPOI A[], int numOfCities) function so that it reads all the lines in the file to the objects in the array.

  5. In the main function invoke the readFileToArray function, passing the reference to the file, the array you created in step ???, and its size.

  6. After invoking readFileToArray you may invoke void printArrayOfCities(GISPOI A[], int size) to print the names and georeferences of the points read from the file.

  7. Ask the user for the name of the text file that contains data in the following format: CityName Latitude Longitude. Here are example files: cities.txt, calif.txt, and pr.txt.

  8. Invoke the method drawPoints(GISPOI* gisLocations, unsigned int size) on the w object as follows so that a point will be shown in the map for each city: w.drawPoints(A, size) (assuming that A is your array). You should obtain something similar to the next figure.

Exercise 2 - the max and min functions

Once we have the information of georeferenced cities in an array of objects, we can start processing them in many interesting ways. We will start with some basic operations.

  1. Read the documentation and implement the function void maxDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB). Invoke the function from main().

  2. Use the void drawLine(const GISPOI &city01, const GISPOI &city02) method of the w object to paint a line connecting the two farthest cities. Notice that the second and third parameters of this method are ** references to the objects that represent the cities** (not their indices in the array).

  3. Read the documentation and implement the function void minDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB). Invoke the function from main().

  4. Use the void drawLine(const GISPOI &city01, const GISPOI &city02) method of the w object to paint a line connecting the two closest cities.

Exercise 3 - compute the cycle distance

  1. Read the documentation and implement the function double cycleDistance(GISPOI A[], int size, int P[]). Invoke the function from main() as indicated in the comments inside the main function:

    1. First with $P = \left(0, 2, 4, 6, 8, 1, 3, 5, 7, 9\right)$
    2. Then with $P = \left(0, 3, 6, 9, 1, 4, 7, 2, 5, 8\right)$

Exercise 4 - more fun

  1. Change your code so that it now opens the pr.txt file. Validate your results and wonder at your great achievement.

Deliverables