Self-Hosted Google Maps egy Raspberry Pi-n
Mindig is lenyűgözött az a gondolat, hogy a saját négy falunk között is fusson egy olyan térkép szolgáltatás, ami nem függ az internetkapcsolattól. Egy olyan „Google Maps”, ami a saját gépemen fut, a saját adataimat használja, és akkor is működik, ha épp nincs net. Az inspiration a N.O.M.A.D. (Network of Offline Mapping and Discovery) projektből jött, akik egy teljes offline szolgáltatás stack-et álmodtak meg. Az ő nyomdokaikon haladva építettem meg a saját verziómat, ami egy Raspberry Pi-n fut, Docker konténerekben, és teljes Magyarországot lefedi.
A hardver – Mi ez a gép?
A szolgáltatás a xorp-archive.xorp.hu nevű gépemen fut, ami nem egy átlagos Raspberry Pi. Egy Raspberry Pi ARM64 architektúrájú eszköz, 8 GB RAM-mal, NVMe HAT kiegészítővel és egy 1 TB-os Crucial CT1000E100SSD8 NVMe SSD-vel. Ez nem a régi SD kártyás megoldás – az NVMe sebessége és megbízhatósága nélkülözhetetlen egy ilyen adatigényes stack működtetéséhez. A gép Ubuntu 24.04 LTS-t futtat, a kernel pedig a Raspberry Pi-hez optimalizált 6.8.0-1057-raspi.
1. ábra: A Raspberry Pi NVMe HAT-tal és SSD-vel. A képen a Raspberry Pi 5, a ráhelyezett M.2 HAT+ és az NVMe SSD látható.
Az ötlet – Miért építsünk offline térképet?
A válasz egyszerű: szabadság. Amikor nincs internet, vagy korlátozott a sávszélesség, a legtöbb térkép szolgáltatás használhatatlanná válik. Gondoljunk csak egy kirándulásra a Pilisbe, egy külföldi utazásra roaming nélkül, vagy épp egy olyan helyzetre, ahol az adatvédelem miatt nem szeretnénk külső szolgáltatókra bízni magunkat. Egy offline térkép ezekre mind megoldást nyújt.
A stack négy fő komponensből áll, amelyek Docker konténerekben futnak:
- OSM Tile Server – a térkép csempék (tile-ok) kiszolgálása
- Photon – címkeresés, geokódolás
- GraphHopper – útvonaltervezés (autó, bicikli, gyalogos)
- GraphHopper PT – tömegközlekedési útvonaltervezés (MÁV, Volánbusz, BKK)
- MapLibre GL UI – a webes felület, ami összefogja az egészet
Az architektúra áttekintése
Minden kérés egyetlen belépési ponton, a map-ui konténeren keresztül történik, ami egy Nginx reverse proxyként szolgál. Ez nemcsak egyszerűbbé teszi a konfigurációt, hanem kiküszöböli a CORS problémákat is, hiszen a böngésző számára minden kérés ugyanarról az origin-ről érkezik.
Böngésző → port 8090
└── Nginx (map-ui konténer)
├── /tile/{z}/{x}/{y}.png → OSM (8081)
├── /photon/api → Photon (2322)
├── /graphhopper/route → GraphHopper (8989)
└── /graphhopper-pt/route → GraphHopper PT (8989)A Docker Compose – Minden együtt
A teljes stack egyetlen docker-compose-osm-extended.yml fájlban van definiálva. Lássuk a lényeget (az IP címeket és elérési utakat természetesen megváltoztattam a valóságban):
services:
osm-import:
image: overv/openstreetmap-tile-server
container_name: osm-import
command: import
shm_size: 1gb
environment:
THREADS: 4
UPDATES: enabled
volumes:
- /home/osm/data/hungary-latest.osm.pbf:/data/region.osm.pbf
- /home/osm/data/hungary.poly:/data/region.poly
- osm-db:/data/database
- osm-tiles:/data/tiles
osm:
image: overv/openstreetmap-tile-server
container_name: osm
command: run
shm_size: 1gb
ports:
- 8081:80
environment:
UPDATES: enabled
THREADS: 4
REPLICATION_URL: https://planet.openstreetmap.org/replication/minute/
volumes:
- osm-db:/data/database
- osm-tiles:/data/tiles
photon:
image: local/photon:1.1.0
container_name: photon
command: ["-listen-ip", "0.0.0.0", "-cors-any"]
ports:
- 2322:2322
volumes:
- /home/osm/photon:/data/photon
graphhopper:
image: local/graphhopper:9.1
container_name: graphhopper
ports:
- 8989:8989
volumes:
- /home/osm/graphhopper:/data
- /home/osm/data/hungary-latest.osm.pbf:/data/region.osm.pbf
graphhopper-pt:
image: local/graphhopper:9.1
container_name: graphhopper-pt
ports:
- 8991:8989
volumes:
- /home/osm/graphhopper-pt:/data
- /home/osm/data/hungary-latest.osm.pbf:/data/region.osm.pbf
- /home/osm/gtfs:/data/gtfs:ro
entrypoint: ["java", "-Xmx6g", "-jar", "/app/graphhopper-web.jar", "server", "/app/config.yml"]
map-ui:
image: local/osm-ui:1.0
container_name: map-ui
ports:
- 8090:80
volumes:
- /root/osm-ui:/usr/share/nginx/html:ro
- /home/osm/map-ui/conf/default.conf:/etc/nginx/conf.d/default.conf:ro1. OpenStreetMap Tile Server – A térkép alapja
Az overv/openstreetmap-tile-server Docker image egy előre összeállított környezet, ami tartalmazza az Apache webszervert, a renderd-et (a csempe renderelő démont), a PostgreSQL-t PostGIS kiegészítővel, és a mod_tile-t, ami a csempe kiszolgálásért felel. Ez a négy komponens együtt teszi lehetővé, hogy a nyers OpenStreetMap adatokból szép, böngészhető térképi csempéket (tile-okat) kapjunk.
Az import folyamat
Minden egy PBF fájllal kezdődik – ez a Protocol Buffer Binary Format, az OpenStreetMap adatok tömörített formátuma. Nekünk a Magyarországra vonatkozó kivonatra van szükségünk, ami a geofabrik.de-ról szerezhető be. Ez a fájl a /home/osm/data/hungary-latest.osm.pbf elérési úton található.
Az import az osm-import konténer egyszeri futtatásával történik:
# Első importálás (csak egyszer kell)
docker compose -f docker-compose-osm-extended.yml run --rm osm-import
# Ha frissíteni kell a PBF-et, töröljük a régi adatbázist és újraimportálunk
docker compose -f docker-compose-osm-extended.yml down
# ... új PBF letöltése ...
docker compose -f docker-compose-osm-extended.yml run --rm osm-import
docker compose -f docker-compose-osm-extended.yml up -d osmA konténer a shm_size: 1gb beállítással fut, mert az importálás és a renderelés során rengeteg megosztott memóriára van szükség a gyorsítótárazáshoz. Ez egy kritikus beállítás – ha kicsi a shared memory, a renderelés rendkívül lassú lesz.
3. ábra: Az OSM tile server folyamatos frissítése – a docker compose logs kimenete mutatja az osm2pgsql importálást és a percenkénti diff letöltést.
Folyamatos frissítés
A legmenőbb funkció a beépített incrementális frissítés. Az UPDATES: enabled környezeti változó hatására a konténer percenként letölti a minute diff fájlokat a planet.openstreetmap.org-ról. Ez azt jelenti, hogy a térképünk gyakorlatilag valós időben frissül – ha valaki módosít egy utat az OSM-ben, az pár percen belül megjelenik a saját szerverünkön is. Természetesen ha nincs internet, a script egyszerűen csendben meghibásodik, és a konténer tovább szolgálja a már meglévő csempéket.
2. Photon – Címkeresés geokódolással
A Photon egy nyílt forráskódú geokódoló motor, ami az OpenStreetMap adatokra épül. A mi stackünkben a local/photon:1.1.0 image fut, ami egy Magyarországra szabott adatbázissal dolgozik. Ez nem a teljes világ – csak Magyarország, ami jelentősen csökkenti a keresési időt és a tárhely igényt.
A Photon adatbázis frissítése egy /root/photon-import.sh script segítségével történik, ami hetente egyszer fut (vasárnap 11:30-kor a cron-ban).
A script működése:
Letölti a legújabb Magyarország Photon dump fájlt(zst tömörítésben) a graphhopper.com-rólEllenőrzi a metaadatokat (ETag, last-modified) – ha nem változott, kihagyja a letöltéstLeállítja a Photon konténertEgy ideiglenes Docker konténerben dekompresszálja és importálja az adatbázistFrissíti a szimbolikus linket az új import könyvtárraÚjraindítja a Photon konténertEllenőrzi a szolgáltatás állapotát egy egyszerű API hívással# A Photon import script lényege (egyszerűsítve):
docker run --rm \
--entrypoint sh \
-v /home/osm/photon:/data/photon \
-v /home/osm/photon-dumps:/data/dumps \
local/photon:1.1.0 \
-lc "zstd -d --stdout /data/dumps/photon-dump-hungary.jsonl.zst \
| java -Xms2g -Xmx4g -jar /app/photon.jar import \
-import-file - -data-dir /data/photon/imports/20260603200523"
# A szimbolikus link frissítése:
ln -sfn /home/osm/photon/imports/20260603200523 /home/osm/photon/photon_dataAz importált adatbázisok verziózott könyvtárakba kerülnek (/home/osm/photon/imports/20260603200523/), így mindig vissza lehet térni egy régebbi verzióhoz, ha valami elromlana. Egy szimbolikus link (photon_data -> imports/20260603200523/) mutat az aktuális verzióra.
4. ábra: Címkeresés a Photon segítségével – „Eger, Dobó tér” keresése és megjelenítése a térképen.
3. GraphHopper – Útvonaltervezés (autó, bicikli, gyalogos)
A GraphHopper egy nagy teljesítményű, nyílt forráskódú útvonaltervező motor, ami az OpenStreetMap úthálózatára épül. A mi stackünkben a local/graphhopper:9.1 image fut, 4 GB RAM allokációval.
A GraphHopper három útvonal profilt támogat:
- car – autós közlekedés, figyelembe veszi az egyirányú utcákat, sebességkorlátozásokat, út típusokat
- bike – biciklis közlekedés, preferálja a kerékpárutakat, kerüli a főútvonalakat
- foot – gyalogos közlekedés, használja a járdákat, gyalogutakat, lépcsőket
A konfiguráció /home/osm/graphhopper/config.yml-ban található:
graphhopper:
datareader.file: /data/region.osm.pbf
graph.location: /data/graph-cache
profiles:
- name: car
- name: bike
- name: foot
profiles_ch:
- profile: car
- profile: bike
- profile: foot
graph.dataaccess.default_type: RAM_STORE
server:
application_connectors:
- type: http
port: 8989
bind_host: 0.0.0.0A Contraction Hierarchies (CH) egy előfeldolgozási technika, ami drámaian felgyorsítja az útvonal keresést. Az autó és bicikli profilokhoz CH-t használunk, ami az első induláskor 5-10 perc előfeldolgozást igényel, de utána a lekérések ezredmásodpercek alatt teljesülnek.
5. ábra: Autós útvonaltervezés a GraphHopper segítségével – Budapestről Debrecenbe (231,5 km, 2 óra 12 perc).
4. GraphHopper PT – Tömegközlekedési útvonaltervezés
Ez a stack talán legmenőbb komponense. A GraphHopper PT (Public Transport) egy speciális konfigurációja a GraphHopper-nak, ami nemcsak az OSM úthálózatot, hanem a menetrendi adatokat (GTFS) is felhasználja a tömegközlekedési útvonaltervezéshez.
GTFS adatforrások
Három GTFS adatforrást használunk, amelyek Magyarország teljes tömegközlekedését lefedik:
SzolgáltatóForrásMéretFrissítés
MÁV (Magyar Államvasutak)mavcsoport.hu (Basic Auth)~5 MBNaponta
Volánbusz (Helyközi buszok)opendata.utas.hu~110 MBNaponta
BKK (Budapesti Közlekedési Központ)go.bkk.hu~55 MBHetente
A MÁV regisztrációs kálvária
A MÁV GTFS elérése sajnos nem nyilvános – a mavcsoport.hu/gtfs oldalon kell regisztrálni, ahol egy rövid adatkezelési hozzájárulás után kapunk Basic Auth felhasználónevet és jelszót. A regisztráció egyszerű, de a mai napig nem értem, miért kell ez egy nyílt GTFS adatfolyamhoz – a MÁV indoklása szerint azért, hogy „tudják, hányan és milyen célra használják az adataikat”. Nos, a mi célunk egyértelmű: jobb tömegközlekedési információt adni a felhasználóknak.
A Volánbusz és a BKK szerencsére nyíltan, mindenféle regisztráció nélkül elérhető – köszönet érte!
A GTFS szinkronizáló script
A /root/gtfs-sync.sh script naponta (hajnali 3:30-kor) letölti mindhárom GTFS fájlt, majd újraindítja a graphhopper-pt konténert, hogy az új menetrend alapján újraépítse a gráfot. Ez a folyamat 2-5 percet vesz igénybe – ezalatt a tömegközlekedési útvonaltervezés nem elérhető.
# A GTFS szinkronizáló script lényege (egyszerűsítve):
# MÁV letöltés (Basic Auth-val)
curl -sSf -u "user:password" \
-o /home/osm/gtfs/mav_gtfs.zip \
"https://www.mavcsoport.hu/gtfs/gtfsMavMenetrend.zip"
# Volánbusz letöltés
curl -sSf -o /home/osm/gtfs/volanbusz_gtfs.zip \
"https://opendata.utas.hu/public-gtfs/volanbusz_gtfs.zip"
# BKK letöltés
curl -sSf -o /home/osm/gtfs/bkk_gtfs.zip \
"https://go.bkk.hu/api/static/v1/public-gtfs/budapest_gtfs.zip"
# GraphHopper PT újraindítása az új menetrendek betöltéséhez
docker restart graphhopper-ptÉrdekesség, hogy a GraphHopper PT konfigurációja egyszerre három GTFS fájlt is be tud tölteni – a gtfs.file paraméterben vesszővel felsorolva:
graphhopper:
gtfs.file: /data/gtfs/mav_gtfs.zip,/data/gtfs/volanbusz_gtfs.zip,/data/gtfs/bkk_gtfs.zip
# ...és az OSM fájl a gyalogos kapcsolatokhozEz azért fontos, mert a tömegközlekedési útvonaltervezésnél nem elég a menetrend – tudni kell, hogyan jutok el a buszmegállóhoz gyalog, amihez pontosan az OSM adataira van szükség.
6. ábra: Tömegközlekedési útvonaltervezés a GraphHopper PT segítségével – Egerből a Budapest-Keleti pályaudvarra, átszállás nélkül, 2 óra 33 perc alatt.
5. A Webes Felület – MapLibre GL UI
A felhasználói felület MapLibre GL JS-re épül, ami a Mapbox GL JS nyílt forráskódú továbbfejlesztett változata. A map-ui konténer egy Nginx Alpine image, ami a statikus fájlokat szolgálja ki és proxy-zik a háttérszolgáltatások felé.
Ami különösen fontos: minden JavaScript és CSS fájl lokálisan van tárolva a /root/osm-ui/vendor/ könyvtárban. Nincs CDN függőség, nincs Google Fonts, nincs unpkg – ha elmegy az internet, a felület továbbra is teljesen működőképes marad.
A UI funkciói
- Térkép nézet – raszteres csempék MapLibre GL-lel, teljes képernyős interakció
- Keresés – autocomplete címkeresés a Photon segítségével, kattintásra odarepülés
- Útvonaltervezés – A→B útvonal négy módban: autó, bicikli, gyalogos, tömegközlekedés
- Kanyarról-kanyarra navigáció – összecsukható lista az útvonal panelen, bármelyik kanyarra kattintva odarepül a térkép
- Több alternatíva – tömegközlekedésnél több útvonal közül választhatunk indulási/idő szerint
- Járat jelvények – színkódolt jelvények a különböző járatokhoz (busz=kék, villamos=sárga, metró=piros, vasút=lila, stb.)
- Rétegek váltása – OSM (alap) és Műhold (ESRI World Imagery) között
- Offline érzékelés – a műhold réteg automatikusan letiltódik, ha nincs internet
7. ábra: A teljes webes felület – a térkép, keresés és útvonaltervezés használata lépésről lépésre.
Offline audit – Mi működik internet nélkül?
Ez a projekt egyik legfontosabb szempontja. Lássuk, mi a helyzet:
Teljesen offline (nincs szükség internetre)
- ✅ Térképi csempék – a PostgreSQL adatbázisból renderelve (Magyarország OSM PBF)
- ✅ Címkeresés – Photon a lokális Magyarország dump-ot használja
- ✅ Útvonaltervezés – GraphHopper ugyanazt a lokális PBF-et olvassa
- ✅ Tömegközlekedés – GraphHopper PT a lokálisan szinkronizált GTFS menetrendekkel
- ✅ MapLibre GL JS/CSS –
/root/osm-ui/vendor/ könyvtárból (nincs CDN) - ✅ Összes HTML/JS/CSS – nincs külső font, nincs Google Fonts, nincs külső API hívás
- ✅ Nginx proxy – minden upstream cél localhost vagy konténer név
Internetet igényel
- ❌ Műhold réteg (ESRI World Imagery) – opcionális, kikapcsolható
- ❌ OSM tile frissítések – minute diff letöltések (ha elérhető a net, különben csendben hibázik)
- ❌ Photon adatfrissítés – heti dump letöltés graphhopper.com-ról
- ❌ GTFS adatok – napi menetrend letöltés
Üzemeltetés – Cron feladatok
A rendszer üzemeltetése nagyrészt automatizált. Három cron job gondoskodik a frissítésekről:
# Heti Photon adatbázis frissítés (vasárnap 11:30)
30 11 * * 0 /root/photon-import.sh >> /var/log/photon-import.log 2>&1
# Napi GTFS szinkronizáció (hajnali 3:30)
30 3 * * * /root/gtfs-sync.sh >> /var/log/gtfs-sync.log 2>&1Az OSM konténer beépített frissítése percenként fut a konténeren belül, nem cron-on keresztül.
Ismert korlátok
- Hiányzó házszámok a Photon keresésben – a Magyarország Photon dump nem tartalmazza az OSM házszintű címkéit. Ez nem hiba, hanem adatkorlát.
- Street View nem elérhető – nincs gyakorlati, önállóan üzemeltethető alternatíva (a Mapillary internetet igényel, a Google Street View pedig API kulcsot és internetet).
- GraphHopper frissítése kézi – amikor az OSM PBF frissül, a GraphHopper graph cache-t manuálisan kell törölni (vagy újraindítani a konténert).
- Transit gráf újraépítés – GTFS frissítéskor 2-5 percig nem elérhető a tömegközlekedési útvonaltervezés.
- GTFS adatok időszerűsége – a menetrendek olyan frissek, amilyen friss az utolsó szinkronizáció (maximum 24 óra késés).
Zárszó
Ez a projekt számomra a szoftveres szabadság egyik legszebb példája. Egy olcsó, kis fogyasztású Raspberry Pi-n fut egy teljes értékű térkép szolgáltatás, ami semmilyen külső függőséggel nem rendelkezik. A nyílt forráskódú OpenStreetMap közösség munkája, a Photon, a GraphHopper és a MapLibre projectek lehetővé teszik, hogy bárki a saját kezébe vegye a térképezést.
A forráskód és a konfigurációk természetesen elérhetők a saját infrastruktúrámon. Ha bárki szeretne egy hasonló rendszert építeni, a N.O.M.A.D. projekt remek kiindulási alap, és az itt leírt konfigurációk is szabadon felhasználhatók.
Következő lépésként szeretném kiterjeszteni a szolgáltatást Szlovákiára, Romániára, Ausztriára, Szlovéniára és Horvátországra is – a Kárpát-medence teljes lefedésére. Továbbá tervben van egy útnyitás (road closure) támogatás is, ahol a felhasználók jelezhetik az aktuális lezárásokat, tereléseket, ami aztán az összes felhasználó számára megjelenne.
Ha kérdésetek van, vagy kipróbáltátok a megoldást és elakadtatok, nyugodtan szóljatok!
#útvonaltervezés #BKK #Docker #GraphHopper #GTFS #MapLibre #MÁV #offline #OpenStreetMap #OSM #Photon #RaspberryPi #térkép #tömegközlekedés #Volánbusz