For
Christmas I decided to treat myself with an Arduino starter kit. I started
doing some basic experiments and I quickly found out numerous website that sell
every sort of sensor: from temperature and humidity, to air quality.
Long story short, I bought a
bunch of sensors to collect spatial data. I have a GPS, accelerometer/magnetometer,
barometric pressure, temperature/humidity, UV Index sensor.
Below is the picture of the
sensors array, still in a breadboard version. Of course I also had to use an
Arduino pro mini 3.3V and an openlog to record the data. Below the breadboard there
is a 2200mA lithium battery that provides 3.7V to the Arduino pro mini.
With this system I can collect 19
columns of data: Date, Time, Latitude and Longitude, Speed, Angle, Altitude,
Number of Satellites and Quality of the signal, Acceleration in X, Y and Z,
Magnetic field in X, Y and Z, Temperature, Humidity, Barometric Pressure, UV
Index.
With all these data I can have
some fun testing different plotting method on R. In particular I was interested
in plotting my data on Google Maps.
These are my results.
First of all I installed the
package xlsx, because I have to first import the data into Excel for some
preliminary cleaning. Sometimes the satellite sensor loses the connection and I
have to delete those entries, or maybe the quality of the signal is poor and
the coordinates are not reliable. In all these case I have to delete the entry
and I cannot do it in R because of the way the GPS write the coordinates.
In fact, the GPS
library, created by Adafruit, writes the coordinate in a format similar to
this: 4725.43N 831.39E
This is read into a string format
in R, so it requires a visual inspection to get rid of bad data.
This format is a combination of
degrees and minutes, so it needs to be cleaned up before use. In the example
above, the real coordinate in decimal degrees in calculated using the following
formula:
47 + 25.43/60 N 8 + 31.39/60 E
Luckily, the fact that R
recognises it as a string facilitates this transformation. With the following
two lines of code I can transform each entry into a format that I can use:
UPDATE 28.02.2014:data$LAT <- as.numeric(substr(paste(data[,"Lat"]),1,2)) + as.numeric(substr(paste(data[,"Lat"]),3,7))/60data$LON <- as.numeric(substr(paste(data[,"Lon"]),1,1)) + as.numeric(substr(paste(data[,"Lon"]),2,6))/60
I found this website http://arduinodev.woofex.net/2013/02/06/adafruit_gps_forma/ where the author suggest a function to convert the coordinates in decimal degrees directly within the arduino code. I tried it and it works perfectly.
Then I assign these two columns as coordinate for the file with the sp package and set the projection to WGS84.
Now I can start working on
visualising the data. I installed two packages: plotGoogleMaps and RgoogleMaps
The first is probably the
simplest to use and let the user create a javascript page to plot the data from
R to google maps through the google maps API. The plot is displayed into the
default web browser and it looks extremely good.
With one line of code I can plot
the temperature for each spatial point with bubble markers:
plotGoogleMaps(data,zcol="Temp")
"Temp" is the name of the column in the SpatialPointsDataFrame that has the temperature data.
By adding an additional line of
code I can set the markers as coloured text:
ic=iconlabels(data$Temp, height=12)
plotGoogleMaps(data,iconMarker=ic,zcol="Temp")
The package RgoogleMaps works
differently, because it allows the user to plot google maps as background for
static plots. This creates less stunning results but also allows more
customisation of the output. It also requires a bit more work.
In this example, I will plot the
locations for which I have a data with the road map as background.
I first need to create the map
object and for centre the map in the centre of the area I visited, I used the
bounding box of my spatial dataset:
box<-bbox(data)
Map<-GetMap(center = c(lat =(box[2,1]+box[2,2])/2, lon = (box[1,1]+box[1,2])/2), size = c(640, 640),zoom=16,maptype = c("roadmap"),RETURNIMAGE = TRUE, GRAYSCALE = FALSE, NEWMAP = TRUE)
Then I need to transform the
spatial coordinates into plot coordinates:
tranf=LatLon2XY.centered(Map,data$LAT, data$LON, 16)x=tranf$newXy=tranf$newY
this function creates a new set
of coordinates optimised for the zoom of the Map I created above.
At this point I can create the
plot with the following two lines:
PlotOnStaticMap(Map)points(x,y,pch=16,col="red",cex=0.5)
as you would do with any other
plot, you can add points to the google map with the function points.
As I mentioned above, this
package creates less appealing results, but allows you to customise the output.
In the following example I will compute the heading (the direction I was
looking at) using the data from the magnetometer to plot arrows on the map.
I computed the heading with these
lines:
Isx = as.integer(data$AccX)
Isy = as.integer(data$AccY)
heading = (atan2(Isy,Isx) * 180) / piheading[heading<0]=360+heading[heading<0]
I did not tilt compensated the
heading because the sensor was almost horizontal all the times. If you need
tilt compensation, please look at the following websites for help:
http://theccontinuum.com/2012/09/24/arduino-imu-pitch-roll-from-accelerometer/
With the heading I can plot
arrows directed toward my heading with the following loop:
PlotOnStaticMap(Map)for(i in 1:length(heading)){
if(heading[i]>=0 & heading[i]<90){arrows(x[i],y[i],x1=x[i]+(10*cos(heading[i])), y1=y[i]+(10*sin(heading[i])),length=0.05, col="Red")}
if(heading[i]>=90 & heading[i]<180){arrows(x[i],y[i],x1=x[i]+(10*sin(heading[i])), y1=y[i]+(10*cos(heading[i])),length=0.05, col="Red")}
if(heading[i]>=180 & heading[i]<270){arrows(x[i],y[i],x1=x[i]-(10*cos(heading[i])), y1=y[i]-(10*sin(heading[i])),length=0.05, col="Red")}
if(heading[i]>=270 & heading[i]<=360){arrows(x[i],y[i],x1=x[i]-(10*sin(heading[i])), y1=y[i]-(10*cos(heading[i])),length=0.05, col="Red")}
}
I also tested the change of the
arrow length according to my speed, using this code:
length=data$Speed.Km.h.*10for(i in 1:length(heading)){if(heading[i]>=0 & heading[i]<90){arrows(x[i],y[i],x1=x[i]+(length[i]*cos(heading[i])), y1=y[i]+(length[i]*sin(heading[i])),length=0.05, col="Red")}if(heading[i]>=90 & heading[i]<180){arrows(x[i],y[i],x1=x[i]+(length[i]*sin(heading[i])), y1=y[i]+(length[i]*cos(heading[i])),length=0.05, col="Red")}if(heading[i]>=180 & heading[i]<270){arrows(x[i],y[i],x1=x[i]-(length[i]*cos(heading[i])), y1=y[i]-(length[i]*sin(heading[i])),length=0.05, col="Red")}if(heading[i]>=270 & heading[i]<=360){arrows(x[i],y[i],x1=x[i]-(length[i]*sin(heading[i])), y1=y[i]-(length[i]*cos(heading[i])),length=0.05, col="Red")}}
However, the result is not
perfect because in some occasions the GPS recorded a speed of 0 km/h, even
though I was pretty sure to be walking.
The arrow plot looks like this: