Monday, October 30, 2017

"Fog of War" Maps with Real-World Data


In this post, I describe an approach for generating "fog of war" maps from real-world location data collected by Google via your mobile phone, in the style of classic real-time strategy video games, implemented in MATLAB.


Growing up, I was a big fan of real-time strategy (RTS) video games, with my favorites in the genre being Age of Empires II, Rise of Nations, Age of Mythology, and Impossible Creatures. A standard feature of these games is called "fog of war", a borrowed term for a game mechanic that hides areas of the map that you have not visited yet and/or are not currently occupying to simulate limitations on human view distance and encourage exploration. Typically, you have full visibility to areas you are currently occupying, and limited visibility to areas you passed through but have since vacated, as seen in the image below.

screenshot from Age of Empires showing the "fog of war" mechanic; source: Giant Bomb

Switching gears, I recently stumbled upon Google Maps Timeline, which allows Google users to peruse their personal location history as recorded by their mobile phones. I immediately found the data fascinating as a way of characterizing my transportation habits with uncanny precision. Putting the two ideas together, I reasoned that I ought to write a program to make a fog of war map with my personal real-world location data.


My personal data encompasses about 120,000 latitude/longitude points dating back to May 2017, or about a point every 1-2 minutes. At a high level, my MATLAB program works like this:

  1. Parse the JSON location data from Google Takeout to extract only the lat/lon points. Other data such as confidence intervals and transportation mode are discarded. 
  2. Define a geographic region of interest with a geographic center, in plain English, and a zoom level. 
  3. Use Google's Geocoding API to translate the geographic center to a lat/lon point. 
  4. Use a 3rd party MATLAB function, plot_google_map.m, to retrieve a map image and lat/lon vectors using the Google Static Maps API. 
  5. Crop the lat/lon points to include only those inside the region defined by the map. 
  6. Generate an opacity map for where to "lift" the fog by plotting a circular marker at each location, then applying a series of Gaussian blurs at varying opacity values. 
  7. Superimpose the map image with the opacity map to generate a shaded map image.
With this setup, the length scale of the unshaded area remains constant with varying zoom, which is not physically realistic, but provides a more interesting and useful map. In this way, as the zoom decreases, the fog of war becomes more general, and as you zoom in, more specific. Otherwise, the un-shaded regions would become nearly invisible at lower zoom levels, a reflection of how small the lengthscale of human vision is relative to the lengthscale of the Earth.


Cambridge, zoom: 15

Boston, zoom: 11

By switching from maptype "hybrid" to "satellite", you can generate maps that look more like what you would see in a standard RTS game, although they are less practically useful for exploring data:

Boston, zoom: 11, maptype: satellite


These maps produced fascinating insights about my location and transportation habits. The fog of war visualization emphasizes only whether or not you have visited a particular location, and not how long/frequently. A heatmap might be a good way of incorporating a duration weighting into the standard fog of war visualization. The low sample frequency, ~1 sample every 1-2 minutes, limits the usefulness of this visualization to areas that you visit most frequently. Areas that I visited on vacation for only a few days produce maps that are disjointed and not very informative. Some online sources say that sample frequency can be increased somewhat despite the frequency not being directly user-adjustable. Overall, I see this method as being a useful tool to aid future urban exploration as I push into new territory and expand my map.

Source Code

Full source code is available via my GitHub.

No comments:

Post a Comment