OSM gravitatsiooni kaart.md 13 KB

Eesti gravitatsioonikaardi loomine OSM teede põhjal on huvitav ja mitmetahuline ülesanne. Siin on, kuidas seda saab teha:


🧭 Mis on gravitatsioonikaart?

Gravitatsioonikaart (või liiklusgravitatsiooni mudel) ei tähenda füüsikalist gravitatsiooni, vaid ruumilist mudelit, mis näitab, kui tõenäoliselt inimesed liiguvad ühest punktist teise, arvestades teedevõrku, kaugust, ligipääsetavust ja sihtkohtade atraktiivsust.


🛠️ Kuidas luua gravitatsioonikaart OSM teede põhjal?

1. Impordi OSM teed PostGIS andmebaasi

  • Kasuta osm2pgsql, nagu sul juba on.
  • Tee eraldi tabel osm_roads, mis sisaldab ainult teid (highway IS NOT NULL).

2. Loo teede võrgustik

  • Kasuta pgRouting PostGISis, et luua graaf teede ühendustest.
  • Vajalikud väljad: source, target, cost, reverse_cost.

3. Defineeri sihtkohad ja lähtekohad

  • Näiteks: linnad, asulad, koolid, tööpiirkonnad (võib võtta OSMist või HOTOSM-ist).

4. Rakenda gravitatsioonimudelit

  • Klassikaline mudel:
    $$T_{ij} = \frac{P_i \cdot Pj}{D{ij}^2}$$ Kus:
    • $(T_{ij})$ = liikumise intensiivsus punktide $(i)$ ja $(j)$ vahel
    • $(P_i, P_j)$ = lähtekoha ja sihtkoha "mass" (nt rahvaarv, töökohtade arv)
    • $(D_{ij})$ = kaugus (või aeg) teedevõrgus

5. Visualiseeri tulemus

  • Kasuta QGIS või Python (nt matplotlib, geopandas) gravitatsioonivälja visualiseerimiseks.

📜 Skriptide ja töövoo pakkumine

Kas soovid, et:

  • Aitan luua pgRouting graafi sinu OSM teede põhjal?
  • Koostan Python-skripti, mis arvutab gravitatsioonivälja ja visualiseerib selle?
  • Loome väiksemate piirkondade jaoks eraldi kaardid?

Anna teada, millise osa soovid kõigepealt automatiseerida või visualiseerida, ja ma koostan vastava skripti!

Et luua pgRouting graaf sinu OSM teede andmete põhjal, tuleb järgida järgmisi samme. Allpool on täielik bash-skript + SQL, mis:

  1. Valmistab teetabeli (osm_roads) OSM andmetest.
  2. Lisab vajalikud veerud pgRoutingu jaoks.
  3. Käivitab pgr_createTopology, et luua graaf.

📝 1. Bash-skript: create_pgrouting_graph.sh

#!/usr/bin/env bash

# Ühenduse andmed
destination_host='localhost'
destination_port='5432'
destination_user='osm'
destination_password='osm'
destination_dbname='data'
destination_schema='aaa_osm'
table_name='osm_roads'

export PGPASSWORD=${destination_password}
CONNECTION_NAME="-h ${destination_host} -p ${destination_port} -d ${destination_dbname} -U ${destination_user}"

# Loome pgRoutingu jaoks sobiva teetabeli
psql ${CONNECTION_NAME} <<EOF
DROP TABLE IF EXISTS ${destination_schema}.${table_name};

CREATE TABLE ${destination_schema}.${table_name} AS
SELECT
  osm_id,
  name,
  highway,
  ST_Length(way) AS cost,
  ST_Length(way) AS reverse_cost,
  way AS geom
FROM
  ${destination_schema}.osm_line
WHERE
  highway IS NOT NULL
  AND way IS NOT NULL;

-- Lisame pgRoutingu jaoks vajalikud veerud
ALTER TABLE ${destination_schema}.${table_name}
  ADD COLUMN id SERIAL PRIMARY KEY,
  ADD COLUMN source INTEGER,
  ADD COLUMN target INTEGER;

-- Loome ruumiindeksi
CREATE INDEX idx_${table_name}_geom ON ${destination_schema}.${table_name} USING GIST (geom);
EOF

# Käivitame pgRouting topoloogia loomise
psql ${CONNECTION_NAME} -c "SELECT pgr_createTopology('${destination_schema}.${table_name}', 0.0001, 'geom', 'id');"

📦 Kasutamine

  1. Salvesta see failina create_pgrouting_graph.sh
  2. Tee see käivitatavaks:

    chmod +x create_pgrouting_graph.sh
    
  3. Käivita:

    ./create_pgrouting_graph.sh
    

📌 Märkused

  • cost ja reverse_cost on mõlemad määratud teelõigu pikkuse järgi. Soovi korral saab lisada ka kiiruspiiranguid või tee tüübi põhiseid kaalusid.
  • 0.0001 on topoloogia tolerants kraadides (umbes 11 m). Seda saab vajadusel muuta.

Kas soovid, et lisaksin ka liiklusklasside põhise kaaluarvutuse (nt motorway = 1, residential = 5 jne)? Või soovid kohe edasi minna gravitatsioonimudeli arvutamisega?

Suurepärane! Lisame nüüd liiklusklasside põhised kaalud, et pgRouting arvestaks erinevate teetüüpide erinevat "kulu" (cost). See võimaldab näiteks eelistada kiirteid ja vältida väiksemaid tänavaid.


🛣️ Liiklusklasside kaalud (näide)

highway tüüp cost kordaja
motorway 1
trunk 1.2
primary 1.5
secondary 2
tertiary 2.5
residential 3
service 4
track 5
footway/path 10

📝 Täiendatud Bash-skript: create_pgrouting_graph_weighted.sh

#!/usr/bin/env bash

# Ühenduse andmed
destination_host='localhost'
destination_port='5432'
destination_user='osm'
destination_password='osm'
destination_dbname='data'
destination_schema='aaa_osm'
table_name='osm_roads'

export PGPASSWORD=${destination_password}
CONNECTION_NAME="-h ${destination_host} -p ${destination_port} -d ${destination_dbname} -U ${destination_user}"

# Loome pgRoutingu jaoks sobiva teetabeli koos kaaludega
psql ${CONNECTION_NAME} <<EOF
DROP TABLE IF EXISTS ${destination_schema}.${table_name};

CREATE TABLE ${destination_schema}.${table_name} AS
SELECT
  osm_id,
  name,
  highway,
  way AS geom,
  ST_Length(way) *
    CASE
      WHEN highway = 'motorway' THEN 1
      WHEN highway = 'trunk' THEN 1.2
      WHEN highway = 'primary' THEN 1.5
      WHEN highway = 'secondary' THEN 2
      WHEN highway = 'tertiary' THEN 2.5
      WHEN highway = 'residential' THEN 3
      WHEN highway = 'service' THEN 4
      WHEN highway = 'track' THEN 5
      WHEN highway IN ('footway', 'path', 'cycleway') THEN 10
      ELSE 6
    END AS cost,
  ST_Length(way) *
    CASE
      WHEN highway = 'motorway' THEN 1
      WHEN highway = 'trunk' THEN 1.2
      WHEN highway = 'primary' THEN 1.5
      WHEN highway = 'secondary' THEN 2
      WHEN highway = 'tertiary' THEN 2.5
      WHEN highway = 'residential' THEN 3
      WHEN highway = 'service' THEN 4
      WHEN highway = 'track' THEN 5
      WHEN highway IN ('footway', 'path', 'cycleway') THEN 10
      ELSE 6
    END AS reverse_cost
FROM
  ${destination_schema}.osm_line
WHERE
  highway IS NOT NULL
  AND way IS NOT NULL;

-- Lisame pgRoutingu jaoks vajalikud veerud
ALTER TABLE ${destination_schema}.${table_name}
  ADD COLUMN id SERIAL PRIMARY KEY,
  ADD COLUMN source INTEGER,
  ADD COLUMN target INTEGER;

-- Loome ruumiindeksi
CREATE INDEX idx_${table_name}_geom ON ${destination_schema}.${table_name} USING GIST (geom);
EOF

# Käivitame pgRouting topoloogia loomise
psql ${CONNECTION_NAME} -c "SELECT pgr_createTopology('${destination_schema}.${table_name}', 0.0001, 'geom', 'id');"

✅ Kasutamine

  1. Salvesta see failina create_pgrouting_graph_weighted.sh
  2. Tee see käivitatavaks:

    chmod +x create_pgrouting_graph_weighted.sh
    
  3. Käivita:

    ./create_pgrouting_graph_weighted.sh
    

Kas soovid nüüd edasi liikuda gravitatsioonimudeli arvutamise ja visualiseerimisega väiksemate piirkondade jaoks?

Et kasutada administratiivpiirkondi hilisemas gravitatsioonimudeli analüüsis (nt teedevõrkude, liikuvus- või ligipääsetavusuuringutes), on vaja igale piirkonnale määrata kaal (attractiveness weight). Kaalud võivad põhineda näiteks:


📊 1. Kaalude tüübid – mida kasutada

Tüüpilised kaalud administratiivpiirkondade jaoks:

Kaalutüüp Allikas / meetod
Elanike arv (populatsioon) Statistikaamet, ruudustik, OSM tagid
Töökohtade arv Registreeritud töökohad või ettevõtted
Teenuste hulk Koolid, haiglad, poed, OSM POI
Pindala ST_Area(geom)
Hõive/rahvastikutihedus Pop / Area
Ligipääsetavus keskusesse Keskmine tee pikkus või aeg keskpunkti
Majandusaktiivsus Kaubandus- ja teenindusasutuste loendus

⚙️ 2. Kuidas lisada kaal PostGIS-andmebaasi

Oletame, et sul on juba tabel ardok_polygons, millel on relation_id, name, admin_level, geom.

Näide: kaalu arvutamine elanike arvu põhjal

Kui sul on näiteks statistikaameti ruudustik või andmed eraldi tabelis (nt population_grid), millel on geom ja population, võid kasutada järgmist:

ALTER TABLE public.ardok_polygons ADD COLUMN weight_pop double precision;

UPDATE public.ardok_polygons p
SET weight_pop = sub.total_pop
FROM (
  SELECT
    a.relation_id,
    SUM(g.population) AS total_pop
  FROM public.ardok_polygons a
  JOIN public.population_grid g
    ON ST_Intersects(a.geom, g.geom)
  GROUP BY a.relation_id
) AS sub
WHERE p.relation_id = sub.relation_id;

🧠 Alternatiiv: kaal OSM andmetest

Kui sul pole statistikaandmeid, saad kasutada ka nt POI arvu OSM andmetes:

ALTER TABLE public.ardok_polygons ADD COLUMN weight_poi integer;

UPDATE public.ardok_polygons p
SET weight_poi = sub.poi_count
FROM (
  SELECT
    a.relation_id,
    COUNT(poi.*) AS poi_count
  FROM public.ardok_polygons a
  JOIN public.osm_pois poi
    ON ST_Intersects(a.geom, poi.geom)
  GROUP BY a.relation_id
) AS sub
WHERE p.relation_id = sub.relation_id;

🛰️ 3. Kaalude kasutamine gravitatsioonimudelis

Gravitatsioonimudelites kasutatakse üldiselt kujul:

Interaction_ij = (Weight_i * Weight_j) / f(distance_ij)

kus Weight võib olla:

  • population, jobs, poi_count, GDP, jne,

  • ja f(distance) – näiteks distance^2, exp(distance), vms.

Saad selleks luua OD-tabeli (origin-destination paarid), mis sisaldab kõigi piirkondade vahelisi kaugusi ning liita kaalud mõlema poole kohta.


🔧 4. Praktiline töövoog

Soovituslik pipeline:

  1. Lisa weight_pop, weight_poi, area_km2, pop_density jms veerud tabelisse ardok_polygons.

  2. Loo OD-paaride tabel (piirkondade omavahelised kaugused).

  3. Kasuta psql, QGIS, Python või R gravitatsioonimudeli rakendamiseks.

  4. Analüüsi tulemusi näiteks piirkondliku liikuvuse modelleerimiseks.


📌 Küsin üle:

  • Kas Sul on statistikaameti andmeid (nt rahvastiku ruudustik)?

  • Kas soovid konkreetset SQL-skripti gravitatsioonikaalude arvutamiseks ja salvestamiseks?

  • Kas gravitatsioonimudel on sul plaanitud PostgreSQL/PostGIS-s sees, või toimub see Pythonis/R-is?

Kui vastad, saan anda veel täpsemad soovitused.

Tabeli osm_pois (Points of Interest) loomine sõltub sellest, kuidas sa OSM andmeid töötled – näiteks kas kasutad osm2pgsql, imposm, osmium või teed käsitööna SQL-ga. Kuna sul juba on osm2pgsql põhine töövoog (kasutades Lua skripti), siis soovitan luua POI-tabeli osm2pgsql kaudu.


🧭 1. POI tabeli loomine osm2pgsql Lua skriptis

Lisa oma olemasolevasse Lua-faili (nt ardok_administrative.lua kõrvale või samasse) järgmine definitsioon, et luua osm_pois tabel, kuhu salvestatakse OSM-i objektid, millel on huvipakkuvad tag’id (nt amenity, shop, tourism, office, jms).

📄 osm_pois.lua (või lisa olemasolevasse Lua-skripti):

local schema = os.getenv("PGSCHEMA") or "public"

local tables = {}

tables.pois = osm2pgsql.define_node_table(
  'osm_pois',
  {
    { column = 'name', type = 'text' },
    { column = 'type', type = 'text' },
    { column = 'tags', type = 'jsonb' },
    { column = 'geom', type = 'point', not_null = true }
  },
  {
    schema = schema,
    proj = 3301
  }
)

function osm2pgsql.process_node(object)
  local tags = object.tags
  local poi_type = tags.amenity or tags.shop or tags.tourism or tags.office or tags.leisure

  if not poi_type then
    return
  end

  tables.pois:insert({
    name = tags.name,
    type = poi_type,
    tags = tags,
    geom = object:as_point()
  })
end

🚀 2. Käivita osm2pgsql koos selle Lua-skriptiga

osm2pgsql --create \
  --database your_database \
  --output=flex \
  --style=osm_pois.lua \
  your.osm.pbf

Või kui kombineerid selle oma olemasoleva skriptiga (nt ardok_administrative.lua), võid need funktsioonid panna ühte faili, ja osm2pgsql loeb mõlemat korraga.


🛠️ 3. Tabeli sisu (näide)

name type tags (jsonb) geom
"Rimi" "shop" { "shop": "supermarket" } POINT(...)
"Kohvik" "amenity" { "amenity": "cafe" } POINT(...)

📌 4. Alternatiiv: luua käsitsi SQL-ga

Kui sa ei kasuta osm2pgsql ja soovid lihtsalt PostGIS-is POI-tabelit testimiseks, võid selle luua käsitsi:

CREATE TABLE public.osm_pois (
  id serial PRIMARY KEY,
  name text,
  type text,
  tags jsonb,
  geom geometry(Point, 3301)
);

Ja siis lisada sinna andmeid käsitsi või mõne OSM-tööriistaga (overpass, osmium, jms).


🧠 Soovitused

  • Kui töötled suurt piirkonda ja vajad ka POI-de värskendamist, kasuta kindlasti osm2pgsql koos --append või --update režiimiga.

  • Kui soovid ainult teatud tüüpi POI-sid (nt hospital, school), lisa vastav filter process_node funktsiooni.


Soovi korral võin aidata ka:

  • kombineeritud skriptiga, mis lisab POI-d olemasolevale relatsioonide töötlusele,

  • päringutega, mis seovad osm_pois ja ardok_polygons (nt ST_Intersects abil kaalu arvutamiseks).