#Arrays - Puerto Rico Map ![main1.png](images/main1.png) ![main2.png](images/main2.png) Arrays help us store and work with groups of data of the same type. The data is stored in consecutive memory spaces which can be accessed by using the name of the array and indexes or subscripts that indicate the position where the data is stored. Repetition structures provide us a simple way of accessing the data within an array. An *object* is an entity that it is used in many programming languages to integrate the data and the code that operates on it, simplifying the modification of large programs. A common task in programming using C++ is working with arrays of objects. In today's laboratory experience you will be working with *georeferenced* data of cities in Puerto Rico, where you will have attributes, such as name of the city, the latitude and longitude of its location, that you will use to illustrate properties in a map. ##Objectives: 1. Dynamically create and manipulate an array of objects. 2. Code functions that process arrays of objects. 3. Practice passing arrays of objects as parameters in a function. 4. Practice the sequential reading of data from a file. 5. Use modular programming. 6. Use repetition and decision structures. ##Pre-Lab: Before coming to the laboratory you should have: 1. Reviewed the concepts related to arrays of objects. 2. Reviewed the concepts related to functions that use arrays of objects. 3. Reviewed how to read data from a file. 4. Studied the concepts and instructions for the laboratory session. 5. Taken the Pre-Lab quiz, available in Moodle. --- --- ##Georeferenced Data Working with arrays of objects is a very common task in programming with C++. Once you have read the object's information from a file or as user input, you should use your algorithmic skills and knowledge of C++ to invoke the adequate methods and functions to process the data correctly. In this laboratory experience, you will be working with *georeferenced* data of the cities in Puerto Rico. When data is *georeferenced* it simply means that it has an associated location in physical space. Typically this **location** is defined in latitude and longitude coordinates. For example, the following is part of a file that contains georeferenced data for some Puerto Rican 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 ``` **Figure 1.** Part of the content of a file with georeferenced data of cities in Puerto Rico; contains name of the city, coordinates for latitude and longitude. --- ###Orthodromic Distance To calculate the distance between two points in the Euclidean plane, you to trace the straight line segment that joins these points and compute its length using the distance formula you studied in your Pre-Calculus course. To calculate the distance between two points in the surface of a sphere you don't use the straight line segment that joins them, you use the shortest distance between these points measured over the sphere. This distance is called the *orthodromic distance*. To calculate the orthodromic distance between two points in the earth globe, you have to use the latitude and longitude coordinates. ###The `GPOI` Class The most common way for C++ programmers to encapsulate data to an entity is by using **classes**. For example, in the case of the georeferenced data, a practical way to encapsulate the information about each city would be to implement a `GeoreferencedPointOfInterest` class that contains at least data members (or attributes) 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 attributes. In this laboratory experience, you are provided a `GPOI` class with the following interface methods: * `GISPOI()`: the default constructor * `GISPOI(QString s, double latitude, double longitude)`: constructor that receives name, longitude and latitude. * `double getLat()`, `double getLon()`: getters for the latitude, 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`. --- --- !INCLUDE "../../eip-diagnostic/PRMap/en/diag-prmap-01.html"
!INCLUDE "../../eip-diagnostic/PRMap/en/diag-prmap-02.html"
!INCLUDE "../../eip-diagnostic/PRMap/en/diag-prmap-03.html"
--- --- ## Reading Data from Text Files in C++ This laboratory experience requires you to read data from a text file. You can skip to the next section if you feel that your file reading skills are competent. Otherwise, read on... C++ provides functions to read and write data to/from files. In this laboratory experience you will be using one of the most rudimentary file input/output schemes provided in C++ to read/write from **text** files. Text files consist exclusively of ASCII characters which represent data in any of the primitive types provided by C++. Typically, the values are separated by spaces. For instance let's assume that the file `nameAge.txt` contains some data about names and ages. ``` Tomas 34 Marta 55 Remigio 88 Andrea 43 ``` To **read** a text file in C++, we need to have a sense of how it is organized and what type of data you would like to read. The example `nameAge.txt` file contains four lines, each consisting of a string and an integer. Here is a simple program to read that file entirely while printing its content. Read the comments to understand the various parts. ``` #include // fstream is the header file that contains classes, functions and // objects to deal with file input and output. #include using namespace std; int main(){ // We shall use these two variables to assign the values read // from each line in the file. string name; int age; // This is the object that will represent the file. ifstream inFile; // We call the open function to open the input file `nameAge.txt` inFile.open("nameAge.txt"); // We check if the file was correctly opened if (!inFile.is_open()) { cout << "Error openning file nameAge.txt\n"; exit(1); } // While there is data in the file, read a string and an int. // Notice how the `>>` symbol is used, similar to when using cin while (inFile >> name >> age) { cout << name << " : " << age << endl; } // Close the file. inFile.close(); return 0; } ``` The `ifstream` object is used for reading a text file **sequentially**. It keeps track of the next position in the file that should be read. Each time that a data is read from the file (using `inFile >> ____`) it advances its position so that the next `inFile >> ___` reads the next data and so forth. Notice the line `inFile >> name >> age`. This instruction accomplishes several tasks: * It reads a `string` and an `int` from the file (if available) and assigns them to the variables `name` and `age`. * If both data were read, the expression evaluates to `true`, thus entering the while block. * If both data could not be read, the expression evaluates to `false` thus ending the while block. Here are some code snippets for common reading tasks. Observe that all of them: 1. Create an `ifstream` object, call the `open` function and check if the file is opened correctly. 2. Create one or more variables to assign the values that are read from the file. 3. Implement a loop which repeats until no more data is available in the file. 3. `close` the file at the end. **Example 1**: Read a file that consists only of integers, accumulate their values into a sum. ``` ifstream inFile; int n; int accum = 0; inFile.open("nums.txt"); if (!inFile.is_open()) { cout << "Error openning file nums.txt\n"; exit(1); } while (inFile >> n) { accum = accum + n; } cout << "Total: " << accum << endl; inFile.close(); ``` **Example 2**: Count the number of lines in a file that consists of names. Then choose the name at the center line. ``` ifstream inFile; string name; int ctr = 0; inFile.open("names.txt"); if (!inFile.is_open()) { cout << "Error openning file names.txt\n"; exit(1); } while (inFile >> name) { ctr++; } cout << "Total number of lines: " << ctr << endl; // These two commands "rewind" the file so that we can start // reading again from the beginning. inFile.clear(); inFile.seekg(0); for (int i = 0; i <= ctr / 2; i++) { inFile >> name; } cout << "The name at the position " << ctr / 2 << ": " << name << endl; inFile.close(); ``` --- --- ##Laboratory Session: ### Exercise 1 - Download and Understand the Code ####Instructions 1. Load the project `prMap` into `QtCreator`. There are two ways to do this: * Using the virtual machine: Double click the file `prMap.pro` located in the folder `/home/eip/labs/arrays-prmap` of your virtual machine. * Downloading the project’s folder from `Bitbucket`: Use a terminal and write the command `git clone http:/bitbucket.org/eip-uprrp/arrays-prmap` to download the folder `arrays-prmap` from `Bitbucket`. Double click the file `prMap.pro` located in the folder that you downloaded to your computer. 2. Compile and run the program. In its current state, the program simply displays a map of Puerto Rico. This map is provided so that you can visualize the results of your program. You may see some warnings which are due to the fact that some of the functions are incomplete. You will complete them throughout this laboratory experience. 3. 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 process. 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 you will implement.** 4. `void maxDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB)` : Given `A`, an array of cities, determines the farthest two cities. Remember that the distance you will calculate is the *orthodromic* distance. The function returns (by reference) the indices of these cities in the array. **This is a function you will implement.** 5. `void minDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB)`: Given `A`, an array of cities, determines the closest two cities. Remember that the distance you will compute is the *orthodromic* distance. The function returns (by reference) the indices of these cities in the array. **This is a function you will implement.** 6. `double cycleDistance(GISPOI A[], int size, int P[])`: Given an array of cities `A`, the size of the array, and an array `P` with a permutation of the integers in `[0, size-1]`, computes and returns the distance to travel the cycle of cities `A[P[0]]` $$\rightarrow$$ `A[P[1]]` $$\rightarrow \cdots \rightarrow$$ `A[P[size-1]]`. Remember that the distance you will calculate is the *orthodromic* distance. For example, if the cities read from the file where Mayagüez, Ponce, Yauco and San Juan (in that order) and the permutation `P` is $$(3, 1, 0, 2$$, the function should compute the distance of a cycle from San Juan $$\rightarrow$$ Ponce $$\rightarrow$$ Mayagüez $$\rightarrow$$ Yauco $$\rightarrow$$ San Juan. **This is a function you will implement.** There are two additional functions that you need to know: 1. `void MainWindow::drawLine(const GISPOI &city01, const GISPOI &city02)`: Given a reference to two `GISPOI` objects, the function draws 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 2 - 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 that you will use first is `pr10.txt` that is 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 successfully 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 if 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 3, 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. Invoke the method `drawPoints(GISPOI* gisLocations, unsigned int size)` on the `w` object so that a point will be shown in the map for each city: `w.drawPoints(A, size)` (*assuming that `A` is the name of your array*). You should obtain something similar to the next figure. ![main1.png](images/main1.png) ### Exercise 3 - Max and Min Functions Once you have the information of georeferenced cities in the array of objects, you 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 draw 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 draw a line connecting the two closest cities. ### Exercise 4 - 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: * First, with $$P = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9)$$ * Then, with $$P =(0, 3, 6, 9, 1, 4, 7, 2, 5, 8)$$ ### Exercise 5 - More Fun! 1. Change your code so that it now opens the `pr.txt` file. Validate your results and marvel at your great achievement! --- --- ## Deliverables Use "Deliverable" in Moodle to hand in the `main.cpp` file. Remember to use good programming techniques, include the name of the programmers involved, and document your program. --- --- ## References [1] https://en.wikipedia.org/wiki/Great-circle_distance