Tourism — arrivals and overnight stays province by province
Tourist flows in accommodation establishments, province by province: arrivals and overnight stays over the years, the foreign-tourism share, average length of stay, and a map of Italy with overnight stays per inhabitant. Official ISTAT data, 2019-2025.
data space «turismo» — the suite’s glue: on your device, multi-user with one invite (link or QR), end-to-end encrypted.
What it records
- Province code
Composed of 3 micro apps
One job each, over the same data: every micro app reads and writes the shared data space “turismo” (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
# 🏖️ Tourism
Tourist flows in accommodation establishments, province by province:
arrivals and overnight stays over the years, the foreign-tourism share,
average length of stay, and a map of Italy with overnight stays per
inhabitant. Official ISTAT data, 2019-2025.
:::::page{title="Your province" icon="📈"}
## The trend in your province
Province code (e.g. `BO`, `MI`, `TO`, `NA`, `VE`).
::input[prov]{value="VE" placeholder="Province code"}
::od-query{into="trend" sql="SELECT anno AS anno, arrivi AS arrivi, presenze AS presenze FROM turismo WHERE sigla = '{#prov}' ORDER BY anno"}
::chart-line{data="trend" x="anno" y="arrivi,presenze" height="16rem"}
### And the coming years?
A linear trend on **overnight stays** extends the series: move the slider for
the horizon. The first run downloads the compute engine (one time only), then
the forecast updates by itself (the R² in the status says how much of the
series the trend explains).
::range[anni]{min="1" max="5" value="3" legend="Years to forecast"}
::ml-forecast{data="trend" x="anno" y="presenze" horizon="#anni" into="trendPrev"}
::chart-line{data="trendPrev" x="anno" y="presenze,previsione" height="16rem"}
:::table{path="trend" headers="Year,Arrivals,Overnight stays" pagesize="8" search="false"}
{anno} | {arrivi} | {presenze}
:::
**Overnight stays** are nights spent, **arrivals** are registered guests:
their ratio is the average length of stay.
:::::
:::::page{title="The detail" icon="🌍"}
## Domestic and foreign
The latest available year, for the chosen province.
::od-query{into="quota" sql="SELECT quota_straniera AS quota_straniera, permanenza_media AS permanenza_media, arrivi AS arrivi, arrivi_stranieri AS arrivi_stranieri FROM turismo WHERE sigla = '{#prov}' AND anno = (SELECT max(anno) FROM turismo WHERE sigla = '{#prov}')"}
:::cards{path="quota" search="false"}
🌍 Foreign tourists: **{quota_straniera}%** of arrivals — average stay **{permanenza_media}** nights
:::
::od-query{into="origine" sql="SELECT unnest(['Foreign', 'Domestic']) AS origine, unnest([arrivi_stranieri, arrivi - arrivi_stranieri]) AS arrivi FROM turismo WHERE sigla = '{#prov}' AND anno = (SELECT max(anno) FROM turismo WHERE sigla = '{#prov}')"}
::chart-pie{data="origine" label="origine" value="arrivi" height="16rem"}
:::::
:::::page{title="The map" icon="🗺️"}
## Overnight stays per inhabitant, province by province
Each province coloured by **tourist overnight stays per inhabitant**, latest
available year: the numerator is ISTAT tourism data, the denominator ISTAT
population. Hover for the value.
::od-query{into="mappa" sql="SELECT p.provincia AS provincia, p.geojson AS geojson, round(t.presenze / ab.pop, 1) AS presenze_per_ab FROM istat_confini_province p JOIN turismo t ON t.sigla = p.sigla JOIN (SELECT c.sigla, sum(po.popolazione) AS pop FROM istat_confini_comuni c JOIN istat_popolazione po ON po.codice_istat = c.codice_istat GROUP BY 1) ab ON ab.sigla = p.sigla WHERE t.anno = (SELECT max(anno) FROM turismo)" limit="150"}
:::map{path="mappa" geojson="geojson" fill="presenze_per_ab" height="28rem"}
**{provincia}** — {presenze_per_ab} tourist overnight stays per inhabitant
:::
Art cities and seaside/mountain destinations top the chart by an order of
magnitude over the rest: a direct indicator of tourism pressure on the
territory.
:::::
---
The data are **tourist flows in accommodation establishments** (source
ISTAT — IstatData, open data): arrivals, overnight stays, foreign-tourism
share and average length of stay, plus ISTAT **population**/**boundaries**
(CC BY 4.0). The sources join on their own via the province code. None of
your data leaves the device.