I’m doing an IAmA on Reddit:
Geek history! :-)
It’s a simple little application that just looks up voter registration information for your address, and later this month will also display your voting location. They are going all out to promote it, with a link on the main Google home page, and a cool video with a few faces you may recognize:
Now go register to vote, or Leonardo will never speak with you again!
This is a test of the 2008 US Voter Info gadget.
Here’s a demo: the Mapping the Votes talk I gave at Google in April, with annotations provided by AnnoTube. This solves a mistake I made in this talk: Like many speakers, I left the text too small in my code examples. It looked fine to the people in the room, but the code is really hard to read in the YouTube video. But with the annotations I can put big, readable text right next to the video at the right time.
Fair warning: At this moment, I’ve only annotated the first 15 minutes of the video, and all of the notes so far are actually web pages in an IFRAME. Also, the AnnoTube plugin itself isn’t quite ready for general use. This is all a bit of work in progress, which started with something I put together for fun during the Google I/O conference. I’m only posting now because the YouTube API team is mentioning it in a post of their own, so please watch this page for updates over the next few days.
For a start, here’s what the timeline for my talk looks like (with URLs shortened to avoid long lines):
As you can see, it’s just a simple list of times, titles, and links or HTML snippets. AnnoTube takes care of connecting the events and watching for the times you specify.
More details soon… Thanks for your interest and patience!
Another day, another primary, another Google map. This time we added a bunch of demographic information using little sparkline graphs, with help from Jim Barnes of the National Journal. I think the voter registration by age is especially interesting. Check it out:
(The map probably won’t load in an RSS feed, so click through to the article to see it.)
You can get this map for your own site!
Here are some links and information that I referred to in the talk.
Maps and mapplets
Decision 2008 - the current election mapplet
Decision 2008 Gadget - the election map as a Google Gadget
Iowa Republican Caucus - an early API map
Iowa mapplet - an early mapplet
Twitter election map - the Super Tuesday twitter map (showing tweets from that day)
Campaign Trail - candidate calendars
New Hampshire in Google Earth - a KML file
Editors and desktop tools
Komodo IDE isn’t cheap, but I figure it paid for itself really fast. There’s also a free Komodo Edit that everyone should install even if you already have a favorite editor. Both versions have real-time syntax checking, where you get squiggly red underlines for syntax errors and squiggly green underlines for warnings, just like the spelling and grammar checkers in a word processor. This has saved me literally thousands of page reloads when testing, since Komodo catches my syntax errors before I even save the file. Komodo runs on Linux, Mac, and Windows.
One nice thing about GUI editors is that the basic editing works the same in all of them (or should), so it’s easy to switch back and forth if some other editor has a feature you want to take advantage of. Besides Komodo, I also use PSPad (free, Windows only), mostly because of its nice HTML/XML pretty-printer. It cleans up unreadable web page source code real quick.
Another expensive-but-well-worth-it tool for Windows and Mac is Araxis Merge, a terrific file compare and merge program with live editing. I use Merge as the diff/merge program for TortoiseSVN, which makes source control a dream.
A couple of free Windows tools I use every day are Zoom+ for screen zooming and my own JKLmouse for precise cursor control with the keyboard of your notebook computer. With JKLmouse, I can use the TrackPoint for fast cursor motion and then the keyboard for fine pixel-by-pixel movement, seamlessly and with no “modes”. (Sorry, I had to brag!)
The election map code is open source and is in two Google Code projects. The current code is in the primary-maps-2008 project, and the code for earliest caucuses and primaries is in the gmaps-samples project. (We moved the code to a new project to avoid filling up gmaps-samples!)
If you look at the code, go easy on me: much of it was written under severe time pressure. I asked if the elections could be delayed when I wasn’t quite ready, but even the mighty Google couldn’t seem to arrange that.
Also, if you read the code using the links provided here, there’s an awful lot of indentation, thanks to Google Code displaying my tab indentation using 8 spaces per tab. Shades of K&R! (So, why do I use tabs instead of two-space indents like everyone else? Well, one of the other benefits of Komodo is that unlike most code editors, it lets me edit in a proportional font. Two spaces in a proportional font is almost like not indenting at all.)
At first, I was using shp2text to convert shapefiles to an easy-to-use XML format (using the
--gpx option), but this loses some of the information in the shapefile. More recently, Zachary Forest Johnson, author of the interesting indiemaps blog, wrote shpUtils.py, which decodes shapefiles into usable Python data.
I extended shpUtils.py to calculate correct centroids, area and other information about the shapes, and to fix a few bugs. The updated version is in the primary-maps-2008 project.
The election maps use the centroids of the state and county polygons to position markers for those states.
Centroids are one of those things that you think you understand and then find out you were completely wrong. My first guess was the same as Zachary’s, to take the arithmetic mean of all the points (X and Y separately). The Wikipedia article even seems to say this, but it’s talking about the centroid of the points, not the centroid of the polygon that those points define. If you read it carefully, the article does give the correct algorithm, but it’s better explained on this page, along with sample implementations in various languages.
Census bureau shapefiles
The state and county outlines in the election maps come from shapefiles provided by the Census Bureau. Most states report votes by county, but a few New England states report by town (County Subdivisions in the Census Bureau page), and a few other states report by congressional district.
D’oh! I completely forgot to talk about this important topic. The Census Bureau shapefiles have too much detail to be usable in a browser-based map. If you draw polygons from them, it will be much too slow. A tile layer can handle more detail, but the graphic files will be larger than they could be, because of the excess detail.
Votes and delegates
The Ruby script that gathers the Twitter updates uses the Jabber::Simple module written by Blaine Cook to create a custom Jabber client that talks to Twitter, and uses the Twittervision API to get geographic information. It parses the XML data with sweet Hpricot, then generates JSON data (but you probably saw that coming). If you like jQuery, you’ll like Hpricot.
The election mapplet code is in decision2008.xml and map.js. The code for the Campaign Trail mapplet is in campaign-trail.xml and campaign-trail.js. The latter file has the latest versions of the
S(), and related functions that I talked about. They are at the top of the file, and not yet documented, but you can find examples of each in the code.
More to come
That’s it for now! I’ll be posting more detailed articles on some of these topics. If there is a particular area you’re interested in, please let me know in the comments.
Update: I posted some notes and links from the talk.
I’m giving a talk at Google tonight at 6pm about the election maps I’ve been working on. I’ll be talking about:
- How to use the same code for a mapplet, a Google Gadget, and a Maps API map
- Turn shape files into map tiles, polygons, and markers
- Collect voting results into JSON objects
- Marker madness - can we make it fast enough?
- Hosting on Google Code and Amazon S3
- A custom Twitter map using Jabber to track keywords
I’ll follow up tomorrow with links to the resources I mention in the talk, and then will post a series of articles going into some of the topics in more detail. If you are at the talk or watch the YouTube video, let me know in the comments what areas are of most interest for follow-on articles.
To register for the talk: http://sv-gtug-4.eventbrite.com/
I’ve been working on a project for Google this last month, a mapplet with primary election and caucus results. We’ve done different versions for the primaries so far. For previous states, the emphasis was on mapping the county-by-county results. The latest one is different, a bit of a Twittervision clone, but filtered for messages related to the elections instead of all Twitter messages.
Some people said it was lame and useless; others complained that they spent all day Tuesday watching it.
We report, you decide. :-)
There are plenty of stories to tell about this project, more later…
Here is a script from IBM’s new CoScripter, to update your Facebook status:
It reads just like the instructions you might write down for someone, but it’s an actual executable script. All the scripts are stored on a wiki so anyone can share and update them.
Very interesting… And definitely not your grandfather’s IBM.
Via Jon Udell.
This is too strange to believe, but it is true.
I’ve noticed that it is impossible to log in as a registered user here at mg.to using Internet Explorer, even though other browsers work fine.
Now I know why:
Internet Explorer does not set a cookie for two-letter domains (Microsoft Knowledge Base)
IE and 2-letter domain-names (crisp’s blog)
No tax funds are used for the operation nor development of the Airport. Airport revenues come from user fees and federal grants.
And where do the federal grants come from?
Ben Appleton of the Google Maps API team posted today that Google has added my GAsync() function to the Mapplet API. I don’t see the function listed in the Mapplet API documentation yet, but it should be there soon.
In the meantime, you can read my post that describes the API and how to use it.
One thing not mentioned in Ben’s post: you can use GAsync() not only to improve your mapplet code, but also to write common code for both a mapplet and a regular Maps API application. To do this, you will probably need to include the GAsync() source code in your application—I don’t know if it’s been made part of the standard Maps API.
Thanks Ben and the Maps API team!
My last post introduced a new GAsync API for Google Mapplets. I wrote that code to speed up the response time in our new Zvents mapplet. Try out our mapplet—it’s a fun way to discover things to do in your area.
Naturally, I was barely done with the mapplet when the thought came, “Could we use this same code as a Maps API application?” The two APIs are mostly the same except for initialization—and the pesky matter of the Async calls, e.g.
map.getCenterAsync() in the Mapplets API vs.
map.getCenter() in the Maps API. But having already written the
GAsync code, it turns out to be easy to make it work in both a mapplet and a Maps API app. The interface to
GAsync doesn’t change at all from the previous version. The only difference is that the function now calls either the Async functions in a mapplet, or the non-Async functions in a Maps API app. And at the end of the function, for the Maps API it calls your callback function immediately—there’s no need to wait for any asynchronous calls.
You still have to write code in the mapplet fashion, with a callback when you want to retrieve information from the map—but now you can write code like this and run it identically in a mapplet or a Maps API app:
Additional code samples are in the original article.
And here is the updated GAsync code. First, a compact version ready to copy into your mapplet + Maps API code:
And a commented version:
As the code says, enjoy!
Update 2007-06-22: Version 2 now supports portable code that runs as both a mapplet and a Maps API app. Read about the update.
If you’re building a Google Mapplet that responds to map movement and resizing, you will soon find yourself writing code like this recent gem of mine:
What is going on here? I have a
search() function that takes the current map size, bounds and center, runs a search and displays pins on the map. In a normal Google Maps application I could have simply coded:
But a Google Mapplet lives in a strange and different world. To isolate mapplet code from the Google domain, Google runs the mapplet in an IFRAME loaded from the gmodules.com domain. Cross-domain browser security prevents your code from communicating directly with the Google Maps frame loaded from maps.google.com.
The mapplet API uses the iframe fragment hack to allow limited communication between the mapplet and the Google map. This has two consequences:
The communication is asynchronous. This doesn’t affect the API for functions that simply set map state—they operate on a “fire and forget” basis. But functions that return information can’t do it directly. You have to provide a callback function that receives the information when it is ready.
The communication is slow. Everything is serialized through the fragment identifier (hash) of a hidden IFRAME. The map page and the mapplet frame each have interval timers running to watch for changes to this hash. A single
getSomethingAsync()function call requires all these steps:
- Mapplet frame sets the hash to represent the function call.
- Map page timer wakes up, makes the actual Maps API call, and sets the hash to represent the return value.
- Mapplet frame timer wakes up, gets the value from the hash, and calls the callback function.
My code listed above makes three of these round trips to the maps page one after the other, because the callback for each function triggers the next step in the series. That’s a lot of timeouts—enough to cause a noticeable delay.
What if we could somehow combine all three information requests into a single round trip? That should speed things up quite a bit. Imagine a different Mapplet async API where you provide a list of Maps API functions and get back all of their responses in a single callback with multiple arguments. My three nested function calls and callbacks could be reduced to:
(The sharp-eyed reader will note that the
search() function could be used directly as the callback because it takes the same arguments:
But we’ll stick with the longer form for this discussion, because it makes it clear what the function arguments are.)
While we’re at it, we can provide a way to retrieve information for more than one object in a single call:
And for functions such as
map.fromContainerPixelToLatLngAsync() which take an additional argument, we can allow an optional arguments array after any function name:
Compare that with the equivalent nested functions using the existing API, which would take about three times longer to run:
Good news: We don’t have to wait for Google to implement this zippy
GAsync API or something like it. Although the public API only exposes individual
xyzAsync() functions, the underlying iframe fragment dispatcher can queue up multiple function calls and return values into a single round trip.
The Google Maps team was kind enough to provide me with a nifty
makeBarrier() function that allows us to queue up a number of async calls and get a single callback when all their values are ready. Using this function, my first example can be written as:
As you can see, it’s up to us to count the functions and keep track of the values, but having done that, we can get all three values in a single round trip through the API. It’s literally three times faster than the nested API calls.
Armed with that information, could we code
GAsync() as a layer on top of the existing mapplet async APIs? Indeed we can!
You can try out the code right now and see the speed difference with my test mapplet. Go to the Google Maps Developer Preview page, log into your Google account, and click the Add Content link under the Mapplets tab (or click the Browse Content button if that is what is there).
The next page will show a number of existing mapplets. Click the tiny Add by URL link next to the search button at the top of the page, and paste this URL into the URL box that opens up. You can also click this link to see the mapplet source code:
(When you paste the link, make sure the http:// isn’t duplicated because of the text already in the box.)
Click the Add button and click OK on the confirmation dialog. Then click Back to Google Maps at the top left corner of the page, and you should see a new entry titled A fast simple mapplet async API. Click it to load the mapplet.
An info window should open in the map, displaying several items of information about the map, and the time it required to collect the information using the
GAsync() API. Then try the Slow Async API radio button to see the performance using nested async calls.
GAsync code used in the test mapplet is:
and the corresponding nested async code is:
Finally, here is the
GAsync source code. First, a compact version suitable for pasting into your own mapplet (or download async.js):
And a heavily commented version that explains how it works:
Of course, there are still cases where you will have to run one async call after another one. If you need one piece of information as input to a subsequent call, nested async functions are the way to do it. Even then, it may be possible to combine some other async calls into a single
GAsync call, wherever they don’t depend on each other’s results. You’ll shave about a quarter second off your mapplet’s response time for every call you combine using
Enjoy your simpler and faster mapplet code!
Thanks to Ben A. of Google for mapplet design and coding tips.