This Month We Learned — September 2020

This Month We Learned — September 2020

This Month We Learned gives us an opportunity to share short stories about what we’ve learned and to highlight the ways we’ve grown and learned both in our jobs and outside of them. This month we learned about styling nodata values with MapboxGLJS expressions, tracking sequences of events in Google Tag Manager, and growing hydroponic basil.

Styling nodata values in a MapboxGLJS chloropleth

Jenny Fung

This month I styled vector tiles with some no data values as a chloropleth using MapboxGLJS and Mapbox style expressions. The starting point was a step expression-based style generated in Mapbox Studio. When approaching styling vector tile maps, I’ve thus far relied on Mapbox Studio for style code because the leanness of Mapbox expression syntax is hard to reason about and write from scratch. The Studio code is usually sufficient, but we ran into a problem here visualizing no data geometries.

const BLUE1 = '#E5FFFF';
const BLUE2 = '#ACE0FF';
const BLUE3 = '#74A9CF';
const BLUE4 = '#045a8d';
const BLUE5 = '#002C5A';

const COLOR_RAMP = [
    BLUE1,
    0.2,
    BLUE2,
    0.4,
    BLUE3,
    0.6,
    BLUE4,
    0.8,
    BLUE5,
];

const style = {
    paint: {
        'fill-color': ['step', ['get', 'vectortile_field_name'], ...COLOR_RAMP],
        'fill-outline-color': 'rgba(0,0,0,0.4)',
        'fill-opacity': 0.7,
    },
};
A choropleth based on the style in the preceding code block. Everything looks great except for the attention-grabbing nodata in the middle of the map.
A choropleth based on the style in the preceding code block. Everything looks great except for the attention-grabbing nodata in the middle of the map.

The default paint.fill-color style property value is #000000 (black). No data geometries, having no specified styling, fallback to that color. We wanted to avoid drawing attention to the holes in the dataset, so we wanted a subtler fill than that.

Unfortunately, step expressions do not allow you to target and style nodata geometries or otherwise override the default value. I looked for another expression type that could, and tried to rewrite the step style. I landed on case expressions:

const NO_DATA_COLOR = 'rgba(48, 48, 48, .3)';
const BLUE1 = '#E5FFFF';
const BLUE2 = '#ACE0FF';
const BLUE3 = '#74A9CF';
const BLUE4 = '#045a8d';
const BLUE5 = '#002C5A';

const COLOR_RAMP = variable => [
    ['==', variable, null],
    NO_DATA_COLOR,
    ['>=', variable, 0.8],
    BLUE5,
    ['>=', variable, 0.6],
    BLUE4,
    ['>=', variable, 0.4],
    BLUE3,
    ['>=', variable, 0.2],
    BLUE2,
    ['>=', variable, 0],
    BLUE1,
    NO_DATA_COLOR,
];

const style = {
    paint: {
        'fill-color': ['case', ...COLOR_RAMP(['get', 'vectortile_field_name'])],
        'fill-outline-color': 'rgba(0,0,0,0.4)',
        'fill-opacity': 0.7,
    },
};
The same map, but with the nodata in the middle appropriately toned down.
The same map, but with the nodata in the middle appropriately toned down.

With case expressions, we can pinpoint setting an rgba  for any value or value range including null, as well as override the default property value. You’ll notice case expressions read like conditional statements (e.g. if/else), so they feel familiar and more approachable. I prefer their readability, maintainability, and flexibility over step expressions and I can see myself pulling them out of the toolkit earlier the next time I style a vector tile map.

Tracking a sequence of events in Google Tag Manager

Karissa Justice

How do you track the second time something happens with Google Tag Manager? Recently, I was working on implementing user engagement metrics for a custom web application Azavea built using Google Tag Manager. The client wanted to understand the usefulness of a modal “wizard” that displayed on page entry and could be reopened at any point to complete a guided selection process. The user could also apply filters and custom search parameters without using the wizard, so the client wanted to track when (and if) the users opened the wizard a second time.

Google Tag Manager generally captures event values in isolation at the moment they happen, but in this case, I wanted to trigger a tag based on previous events. After discussing with the client, I realized that I only need to differentiate between the first time the event happened and all subsequent events. But another scenario that would require more granular tracking might be displaying a floating message banner after a user visits a page a certain number of times in a session (such as a pricing page). After doing a little research, I realized that the best way to track how many times an event has occurred in Google Tag Manager is to use the Data Layer. 

The Data Layer can be intimidating, but in this case, it’s very easy to count the number of events, or track a sequence of events using a custom data layer variable.

First, create a custom Data Layer Variable. We’ll call it Modal Use.

Create a Data Layer Variable Named Modal Use, and set the Default Value to Entry.
Create a Data Layer Variable named Modal Use, and set the Default Value to Entry.

Because I knew the Modal would always display the first time a user landed on the page, I added a Custom HTML tag to set the value of the data layer variable to “Entry” when the window has finished loading. If the modal is not loaded on page entry and you wanted to count how many times it’s been opened, you could set it to “0” instead.

<script type="text/javascript" id="cw-modal-use-definition">
  (function() {
   
     dataLayer.push({
     'Modal Use': Entry',
     });
  })();
</script>

Once you complete this step, you should be able to view the value of your custom variable in the Data Layer window of Preview Mode.

In Preview Mode, switch to the Data Layer tab and check that the Data Layer Variable you created is displayed.
In Preview Mode, switch to the Data Layer tab and
check that the Data Layer Variable you created is displayed.

For my specific use case, I created a second Custom HTML tag that updates the value of the data layer variable to “Reopened” that is triggered when the user clicks on the button to reopen the wizard.

<script type="text/javascript" id="cw-modal-use-definition">
  (function() {
   
     dataLayer.push({
     'Modal Use': 'Reopened',
     });
  })();
</script>

But you could also create a Custom Javascript Variable that increments each time an event occurs, or determines whether or not events are being completed in a specific sequence by comparing the current value of the custom data layer variable to a new event value, and updating or resetting the custom data layer variable accordingly.

The value that is stored in the Data Layer Variable can now be used in a Google Analytics Event tag. 

Event Tracking Parameters in Google Tag Manager can be set to variables like Modal Use, which dynamically calculate a value when the event is triggered, or they can be static values which you define for each tag.
Event Tracking Parameters in Google Tag Manager can be set to variables like Modal Use, which dynamically calculate a value when the event is triggered, or they can be static values that you define for each tag.

Remember that the Data Layer Variable value is only tracked locally unless you capture it with a Google Analytics or other tag type. In my case, I captured the value when the user clicked the Submit button on the modal.

Growing hydroponic basil and other plants

Ross Bernet

Last summer my housemate quietly became a prolific basil farmer overnight. Or so it seemed to me anyhow. I woke up one normal July morning only to find an abnormal quantity of large basil plants in our backyard. How had he done this without my notice? (Best not for me to look too deeply into the question) The answer: hydroponics.

He had cut holes into the lids of two five-gallon buckets and filled them with permeable baskets and rooted basil plants in a medium of small clay balls. I don’t remember any intermediate sized basil plants. It went from nothing to six massive basil plants, each one a foot and a half tall and wide. We were equipped to make pesto pizza for our entire block.

I had to know his secrets. He directed me to a few YouTube videos with titles like “Grow Infinite Basil Plants for using Hydroponics or the Kratky Method,” “how to grow herbs at home without soil 🌿.” “Cheap & Easy DIY Hydroponics | Ditch the expensive stuff for a $1 Pool Noodle” that had millions of views collectively. Boy was I late to the game.

It took me a while to get to it, life happened, 2020 is happening, I moved to California, thankfully I committed to writing a TMWL about it, etc. etc. But I finally learned enough to do it, and did it. As of the time of this writing (October, 13, 2020) I have 12 plants growing in three five gallon buckets in my backyard (Basil, Kale, Spinach, Arugula, Rosemary, and Chard). I hope in three months I’ll be feasting on salad. Perhaps I will update this TMWL (going to slack now: /remind me in three months “if you have a nice salad, update that TMWL post you wrote in October”)

A Slackbot reminder showing that the previous joke about updating this post if Ross has a nice salad is actually quite serious.
#accountability

Step 1: Gather supplies

Here are the supplies:

  1. Five gallon, food safe bucket
  2. Hydroponic rockwool
  3. Hydroponic Fertilizer
  4. Hydroponic 3 inch cups
  5. Clay balls
  6. Seeds
  7. Blade for cutting five gallon bucket lid
  8. THAT’S IT!

Step 2: Cut 3 inch holes in the lids of the buckets

Step 3: Plant seeds in rockwool

Then wait a couple weeks.

Step 4: Combine clay balls with seedlings in three inch cups

Step 5: Fill five gallon buckets with water and mix in fertilizer

Step 6: Adjust water so it just touches the bottom of the clay balls

Step 7: Wait three months and eat salad and pesto pizza

(no image yet)

There are ways to get more complicated with this. But depending on what time of day it is and what activity I am participating in, I am not a plant scientist. I am just trying to have some fun. Something that I like about hydroponics is that I can leave for a long weekend during a heatwave, and come back without worrying if my plants have withered. All the water they need is right there at their roottips.