In recent years, newspapers have been using maps online to add inter- active Web components to projects and stories. Typically, they have ported geographic data to a Web-based map server or used plug-ins for ArcGIS or similar prod- ucts to create HTML-based maps. Over the summer, we looked for a way to add an interactive component to a property tax project we planned to publish in the fall. We explored the traditional options, but we wanted it to be truly dynamic and to display almost every parcel (about 400,000) in five Florida counties.
We considered renting space on a mapping server but it was expensive: $5,000 for four to six months. It was also slow, clunky and particularly difficult to use for the uninitiated Web user. At the time, Google had just re- leased the beta version of the Google Maps APIs (application programming interfaces). Google Maps is clearly the best online mapping service (Yahoo! has just started a similar mapping service) and its underlying maps can be built into anyone's Web-based ap- plications.
It quickly became clear to us that we could use Google Maps to deliver a far more satisfying online experi- ence. Here is how we did it: Using ArcGIS 9.1 we calculated the center point of every parcel in the five counties: Google Maps uses latitude and longitude coordinates to plot points and lines. From there we extracted market, assessed and taxable values from the counties' 2004 and 2005 tax roll data and created a table in databases with the parcel coordinates, property values and calculated property taxes for each of the years.
(Example 1) GEvent.addListener( map, "click", function(a,p) { //here if == null && map.getZoomLevel > { map.centerAndZoom p, map.getZoomLevel } else if ( o == null && map.getZoomLevel()>0) map.centerAndZoom p, map.getZoomLevel( -1);1 (Example 2) function parcel() { // Generate a new XMLHTTP Object xmlhttp = false; try { xmlhttp = new ActiveXObject ("Msxml2.XMLHTTP"); } catch (e) { try { xmlhttp = new ActiveXObject ("Microsoft.XMLhTtp"); } catch (E) { xmlhttp = false; } if (!xmlhttp && typeof XMLHttpRequest!='undefined') { xmlhttp = new XMLHttpRequest // Get the current map bounds var b = map.getBoundsLatLng // Send an XMLHTTP Request, asking for the bubble data xmlhttp.open("*GET","SH/cgibin/geo2.cgi?x="+b.minX+"&y="+b.m inY+"&x2="+b.maxX+"&y2="+b.maxY,f true) xmlhttp.onreadystatechange = process; xmlhttp.
send (null) (Example 3) # The table columns we want to output my @col = (id lon lat name address soh just assd txbl home tax cat sohtax rate cnty brk name_05 soh_05 just_05 assd 05 txbl_05 home 05 brk_05 rate_05 sohtax 05 tax_05 cat_05 justg sohg assg txblg taxg sohtaxg sohbrk sohbrk_05); my $ret = "Content-type: text/xml\n\n<items>"; # Only do 2/3 of the available area my $x=(p(x2')- p(x')) (1/6); my by=(p(y2')- p(y'))*(1/6); = my (p('x')+p('x2')) / 2 my (p('y') + p('y2')) # Request a slice of parcels my $q = $db->prepare ( "SELECT *, SQRT (POW (parcel_lon- $cx,2) + POW (parcel_lat - $cy,2)) as C FROM parcels WHERE parcel_lon >= ?
AND parcel_lat >= ? AND parcel_lon <= AND parcel_lat <= and par- cel_just_05 > 0 ORDER BY C LIMIT 40:f is $q->execute p('x') $x, p('y') $y, p('x2') $y The project examined Florida's de- cade-old Save Our Homes amend- ment, which caps annual increases in assessed values (and so taxable values) for resident homeowners. We wanted the Web application to show how the amendment had created wild discrepancies in property taxes for neighbors, even those living in virtually identical houses.
Google's map does its magic through client-side scripting and Asynchro- nous JavaScript and XML (AJAX). AJAX allows a programmen to grab data from a server without reposting a page. It is the essence of how Google Maps works. In our case, we hired John Resig, a Rochester Institute of Technology senior, to build the skeleton of the application and we adapted his code. We found him by doing a Google search and came across his Web blog (http://ejohn.org) describing techniques using the Google Maps API.
(For more about the APIs see www.google.com/apis/map.) Anyone planning to work with Google Maps (or the similar Yahoo! Maps) must be a proficient or know a profi- cient programmer in avaScript and a server-side language. In our case we used PERL on the server, but could have picked from other languages, including the various NET flavors. The Google APIs are pretty com- plete, but because of differences in browsers, the programming can be tricky.
Microsoft's Internet Explorer 6, for example, renders icons on a map very slowly. Mozilla Firefox, however, renders icons almost instantly. We also wanted to change some of the behavior of the map from the standard Google Maps. The most important being that a click on the map drills down two levels and cen- ters on the click. (We wanted to get users down to the neighborhood as quickly as possible.) (Example 4) Print out the resulting XML $ret .= "<item join map{ $row->{\"parcel_$_\" = s/&/&/g; $row->{\"par cel_$_\"} = s/'/'/g; $row->{\"parcel_$_*)\" nso } @col ) "/>"; (Example 5) // from the XML, gets the cat of property var n = it [i] .getAttribute ("cat_05") 0; var 35 ( * 5 ) ; // and figure out if is a homesteaded property var ic = new GIcon icon ic.image = "img/bubble" + [i] getAttribute ("home 05") == "1" ?
"check" na ) n + " . png" ; // and creates an icon ic.iconSize = new GSize j, j ic.iconAnchor = new GPoint j - 20, j - 10 ) ; c.infoWindowAnchor = new GPoint 10, 10 // and places the icon on the map using the latitude and longitude of the parcel contained in the XML var III = new GMarker ( new GPoint ( it [i] getAttribute ("lon"), .getAttribute lat") ),ic); Example 1 shows the vaScript code to add click and zoom func- tionality.
This snippet is part of the windows.onload function(). Effec- tively, it grabs the point of a click and zooms in two levels, centered on the point of the click. At the lowest extent of the map, a client-side function fires that initiates a PERL script on the server that in turn queries a database and returns XML-formatted data. The client-side script then picks back up and places the icons on the map.
Example 2 shows the vaScript that fires the PERL script on the server. The first half of the code allows the browser to accept XML-formatted data. The second half grabs the coordinates of two opposite corners of the map and sends them to the PERL script. See Example 3 for PERL code to grab the data from a MYSQL server using the map boundaries to gather the appropriate parcel data for the map. After doing some server-side calcu- lations to assign a value that would determine the size and type of icon to be displayed on the map, we then returned the data to the client using the code in Example 4.