Parler Metadata in Leaflet

metadata maps leaflet

Interactive cluster map and timeline of GPS data

Sam Parmar https://parmsam.github.io/
01-16-2021

Background

Recently heard about the Parler web scrape that happened a few days ago. It was awesome to read about the archiving work that @donk_enby led (link to her Twitter page). More info on Parler here.

Found the processed metadata on Github yesterday evening. Thanks to zumbov2 on Github for his repo and data export. Got to work after I had the data. Thought it would be cool to see in Leaflet, combined with leaftime for timelines in Leaflet.

The raw data source I used had 64,399 records with GPS coordinates from 2011-08-11 to 2021-01-10. Had creation date, latitude, and longitude fields to work with. The count I read across multiple articles was 68K, but there are ~5% of records with missing or bad coordinates. Checked full data that Kyle Mcdonald posted and obtained same record count after filtering out for records long/lat at origin (0,0).

Note that the data represents only content that was publicly available, as pointed out by donk_enby on her Vice article and this Wired article.

The first map is a cluster map focused on Washington DC on January 6, 2021. Second is a timeline that looks at the data from November 1, 2020 onward (US election occurred on Tuesday, November 3). Feel free to interact with the maps as you like. This was a great opportunity for me to learn more about Leaflet. Code included below maps.

Other cool projects

Maps

Code

parler_all <- read_csv("https://raw.githubusercontent.com/zumbov2/parler-video-metadata/main/parler_vids_geolocation_time.csv")

dt2 <- parler_all %>% 
  mutate(CreateDate = ymd_hms(CreateDate)) %>%
  mutate(start = as_date(CreateDate), end = as_date(CreateDate)) %>%
  filter(start >= as_date("2020-11-01")) %>%
  mutate(start = ymd_hm(paste(start,"00:01 AM")), 
    end = ymd_hm(paste(end,"11:59 PM"))
  )

#dt2 parler video data time lapse
dt2_geo <- geojsonio::geojson_json(dt2)

#on 2021-6-1
dt2_jan6 <- dt2 %>% 
  filter(date(CreateDate) == "2021-01-06") 
  # filter(lat > 38.6 & lat < 39.2)
tag.map.title_white  <- tags$style(HTML("
  .leaflet-control.map-title-white { 
    position: sticky !important;
    left: 25%;
    right: 50%;
    text-align: center;
    font-weight: bold;
    font-size: 18px;
    color: WhiteSmoke;
  }
"))
title <- tags$div(
  tag.map.title_white, HTML("Parler Metadata on Jan 6, 2021")
)  
tag.map.title_black  <- tags$style(HTML("
  .leaflet-control.map-title-black { 
    position: sticky !important;
    left: 25%;
    right: 50%;
    text-align: center;
    font-weight: bold;
    font-size: 18px;
    color: black;
  }
"))
title2 <- tags$div(
  tag.map.title_black, HTML("Parler Metadata over Time")
)  
# Leaflet map1
leaflet(dt2_jan6) %>% 
  addControl(title, position = "topright", className="map-title-white") %>%
  setView(lng = -77.025, lat = 38.892, zoom = 12) %>%
  addProviderTiles(providers$Esri.NatGeoWorldMap) %>%
  addCircleMarkers(
    color = "red", radius=7,
    stroke = FALSE, fillOpacity = 0.7, 
    popup = ~paste(as.character(CreateDate),"at (", as.character(lat), as.character(lon),")"),
    labelOptions = labelOptions(style = list("font-size" = "8px")), 
    clusterOptions = markerClusterOptions(), clusterId = "quakesCluster"
    ) %>% 
  addMiniMap(
    tiles = providers$Esri.WorldStreetMap,
      toggleDisplay = TRUE) %>%
  addEasyButton(easyButton(
    states = list(
      easyButtonState(
        stateName="unfrozen-markers",
        icon="ion-toggle",
        title="Freeze Clusters",
        onClick = JS("
          function(btn, map) {
            var clusterManager =
              map.layerManager.getLayer('cluster', 'quakesCluster');
            clusterManager.freezeAtZoom();
            btn.state('frozen-markers');
          }")
      ),
      easyButtonState(
        stateName="frozen-markers",
        icon="ion-toggle-filled",
        title="UnFreeze Clusters",
        onClick = JS("
          function(btn, map) {
            var clusterManager =
              map.layerManager.getLayer('cluster', 'quakesCluster');
            clusterManager.unfreeze();
            btn.state('unfrozen-markers');
          }")))))
#Leaflet map2
leaflet(dt2_geo, options = leafletOptions(zoomControl = TRUE, 
                                          dragging = TRUE, 
                                          preferCanvas = FALSE)) %>%
  addTiles() %>%
  addControl(title2, position = "topright", className="map-title-black") %>%
  setView(lng = -77.025, lat = 38.892, zoom = 6) %>%
  addTimeline(
    width = "40%",
    sliderOpts = sliderOptions(
      formatOutput = htmlwidgets::JS("function(StartDate) {return new Date(StartDate).toDateString()}"),
      # steps=length(unique(dt2$start)),
      position = "bottomright",
      duration = 130000,
      showTicks = TRUE,
      enablePlayback = TRUE,
      enableKeyboardControls = TRUE, 
      waitToUpdateMap = FALSE),
    timelineOpts = timelineOptions(
      styleOptions = styleOptions(
        color = "red",
        fillOpacity = 0.5,
        radius = 2))
    ) %>%
  addEasyButton(easyButton(icon="fa-crosshairs", title="Locate Me",
               onClick=JS("function(btn, map){ map.locate({setView: true}); }"))) %>%
  addEasyButton(easyButton(icon="fa-globe", title="Zoom to Level 1",
               onClick=JS("function(btn, map){ map.setZoom(1); }")))

Corrections

If you see mistakes or want to suggest changes, please create an issue on the source repository.

Citation

For attribution, please cite this work as

Parmar (2021, Jan. 16). Data Breadcrumbs: Parler Metadata in Leaflet. Retrieved from https://databreadcrumbs.com/posts/2021-01-16-parler-metadata/

BibTeX citation

@misc{parmar2021parler,
  author = {Parmar, Sam},
  title = {Data Breadcrumbs: Parler Metadata in Leaflet},
  url = {https://databreadcrumbs.com/posts/2021-01-16-parler-metadata/},
  year = {2021}
}