gemiddelde track maken

Ik heb sinds ik mijn GPSr gebruik, al veel tracks verzameld. Natuurlijk lopen er veel tracks over de zelfde weg. Graag zou ik nu 1 gemiddelde track willen maken van de overlappende stukken. In eerste instantie gewoon middelen; als het later kan, graag met een gewogen gemiddelde (hogere nauwkeurigheid geeft meer waarde aan de track, een oude track heeft minder waarde). Zo’n track is dan erg makkelijk om te zetten in JOSM naar een way. En wat natuurlijk met gps punten kan, kan ook met nodes.

GPSBabel lijkt de tool, maar kan dit (nog) niet zo ver als ik kan zien. Verder heb ik maar 1 ander software pakket gevonden waar het zou kunnen. Het betreft topofusion (http://www.topofusion.com). Echter, dat betreft (deels betaalde) windows software en het is mij niet gelukt om het onder linux met wine aan de praat te krijgen.
Zij implementeren wel precies wat ik wil. Op de ‘Trail Networks’ sectie (http://www.topofusion.com/network.php) staat wat ze doen. De paper waar ze naar verwijzen is zeer interresant. In excel heb ik dit al nagebouwd, maar dit vereist veel handwerk.

Mijn vragen:

  1. Weet iemand software, waarmee ik tracks kan middelen? De software moet op linux werken.

  2. Mocht bovenstaande niet bestaan, wie kan helpen om het te programeren, of doet hetzelf? Wellicht om het als een JOSM-plugin of GPSBabel filter te publiceren. Mijn progameerkennis is op basis niveau, maar ik leer snel.

Ik heb met mijn afstudeerproject iets vergelijkbaars gedaan. Het algoritme gaat alleen uit van punten in plaats van stukken track:
http://dl.dropbox.com/u/2106224/verslag-jvwilge-100507-final.pdf

De code staat hier:
https://github.com/jvwilge/gps-running-route-discovery
Kennis van maven, git en Scala is wel handig. Mocht je dat niet hebben dan is het waarschijnlijk handiger om zelf wat aan de hand van de pdf te maken, of wellicht heb je wat aan de papers waar naar verwezen wordt.

Hallo Jeroen,

Dat ziet er ook leuk uit. Jij hebt alleen een andere techniek gebruikt.
Bij topofusion gebruiken ze inderdaad nog een id van de route en proberen bij een punt uit de referentie track, het dichtst bij zijnde punt van een andere track te vinden om daarmee te middelen. Jij maakt echter core points die als richting dienen, waarna je halverwege naar het volgende core point weer het beste punt probeert te vinden.

Een paar vragen:
Wat doe je als er in de gele radius meerdere punten zitten met dezelfde inRadius waarde? Neem je dan diegene die het dichtstbij ‘s’ is?
Hoe bepaal jij wat een knooppunt is? En wat als er geen punten in de radius zitten, maar wel er net buiten? Het is dus geen doodlopende weg.

Helaas is mijn Scala kennis ondermaats. Vooral de ‘_._2’ zijn hoofdbrekers. Ik denk waarde 2 van een functie van een object; object.functie2.
Heb je ook zoiets geprobeerd in bv R? Dat is voor mij iets makkelijker lezen.

Heb je ook een oplossing om de routes die je 1x hebt gelopen toe te voegen aan je netwerk? Het zou jammer zijn als die ineens niet meer mee tellen. Ze zijn immers wel gelopen.

In je paper gebruik je nog een groene radius. In de code heb je het over redRadius/2. Ik neem aan dat je hetzelfde bedoelt cq doet, alleen de waarde bepaling is anders.

Ik gooi even iets uit de wiskunde in de groep dat je helpt bij het bepalen van de “beste” (die er niet is) oplossing: De kleinste-kwadratenmethode

Dat is de derde methode die ik ook al had gevonden. Dit keer in een paper van de US navy. Maar die methode lijkt nog veel moeilijker.
Ik kan ook de mogelijkheid niet vinden hoe het in bv Grass of Qgis zou moeten. Ook een zoektocht in R levert nog niets op.

Na een aantal dagen stoeien, heb ik een eerste opzet voor elkaar.
Het is geschreven in R. Input is voorals nog 2 *.csv files en de output ook een *.csv.
Zoals al eerder gezegd, ik ben geen programeer held, dus wie verbeteringen ziet, meld het.
Verder probeer ik het script nog aan te passen zodat ik meerdere files als input kan hebben.
Ook moet er aan de csv die gpsbabel maakt, nog een eerste regel ingevoegd worden, aangezien R die nu pakt als column names.
Het eerste punt wordt nu niet meegenomen.

Procedure:

  • Maak *.csv van je *.gpx mbv gpsbabel:

gpsbabel -i gpx -f test1.gpx -o csv -F test1.csv

  • Voer R code uit

- De output van R (averageTrack.csv) met sed bewerken om de eerste regel te verwijderen:

sed -i ‘1d’ averageTrack.csv

  • Daana met gpsbabel terugzetten in *.gpx

gpsbabel -i csv -f averageTrack.csv -x transform,trk=wpt,del -o gpx -F averageTrack.gpx

Het echte werk gebeurt in ~60 regels R code:


read 2 files

route1 ← read.csv (“test1.csv”, header=FALSE)
route2 ← read.csv (“test2.csv”, header=FALSE)

do some pre-processing

colnames(route1) ← c(“lon”, “lat”, “routeID”)
colnames(route2) ← c(“lon”, “lat”, “routeID”)

#combine the routes
tracks ← rbind(route1,route2)

add routeID

tracks[,3] ← c(rep(1,each=nrow(route1)),rep(2,each=nrow(route2)))

ntracks ← 2

Find the track with the highest number of points

max_points=0

count per id

find the highest count

for (i in 1:ntracks) {
tmpdf ← tracks[tracks[, 3]==i, ]
if (max_points < nrow(tmpdf)) {
max_points ← nrow(tmpdf)
refRouteID ← i
refRouteID
}
}

Find distance between two points

refRouteXcor ← tracks[tracks[, 3]==refRouteID, 1]
refRouteYcor ← tracks[tracks[, 3]==refRouteID, 2]
newTracks ← tracks[tracks[, 3]==refRouteID, ]
averageTrack ← newTracks
averageTrack[,3] ← ntracks+1

for all points in refRoute → k

for all tracks in tracks → l

for all points in a track → m

for (l in 1:ntracks) {
if (l != refRouteID) {
for (k in 1:max_points) {
RouteXcor ← tracks[tracks[, 3]==l, 1]
RouteYcor ← tracks[tracks[, 3]==l, 2]
shortest_distance=999999
for (m in 1:length(RouteXcor)) {
distance ← sqrt((RouteXcor[m]-refRouteXcor[k])^2+(RouteYcor[m]-refRouteYcor[k])^2)
if (distance < shortest_distance) {
shortest_distance ← distance
pointID ← m
}
}
newTracks ← rbind(newTracks, c(RouteXcor[pointID],RouteYcor[pointID],l))
}
}
}

Average all tracks into one single track

for (n in 1:(max_points)) {
averageTrack[n,1] ← (newTracks[n,1] + newTracks[n+max_points,1]) / ntracks
averageTrack[n,2] ← (newTracks[n,2] + newTracks[n+max_points,2]) / ntracks
}

#Saving the resulting track in averageTrack.csv.
averageTrack ← averageTrack[,-3]
write.table(averageTrack, file = “averageTrack.csv”, quote = FALSE, row.names=FALSE, col.names=FALSE, sep=“,”, dec=“.”)


Alle *.csv in een directory worden nu gelezen en geclipped op een bounding box.
Daarna wordt een average track berekend en opgeslagen naar averageTrack.csv

Nog steeds even omzetten naar *.gpx en het is klaar voor gebruik.

Het omzetten van 11 tracks (1.3 MB) naar csv, inlezen, clippen, bereken en de averegeTrack omzetten duurt bij mij net geen 3 seconden.

Sorry voor het wat latere antwoord, moest zelf ook weer even in mijn code en paper duiken om te kijken wat ik nou ook alweer gedaan heb :wink:

Ik pak inderdaad het dichtsbijzijnde punt en alles wat binnen de rode radius valt ‘wis’ (tussen quotes omdat ik ze later nog gebruik) ik uit de set en ga verder zoeken in de gele radius. Als je dan nog wat vindt heb je een knooppunt. Hierdoor krijg je wel een boom zonder cykels erin. Als ik helemaal klaar ben zet ik de gewiste punten punten weer terug en kijk ik welke cykels toegestaan zijn.

Net buiten de radius is pech, ik heb voor mijn dataset de radius ruim genoeg gekozen dat dit niet voorkomt. Waarschijnlijk gaat het inderdaad mis als je logt met een hogere snelheid (bv. fiets of auto).

_._2 is inderdaad het tweede veld van een tupel (of tripel enz.) van het invoerobject (meestal wat voor => staat). Ik heb niets in R van die bepaling, alleen wat in R gedaan voor de verdeling bij de doorsnede. R was voor mij weer heel lastig, dus ben bang dat ik je daar niet bij kan helpen.

De 3e parameter (minPts) in Cleanup.scala kun je mee spelen om te zorgen dat een route eerder als route wordt gezien. Ik heb hem op 4 gezet, maar je zou hem ook op 1 kunnen zetten. Nadeel is dat als je 2 routes hebt die toevallig ver van elkaar afliggen, maar wel dezelfde route zijn als twee afzonderlijke routes wordt gezien. Ook weer op te lossen door je radius groter te maken. Het is vooral veel spelen met de parameters om een goed resultaat te krijgen.

redRadius/2 is het middelpunt van de groene radius, de grootte ervan is ook in te stellen en niet de helft van de rode.

I’ve made a user guide and home for Michiels script.