Cicero How-To #1: Make Embeddable Web Maps of Political Districts

Cicero How-To #1: Make Embeddable Web Maps of Political Districts

Developers can get a wealth of political information from our Cicero API, including address-based geocoding and coordinate-matching to congressional and other legislative districts, Census blocks and watersheds, legislator contact information, and election events from around the world. However, one of my favorite features of Cicero, the map call, is actually one of our least used.

With the Cicero API’s map call, you can get a customizable image of a district’s boundaries, and easily embed one or many of these into an interactive map on your website with just a bit of JavaScript.

In this blog post, I’ll explain how to:

  1. secure your Cicero account info, set up PHP cURL, and get a token from Cicero
  2. get information on a legislative district that represents a particular address
  3. get a boundary image of that district
  4. and display that image in a Google Maps widget.

I’ll be using a mix of PHP, HTML, and JavaScript. Really though, you could do this using any language, and any web mapping framework – such as the excellent open-source Leaflet.

This blog post is written for the absolute beginner. I expect you’ve done some programming and seen some PHP, HTML, and JavaScript code before, and have a handle on at least the concept of a ReST API. You will need a webserver running PHP, even if that’s only a local test server like XAMPP. Of course, you could follow these steps on a production webserver as well. I will do my best to make no other assumptions, but if you get stuck you can always ask me for help. More advanced readers may want to save time and skip ahead to step 3 or 4, where we start actually using Cicero. Many people, beginners and advanced users alike, may want to skim over significant portions of this post. It may be long, but I assure you, it’s not actually that hard!

All the example files I refer to in this post are available in the cicero-examples GitHub repository.

Finally, in a few weeks, I’ll be writing a second blog post that expands on the simple example code I use in this one, by employing JQuery and extra features of the Google Maps API to make your district maps even more interactive. Stay tuned!

Step 1: Secure your Cicero Account Details

To get a token from Cicero, you’ll need to pass your username and password to the API via an HTTP POST request. We’re going to use an extension PHP has called cURL to do that in step 2. First we need to secure your account info. While visitors can’t see PHP, in some circumstances (if logging is on by mistake, etc) they could find out your Cicero details if you don’t put some protection in place. I recommend making a separate PHP file with just your Cicero username and password set to constants. Put that file in a folder outside of your website’s main web-accessible directory and then use PHP’s include() or require() statements to bring that separate file into your main PHP code. You can direct PHP to look for include files in other directories either through your php.ini file’s include path or simply specifying a relative path in your include statement. If you don’t have access to a protected directory outside your main web-accessible one, if you’re running the Apache webserver, you can protect include files via clever use of the .htaccess file (hint: it’s the second post in that article).

Here (github) is my include.php file:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/include.php”]

Penny for Your Address?

Notice the third variable I put into this file, $search_loc. It doesn’t actually have anything to do with your Cicero account details. This will be the address we will pull the elected official and map info from in this example. We’ll use Azavea’s Philadelphia office for this post, but you can experiment later and set this to a different address. More data (state, country and zip code, as well as city and street) will ensure a greater chance of successful geocoding and could make response time faster. Obviously, in a more complete web application, you might be getting this address from a user-submitted form rather than hardcoding it like we are for our example.

Step 2: Setup PHP cURL

Moving along! We said earlier we’d be using PHP’s cURL extension to make POST and GET requests to Cicero. There are other ways to do this, but cURL is widely used and fairly easy – depending on your server. Sometimes, it’s not enabled by default in certain PHP installations. You can find out if you have it installed by making and navigating to a PHP page with the phpinfo() function:

<?php
phpinfo();
?>

Search the page (Ctrl+F works well) for “curl” info. If it’s there, congrats! If not, and you have access to your php.ini file, search for something like the following line:

;extension=php_curl.dll

If your line has a semicolon in front of it like above (that’s PHP’s line comment symbol), go ahead and delete just the ; to enable the extension. If you can’t find the line, just add it to the file without the semicolon. If you’re using Linux or Mac OS X, the extension will be named php_curl.so instead of .dll – .dll is for Windows.

Next, we’ll need to write a function to use cURL to send data up to Cicero and process the responses we get back. I’ve shamlessly stolen the function in my get_cicero_info.php file (on github) from my colleague Joe Tricarico’s excellent Hello World – PHP example in the Cicero documentation:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “4” end_line = “17” ]

All get_response() does is take any URL, and optionally any data to be used in a POST request ($postfields), initializes cURL, makes the appropriate POST or GET request to the url specified, reads the resulting response into the variable named $json, and then runs $json through a PHP library function called json_decode to give us a variable with data we can manipulate and use in PHP.

JSON stands for JavaScript Object Notation, and if you do anything with APIs like Cicero, it should be your best friend because it is just So. Much. Easier. to read as a human and use in programs than it’s older cousin, XML (eXtensible Markup Language). Cicero supports both JSON and XML responses, but we’ll use JSON in this tutorial. If you want to learn more about JSON and how to use it with PHP, Michael Nitschinger has a fantastic blog post on the subject.

Step 3: Get a Cicero Token

OK, we finally get to do something with the API in this step! Also in my get_cicero_info.php file, I’ve written another function, get_cicero_token(), to do exactly that:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “18” end_line = “36” ]

This function I also based off parts of Joe’s Hello World – PHP example, with a few modifications. We take the $username and $password from our previously-included separate file, and use PHP’s urlencode() function to properly encode them before we send them over HTTP to Cicero. (Some characters like commas, apostrophes, and the like can cause problems if they were sent without first URL encoding them.) Then, we use our get_response() function written earlier, give it the url to get a new token, and the username and password as “postfields”, joined by an ampersand (&) just like you might see in a URL. (PHP string interpolation works easily enough we can just put $username and $password into the postfields string – great!). Now that we (hopefully) have a response, we check that the “success” attribute of the response object that Cicero gave us is True. If it’s not, we’ll exit the PHP program because we can’t do anything without a token. Your code may have a bug, or your Internet connection may be down, or Cicero may be temporarily unavailable. If we did get a successful response, this function makes a $token_info array with both our token value and Cicero user ID. We’ll use this array for other API calls.

Step 4: Query Cicero for Officials

(i.e, Where are You and What do You Want?)

This is where it gets exciting. Now we’ll write a PHP function to query Cicero for elected official data, and get what we need to later request a district boundary image from Cicero in step 5 and build a Google Maps widget in step 6.

Let’s take a look at my get_cicero_info.php file’s get_map_info_by_location_and_district_type() function below (boy, that’s a mouthful):

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “37” end_line = “62” ]

This function takes the $token_info array our last function made, as well as the $search_loc that we hardcoded in our include.php file, and $district_type (new) – which I’ve set in this example to STATE_LOWER, one of the many district types available in Cicero. If you wanted something else (like your US House district), just use the appropriate Cicero district type name in place of what I’ve used. Some addresses may belong to multiple districts of a certain district type (the LOCAL type is a good example, when a city might have several at-large seats like a Mayor and Controller, etc), so even when filtering this way you may need to put in extra program logic to find exactly the right one you want. When it returns more than one, Cicero orders officials by last name alphabetically.

Once again, we URL encode the data we’ll pass to Cicero (useful in particular for the $search_loc address, as it converts spaces to +’s and other appropriate URL formatting changes). Our function then builds a $query_string from our information, and specifies that we want JSON output (with “format=json”). Note that since $token_info is an array, we have to add curly braces to our string interpolation syntax here to get at our token and user ID elements:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “40” end_line = “44” ]

We then append our $query_string to the main URL for Cicero’s official call, and get a response:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “44” end_line = “45” ]

With Cicero calls that use geocoding, like official, a list of “candidates” objects will be part of the response. Sometimes a given address will geocode to multiple lat/long points – the official information for each of these points will be a candidate object in the list. Usually, only one candidate object is returned though, so we assume that in this example. In our code, there’s a check to make sure that Cicero was able to find at least one geocoded candidate location and get the officials from the address you gave it:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “46” end_line = “50” ]

Since $candidates is a list, even if it only contains one object like our example right now, we have to reference the 0th element in the rest of our code.

We want to get the geocoded point Cicero produced for our address, because we’ll use this later in the Google Maps widget as the center of the map. To do this, we’ll get the x and y values of the 0th candidate Cicero gave us, and make a new array ($lat_long_of_search_loc) that contains them:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “51” end_line = “54” ]

This function also gets our crucial $unique_district_id for the district we want to display on the map, too. Each district in Cicero has a unique district ID (also called a “primary key”), and while you can request a district map with other information, after you’ve gotten an official object this is the easiest and most fool-proof:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “55” end_line = “56” ]

Finally, we’ll stuff our address’s lat/long and the unique district ID into a new PHP array, $map_info, so we can use them in our final PHP function:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “57” end_line = “60” ]

Step 5: Grab a District Map and Get Ready for JavaScript

So we have a latitude and longitude to center a map on, and we have a unique district ID from Cicero. We’re almost done with our PHP server-side programming.

Let’s take a look at the get_cicero_info.php file‘s get_district_map() function, and the last few lines in the file:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/get_cicero_info.php” start_line = “61” end_line = “82” ]

What’s going on here? This function takes our $token_info array again, along with our new $map_info array. We build a new $query_string and $map_url for the map call this time. There are a few parameters in this one that we haven’t seen until now:

  • include_image_data – This tells Cicero to send along the base64 image data as well as a link to a PNG image hosted on our servers. This is optional, but has advantages over using the hosted image URL which is why we use it in this example. If you take a look at the JSON response, you’ll see the base64 data as a huge stream of seemingly senseless characters – it’s how we can represent an image in text form. You can actually put this base64 stream in the src= part of the HTML image tag, and browsers will render it as an image. Pretty cool, huh? If you use the URL given to the district map image given in Cicero’s response instead, be warned that image is only guaranteed to stay on our servers for 7 days. The advantage to using base64 data is that we will already have it handy when it comes time to display the map,  so we won’t need to download the PNG then which saves us time and bandwidth.
  • srs=4326 – If you’re not a geographer, you might not know what a Spatial Reference System (SRS) is, nor would you know what EPSG codes or other SRIDs are. But you don’t really need to! Just know for now that Google Maps takes latitude and longitude data, which is EPSG code 4326, and the one we’ll be using. If you don’t set this, Cicero defaults to a map projection system called “Web Mercator,” which might play nice with other mapping frameworks, but not Google Maps. (Even though Google Maps is based on Web Mercator, the Image Overlay function we’ll be using assumes regular latitude and longitude.)
  • width=500&height=500 – These specify the dimensions of the map image Cicero will return, and are only two of a slew of other map style options – including colors! The max is 3300 pixels, but 500 or 1000 loads faster and the visual difference is negligible except when a user zooms in closely. In my upcoming second blog post, we’ll discuss dynamically getting district overlay images of different sizes depending on the user’s browser window size and as a user zooms into the map, which will maintain image quality much better. For now though, we’ll keep it simple and hardcode 500 pixels, and limit the maximum zoom level in Google Maps in step 6.

Our function gets a response from Cicero, verifies that a map actually was returned in the response (exits the script if not), and then gets the data for the map object (including the image URL, and the lat/long data to properly position it on our map widget) and sets that to the $district_map_image_data array.

Since we’ll need to use this data in JavaScript code next, we return a new json_encoded PHP array of the lat/long of our center point, and the $district_map_image_data. Finally, the last three lines execute all our PHP functions and create a final $map_data_for_javascript variable with the info we’ll need as we move to client-side JavaScript in step 6!

Step 6: To the Browser!

At last! Let’s write a few lines of HTML and JavaScript, and we’ll finally get to see a district map from Cicero.

I’ve used Google’s Ground Overlay tutorial and code sample file as a base for my display_gmaps_overlay.php file. I’ll highlight important parts here, but you should really take a look yourself and follow along.

NOTE: My demo file uses the Google Maps JavaScript API without an API key. Google will let you do this for development, but if you want to put this code on a live site, you’ll need to register with them and get a key. If you’re using my file, you will need to add your key in the “googleapis.com” URL.

Since PHP is executed on the server (server-side), and JavaScript is executed in the browser (client-side), there’s a small kludge to get them to work together. (I’ll improve this using JQuery in my next blog.) Even though it is mostly HTML and JavaScript, display_gmaps_overlay.php is still a PHP file. We need to include get_cicero_info.php:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/display_gmaps_overlay.php” start_line = “0” end_line = “3” ]

to give us access to the output of the functions we just made, and then also set our PHP variable $map_data_for_javascript to a JavaScript variable. We can do this, oddly enough, with a “php echo” statement:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/display_gmaps_overlay.php” start_line = “16” end_line = “19” ]

The PHP in this file and the included one is processed on the server, and the new JavaScript var map_data_from_php is set to the output of PHP’s echo of $map_data_for_javascript (which, as you should remember, we made as a JSON-formatted string with json_encode), and the client is none the wiser. We’ve taken a PHP object and made it a JavaScript object!

Following Google’s code sample, we next need to create a map object centered at our address’s geocoded latitude/longitude. Because map_data_from_php is JSON, we can navigate the fields using simple dot syntax:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/display_gmaps_overlay.php” start_line = “19” end_line = “22” ]

Next comes a tricky part: defining the bounds of our district image – the lat/long of where it starts and ends. If we didn’t define these, Google Maps would have no idea where to put our image. If we define them incorrectly, Google Maps either won’t show our district image at all or it will be horribly contorted:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/display_gmaps_overlay.php” start_line = “22” end_line = “27” ]

Let’s step through this, because the ordering of your arguments is very important here. There are two Google Maps API calls used – google.maps.LatLngBounds(), and google.maps.LatLng(). The first takes two (lat,long) pairs as arguments and makes what we geographers call a “bounding box”. Assuming North is “up”, Google specifies these pairs must be given in the order of southwest (“bottom left”) first, northeast (“top right”) second. IE, LatLngBounds(southwest point, northeast point). For example, if you wanted to draw a bounding box over most of the state of New Jersey, you’d put Philadelphia’s lat/long point first, and New York City’s lat/long point second. The second API call, LatLng(), makes *one* (lat,long) pair given two decimal numbers.

We’re using values from the “extent” object of the “district_map_image_data” object of the “map_data_from_php” variable to make all of these points. In JavaScript’s dot syntax, that’s map_data_from_php.district_map_image_data.extent.whateverValue. y_min, x_min, y_max, and x_max are all values in the extent object, that we got from the Cicero API with their default names – even though they took a trip through PHP, they’re essentially unchanged.

A note about Latitude and Longitude


Let’s look at the extent object (note, this is not in the example files):

"extent":{"x_min":-75.167614,"srid":4326,"y_max":40.002104,"y_min":39.927964,"x_max":-75.093474}

Like every location in the lower 48 contiguous United States, Philadelphia has a positive, northern latitude and a negative, western longitude. The decimal degree numbers in the extent object above may be confusing if you’re more familiar with the degrees/minutes/seconds way of measuring latitude and longitude (ie, 39°N 59′ 59″), but they represent the same thing and are easily converted. Computation is much easier with decimal degrees, which is why Google Maps and Cicero use that format.

The following can be counter-intuitive to many people: Latitude is Y, Longitude is X. That’s not the way the lines seem to appear on a globe, but it’s true if you think about which axis each is measuring.

So, Google Maps is looking for LatLngBounds(southwest point, northeast point), in that order. Use the extent values Cicero gave you to construct those points: the Southwesterly vertex of the map image will use the pairing (y_min, x_min), and the Northeasterly vertex will use the point (y_max, x_max). Doesn’t make sense? Remember: since the X/longitude values are negative in the Western hemisphere, x_min=-75.16… is actually further West than x_max=-75.09….

Near the finish line…

Next we need to set some map options and initialize the map, which I’ve kept mostly the same as in Google’s code sample. Set our “azavea” var to the center, and set the maxZoom at 16. Zooming in any higher would make our simple 500 pixel image overlay fuzzy. Note that we’ll improve this later in my next blog:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/display_gmaps_overlay.php” start_line = “28” end_line = “36” ]

If you want a marker at your address, create it:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/display_gmaps_overlay.php” start_line = “36” end_line = “42” ]

Finally, we’ll make our “districtoverlay” variable using the image URL given to us by Cicero, the imageBounds we defined above (and I explained at length), and add the overlay to the map:

[github file = “/andrewbt/cicero-examples/blob/master/Maps_call_blog/display_gmaps_overlay.php” start_line = “42” end_line = “47” ]

That just about covers it! If you put these github files on a server with PHP, and navigate to display_gmaps_overlay.php using a browser, you should see an interactive Google map widget with your district image load! Congratulations! If you’re integrating this into a larger web application, perhaps this page could pop up when a representative’s name is clicked, or the map size could be shrunk down and otherwise embedded into the page. Either way, now you’ll be able to give your politically-minded website viewers some interactive, geographic context as to who their elected officials are.