The Big Picture

Here is how we would break down the AM3 app:

  1. User input to provide some initial information, in this case names for their hometown and other places they have visited. We usually need some HTML interface elements (e.g., input, textarea, button, etc.) to provide the way for user to enter it, as well as some Javascript/jQuery code to collect the information and user it further.
  2. API Requests to gather data from the APIs the app makes use of. In this case the Wikipedia API and the Google Maps API. The Wikipedia API is called for each city to provide the image and the first three sentences of the Wiki article about it. The Google Maps API is used to find the geo-coordinates of every city (in order to put the marker on the map), as well as to calculate the distance between two cities.
  3. Data processing and storage to clean up the data from the API response and store the information on our app in a way that is easy to use and reuse (e.g., clicking again on an info window, shouldn't send a new request to Wikipedia).
  4. Information representation to take the raw data and turn them into meaningful and visually appealing elements on the page: markers, info window with text + images, distances, etc.
  5. Animation features to entertain the user by taking them to a journey in the form of a slideshow of places they have visited.

User Input

The interface for gathering user input has only three elements: input, textarea and a button. All of them were styled with CSS to change their appearance to fit the page look. For the purposes of building the app, you don't need to deal with the CSS at this point.

The elements are showing step-by-step on the page, because they are hidden with the property display : none. When a user presses "enter" on the keyboard, the next elements becomes visible. To detect that an "enter" event was triggered, we use the onkeypress event attribute in combination with the ASCII code for the "enter" key, which is 13. If you have never come across the ASCII code before, here is a very short summary.

Example

Here is a short example of using the onkeypress event, which shows how the function can take an optional parameters, e that refers to the event being handled.

Type some text in the box and see the ASCII code for each of them.

var input = document.getElementById("box");
var p = document.getElementById("text");

input.onkeypress = function (e) {
  p.innerHTML += "Key code is: " + e.which + 
                  " Character is: " + 
                  String.fromCharCode(e.which) + 
                  "<br>";
};

I have also prepared a jsFiddle where you can play and change this example. Knowing that the chode for "enter" is 13, you can perform a test to make something happen on the page.

API Requests

The AM3 app uses two API: the Wikipedia API to retrieve information about a city/town, and the Google Maps API to find the geolocation of cities as well as calculate the distance between them.

To use the Wiki API we send an HTTP request as we did for the apps in AM1. Wiki API doesn't support CORS, thus, we'll need to use the JSONP method discussed in the AM1 notes. Below is how the URL of the request looks like:

http://en.wikipedia.org/w/api.php?format=json&action=query&generator=search&gsrsearch=Boston&gsrlimit=1&prop=pageimages|extracts&exintro&explaintext&exsentences=3&callback=functionNameHere       

You can look at the results of this URL by clicking on this link.

Problems

Working with the Wikipedia API is a bit tricky, because their documentation is not very user-friendly. I was able to find what I needed in StackOverflow. Here are some things to have in mind:

  1. Order of parameters matters. The name of the city (shown in bold) above needs to be in that position, instead of taking the pair gsrsearch=Boston and appending at the end of the URL. This means that you'll need to break the URL into parts, in order to concatenate the name of each different city. The other bold parameter values are things you can change and correspond to number of results, number of sentences to extract from the page, and the name of your callback function to process the data.
  2. Spelling and casing of words matters. Because Wikipedia is huge, any misspelling of a name might cause the query to be answered differently. For common words you might get different results for different casing. It is outside of the scope of this app to be able to deal with such issues. Try to be as accurate as possible in typing.

We will look at the Google Maps API requests on the next set of notes, given that we make such requests through the library provided by Google.

Data Processing and Storage

As you remember from AM1 and AM2, the response from the API will come in a JSON format. Often, you don't need everything that is stored in the response. However, you might want to store the response to avoid having to recall the API for multiple times. You can use arrays or objects of objects to store your information. You already know what arrays are (an array (Java) and a list (Python)) and that they store data sequentially. You need an index (0, 1, ..., length-1) to access every element.

An object of objects is often how the JSON data from an API will look like. The screenshot below shows the Wikipedia answer for the request in the previous section.

Notice how to access the information for Boston, we need to write query.pages["24437894"], but, in general, we will not know in advance the id of a result. Thus, we will need to use a for loop to iterate through the results to access every object in turn. Below is an example to try out. Try to guess what the results will be, then, try it out on the console.

var merchItems = {
  "101": {"name": "Blue Socks", "price": 5.99},
  "112": {"name": "Red Dress", "price": 34.99},
  "245": {"name": "White Shirt", "price": 27.99}
}
for (var key in merchItems){
  console.log(merchItems[key].name);
} 

Objects of objects can be used to store information too, that is easy to access if one knows the key. For example, in AM3 I have stored the town names in an array, and I stored the Wikipedia results in an object, where every property is the name of a city, and the value for each of them is the formatted results of the response (I have stored the HTML I want to show up in the info window). Similarly, I store the geocoding information for every city in an object of objects (see example below). Then, to show markers of every city, I take one element from the city names array, and use it as a key to retrieve the information from the stored Wikipedia results, and the coordinates from the Google results.

You might ask why store results? Because of delay (or latency). If we send the requests for information on the moment we want to show the markers on the page, it will take several milliseconds for the answer to appear. In fact, you can verify this in the screencast, by noticing how all the images (which are not on our computer) are displayed after the text.

Information Representation

In AM3, we use markers on the Google Map to display a location. Markers are objects in the Google Maps API that are easy to create and attach to the map.

Then, we use info windows attached to a marker to display HTML code within the window. There is only one info window object that is used by the app. All markers share that same object. This is why we only see one info window open a a time. If you click on a different marker, the info window moves from one marker to the other. The object info window has a method .setContent() to which we can pass as an argument a string that contains HTML code which we have prepared from the Wikipedia data, using tags such as h1 for the name of the city, img for the city photo, and p or span to show the text. We can apply CSS to these elements, in the same way we do for our normal page.

Finally, the names of the cities and the distances from the hometown to each of them are displayed in a transparent stripe at the bottom of the map. Each name + distance is stored in a div to which we can apply a simple CSS class where the text color is changed. By adding or removing this class, we give the impression of moving from one div to the other.

Width of images for Wikipedia

The images returned by Wikipedia have a really small size, usually around 30-50 pixels. However, the URL of the image can be changed to identify the need for a larger width. You will need to write code that uses the value of the property width to find the portion of the URL you can change with the string .replace() method.

Animation Features

The animation feature (slideshow effect by moving from one city to another automatically) is created with the built-in Javascript function setInterval(). This function takes two parameters:

  setInterval(function_to_invoke, milliseconds);

The function starts a cound-down timer and at the end of it runs the provided function. This is repeated forever, unless we invoke clearInterval. To do that, we neet to call setInterval like in the example below:

var intervalID = setInterval(function_to_invoke, millseconds);
// do other things
// if some conditions is fulfilled (e.g., we visited all cities), clear the interval
clearInterval(intervalID);
    

You can see an example of the setInterval function in action in jsFiddle I created.

More on setInterval

  1. It is important that when you pass your function as an argument to setInterval to not invoke it by adding the parentheses. Try this out to see what happens. In the above jsFiddle, go and change var intervalID = setInterval(showBoxes, 1000); to var intervalID = setInterval(showBoxes(), 1000);. What happens?
  2. If you need to invoke a function with arguments, you can use an anonymous function to do this:
    var myInterval = setInterval(function() {doSomething("wendy", 2018);}, 1500); 
  3. The setTimeout method has a "sister" method, setTimeout, which wait for a certain amount of time and then executes a function. This is a good trick to have your code stop for a moment, while you wait for something else to happen. To do that, you invoke setTimeout with an empty function:
    setTimeout(function(){ }, 2000);

W3 Schools has some examples and more background on setInterval and setTimeout:

  • The setInterval method.
  • Javascript Timing Events.