Rejestrowanie wysokości przez odbiorniki GPS jak wiadomo odbywa się z błędem. Do tej pory mi to wisiało, ale problem stanął, gdy zachciało mi się dodać nachylenie do danych GPS (wyświetlanych jako napisy generowane autorskimi skryptami z pliku GPX/TCX), które to nachylenie nie zgadzało się wielokrotnie ze stanem faktycznym. Szczególnie tajemnicze były różnice na plus/minus 300% i więcej...
W trakcie prób rozwiązania tego problemu dowiedziałem się tego i owego na temat rejestracji wysokości. I o tym jest ten wpis. Natomiast problem z obliczaniem nachylenia został nierozwiązany. Wartości nachylenia pokazywane w trakcie jazdy przez urządzenia, takie jak Garmin Edge 500 są wiarygodnie, co by świadczyło, że zaimplementowano w nich jakiś całkiem sprytny algorytm wygładzający. Szukałem co na ten temat wie google, ale nic nie znalazłem. Próbowałem wygładzać wysokość/nachylenie w R za pomocą funkcji loess (a także średniej ruchomej) -- rezultaty były kiepskie.
Znalazłem natomiast informacje w jaki sposób można poprawić dokładność danych o wysokości, zarejestrowaną (niedokładnie) przez urządzenie GPS. Otóż można albo skorzystać z usługi Google Elevation (GE) albo użyć danych SRTM, przy czym minusem GE jest dzienny limit 2500 zapytań.
Zaczniejmy od prostszego przypadku, tj. dodania/zmiany wysokości z modelu SRTM. W tym celu pobieramy/instalujemy pakiet srtm.py. Pakiet zawiera m.in narzędzie pn. gpxelevations
:
## Dodaje dane SRTM, zapisuje do pliku PLIK_SRTMS.gpx gpxelevations -s -o PLIK.gpx -f PLIK_SRTMS.gpx ## Wygładza dane i zapisuje do pliku PLIK_SRTMS.gpx gpxelevations -o 20160511.gpx -f PLIK_SRTMS.gpx
Dane SRTM można też dodać/zamienić posługując się okienkową aplikacją pn. GPSPrune, jak ktoś lubi klikać ale nie lubi wiersza poleceń.
Zakładając, że dane są w pliku GPX pobranym z urządzenia, poniższy skrypt wygeneruje plik CSV zawierający m.in. wysokość oryginalną, wysokość z modelu SRTM oraz wysokość wygładzoną:
#!/bin/bash FILE=`basename $1 .gpx` if [ -f "$FILE.gpx" ] ; then ## Dodanie lepszych wysokości gpxelevations -o $FILE.gpx -f ${FILE}_SRTM.gpx gpxelevations -s -o $FILE.gpx -f ${FILE}_SRTMS.gpx ## Zamiana na CSV (skrypt gpx2csv.pl zamieszczono dalej) gpx2csv.pl ${FILE}.gpx > ${FILE}.csv && \ gpx2csv.pl ${FILE}_SRTM.gpx > ${FILE}_SRTM.csv && \ gpx2csv.pl ${FILE}_SRTMS.gpx > ${FILE}_SRTMS.csv ## Scalenie w jeden plik CSV paste -d ';' ${FILE}.csv ${FILE}_SRTM.csv ${FILE}_SRTMS.csv | \ awk -F';' ' BEGIN{ print "daytime;lat;long;ele;srtm;srtms"; };\ {print $1 ";" $2 ";" $3 ";" $4 ";" $8 ";" $12}' > ${FILE}_ALLE.csv else echo "*** USAGE: $0 PLIK.gpx ***" fi
Teraz można porównać wysokość oryginalną, wysokość SRTM oraz wygładzoną na wykresie:
library(reshape) require(ggplot2) args = commandArgs(trailingOnly = TRUE); if (length(args)==0) { stop("Podaj nazwę pliku CSV", call.=FALSE) } fileBase <- gsub(".csv", "", args[1]); outFile <- paste (fileBase, ".pdf", sep = ""); d <- read.csv(args[1], sep = ';', header=T, na.string="NA"); ggplot(d, aes(x = as.POSIXct(daytime, format="%Y-%m-%dT%H:%M:%SZ"))) + geom_line(aes(y = ele, colour = 'zarejestrowana', group = 1), size=.5) + geom_line(aes(y = srtm, colour = 'srtm', group = 1), size=.5) + geom_line(aes(y = srtms, colour = "srtm (wygładzona)", group = 1), size=.5) + ylab(label="Wysokość [mnpm]") + xlab(label="czas") + labs(colour = paste("Plik:", fileBase )) + theme(legend.position="top") + theme(legend.text=element_text(size=12)); ggsave(file=outFile, width=12, height=5) ## Uruchomienie: ## Rscript gps_vs_srtm.R PLIK.csv
Oryginalne dane z urządzenia są systematycznie zaniżone.
Dodanie danych z Google Elevation jest równie proste. Poniższy skrypt -- jako przykład -- pobiera wysokość dla punktu o współrzędnych podanych jako argumenty wywołania (szerokość/długość):
use LWP::Simple; use JSON qw( decode_json ); my $geocodeapi = "https://maps.googleapis.com/maps/api/elevation/json?locations"; ## szerokość = $ARGV[0] ; długość = $ARGV[1] my $url = $geocodeapi . "=$ARGV[0],$ARGV[1]"; my $json = get($url); my $d_json = decode_json( $json ); if ( $d_json->{status} eq 'OK' ) { $elevationG = $d_json->{results}->[0]->{elevation}; $resolutionG = $d_json->{results}->[0]->{resolution}; $latG = $d_json->{results}->[0]->{location}->{lat}; $lngG = $d_json->{results}->[0]->{location}->{lng}; } print "$elevationG\n";
Drugi z wykresów zawiera dane pobrane z Google Elevation Service.
Na zakończenie jeszcze skrypt zamieniający gpx na csv:
#!/usr/bin/perl # Wykorzystanie gpx2csv.pl PLIK.gpx > PLIK.csv # use XML::DOM; my $parser = new XML::DOM::Parser; for my $file2parse (@ARGV) { my $doc = $parser->parsefile ($file2parse); for my $t ( $doc->getElementsByTagName ("trk") ) { for my $p ( $t->getElementsByTagName ("trkpt") ) { $node++; my $latitude = $p->getAttributeNode ("lat")->getValue() ; my $longitude = $p->getAttributeNode ("lon")->getValue() ; $gmttime = $p->getElementsByTagName ("time")-> item(0)->getFirstChild->getNodeValue(); $altitude = $p->getElementsByTagName ("ele")-> item(0)->getFirstChild->getNodeValue(); printf "%s;%s;%s;%s\n", $gmttime, $latitude, $longitude, $altitude; } } }
Podoba mi się ten wpis
OdpowiedzUsuń