.marker([lat, lon], { icon: BigFootIcon })
L.addTo(map);
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:
- 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:
- I had to adapt the html/css in that video to work with Quarto by making a custom html called
---
format:
closeread-html:
include-in-header: assets/before.html
---
- 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.
- 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()[0, 1])
.domain([-180, 0])
.range(
.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:
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 :
= d3.scaleLinear()
bfScale1 .domain([0, 1])
.range([1920, 2025])
.clamp(true)
= bfScale1(crProgressBlock) bf1
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
:
= d3.scaleLinear()
bfScale2 .domain([1, 5])
.range([1930, 2020])
.clamp(true)
= bfScale2(crTriggerIndex) bf2
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)
= bf_points.features.filter(feature => {
fp 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
.forEach(feature => {
fpconst lat = feature.geometry.coordinates[1]; // Latitude from GeoJSON
const lon = feature.geometry.coordinates[0]; // Longitude from GeoJSON
.marker([lat, lon], { icon: BigFootIcon })
L.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!