factcircuit
FactCircuit는 불변 출처와 단계별 검증, 제한된 검색, 재현 가능한 평가를 제공하는 감사 가능한 주장·증거 검증 루프입니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
An interactive map of every recorded pedestrian fatality in the United States from 2001 to 2023 — over 123,000 incidents — drawn from the NHTSA Fatality Analysis Reporting System (FARS).
Each dot on the map is a person who died. The map makes two arguments visually:
1. Darkness kills. Fatalities cluster at dusk, night, and dawn — the solar overlay makes this causally visible, not just decorative.
2. The same roads keep killing. The road heat layer traces the arterial pathology of every city. The same stretches appear year after year.README 전문 보기 ↗An interactive map of every recorded pedestrian fatality in the United States from 2001 to 2023 — over 123,000 incidents — drawn from the NHTSA Fatality Analysis Reporting System (FARS).
Each dot on the map is a person who died. The map makes two arguments visually:
Static mode (default)
Animate mode
Incident popup
Road heat layer
Year range selector
| Layer | Technology | Notes |
|---|---|---|
| Frontend | MapLibre GL JS | Open-source Mapbox fork — no API key |
| Map tiles | OpenFreeMap | Free, no rate limits |
| Solar math | SunCalc.js | Per-slot centroid solar altitude curves |
| Backend | Python / Flask | REST API serving GeoJSON |
| Database | PostgreSQL + PostGIS | Spatial indexing for fast bbox queries |
| ETL | Python (stdlib only) | Processes raw FARS zips → PostGIS |
| Dev environment | Docker Compose | One command to start |
| Deployment target | Render + Neon | Zero ongoing cost |
data/raw/# 1. Clone (sparse — skips the large data zips)
git clone --filter=blob:none https://github.com/eddielathamjones/pedestrian-safety-mapper.git
cd pedestrian-safety-mapper
# 2. Configure environment
cp .env.example .env
# 3. Start PostGIS + Flask
docker compose up -d
# 4. Load data
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# Single year
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/pedestrian_safety \
python -m src.data_processing.etl --years 2023 --data-dir data/raw
# All years (requires full data/raw/ checkout)
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/pedestrian_safety \
python -m src.data_processing.etl --years 2001-2023 --data-dir data/raw
# 5. Open http://localhost:5001
python scripts/data_download.py
Downloads all years to data/raw/ as FARS{year}NationalCSV.zip. Decimal lat/lon coordinates are available from 2001 onwards; the ETL filters out records with missing or sentinel coordinates.
NHTSA publishes a new FARS year annually, typically August–October for the prior year. The app checks for this automatically — no manual monitoring needed to know when new data lands:
GET /api/data-status compares the live DB's max year against what's published on NHTSA and is polled once per page load (15-minute cache); if a newer year is available, the frontend shows a "Data available through YYYY" badge.scripts/check_fars_update.py does the same check standalone (python scripts/check_fars_update.py), useful for a cron/CI check outside the running app. It exits non-zero when an update is available.When a new year is available, ingest it:
# 1. Download just the new year (not the full 1975-2024 range)
python scripts/data_download.py --years 2025
# 2. Run the ETL for that year only
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/pedestrian_safety \
python -m src.data_processing.etl --years 2025 --data-dir data/raw
# 3. Update DB_MAX_YEAR in scripts/check_fars_update.py to the new max
The ETL does not deduplicate — it's a plain INSERT, not INSERT ... ON CONFLICT. Only run it for years not already in the DB; re-running it for an already-loaded year will duplicate every row for that year. If a year needs to be reloaded, delete it first: DELETE FROM incidents WHERE year = 2025;.
pedestrian-safety-mapper/
├── src/
│ ├── backend/
│ │ ├── app.py # Flask API
│ │ └── schema.sql # incidents table + GiST spatial index
│ ├── frontend/
│ │ ├── index.html
│ │ ├── css/app.css
│ │ └── js/app.js # map, animation, filter, solar overlay, popup
│ └── data_processing/
│ └── etl.py # FARS zips → PostGIS
├── data/
│ └── raw/ # FARS zip files (not tracked in git)
├── docs/
│ ├── roadmap.md # Design direction and priorities
│ ├── solar-correction.md # Solar position methodology — full technical writeup
│ ├── future-ml-direction.md
│ └── shaping/ # Product design decisions (R, shapes, slices)
├── scripts/
│ └── data_download.py
├── docker-compose.yml
├── requirements.txt
└── .env.example
GET /api/incidentsReturns a GeoJSON FeatureCollection of pedestrian fatalities.
| Parameter | Type | Required | Description |
|---|---|---|---|
year | integer | yes | Year to query (2001–2023) |
bbox | string | no | minLon,minLat,maxLon,maxLat |
Example response:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-86.69, 33.54] },
"properties": {
"year": 2022, "month": 1, "day": 2,
"hour": 18, "minute": 48,
"lgt_cond": 2, "weather": 1, "route": 5, "rur_urb": 2,
"state": 1, "county": 73,
"age": 27, "sex": 1, "inj_sev": 4
}
}]
}
GET /api/summaryReturns total incident counts by year.
{ "2001": 4389, "2022": 7522 }
| Field | Source | Description |
|---|---|---|
geom | ACCIDENT | Point geometry (EPSG:4326) |
year, month, day | ACCIDENT | Crash date |
hour, minute | ACCIDENT | Local wall-clock time (99 = unknown) |
lgt_cond | ACCIDENT | 1=Daylight, 2=Dark–not lit, 3=Dark–lit, 4=Dawn, 5=Dusk |
weather | ACCIDENT | 1=Clear, 2=Rain, 3=Sleet, 4=Snow, 5=Fog |
route | ACCIDENT | 1=Interstate, 2=US Hwy, 3=State, 4=County, 5=Local |
rur_urb | ACCIDENT | 1=Rural, 2=Urban (available 2013+) |
state, county | ACCIDENT | FIPS codes |
age, sex | PERSON | Victim demographics (FARS sentinel values suppressed in display) |
inj_sev | PERSON | Injury severity (all records = 4, fatal) |
The map applies a solar position overlay rather than using the FARS lgt_cond field directly. Clock time is a social construct that varies by time zone and daylight saving. Solar altitude is a physical measurement.
For each hour slot, the app computes a centroid from all incidents at that hour, then uses SunCalc.js to calculate the sun's altitude at the equinox for that location. This produces a 24-element solar curve that drives the night overlay opacity and Sun HUD display.
Full technical writeup — altitude formula, UTC offset approximation, smoothstep opacity mapping, per-slot centroid method, and known limitations — is in docs/solar-correction.md.
| Slice | Status | Description |
|---|---|---|
| V1 — Data on the map | ✅ Done | Fatalities as interactive points |
| V2 — Year selector | ✅ Done | Multi-year filter (2001–2023) |
| V3 — Incident popup | ✅ Done | Date, time, lighting, weather, demographics, Street View |
| V4 — Viewport loading | ✅ Done | Fetch only visible incidents on pan/zoom |
| V5 — Extended history | ✅ Done | Full 2001–2023 dataset (123k+ incidents) |
| V6 — Solar overlay + animation | ✅ Done | 24h and week animation, solar-corrected night overlay, Sun HUD |
| V7 — Static filter mode | ✅ Done | Time window, day-of-week chips, presets, solar condition label |
| V8 — Road heat layer | ✅ Done | Kernel-density heat lines on roads |
| Phase 2 — Solar terminator | 💡 Planned | Real terminator line as a GeoJSON layer |
| Phase 2 — Sound layer | 💡 Planned | Ambient tone per dot on first appearance |
| Phase 2 — Streetlight overlay | 💡 Planned | OSM streetlight data correlated with fatality clusters |
| Phase 3 — Predictive risk model | 💡 Planned | ML risk scoring from FARS feature data |
| Phase 3 — Street View forensics | 💡 Planned | Embedded panel, thumbnails, vehicle direction |
See docs/roadmap.md for design direction and priorities.
MIT — see LICENSE for details.
Research and educational use. Consult official sources for safety-critical decisions.