En pystynyt sittenkään pitämään näppejäni irti avoimen datan koodailuista. Olin jo vähän aikaa miettinyt, että olisi kiva kokeilla, miten noita Maanmittauslaitoksen esim. maastokarttoja saisi kännykkään näppärästi. iPhonessa on muutama ohjelma, jotka jotain noista kartoista näyttää. Ajattelin kuitenkin, että olisi parempi tehdä yleinen kirjasto, jonka avulla kuka tahansa saisi nuo kartat mukaan Android ohjelmiinsa.
Tämän tekeminen tuntui aika isolta hommalta, kunnes törmäsin avoimeen osmdroid-kirjastoon. Sillä pystyy helposti näyttämään Android-kännyköissä erilaisia karttoja, kuten OpenStreetMap-kartan. Vielä kun Maanmittauslaitoksella oli sopivan oloinen web rajapinta karttoihin eli Web Map Service-palvelun (WMS), joka on mahdollisesti tulossa aivan vapaaseen käyttöön, tuntui karttojen yhdistäminen osmdroid-ohjelmaan suoraviivaiselta jutulta.
Ensin piti hakea tunnukset rajapinta-palveluun, jotka saikin sutjakkaasti. Sitten osmdroid-softaa piti puukottaa hieman lisäämällä sinne uusi lähde karttojen laatoille (eng. tile).
public class TileSourceFactory {
...
public static final OnlineTileSourceBase MML =
new MML__XYTileSource("Maanmittauslaitos",
ResourceProxy.string.mml,
0, 18, 256, ".png",
"http://opendata.fi/tiles/proxy.php");
...
private static ArrayList mTileSources;
static {
mTileSources = new ArrayList();
mTileSources.add(MML);
...
}
}
Kuten koodista näkyy, tein oman “karttaproxyn” verkkoon (kyllä opendata.fi -domain on minulla käytössä
). Varsinainen karttojen laattalähde (MML__XYTileSource) yksinkertaisesti kertoi, miten tuohon minun proxyyn kysely pitää tehdä.
public class MML__XYTileSource extends XYTileSource {
....
@Override
public String getTileURLString(final MapTile aTile) {
return getBaseUrl()+"?zoom=" + aTile.getZoomLevel() +
+ "&X=" + aTile.getX() + "&Y=" + aTile.getY();
}
}
Tuolla proxyllä lähinnä kierretään WMS-palvelun vaatima HTTP Basic tunnistautuminen. Nyt kännykän sijaan proxy tunnistautuu palveluun käyttäjätunnuksella ja salasanalla. Proxy myös muuntaan nuo OpenStreetMap laattanumerot WGS-84 koordinaateiksi ja sitten vielä WMS-palvelun käyttämiksi ETRS-TM35FIN tasokoordinaateiksi.
$url = "https://ws.nls.fi/rasteriaineistot/image?service=WMS" .
"&version=1.1.1&request=GetMap&width=256&height=256".
"&format=image%2Fpng&layers=maastokartta_500k".
"&styles=normal&srs=EPSG%3A3067&bgcolor=ffffff".
"&transparent=false&time=&bbox=";
$zoom = $_GET['zoom'];
$X = $_GET['X'];
$Y = $_GET['Y'];
//Lasketaan kulmien koordinaatit minimi NE ja maksimi NE
$Y=$Y+1;
$wgs2 = tiles_to_wgs84($X,$Y,$zoom);
$etr2= wgs_etrs($wgs2['lat'], $wgs2['lon']);
$X = $X + 1;
$Y = $Y - 1;
$wgs4 = tiles_to_wgs84($X,$Y,$zoom);
$etr4= wgs_etrs($wgs4['lat'], $wgs4['lon']);
$url .= $etr2['E'] .",".$etr2['N'].",".$etr4['E'].",". $etr4['N'];
PHP:n cURL-functioilla käydään hakemassa karttakuva Maanmittauslaitoksen WMS-palvelusta, josta se heitetään se suoraan kännykkäsovellukselle.
$ch = curl_init();
$timeout = 0;
curl_setopt ($ch, CURLOPT_URL, $url);
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
// Getting binary data
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERPWD, "käyttäjätunnus:salasana");
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);
$image = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
$im = @imagecreatefromstring($image);
header("Content-type: image/png");
imagepng($im);
imagedestroy($im);
Laskentakaavat joilla OpenStreetMap laattanumerot muutetaan WGS-84 koodinaateiksi ja sitten vielä ETRS-TM35FIN tasokoordinaateiksi piti hakea kissojen ja koirien kanssa netistä. Lopullinen totuus löytyi täältä ja täältä.
function tiles_to_wgs84($x,$y,$zoom){
$n = pow(2, $zoom);
$lon_deg =$x / $n * 360.0 - 180.0;
$lat_rad = atan(sinh( pi() * (1 - 2 * $y / $n)));
$lat_deg = $lat_rad * 180.0 / pi();
$wgs = array();
$wgs['lat'] = $lat_deg;
$wgs['lon'] = $lon_deg;
return $wgs;
}
function wgs_etrs($la, $lo){
$Ca = 6378137.0;
$Cb = 6356752.314245;
//WGS84 ellipsoid
//$Cf = 1.0 / 298.257223563;
$Cf = 1.0 / 298.257222101;
$Ck0 = 0.9996;
$Clo0 = deg2rad (27.0);
$CE0 = 500000.0;
$Cn = $Cf / (2.0 - $Cf);
$CA1 = $Ca / (1.0 + $Cn) * (1.0 + pow($Cn, 2.0) / 4.0 + pow($Cn, 4.0) / 64.0);
$Ce = sqrt(2.0 * $Cf - pow($Cf, 2.0));
$Ch1 = 1.0/2.0 * $Cn - 2.0/3.0 * pow($Cn, 2.0) + 37.0/96.0 * pow($Cn, 3.0) - 1.0/360.0 * pow($Cn, 4.0);
$Ch2 = 1.0/48.0 * pow($Cn, 2.0) + 1.0/15.0 * pow($Cn, 3.0) - 437.0/1440.0 * pow($Cn, 4.0);
$Ch3 = 17.0/480.0 * pow($Cn, 3.0) - 37.0/840.0 * pow($Cn, 4.0);
$Ch4 = 4397.0/161280.0 * pow($Cn, 4.0);
$Ch1p = 1.0/2.0 * $Cn - 2.0/3.0 * pow($Cn, 2.0) + 5.0/16.0 * pow($Cn, 3.0) + 41.0/180.0 * pow($Cn, 4.0);
$Ch2p = 13.0/48.0 * pow($Cn, 2.0) - 3.0/5.0 * pow($Cn, 3.0) + 557.0/1440.0 * pow($Cn, 4.0);
$Ch3p = 61.0/240.0 * pow($Cn, 3.0) - 103.0/140.0 * pow($Cn, 4.0);
$Ch4p = 49561.0/161280.0 * pow($Cn, 4.0);
$la = deg2rad($la);
$lo = deg2rad($lo);
$l = $lo - $Clo0;
$cos = cos($la)*$l;
// Mittakaavakerroin
//$Ck0 = $Ck0*(1 + 0.5 * pow($cos,2));
//logger("mittakaavakerroin: $Ck0");
$Q = asinh(tan($la)) - $Ce * atanh($Ce * sin($la));
$be = atan(sinh($Q));
$nnp = atanh(cos($be) * sin($lo - $Clo0));
$Ep = asin(sin($be) * cosh($nnp));
$E1 = $Ch1p * sin(2.0 * $Ep) * cosh(2.0 * $nnp);
$E2 = $Ch2p * sin(4.0 * $Ep) * cosh(4.0 * $nnp);
$E3 = $Ch3p * sin(6.0 * $Ep) * cosh(6.0 * $nnp);
$E4 = $Ch4p * sin(8.0 * $Ep) * cosh(8.0 * $nnp);
$nn1 = $Ch1p * cos(2.0 * $Ep) * sinh(2.0 * $nnp);
$nn2 = $Ch2p * cos(4.0 * $Ep) * sinh(4.0 * $nnp);
$nn3 = $Ch3p * cos(6.0 * $Ep) * sinh(6.0 * $nnp);
$nn4 = $Ch4p * cos(8.0 * $Ep) * sinh(8.0 * $nnp);
$E = $Ep + $E1 + $E2 + $E3 + $E4;
$nn = $nnp + $nn1 + $nn2 + $nn3 + $nn4;
$ETRS = array();
$ETRS['N'] = $CA1 * $E * $Ck0;
$ETRS['E'] = $CA1 * $nn * $Ck0 + $CE0;
return $ETRS;
}
Kun nämä saadaan tehtyä, ilmestyy tälläinen hieman sekava kartta kännykkään.

Kun tuota kuvaa tarkemmin katsoon, niin ohjelma lataa karttalaattoja hieman pieleen. Vierekkäiset karttalaatat eivät aivan täsmälleen osu kohdalleen ja siksi kuva on vähän sekava. Syy on (kai) se, että nuo WMS-palvelun laattojen koordinaatiolasku ei ole noista eri koordinaatistoista (WGS-84 ja ETRS-TM35FIN) johtuen ihan niin suoraviivainen kuin miten sen olen tehnyt. Tähän oikeastaan jätin homman, koska ajattelin, että ehkä joku geo-viisaampi osaisi neuvoa, miten jatkaa tästä eteenpäin.
Toivottavasti en tallonut kenenkään varpaille, koska tämä aihe liippaa myös hyvin läheisesti Apps4Finland-kilpailua, jota olen järjestämässä. Tämä kuitenkin osoitti, että tuollaisen ohjelman tekeminen ei ole mahdotonta ja vaikka homma jäi vielä aika pahasti vaiheeseen (esim. zoomauksen ottaminen paremmin huomioon proxyssä), tästä on hyvä jatkaa kenen tahansa ja tehdä kiinnostava ja etenkin toimiva sovellus Androidiin, joka käyttää noita karttoja. Ja tietenkin toivon näkeväni noita sovelluksia myös Apps4Finland-kilpailussakin!