See your data in real time with Data-driven styling

This document describes why and how to implement dynamic Data-driven styling of Google Boundaries using the Maps JavaScript API, which is useful for a variety of use cases across industries and segments.

Taxi Counts in NYC by Postal Code
Animated taxi counts in New York City by postal code boundary (simulated, time-lapsed):
Taxi counts in NYC by Postal Code (time-lapse) Map legend

Data-driven styling is a Google Maps Platform capability that allows you to utilize Google's administrative boundary polygons, apply styling to those polygons for display on your maps, and combine your own data to create rich, customized maps that can be used for visual analysis and improved understanding of your data. This document will discuss some use cases that explain why and how you can visualize your data with Data-driven styling on a map in near real-time by integrating dynamic data feeds.

Data-driven styling lets you create maps that show the geographic distribution of data, dynamically customize polygon style, and interact with your data through the click events. These features can be used to create informative and engaging maps for a variety of use cases and industries.

Here are some examples of use cases that could be applicable to a map displaying dynamic data updates on Data-driven styling:

  • Ride sharing: Real-time updates can be used to identify high demand areas, in which case some providers may have surge pricing.
  • Transportation: Real-time updates can be used to identify areas of congestion, which will help with determining the best alternative routes.
  • Elections: On election night, real-time data polling data can be used to visualize results as they happen.
  • Worker safety: Real-time updates can be used to track events as they unfold in real-time, identify high-risk areas, and provide situational awareness to responders in the field.
  • Weather: Real-time updates can be used to track the movement of storms, identify current hazards, and provide warnings and alerts.
  • Environment: Real-time updates can be used to track the movement of volcanic ash and other pollutants, identify areas of environmental degradation, and monitor the impact of human activity.

In all of these situations, customers can unlock additional value by combining their real-time data feeds with Google's boundaries to quickly and easily visualize their data on a map, giving them the superpower of near-instant insights for better informed decision-making.

Architectural Approach to the Solution

Now let's walk through building a map using Data-driven styling to visualize dynamic data. As illustrated earlier, the use case is a count of NYC taxis visualized by postal code. This can be helpful for users to understand how easy it will be to hail a taxi.

Here is an application architecture diagram of the approach:
application architecture

The Dynamic Data-driven Styling Solution

Now let's walk through the steps needed to implement a dynamic Data-driven styling choropleth map for your dataset.

This solution enables you to visualize a hypothetical dataset of real-time taxi density around New York City by postal code. While this may not be real-world data, it has real-world applications and provides you with a sense of the power and capabilities of how dynamic data can be visualized on the map with Data-driven styling.

Step 1: Choose data to visualize and join to a boundary Place ID

The first step is to identify the data you want to display and ensure it can be matched to Google's boundaries. You can perform this matching client-side by calling the findPlaceFromQuery callback method for each postalCode. Note that postal codes in the US have distinct names, but other administrative levels do not. You will want to ensure you select the correct Place ID for your query in cases where there could be ambiguous results.

const request = {
    query: postalCode,
    fields: ['place_id'],
function findPlaceId() {
   placesService.findPlaceFromQuery(request, function (results, status) {
      if (status === google.maps.places.PlacesServiceStatus.OK) {

If your data has latitude and longitude values, you can also use the Geocoding API with component filtering to resolve those lat/lon values to Place ID values for the feature layer you are interested in styling. In this example you will be styling the POSTAL_CODE feature layer.

Step 2: Connect to real-time data

There are a variety of ways to connect to data sources, and the best implementation will depend on your specific needs and technical infrastructure. In this case, let's assume that your data lives in a BigQuery table with two columns: “zip_code” and “taxi_count”, and you will query it via a Firebase Cloud Function.

async function queryNycTaxis() {
   // Queries the NYC Taxis dataset.

   // Create a client
   const bigqueryClient = new BigQuery();
   // The SQL query to run
   const sqlQuery = 'SELECT zip_code, taxi_count
   const options = {
      query: sqlQuery,
      // Location must match that of the dataset(s)
      // referenced in the query.
      location: 'US',
   // Run the query
   const [rows] = await bigqueryClient.query(options);
   rows.forEach(row => {
      const postalCode = row['zip_code'];
      const taxiCount = row['taxi_count'];

Next you will want to ensure you keep the data fresh. One way of doing this is to call the above query using a web worker, and set a timer to refresh your data using the setInterval function. You can set the interval to an appropriate value, such as refreshing the map every 15 seconds. Each time the interval time passes, the web worker will request updated taxiCount values per postalCode.

Now that we can query and refresh the data, let's ensure the appearance of the map polygons reflect these changes.

Step 3: Style your map with data-driven styling

Now that you have the dynamic data values needed to create and apply a visual style to the postal code boundaries in your Maps JavaScript instance as a JSON object, it's time to give it some style as a choropleth map.

In this example you will style the map based on the number of taxis in each postal code, giving your users a sense of taxi density and availability in their area. The style will vary depending on the taxi count values. The areas with the fewest taxis will have a purple styling applied, and the color gradient will move through red, orange, and end at NYC taxi yellow for the highest density areas. For this color scheme you will apply these color values to fillColor and strokeColor. Setting the fillOpacity to 0.5 allows your users to see the underlying map, and setting the strokeOpacity to 1.0 allows them to differentiate between the boundaries of same-colored polygons:

const featureLayer = map.getFeatureLayer(
); = (featureStyleFunctionOptions) => {
   const placeFeature = featureStyleFunctionOptions.feature;
   // taxiCount per (postal_code) PlaceID 
   const taxiCount = zips[placeFeature.placeId];
   let fillColor;
   let strokeColor;
// Apply colors to features based on taxiCount values
if (taxiCount < 8) {
   fillColor = "#571845";  
   strokeColor = "#571845"; 
} else if (taxiCount < 13) {
   fillColor = "#900c3e";
   strokeColor = "#900c3e";
} else if (taxiCount < 21) {
   fillColor = "#c60039"; 
   strokeColor = "#c60039"; 
} else if (taxiCount < 34) {
   fillColor = "#fe5733";
   strokeColor = "#fe5733";
// keep else if or the whole map gets this style with else
} else if (taxiCount >= 34) { 
   fillColor = "#fec30f";
   strokeColor = "#fec30f";
return {
   fillOpacity: 0.5,
   strokeOpacity: 1.0,


Data-driven styling for Google boundaries unlocks the ability to use your data to style your map for a variety of implementations across industries and segments. Connecting to real-time data enables you to communicate what is happening, where it is happening, and as it happens. This capability has the potential to unlock the value of your real-time data and help your users better understand it in real-time, in the real world.

Next Actions


Principal author:

Jim Leflar | Google Maps Platform Solutions Engineer