krz/omaha-metro-blotter
Archive of police activity and ALPR surveillance across the Omaha metro.
clone: git clone https://gitbay.org/krz/omaha-metro-blotter.git
archive: README.nfo · raw
1┌──────────────────────────────────────────────────────────────┐
2│ O M A H A I N C I D E N T S [ KRZ ] krz.sh │
3└──────────────────────────────────────────────────────────────┘
4
5WHAT
6 police activity across the omaha metro, pulled from the agencies'
7 own feeds and joined against alpr camera locations from
8 openstreetmap.
9
10COVERAGE
11 omaha pd dcgis arcgis view, 2022-01-01 onward,
12 nibrs offence records, updated daily.
13 no stop or disposition data.
14 bellevue pd sarpy county publiccrimemap, cad calls
15 papillion pd for service with stop type, disposition
16 la vista pd and category. rolling 12-month window:
17 sarpy county so records age out of the feed, so the local
18 archive is the only long-term copy. gretna
19 and springfield appear as fire only; their
20 police departments do not report to it.
21 council bluffs pd cbpd public cfs feed, refreshed every ten
22 minutes, rolling 12-month window. stop
23 type, disposition, priority, response time.
24 street addresses withheld; points exact.
25 ralston pd no machine-readable feed. absent.
26
27 alpr cameras openstreetmap via overpass, the same data
28 deflock renders. 168 nodes in the metro
29 bbox. odbl, attribution required.
30
31SETUP
32 uv venv .venv
33 uv pip install --python .venv/bin/python -r requirements.txt
34
35USE
36 .venv/bin/python ingest.py --full # first run, backfill
37 .venv/bin/python ingest.py # daily, last 30 days
38 .venv/bin/python ingest.py cbpd sarpy # one source at a time
39 .venv/bin/python ingest.py opd_csv # 2015-2023 csv archive
40 .venv/bin/python app.py
41
42ARCHIVE
43 .github/workflows/daily-pull.yml runs the pull at 11:17 utc and
44 keeps the database as metro.db.gz on the "archive" release, so the
45 archive does not depend on any one machine. each run restores that
46 asset, pulls, refuses to publish if any source came back with fewer
47 rows than it started with, then uploads and fails loudly if a feed
48 has not moved in seven days.
49
50 first run: trigger it manually with bootstrap enabled, which pulls
51 every feed in full and creates the release. after that the restore
52 step is mandatory -- a bootstrap over a live archive throws away
53 whatever has already aged out of the sarpy and council bluffs
54 feeds.
55
56 github disables scheduled workflows after 60 days without repo
57 activity, and emails first. that is the most likely way this stops
58 quietly.
59
60 to run the pull locally instead:
61
62 0 6 * * * cd /path/to/omaha-incidents && .venv/bin/python ingest.py
63
64NOTES
65 all three arcgis services return utc epochs; their where-clause
66 literals do not agree (opd and council bluffs utc, sarpy central).
67 ingest.py stores occurred_at in local time.
68
69 each feed has its own taxonomy and none of them are comparable, so
70 ingest.py derives one cross-agency flag, is_stop, per source. stop
71 outcomes compare citation and arrest rate by substring, which is
72 all the two disposition vocabularies support: an agency that
73 records warnings less thoroughly shows a higher citation rate for
74 that reason alone.
75
76 colour scheme follows prefers-color-scheme. plotly writes colours
77 into the figure, so assets/theme.js reports the media query into a
78 store and app.py builds each figure from it. restyling after the
79 fact does not work: swapping a maplibre basemap at runtime leaves
80 it rebuilding with no data layers.
81
82 the camera-proximity panel compares stops against a non-stop
83 baseline. cameras and stops both concentrate on arterials, so a
84 gap between the curves is a starting point, not a finding.
85
86 raw_data/ingress.db is the old 2015-2023 sqlite build. nothing
87 reads it any more.
88
89SCREENSHOTS
90 screenshots/*.png are from the previous 2015-2023 dashboard.
91
92┌──────────────────────────────────────────────────────────────┐
93│ krz.sh │
94└──────────────────────────────────────────────────────────────┘