| import streamlit as st |
| import pandas as pd |
| import altair as alt |
| import json |
| from pathlib import Path |
| |
| |
|
|
| root = Path(__file__).resolve().parent.parent |
| tiles_path = root / "data" / "tiles" |
|
|
| |
| st.set_page_config(page_title= "Mass Surveillance Across America", layout= "wide") |
| chartwidth= 950 |
| mapheight= 560 |
| chartheight= 420 |
| st.title("Mass Surveillance Across America") |
| st.subheader("Visualizing Growing Camera Prevalence in the United States") |
| |
| st.markdown("""Authors: Thomas Southey, Sam, Tergel, Esther Valentin""") |
|
|
| |
| st.markdown("Across the United States, thousands of cameras are embedded into the infrasturcture of our everyday lives, mounted in our neighborhoods, street poles, parking lots, and highways there are roughly 70,000 cameras within this dataset alone. This project sets out to map and visualize that network, drawing on a public dataset compiled through a GitHub project called FLOCK, which aggregates camera location data from multiple open-source contributors, including OpenStreetMap, the Electronic Frontier Foundation's Atlas of Surveillance, and community-sourced Flock Safety camera locations. The original dataset included international camera records, but for the purposes of this article and its visualizations, we have narrowed our focus to camera records within the United States. ") |
| |
| |
| |
| |
| |
| |
| st.divider() |
|
|
| |
| |
| |
| @st.cache_data |
| def loadtiles(): |
| files= list(tiles_path.rglob("*.json")) |
| rows= [] |
| for file in files: |
| if file.name== "index.json": |
| continue |
| with open(file, "r", encoding= "utf-8") as f: |
| tile= json.load(f) |
| if "features" not in tile: |
| continue |
| for feature in tile["features"]: |
| props= feature.get("properties", {}) |
| geom= feature.get("geometry", {}) |
| if geom.get("type")!= "Point": |
| continue |
| coords= geom.get("coordinates", [None, None]) |
| if coords[0] is None or coords[1] is None: |
| continue |
| rows.append({ |
| "lon": coords[0], |
| "lat": coords[1], |
| "operator": props.get("operator", "unknown"), |
| "name": props.get("name", "unknown"), |
| "manufacturer": props.get("manufacturer", "unknown"), |
| "type": props.get("camera:type", props.get("type", props.get("surveillance:type", "unknown"))), |
| "direction": props.get("camera:direction", "unknown"), |
| "website": props.get("contact:webcam", "unknown")}) |
| return pd.DataFrame(rows) |
|
|
| df = loadtiles() |
|
|
| |
| df["operator"]= df["operator"].fillna("unknown").replace("", "unknown") |
| df["manufacturer"]= df["manufacturer"].fillna("unknown").replace("", "unknown") |
| df["name"] = df["name"].fillna("unknown").replace("", "unknown") |
| df["type"] = df["type"].fillna("unknown").replace("", "unknown") |
|
|
| |
| df= df[(df["lon"] >= -130) & (df["lon"] <= -60) & (df["lat"] >= 20) & (df["lat"] <= 55)] |
|
|
| |
| |
| |
| |
| |
| companyfix = { |
| |
| "Q108485435": "Unknown", |
| "wikidata=Q108485435": "Unknown", |
| "wikidata=q108485435": "Unknown", |
| "wikidata=Q135925643": "Unknown", |
|
|
| |
| "?": "Unknown", |
| "UNKNOWN": "Unknown", |
| "unknown": "Unknown", |
| "Unknown": "Unknown", |
| "unkn": "Unknown", |
| "Unkn": "Unknown", |
| "Unkwn": "Unknown", |
|
|
| "FLOCK SAFETY": "Flock Safety", |
| "FLOCK Safety": "Flock Safety", |
| "Floc Safety": "Flock Safety", |
| "Flock": "Flock Safety", |
| "flock": "Flock Safety", |
| "floc": "Flock Safety", |
| "Flock Group Inc.": "Flock Safety", |
| "Flock Safetu": "Flock Safety", |
| "Flock Saftey": "Flock Safety", |
| "Flock Safety Inc": "Flock Safety", |
| "Flock Surveillance": "Flock Safety", |
| "Flow Safety": "Flock Safety", |
|
|
| "Motorola": "Motorola Solutions", |
| "Motorolla": "Motorola Solutions", |
| "Mortorola Solutions": "Motorola Solutions", |
| "Motorola/Vigilant": "Motorola Solutions", |
| "Motorola?": "Motorola Solutions", |
|
|
| "AXIS": "Axis Communications", |
| "Axis": "Axis Communications", |
| "axis": "Axis Communications", |
| "Axis Communications AB": "Axis Communications", |
|
|
| "HIK VISION": "Hikvision", |
| "HIK Vision": "Hikvision", |
| "HIK vision": "Hikvision", |
| "HIKVision": "Hikvision", |
| "HK Vision": "Hikvision", |
| "HikVision": "Hikvision", |
| "hikvision": "Hikvision", |
| "Hangzhou Hikvision Digital Technology Co., Ltd.": "Hikvision", |
| "NIK VISION": "Hikvision", |
|
|
| "Hanwha (Wisevision)": "Hanwha", |
| "Hanwha Techwin": "Hanwha", |
| "Hahwha Techwin": "Hanwha", |
| "Wisevision (hanwhavision), série Q ou X": "Hanwha", |
| "Wisenet": "Hanwha", |
| "Wisenet Hanwha": "Hanwha", |
|
|
| "Dahua Technology": "Dahua", |
| "Dahua Technology Co., Ltd": "Dahua", |
| "dahua": "Dahua", |
| "Alhua": "Dahua", |
| "Alhua technology": "Dahua", |
| "alhua": "Dahua", |
|
|
| "Avigilon": "Avigilon", |
| "avigilon": "Avigilon", |
|
|
| "Ring Inc": "Ring", |
| "ring": "Ring", |
|
|
| "Rekor Systems": "Rekor", |
| "Rektor": "Rekor", |
|
|
| "Reolink Duo 4k 180deg Panorama": "Reolink", |
| "realink": "Reolink", |
| "reolink": "Reolink", |
|
|
| "Ubicquia": "Ubicquia", |
| "Ubiquiti": "Ubiquiti", |
| "ubiquiti": "Ubiquiti", |
|
|
| "Neology, Inc.": "Neology", |
| "Neology, Inc": "Neology", |
|
|
| "Leonardo US Cyber and Security Solutions, Inc.": "Leonardo", |
|
|
| "LiveView Technologies": "LVT", |
| "LifeView Technologies": "LVT", |
|
|
| "Verkada Inc.": "Verkada", |
|
|
| "ZTE Netview": "ZTE NetView", |
|
|
| "Dekom": "DEKOM", |
|
|
| "cyber Secure": "Cyber Secure", |
| "Cyber Secure?": "Cyber Secure", |
| "yber Secure?": "Cyber Secure" |
| } |
| df["company"] = df["manufacturer"].replace(companyfix) |
| st.markdown(f"""This loaded **{len(df):,} cameras** from the FLOCK files.""") |
|
|
| |
| st.header("Are surveilling cameras a growing epidemic?") |
| st.markdown("Our central interactive visualization allows you explore the FLOCK dataset across the United States by selecting one or more companies from the drop down menu. Each dot on the map represent a single camera record from the dataset correlating with the selected companies on the dropdown. As cameras are plotted with their geographic coordinates, as you toggle with our visualization, you will begin to see different geographic patterns in how vendors have distributed their infrastructure. Our visualization allows you to zoom in and out of the map to get a closer look at specific cities, regions, and states. We encourage you to explore the map with curiosity and a critical eye while receiving a better depiction of the surveillance state in the United States. ") |
| |
| |
| |
| |
|
|
| |
| companyorder = df["company"].value_counts().index.tolist() |
| companies = companyorder |
| selectedcompanies = st.multiselect("Select companies", options= companies, default= companies[:5], help= "The top 5 are shown for clarity, but use this to filter to select which companies as you like :)") |
| if selectedcompanies: |
| mapdf = df[df["company"].isin(selectedcompanies)] |
| else: |
| mapdf = df.copy() |
|
|
| |
| mapdf = mapdf.sample(min(len(mapdf), 5000), random_state= 676767) |
| |
| mapchart = alt.Chart(mapdf).mark_circle(size= 8, opacity= 0.35).encode( |
| x= alt.X("lon:Q", title= "Longitude", scale= alt.Scale(domain= [-130, -60])), |
| y= alt.Y("lat:Q", title= "Latitude", scale= alt.Scale(domain= [20, 55])), |
| color= alt.Color("company:N", title= "Company", sort= companyorder, scale= alt.Scale(domain= companyorder), legend= alt.Legend(columns= 2)), |
| tooltip= ["name", "company", "operator", "type", "direction", "lat", "lon"]).properties(width= chartwidth, height= mapheight, title= "Camera Records in the United States").interactive() |
| st.altair_chart(mapchart, width= chartwidth) |
|
|
| st.markdown("Not all surveillance cameras are created equally, and not all of the companies behind them operate the same way. The following two visualizations provide a depiction into the top camera operators, followed by the most common types of cameras which are ultimately differentiated through their purpose. In the bar chart below, the two most common companies are labeled as “Unknown” and Flock Safety. The unknown label represents cameras whose manufacture could not be identified, while Flock Safety is a company that markets its camera products primarily to law enforcement agencies, private communities, and homeowners associations. The association between the companies that deploy these cameras with specific purposes reflects a deliberate system shaped by the interests and contracts of the institutions that fund and operate it. ") |
| |
| |
| |
| |
| |
|
|
| st.divider() |
|
|
| |
| st.header("Most Common Companies?") |
| opcounts = df["company"].value_counts().head(15).reset_index() |
| opcounts.columns = ["company", "count"] |
|
|
| opchart = alt.Chart(opcounts).mark_bar().encode( |
| x= alt.X("count:Q", title= "Number of camera records"), y= alt.Y("company:N", title= "company", sort= "-x"), |
| tooltip= ["company", "count"]).properties(width= chartwidth, height= chartheight, title= "Top companys in the Dataset") |
| st.altair_chart(opchart, width= chartwidth) |
|
|
| |
| |
| st.divider() |
|
|
| |
| st.header("What types of cameras are we looking at?") |
| typecounts= df["type"].value_counts().head(12).reset_index() |
| typecounts.columns = ["type", "count"] |
|
|
| typechart = alt.Chart(typecounts).mark_bar().encode( |
| x= alt.X("type:N", title= "Camera type", sort= "-y"), y= alt.Y("count:Q", title= "Number of records"), color= alt.Color("type:N", legend= None), |
| tooltip= ["type", "count"]).properties(width= chartwidth, height= chartheight, title= "Most Common Camera Types") |
|
|
| st.altair_chart(typechart, width= chartwidth) |
|
|
| st.markdown("These camera types reflect a broader trend in public safety technology that acknowledges where the line between observation and intervention is increasingly thin. Understanding what kinds of cameras exist, and in what quantities, is a foundational step in being able to have an informed public conversation about where and how these tools are used. ") |
| |
| |
| |
| |
| |
| |
| st.caption("Visualization created via the FLOCK camera dataset.") |
| |
| st.divider() |
|
|
| |
| st.header("Why You Should Care") |
| |
| st.markdown("Surveillance infrastructure is not neutral. The choices made about where to place cameras, which companies contract with, and what data to retain are decisions made by institutions and can have real consequences for the communities living within the field of view. Historically, the communities who are most heavily surveilled have also been the most marginalized, this includes low-income neighborhoods, communities of color, and densely populated urban areas (AmericanBarOrg, 2024). Companies such as Flock Safety use cameras such as ALPR’S (Automated License Plate Recognition cameras), don’t simply record, but build a database of movement that can be shared across law enforcement agencies with little public oversight or consent from the people being tracked. As the implementation of this technology steadily increases, its widespread adaption expands into spaces that were considered to be unmonitored and private. These visualizations hope to be a starting point for critical awareness and conversation surrounding surveillance architecture and the communities it most deeply impacts. ") |
| |
| |
| |
| |
| |
| |
|
|
| |
| st.header("Sources") |
| st.markdown(""" |
| - FLOCK camera dataset: https://github.com/Ringmast4r/FLOCK |
| - Visualizations created by us using Python, Altair, and Streamlit. |
| - Dataset was loaded from the master repository tiled JSON files to avoid the 100mb limit. |
| - https://www.americanbar.org/groups/crsj/resources/human-rights/2024-june/mass-surveillance-dangerous-american-communities-reforming-section-702/ """) |