Data Sources and Details

Where we got it, how we made it

Why Bigfoot sightings?

Why not Bigfoot sightings??????

This project was adapted from a bigfoot shiny dashboard Russ built a while back. The full dashboard can be seen here

Data source, scraping, and processing details can be found here in Russell’s GitHub repo.

The GitHub repo also, (sort of), explains how we built the interactive leaflet map and the rest of the shiny dashboard including its custom Javascript and CSS.

tl;dr - The data was scraped from a bigfoot sightings database using a python script, cleaned in R, and then Russell made a sweet Shiny app displaying the results. We then adapted the outputs for this closeread doc.

How we made it

The closeread page (plus Quarto in general) was both easy and challenging at times. Here are some of the challenges + lessons learned:

  1. The parallax effect was a pain to figure out but this video helped a ton
    • I had to adapt the html/css in that video to work with Quarto by making a custom html called before.html and putting it in the header like this:
---
format:
    closeread-html:
        include-in-header: assets/before.html
---
  1. I used the amazing artwork by Tati.Dsgn to get the parallax background
    • I had to save that art and unwrap all the layers using Figma in order to get the dark treeline as a separate image that ‘covers’ the background as the user scrolls. Figma is a great tool for editing images like that.
  2. The actual closeread stuff was straightforward until the ojs stuff :(
    • I wanted to make a map that adds markers when the user scrolls down, like this in the closeread docs
    • I spent hours with chatGPT and reading that document…
    • I had an issue when it came to combining the ojs leaflet map I created alongside the tweaked html for the parallax effect - the two did not mix well at all. Let me explain:

The closeread docs for ojs are great. They use crProgressBlock as a variable to indicate how far along the page a user has scrolled.

They then take that variable and apply some basic math to it. For example, when the user starts scrolling at 0%, they set the variable angle1 to be -180. And angle1 will change as the user scrolls down until it gets to angle = 0. Like this:

angleScale1 = d3.scaleLinear()
  .domain([0, 1])
  .range([-180, 0])
  .clamp(true)
    
angle1 = angleScale1(crProgressBlock)

I wanted to do this for my ojs leaflet map; when a user scrolls, the markers (bigfoot icons and info) get added to the map.

I can add markers to the map like this, where it takes in the lat and lon of the point and plots it:

L.marker([lat, lon], { icon: BigFootIcon })
    .addTo(map);

But, I want the points to be added based on the year variable they have in the dataset. So when a user scrolls, the year increases and the data points are filtered to add more to the map.

I tried it like in the closeread docs by using crProgressBlock but this WILL NOT WORK in my case :

bfScale1 = d3.scaleLinear()
  .domain([0, 1])
  .range([1920, 2025])
  .clamp(true)
    
bf1 = bfScale1(crProgressBlock)

In my parallax set up, I have this in my scss file:

html {
    overflow: hidden;
}

It’s necessary for the scroll effect, but it also means that the scroll counter gets messed up and will no longer count a user’s scroll progress on the page.

So I used a different variable to track a user’s progress instead, the crTriggerIndex:

bfScale2 = d3.scaleLinear()
  .domain([1, 5])
  .range([1930, 2020])
  .clamp(true)
    
bf2 = bfScale2(crTriggerIndex)

Now, as the user scrolls, the TriggerIndex counts progress to where they are at on the page according to the scrolly trigger. So like, if I have a cr-section along with 5 text points in my sidebar, it will divide the crTriggerIndex into 5 numbers, The 1st being the first scroll trigger, and increase my variable based on the proceeding triggers, like this:


::::{.cr-section}

trigger 1 @cr-map # bf2 = 1930

trigger 2 @cr-map # bf2 = 1960

trigger 3 @cr-map # bf2 = 1980

trigger 4 @cr-map # bf2 = 2000

trigger 5 @cr-map # bf2 = 2020

:::{#cr-map}
sticky section here with my map
:::

::::

And it looks like this to where I can use my counting variable to dynamically filter the dataset when the user scrolls (and add the filtered dataset points to the map)

fp = bf_points.features.filter(feature => {
  const year = parseInt(feature.properties.year); // Convert year to integer
  return year <= bf2;  // Only keep points with year less than or equal to bf2
});

// Add markers for each filtered point
fp.forEach(feature => {
  const lat = feature.geometry.coordinates[1]; // Latitude from GeoJSON
  const lon = feature.geometry.coordinates[0]; // Longitude from GeoJSON

  L.marker([lat, lon], { icon: BigFootIcon })
    .bindPopup(`
        <p><strong>${feature.properties.summary}</strong></p>
        <p><strong>Report Date: </strong>${new Date(feature.properties.report_date).toLocaleDateString()}</p>
        <p><strong>Classification: </strong>${feature.properties.classification}</p>
        <p><strong>Environment: </strong>${feature.properties.environment}</p>
        <a href="${feature.properties.url}" target="_blank">Read full report</a>
    `)
    .addTo(map);
});

General Advice

This is all very hacked together. I was pasting random code from chatGPT and other leaflet maps I found on the web to cobble this together. It’s rough, but feels good to have it somewhat work.

If you want to use and OJS cell like this, I cannot recommend adding this to your testing document enough. Put this outside of a cr-section so that you can see all the attributes of your scrolling variables. It will let you see how your custom variables get derived based on where the user is on the page:

:::{.counter style="position: fixed; top: 10px; right: 10px; background-color: skyblue; border-radius: 5px; padding: 18px 18px 0 18px; line-height: .8em;"}
```{ojs}
md`Active sticky: ${crActiveSticky}`
md`Active trigger: ${crTriggerIndex}`
md`Trigger progress: ${(crTriggerProgress * 100).toFixed(1)}%`
md`Scroll direction: ${crDirection}`
md`Progress Block progress: ${(crProgressBlock * 100).toFixed(1)}%`
md`-----`
md`(derived) derived var1: ${bf1.toFixed(1)}°`
md`(derived) bf2: ${bf2.toFixed(1)}`
```
:::

If all else fails, post a discussion in the closeread github site and feel free to tag me. I’d be happy to help and/or learn more about this stuff!