Tämä artikkeli koskee Jäljen 2. sukupolven arkkitehtuuria; palvelusta on sittemmin julkaistu nykyinen 3. sukupolven versio.
Jälki on kehittämäni reittipalvelu, joka on suunnattu pyöräilyn aktiiviharrastajille. Palvelu sisältää käyttäjien tuottamaa reittidataa ja muita karttapalveluita pyöräilijöiden tarpeisiin.
Palvelun tekninen toteutus jakaantuu neljään eri osakokonaisuuteen:
- Sovelluspalvelin
- Reittieditori
- GIS-apupalvelin
- Taustakartat
1. Sovelluspalvelin
Pääsovelluspalvelin muodostaa Jälki-palvelun rungon. Kyseessä on jokseenkin tavanomainen Rails-sovellus, joka pitää sisällään kymmenkunta erilaista mallia. Ohjelmisto huolehtii useimmista Jälki-verkkopalvelun käyttäjälle näkyvistä toiminnoista.
Avoimesta lähdekoodista löytyy paljon hyödyllisiä rutiineja. Esimerkkiksi reitin tallentaminen gpx-formaatissa onnistuu valmiin gemin avulla:
def to_gpx
gpx = GPX::GPXFile.new
track_point_data = JSON.parse(self.route_data)
track = GPX::Track.new(:name => title)
segment = GPX::Segment.new
track_point_data.each do |point|
segment.points << GPX::TrackPoint.new(
{lat: point[1], lon: point[0]}
)
end
track.segments << segment
track.name = title
gpx.tracks << track
gpx.name = title
gpx.to_s
end
Vastaavasti avoimia rajapintoja hyödyntää. Tässä haetaan koordinaattien perusteella paikkakunnan nimi:
query = "lat=#{lat}&lon=#{lon}&zoom=10&addressdetails=1"
uri = URI.parse "https://nominatim.openstreetmap.org/reverse?format=json&#{query}"
response = nil
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Get.new uri
response = http.request request
end
2. Reittieditori
Jäljen uusi Jem-niminen reittieditori valmistui keväällä 2019. Tarve räätälöidylle editorille tuli, kun valmiit komponentit – esimerkiksi Leaflet Draw – eivät tarjonneet kaikkea tarvittavaa toiminnallisuutta.
Sisäisesti Jem muodostaa reitin käyttämällä segmenttejä. Segmentti koostuu alku- ja loppupisteestä ja niiden välillä kulkevasta reittiviivasta. Reittiviiva itsessään voi koostua useasta välipisteestä, mutta editori ei ota niiden muodostumiseen kantaa. Koko reitin kattava reittiviiva muodostetaan yhdiställä kaikki segmentit yhdeksi yhtenäiseksi viivaksi.
3. GIS-apupalvelin
Pääsovelluspalvelimen apuna on tällä hetkellä erillisenä ohjelmistona ja REST-rajapintana toteutettu paikkatietoa tarjoileva palvelin. Se huolehtii mm. korkeuskäyrien laskentaan ja reititykseen liittyvistä toiminnoista.
Alunperin pelkästään korkeusdatan käsittelyyn suunniteltu palvelinohjelmisto alkoi laajentua uuden reittieditorin kehityksen yhteydessä. Jälki-palvelun suuren käyttäjämäärän takia en kokenut mieluisaksi Openstreetmapin ilmaisten rajapintoinen käyttöä, vaan päätin laajentaa jo olemassaolevan GIS-palvelimen toimintoja.
Tämä Elevator-niminen ohjelmisto tarjoaa muutaman itsenäisen rajapinnan, joiden taustatoteutus poikkeaa toisistaan. Rajapinnan palauttaman datan generointiin käytettäviä valmiita apuohjelmia ovat Gdal ja Overpass, jonka lisäksi kirjoitin reititystoimintoja varten oman C++-kielisen ohjelman. Päätökseni käyttää yksittäisiä pienempiä apuohjelmia isompien Overpass- ja Osm3s-palvelimien sijaan perustui siihen, että halusin pitää järjestelmän mahdollisimman yksinkertaisena ja helppona ylläpitää.
source_data = body["coordinates"].map do |item|
lat = item[0]
lon = item[1]
datasources = GeotiffFile.where(
"min_lat <= ? AND min_lon <= ?\
AND max_lat >= ? AND max_lon >= ?",
lat, lon, lat, lon
).pluck(:filename)
{lat: lat, lon: lon, datasources: datasources}
end
...
data_sources.each do |data_source|
path = "#{Rails.application.config.elevation_data_dir}/MML/#{data_source}"
output = `gdallocationinfo -wgs84 -valonly #{path} #{coordinate[:lon]} #{coordinate[:lat]}` unless path.blank?
if output.present?
result = output.to_f
result = nil if output.start_with? "Error" or result.blank?
break
end
end
4. Taustakartat
Suuresta käyttäjämäärästä johtuen Jälki käyttää omia taustakarttoja. Ilmaisten Openstreetmapin karttapalvelimien käyttö näillä käyttäjämäärillä ei enää olisi kohtuullista, ja aikaisemmin käyttämäni Mapbox alkoi Jäljen suosion kasvaesas tulla liian kalliiksi.
Taustakartat perustuvat Openstreetmapin dataan. Ylläpidon yksinkertaistamisen ja toimintavarmuuden vuoksi käytän esirenderöityä karttatiiliä. Näin ollen palvelin tarjoilee tuiki tavallisia png-kuvia, eikä mitään monimutkaisempaa palvelinsysteemiä tähän tarvita.
Karttatiilien generointi tapahtuu omalla työasemallani. Tavanomaiseen tapaan paikalliseen Postgis-laajennuksilla varustettuun PostgreSQL-tietokantaan ladataan ensin datasetti Openstreetmapista. Karttatyylinä on hieman ehostettu OSM-perustyyli; sisemmille zoomaustasoille on vain lisätty maastopolkujen värikoodaus niiden vaikeuden mukaan.
Renderöinti tapahtuu käyttäen Mapnik-kirjastoa ja alunperin Openstreetmapin luomia, mutta pitkälle kustomoituja Python-skriptejä. Kartan generoinnin nopeuttamiseksi olen hionut ja optimoinut renderöintiputkea Bash-skriptejä, rinnakkaisprosessointia ja Overpass Turbo -palvelua apuna käyttäen. Lopputuloksena Suomen alueelta ja zoomaustasoilta 0-16 on noin kymmenen gigatavua karttakuvia.