Arrays - Puerto Rico Map
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:
Dynamically create and manipulate an array of objects.
Code functions that process arrays of objects.
Practice passing arrays of objects as parameters in a function.
Practice the sequential reading of data from a file.
Use modular programming.
Use repetition and decision structures.
Pre-Lab:
Before coming to the laboratory you should have:
Reviewed the concepts related to arrays of objects.
Reviewed the concepts related to functions that use arrays of objects.
Reviewed how to read data from a file.
Studied the concepts and instructions for the laboratory session.
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 constructorGISPOI(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
: givenB
, anotherGPOI
object, returns the orthodromic distance (or the closest distance) between the invokingGPOI
andB
.
string
objects as follows: string S[5]
and we assign the string "chicken"
to each object. Which of the following would change the object at index 3 to "chickan"
?
S[4] = "a";
S[3][4] = 'a';
S.at(3) = "a";
S.replace(3,4, 'a');
S[3][5] = 'a'
. This instruction performs the following task: *change the letter at index 5 of object with index 3 to 'a'
.
The option S[3] = "a"
changes the whole content of object with index 3 to "a"
The intention of S.at(3) = "a"
is changing the content of the object with index 3 to "a"
. However, the instruction is not valid because the result of the at()
method is a string constant, whose value cannot be changed.
The option S.replace(3,4,'a');
is not valid because the replace()
method works on strings, not on arrays.
string
objects as follows: string S[10]
and that we assign strings to each of the objects. Which of the following prints the length of element 7 of array S
?
cout << S[7].length();
cout << S.length()[7];
cout << S.length(7);
cout << S.at(7).length();
cout << S[7].length();
, cout << S.at(7).length();
. In both, the length()
method is preceded by an expression can be evaluated to element 7 of array S. Both S.at(7)
and S[7]
are expressions that refer to element 7.
The option cout << S.length()[7];
is invalid because it is invoking the method length()
on an array of strings. The length()
method can be invoked on any string
object but not an array of objects.
The option cout << S.length(7)
is invalid because it is trying to invoke the length()
method on an array of strings. Furthermore, the length()
method does not require arguments.
QPoint
objects as follows: QPoint P[7];
and that we assign the x
and y
values to the objects. Which of the following can be use to compute the sum of the y coordinates of all points?
The alpha option includes a P.y()
in the expression inside the loop. P.y()
is not a valid instruction because P
is an array, not a QPoint
object, thus getter y()
cannot be invoked on P
.
The gamma option contains a[i]
in the expression that is inside the loop. a[i]
is not valid because a
is not array, it is an int
.
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 <iostream>
// fstream is the header file that contains classes, functions and
// objects to deal with file input and output.
#include <fstream>
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 anint
from the file (if available) and assigns them to the variablesname
andage
. - 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:
- Create an
ifstream
object, call theopen
function and check if the file is opened correctly. - Create one or more variables to assign the values that are read from the file.
- Implement a loop which repeats until no more data is available in the file.
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
Load the project
prMap
intoQtCreator
. 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 commandgit clone http:/bitbucket.org/eip-uprrp/arrays-prmap
to download the folderarrays-prmap
fromBitbucket
. Double click the fileprMap.pro
located in the folder that you downloaded to your computer.
- Using the virtual machine: Double click the file
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.
Open the
main.cpp
file. This is the file where you will be writing your code. This file contains the following functions:void printArrayOfCities(GISPOI A[], int size)
: GivenA
, an array ofGISPOI
objects and its size, prints all the cities in the array. You may use this function as part of your debugging process.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.void readFileToArray(ifstream &file, GISPOI A[], int numOfCities)
: Given theifstream
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.void maxDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB)
: GivenA
, 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.void minDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB)
: GivenA
, 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.double cycleDistance(GISPOI A[], int size, int P[])
: Given an array of citiesA
, the size of the array, and an arrayP
with a permutation of the integers in[0, size-1]
, computes and returns the distance to travel the cycle of citiesA[P[0]]
A[P[1]]
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 , the function should compute the distance of a cycle from San Juan Ponce Mayag�ez Yauco San Juan. This is a function you will implement.
There are two additional functions that you need to know:
void MainWindow::drawLine(const GISPOI &city01, const GISPOI &city02)
: Given a reference to twoGISPOI
objects, the function draws a line between them.void drawPoints(GISPOI* gisLocations, unsigned int size);
: Given an array ofGISPOI
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.
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 ispr10.txt
that is in thedata
directory. You need to provide the completepath
to the file as a parameter to theopen()
method of yourifstream
object. As always, when using files you should verify if the entered name is a file that can be successfully opened for reading.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.Dynamically create an array as big as the number of lines in the file.
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.In the
main()
function invoke thereadFileToArray
function, passing the reference to the file, the array you created in step 3, and its size.After invoking
readFileToArray
you may invokevoid printArrayOfCities(GISPOI A[], int size)
to print the names and georeferences of the points read from the file.Invoke the method
drawPoints(GISPOI* gisLocations, unsigned int size)
on thew
object so that a point will be shown in the map for each city:w.drawPoints(A, size)
(assuming thatA
is the name of your array). You should obtain something similar to the next figure.
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.
Read the documentation and implement the function
void maxDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB)
. Invoke the function frommain()
.Use the
void drawLine(const GISPOI &city01, const GISPOI &city02)
method of thew
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).Read the documentation and implement the function
void minDistances(GISPOI A[], int size, int &idxCityA, int &idxCityB)
. Invoke the function frommain()
.Use the
void drawLine(const GISPOI &city01, const GISPOI &city02)
method of thew
object to draw a line connecting the two closest cities.
Exercise 4 - Compute the Cycle Distance
Read the documentation and implement the function
double cycleDistance(GISPOI A[], int size, int P[])
. Invoke the function frommain()
as indicated in the comments inside themain()
function:- First, with
- Then, with
Exercise 5 - More Fun!
- 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.