Public safety — crime province by province
Crimes reported to the police, province by province: trends over the years, a comparison between the main crime types, and a map of Italy with the rate per 100,000 inhabitants. Official ISTAT data, 2020-2024.
data space «sicurezza-pubblica» — the suite’s glue: on your device, multi-user with one invite (link or QR), end-to-end encrypted.
What it records
- Province code
- Province code (e.g. BO)
Composed of 3 micro apps
One job each, over the same data: every micro app reads and writes the shared data space “sicurezza-pubblica” (with your consent, asked on first open). Everything stays on your device — and with a single invite the whole suite goes multi-user, end-to-end encrypted.
How it works
This is a Reactive app: a plain text document that your browser turns into a working app — forms, live data, tables, statistics and even charts, with nothing to install or configure.
- Use it right away. Hit “Use this app”: it opens right in your browser, ready to go, with data saved only on your device.
- Your data stays yours. Everything is stored locally on your device, kept separate for each app: no account, no server, nothing to set up.
- Make it your own. The text below is the entire app: copy it, tweak fields, views and words, and your changes become interactive instantly.
- Share it as a file. An app is a simple file: save it, export it or share it with a link; and with encrypted collaboration several people edit the same data in real time.
The app’s source
# 🚨 Public safety
Crimes reported to the police, province by province: trends over the years,
a comparison between the main crime types, and a map of Italy with the rate
per 100,000 inhabitants. Official ISTAT data, 2020-2024.
:::::page{title="Your province" icon="📈"}
## The trend in your province
Province code (e.g. `BO`, `MI`, `TO`, `NA`, `PA`).
::input[prov]{value="BO" placeholder="Province code"}
::od-query{into="trend" sql="SELECT anno AS anno, tasso_per_100k AS tasso FROM delitti WHERE sigla = '{#prov}' AND tipo_reato = 'totale' ORDER BY anno"}
::chart-line{data="trend" x="anno" y="tasso" height="16rem"}
:::table{path="trend" headers="Year,Crimes per 100,000 inh." pagesize="6" search="false"}
{anno} | {tasso}
:::
The rate is **per 100,000 inhabitants**: already normalized, comparable
across provinces of different size without further cross-referencing.
:::::
:::::page{title="The crimes" icon="🔍"}
## What kind of crime
The latest available year, for the chosen province: theft, robbery,
intentional homicide and drug offences (rate per 100,000 inhabitants).
::input[prov]{value="BO" placeholder="Province code (e.g. BO)"}
::od-query{into="reati" sql="SELECT CASE tipo_reato WHEN 'furti' THEN 'Theft' WHEN 'rapine' THEN 'Robbery' WHEN 'omicidi_volontari' THEN 'Intentional homicide' WHEN 'stupefacenti' THEN 'Drug offences' ELSE tipo_reato END AS reato, tasso_per_100k AS tasso FROM delitti WHERE sigla = '{#prov}' AND tipo_reato != 'totale' AND anno = (SELECT max(anno) FROM delitti) ORDER BY tasso DESC"}
::od-query{into="serieReati" sql="SELECT anno AS anno, CASE tipo_reato WHEN 'furti' THEN 'Theft' WHEN 'rapine' THEN 'Robbery' WHEN 'omicidi_volontari' THEN 'Intentional homicide' WHEN 'stupefacenti' THEN 'Drug offences' ELSE tipo_reato END AS reato, tasso_per_100k AS tasso FROM delitti WHERE sigla = '{#prov}' AND tipo_reato != 'totale' ORDER BY anno DESC, tasso DESC" limit="200"}
**Click a bar** to filter the historical series on that offence (second
click: filter cleared — the cross-filtering of real BI tools).
::::dashboard{path="reati"}
::chart-bar{data="reati" x="reato" y="tasso" height="18rem"}
:::table{path="serieReati" headers="Year,Offence,Rate per 100k" pagesize="8" search="false"}
{anno} | {reato} | {tasso}
:::
::::
::chart-pie{data="reati" label="reato" value="tasso" height="16rem"}
:::::
:::::page{title="The map" icon="🗺️"}
## Crime rate, province by province
Each province coloured by **total crimes per 100,000 inhabitants**, latest
available year. Hover for the value.
::od-query{into="mappa" sql="SELECT p.provincia AS provincia, p.geojson AS geojson, d.tasso_per_100k AS tasso FROM istat_confini_province p JOIN delitti d ON d.sigla = p.sigla WHERE d.tipo_reato = 'totale' AND d.anno = (SELECT max(anno) FROM delitti)" limit="150"}
:::map{path="mappa" geojson="geojson" fill="tasso" height="28rem"}
**{provincia}** — {tasso} crimes per 100,000 inhabitants
:::
The crime rate also depends on how much a territory *reports*, not only on
how much happens: big cities and tourist areas tend to top the charts for
property crime.
:::::
---
The data are **crimes reported to the judicial authority by the police**
(source ISTAT — IstatData, open data), rate per 100,000 inhabitants by crime
type, and ISTAT **boundaries** (CC BY 4.0). The sources join on their own
via the province code. None of your data leaves the device.