Hack 20. Track Official Storm Reporting

Follow the path of the latest hurricane on a Google Map.

Google Map hacking started for me in early March 2005, when I became aware of some of the great hacks that were already being created. My interest was particularly piqued after seeing the beta release of Adrian Holovaty's Chicagocrime.org web site, as described in "Examine Patterns of Criminal Activity" [Hack #18]. I immediately realized the myriad other applications for this new mapping technology. To be more precise, I determined that it would be beneficial to develop a storm-reporting mapping site, which you can visit today at http://www.stormreportmap.com/, as shown in Figure 3-7.

Figure 3-7. The weather on Google Maps

You can click on a marker to see more details. For example, Figure 3-8 shows a report of hail in Sioux, Nebraska.

Another interesting feature is the hurricane tracking maps. As shown in Figure 3-9 you can see the tracks of tropical storms.

Clicking on the marker icons gives you more information about the storm at that point. In Figure 3-10, we see that Irene has been bouncing between a tropical storm and a tropical depression.

The data source for the web site is the National Oceanic & Atmospheric Administration's (NOAA) National Weather Service (NWS) Storm Prediction Center (SPC), which can be found online at http://spc.noaa.gov/. One of the top products that the SPC maintains is their Storm Report tracker at http://spc.noaa.gov/climo/. This product takes reports from trained weather spotters, emergency and first responders, and local residents, and then maps them to an unattractive, static page. Moreover, the reports also don't give much in the way of comprehensible locations, aside from latitude/longitude coordinates, and the county and state where the storm was reported. Additionally, since each report is submitted into the database as a separate event, there are times when several tornado reports received are actually all from the same tornado. Due to these shortcomings in the original product, and because of its general popularity, I felt that my project might enhance the product's basic functionality.

Figure 3-8. Hail in Nebraska

Figure 3-9. Hurricane-tracking maps

Figure 3-10. Good night, Irene!

 

3.5.1. Getting the Data

The SPC began collecting storm report data in mid-1999. Unfortunately, they did not begin putting the data into a web-friendly format until early 2004. Therefore, without parsing through three different versions of web sites, the project will only show storm report data from early 2004 on.

The data is obtained through a Comma Separated Values (CSV) file that is posted along with each update. This very friendly format allows me to parse through the file, convert it into an array, format it to my liking, and push the contents out as an XML file. If you are getting your data from a third-party site through an XML or RSS feed, it's important to realize that bandwidth often costs money.

Before beginning this project, I realized that obtaining the data I want and in the timeframe I wanted it might present a problem for the remote SPC web site. Our assumptions using information obtained from the site were the following:

I couldn't develop a project that would simply leech off the SPC web site's data for every visit to my siteit's disrespectful, lazy, and, most of all slow. I determined that the best model would be to use a back-end database to store all the requested data and load the most current data using a series of timestamps.

Here's how the site handles the data refresh when a user visits the site:

  1. The site loads the report data for the request.
  2. A timestamp indicating when the data was last updated for each day is stored in the database.
  3. Since each page load requires that the database load the report data, the site also checks to see when the data was last updated.
  4. The site updates today's data, if more than 60 minutes have elapsed between the stored timestamp and the current timestamp.
  5. The site updates yesterday's data, if more than 24 hours have elapsed between the stored timestamp and the current timestamp.
  6. For historical data, no timestamp checking is done, because the most current data is already loaded, based on previous assumptions.
  7. The page displays the data to the user.

This model drastically reduces the traffic and bandwidth sent to the remote SPC web site, and I would recommend using it for any site that uses regularly updated third-party data from remote web sites.

3.5.2. The Hack

When the Google Maps API was finally released, I decided to check out its functionality firsthand. Since the original site was weather related and hurricane season was just beginning to get in the swing of things, a hurricane tracker using the new API was in order.

The Hurricane Track Map uses the Google Maps API to develop a Google Map without jumping through all the hoops that the pre-API XSLT methods required. The catch is that there is quite a bit more JavaScript development that you will need to include. Google has gone to great lengths to try and give enough documentation for a novice developer to get started, but even some veteran programmers have trouble getting it to work correctly.

This code snippet shows just how you can use the GXmlHttp class from the Google Maps API to load XML data from a file on your server. As usual, you cannot retrieve remote files from other sites using AJAX; only files within your domain or host can be loaded, which is another good reason to cache the data in your own database first.

var request = GXmlHttp.create(); request.open("GET", "data.xml", true); request.onreadystatechange = function() { if (request.readyState == 4) { markers = []; points = []; infoHtmls = []; categorypoly = []; var xmlDoc = request.responseXML; var markers = xmlDoc.documentElement.getElementsByTagName("marker"); // Loop through the XML document and grab the data contained // with the tags. Store that data into an array. for (var i = 0; i < markers.length; i++) { points[i] = new GPoint(parseFloat(markers[i].getAttribute("lng")), parseFloat(markers[i].getAttribute("lat"))); name = markers[i].getAttribute("name"); type = markers[i].getAttribute("type"); advisory = markers[i].getAttribute("advisory"); timestamp = markers[i].getAttribute("timestamp"); windspeed = markers[i].getAttribute("windspeed"); pressure = markers[i].getAttribute("pressure"); moving = markers[i].getAttribute("moving"); point = points[i]; // Append a hurricane category rating on it based on the // Wind Speed. if (type == "Tropical Depression") {category = "TD";} else if (type == "Tropical Storm") {category = "TS";} else { // Its a hurricane if (windspeed >= 74 && windspeed <= 95) {category = "1";} else if (windspeed >= 96 && windspeed <= 110) {category = "2";} else if (windspeed >= 111 && windspeed <= 130) {category = "3";} else if (windspeed >= 131 && windspeed <= 155) {category = "4";} else {category = "5";} } categorypoly[i] = category; // Call to the createMarker function to create the marker var marker = createMarker(point, name, type, advisory, timestamp, windspeed, category, pressure, moving); // Overlay the markers on the map map.addOverlay(marker); }

The createMarker( ) function is called in order to build the info window for that particular marker. Also, this function assigns a custom icon to the marker, depending on whether the storm is classified as a tropical storm, a tropical depression, or a category 1 through 5 hurricane.

Next, our code adds a polyline to go along with the markers. This is valuable, because hurricane tracks are very unpredictable, and often make loops and turns before they make landfall or get pushed back out to sea.

var pointset = []; // Loop through the array of points created above. Each point, based // upon the category it received will receive a line segment color as // well as the width. for (q=0; q < points.length; q++) { pointset.push(points[q]); if (categorypoly[q] == "TD") {color = "#660099";size = 5;} else if (categorypoly[q] == "TS") {color = "#333399";size = 7;} else if (categorypoly[q] == "1") {color = "#33FFFF";size = 9;} else if (categorypoly[q] == "2") {color = "#33FF66";size = 11;} else if (categorypoly[q] == "3") {color = "#FFFF66";size = 13;} else if (categorypoly[q] == "4") {color = "#FF9933";size = 15;} else {color = "#FF3333";size = 17;} // Add point to the GPolyline so the map can draw it. map.addOverlay(new GPolyline(pointset, color, size)); pointset = []; pointset.push(points[q]); }

 

3.5.3. See Also

Anthony Petitoa

Категории