Track of 4 cats.

Data collected with my Tractive GPS Pet Tracker

For about one year I have used the Tractive Pet Tracker to track my families (and my) cats, my grandmothers cats and our road trip to Sweden (German). Now I want to share that data with you.

In this post I will

  • Share the raw data with you
  • Filter data (separate tracks for each cat and our vacation)
  • Share the filtered data with you
  • Show how to create a map (using leaflet) of our vacation
  • Create a map with a track for each cat

I also wrote a blog post for Revolution Analytics, showing how to create a map with different tracks for each month.

The data

As already mentioned the data was collected using the tracker from the company Tractive.

Maya wearing her tracker (nh)
Maya wearing her tracker (nh)

Data can be downloaded from their website in gpx or kml format when you have a premium subscription. I used the gpx format.

Such a file looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0"?>
<gpx creator="Tractive GeoData Util" version="1.1" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
  <metadata>
    <link href="http://www.tractive.com">
      <text>Generated by Tractive Platform Service</text>
    </link>
  </metadata>
  <trk>
    <name>Tracker LJWSIZUT, 2016-01-08T08:37:33+00:00 2016-01-07T23:19:26Z</name>
    <trkseg>
      <trkpt lat="48.3202716" lon="14.625945">
        <ele>427</ele>
        <time>2016-01-06T23:43:05Z</time>
        <cmt>speed: 0.5</cmt>
      </trkpt>
      <trkpt lat="48.320325" lon="14.62614">
        <ele>394</ele>
        <time>2016-01-07T01:49:05Z</time>
        <magvar>64</magvar>
        <cmt>speed: 3.8</cmt>
      </trkpt>
    </trkseg>
  </trk>
</gpx>

Since the tracker does not save which cat is wearing the tracker (although you can select it in the phone app), I took notes in an Excel sheet.

Reading the data

In a previous post I already showed how you can read gpx data like from my Pet Tracker. I updated my method to comply with a recommendation I found in this great article (library vs. require). I also converted the time column to a date to be able to compare it.

You need to install these libraries:

  • XML
  • plyr
?View Code RSPLUS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
readTrackingFile<-function(filename) {
  library(XML)
  library(plyr)
  xmlfile<-xmlParse(filename)
  xmltop<-xmlRoot(xmlfile)
  tracking<-ldply(xmlToList(xmltop[['trk']][['trkseg']]), function(x) {
    data.frame(x)
  })
 
  tracking<-data.frame("ele"=tracking$ele[seq(1, nrow(tracking), 2)], "time"=as.character(tracking$time[seq(1, nrow(tracking), 2)]),"lat"=tracking$.attrs[seq(1, nrow(tracking), 2)], "lon"=tracking$.attrs[seq(2, nrow(tracking), 2)])
 
  tracking$ele<-as.numeric(levels(tracking$ele))[tracking$ele]
  tracking$lat<-as.numeric(levels(tracking$lat))[tracking$lat]
  tracking$lon<-as.numeric(levels(tracking$lon))[tracking$lon]
 
  time_pattern<-"%Y-%m-%dT%H:%M:%SZ"
  tracking$time<-strptime(as.character(tracking$time), time_pattern)
  #x<-strptime(as.character(tracking$time), "%Y-%m-%dT%H:%M:%SZ")
  tracking$min<-60*tracking$time$hour + tracking$time$min
 
  message(paste("read", nrow(tracking), "tracking points"))
 
  return(tracking)
}

After a first look at the data I saw that there were two points with a latitude < 30, which would be somewhere south from South Africa and I know that I wasn't there and I hope neither were my cats 😉 So I removed them.

?View Code RSPLUS
1
2
3
track<-readTrackingFile("../../data/LJWSIZUT.gpx")
plot(track$lon, track$lat, pch=19, cex=0.5) # showed that some were far off
track<-track[track$lat > 30,]

Analyse our vacation

To get only the track for our vacation I took everything that was between start and end time of our travel.

?View Code RSPLUS
1
2
3
4
5
6
7
time_pattern<-"%Y-%m-%dT%H:%M:%SZ"
vacation_start<-strptime("2015-07-23T05:00:00Z", time_pattern)
vacation_end<-strptime("2015-08-04T22:00:00Z", time_pattern)
track_vacation<-track[track$time>vacation_start & track$time<vacation_end,]
track_vacation<-track_vacation[!is.na(track_vacation$min),]
 
write.csv(track_vacation, file="../../data/track_vacation.csv", row.names=FALSE)

Leaflet map of our vacation

Therefore we need the library leaflet which can be installed from github.

?View Code RSPLUS
1
2
3
library(devtools)
devtools::install_github("rstudio/leaflet")
library(leaflet)

The next piece of code shows how you create a standard map, fit the bounds to your data and add lines for a track.

?View Code RSPLUS
1
2
3
4
myMap <- leaflet() %>% addTiles()
myMap  # a map with the default OSM tile layer
myMap %>% fitBounds(min(track$lon), min(track$lat), max(track$lon), max(track$lat))
myMap %>% addPolylines(track_vacation$lon, track_vacation$lat)
Track of our roadtrip to Sweden.
Track of our roadtrip to Sweden.

Visualise tracks per cat

To prepare the cat data I selected everything that did not happen during our holiday. To match my cats to each track I had to read my Excel file (for which I highly recommend the xlsx package). For some days I didn’t take notes which cat had the tracker, so I added another category “Unknown”.

?View Code RSPLUS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
track_cat<-track[track$time<vacation_start | track$time>vacation_end,]
 
# prepare cats
cats<-read.xlsx("../tractive/data/Katzen1.xlsx", sheetIndex=1, header=FALSE, stringsAsFactors = FALSE)
names(cats)<-c("TrackingDate", "Cat")
cats<-cats[!is.na(cats$Cat),]
time_pattern<-"%d. %B %Y"
cats$TrackingDate<-strptime(paste(cats$TrackingDate, "2015"), format = time_pattern)
 
# add cat name
track_cat$cat<-"Unknown"
for (i in 1:nrow(track_cat)) {
  cat_idx<-which((cats$TrackingDate$mday == track_cat[i,"time"]$mday) 
                 & (cats$TrackingDate$mon+1 == track_cat[i,"time"]$mon+1)
                 & (cats$TrackingDate$year+1900 == track_cat[i,"time"]$year+1900))
  if (length(cat_idx) == 1) {
    track_cat[i,"cat"]<-cats[cat_idx, "Cat"]
  }
}
 
# remove NA
 
track_cat<-track_cat[!is.na(track_cat$lon),]
summary(track_cat[,c("lon","lat")])
summary(track_cat[abs(track_cat$ele-423)<30,c("lon","lat")])
write.csv(track_cat, file="../../data/track_cat.csv", row.names=FALSE)
write.csv(track_cat_clean[track_cat_clean$cat!="Unknown",], file="../../data/track_cat_clean.csv", row.names=FALSE)
 
col<-c("gray", "blue", "pink", "red", "green")
mycats<-unique(track_cat$cat)

To create a map with different tracks you can simply loop over them. I added the group parameter so that I can later add checkboxes to activate/deactivate single tracks.

?View Code RSPLUS
1
2
3
4
5
6
7
catMap<-leaflet() %>% addTiles()
for (i in 1:length(mycats)) {
  cat<-mycats[i]
  catMap<-catMap %>% addPolylines(track_cat[track_cat$cat==cat,"lon"], track_cat[track_cat$cat==cat,"lat"], col=col[i], group=cat)
}
 
print(catMap)
Map of raw cat data. Lots of noise.
Map of raw cat data. Lots of noise.

After that I saw that probably some more data was wrong. My theory was that at times when the tracker sent a wrong location (because it was just turned on or some other disturbance) the elevation must also be wrong. So I took the median elevation of 423 and removed every data point that deviated further than 30 meters. There might be more sophisticated ways to do this but it seemed to work.

I also removed all points that I could not assign to a cat, because they just added unnecessary noise to the map.

The last few lines show how to add the above mentioned checkboxes. Now I can also select/unselect cats and look at each one separately. I think you can see very well that each of the four cats has certain areas where they go a lot. I think that’s really, really cool 🙂

?View Code RSPLUS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
track_cat_clean<-track_cat[abs(track_cat$ele-423)<30,]
 
catMap<-leaflet() %>% addTiles()
for (i in 1:length(mycats)) {
  cat<-mycats[i]
  if (cat != "Unknown") {
    catMap<-catMap %>% addPolylines(track_cat_clean[track_cat_clean$cat==cat,"lon"], track_cat_clean[track_cat_clean$cat==cat,"lat"], col=col[i], group=cat) 
  }
}
 
catMap %>%
addLayersControl( 
  overlayGroups = mycats[2:5], 
  options = layersControlOptions(collapsed = FALSE))
Track of 4 cats.
Track of 4 cats.

The data

All the data is stored in my github repo.
The source code is also available in my github repo.

7 thoughts on “Data collected with my Tractive GPS Pet Tracker”

Leave a Reply

Your email address will not be published. Required fields are marked *