how with a few clicks these gps traces can be directly visualized on an OpenStreetMap (read only)it those maps is based on detailed gps traces. Here in this project I will describe:
- how detailed gps traces can be made by an Android phone which are also in real time uploaded to the Artik cloud
- how with a few clicks these gps traces can be directly visualized on an openstreetmap (read only)
- how these gps traces can be converted to proper gpx format in a few click so that they can be uploaded in openstreetmap (editable mode)
All you need is an Android phone, 3€ for the tasker app and some time for hacking this together.
The innovative part here is that those gps tracks are in real time uploaded and can be immediately visualized on openstreetmap. Moreover besides the track interesting extra information (like accuracy of the gps fix) is also nicely visualized. So you can immediately see if your tracks are already properly mapped in OpenStreetMap or not.
Demo ScreenshotsHere above you see a track consisting of 30 points created by the "Loop Send Location" task (see A.2.3 below). The nice thing is that it not only shows the track but also the accuracy of each point through a blue circle and this directly on the OpenStreetmap. So you see that this track within its accuracy nicely follows the dotted path on the OpenStreetMap.
Here below you see the location information for the first and last point of the track. Based on the elapsed time (see time) you can see that it is retrieving a new location point every second.
I also exported a track to gpx xml format, copy-pasted it to a file, uploaded this file in openstreetmap and opened openstreetmap editor where I successfully managed to show the uploaded track (see fine blue-green line next to the dotted blue line in the screenshot below):
The diagram above gives an overview of the architecture. So there are 2 main parts:
- Part A: At the left you have an Android Phone that will track your location with a high frequency and upload this location data in real time to the Artik Cloud
- Part B: At the right you have a Node-Red application that is able to retrieve the messages from the Artik Cloud and also provides a web user interface that shows these messages on an openstreetmap and is able to export these messages to a gpx track.
In the sections below the setup of these 2 main parts are further explained.
PrerequisitesIn order to fully understand the description below you only need basic knowledge about:
- javascript and html
For this you need to logon to the Artik Cloud website.
The following things must be created in the artik cloud developer dashboard
- Device Type = describes a category of devices all using the same Manifest
- Manifest = describes the message format for the Device Type
The following thing must be done in your artik cloud.
- Connect your Device = specifying a certain device of a certain device type that can connect to Artik Cloud.
A.1.1 Create Device Type in Artik Cloud Developer
I have created a simple device type with name "Android Device" as specified below. I would recommend to give it a more specific name.
A.1.2 Create Manifest in Artik Cloud Developer
Here below you see the manifest I have created for my "Android Device". Same manifest in json format can be found in code section below (see attachment "www.android.com-v7-manifest.json").
The list of fields is based on the location information that is accessible via the Tasker App. Note that there are 2 independent ways a mobile phone can track the location: gps or net (network). So both ways are tracked in the group fields "location_from_gps" and "location_from_net". For both ways the manifest is not only specifying the coordinates (latitude and longitude) but also the accuracy of these coordinates and the fix_time (= time when your mobile phone determined the location) which could be quite different from the actual time.
There are plenty of other variables that are accessible via the Tasker App. So the manifest below is not only specifying the location information but also the battery percentage (field battery_level) and the current time (field "time_seconds"). FYI the full list of tasker variables can be found here.
Here below an actual example of such a message:
{
"battery_level": 35,
"location_from_gps": {
"altitude": 57.0731125161809,
"latitude": 51.183009119124954,
"accuracy": 4,
"longitude": 4.469106932436154,
"fix_time": 1475510126,
"speed": 6.369436
},
"location_from_net": {
"latitude": 51.1900941,
"accuracy": 899,
"fix_time": 1475510100,
"longitude": 4.4738366
},
"time_seconds": 1475510127
}
Note that all times are expressed in seconds since some time in January, 1970.
A.1.3 Connect your Device in Artik Cloud
For this you need to login to Artik Cloud, goto "My Artik Cloud" tab and select "Devices" and then select "+ Connect another Devices..." box. Then specify (or search) the Device Type you have created earlier (see step A.1.1) and specify a name (e.g. "myPhone") for your device. If you then go back to the "My Artik Cloud" tab it should look like this:
If you then click on the "Device Info" icon (see figure above) of your device you will get the screen below (note that I have masked a part of the Device Token).
You will need this "Device ID" and "Device Token" for connecting your device (= your mobile phone) to the Artik cloud.
A.2. Setting up your mobile phone for location tracking + sending this location information to the Artik Cloud.Here we will describe the different steps to configure your mobile phone so that your mobile phone is ready to track your location.
So in a nutshell (for details see below) I have created 3 tasker tasks:
- "Send Location" = get the location and sends the location details to the Artik Cloud
- "Loop Send Location" = Is calling the "Send Location" task in a loop
- "Stop Loop Send Location" = Is stopping the "Loop Send Location Task".
I also created 3 widgets of type "Task Shortcut" to the above Task. This way I could easily launch those tasks whenever I want.
Not directly related to this project, but I have also created a tasker "profile" that triggers the "Send Location" every 12 minutes to track your location throughout the day. But this didn't work very well. I had both problems with getting a proper location fix (most likely because most of the time I was inside and then your device can not get a gps fix) and sending the message to the Artik cloud (this issue is not caused by the Artik cloud as I had same problem when POSTing message to other destination. So I think it is something with my phone or the RESTask for tasker plugin).
A.2.1. Software to install on Android phone
You need to install following apps:
- Tasker (3€)
- RESTask for Tasker : is a plugin for Tasker that I have used to "POST" messages to the Artik Cloud.
Note that for Tasker there is also "MQTT Publisher Plugin" which I have tried first for connecting to the Artik Cloud. This would have been a better option than the RESTask plugin as MQTT is a more light weight protocol than HTTP POST. But in the end I have not used this plugin as I couldn't get it working in a reliable manner (see https://github.com/nosybore/Tasker-MQTT-Publish-Plugin/issues/13).
A.2.2 Create "Send Location" Task
See also code section.
Here below you can see that the "Send Location" Task consists of 11 Actions. Basically it will first try to get a location fix (Action 1), split the %LOC and %LOCN variables into latitude and lontitude coordinates (Actions 3, 4, 5, 6) and send the location info (+ battery level) to the Artik Cloud (Action 7). For debugging I have added a number of Flash actions and I am also storing the response of the Artik Cloud and the json message into a file response.txt and jsonmsg which is created in the root folder of your Android phone.
Here below you see how I have configured RESTask (action 7) to connect to the Artik Cloud. In the "Headers" tab you must specify that the Content-Type = application/json and for the Authorization Bearer you must specify the "Device Token" that is generated in step A.1.3 (see "Device Info" screenshot) !
Here below you see the complete contents (= json message) of the "Custom body" setting. You see that json "data" element has exactly the same fields as the Manifest we have created in step A.1.2. It also shows which tasker variables are mapped to the data fields.
Note that "sdid" value must be the "Device ID" that is generated in step A.1.3 (See "Device Info" screenshot).
{"data" :
{"battery_level":%BATT,
"location_from_gps":{"accuracy":%LOCACC,
"altitude":%LOCALT,
"fix_time":%LOCTMS,
"latitude":%gps_location1,
"longitude":%gps_location2,
"speed":%LOCSPD},
"location_from_net":{"accuracy":%LOCNACC,
"fix_time":%LOCNTMS,
"latitude":%net_location1,
"longitude":%net_location2},
"time_seconds":%TIMES},
"sdid": "4c7c90fe431b4a6e93e02d94dc6f5255",
"type": "message" }
A.2.3 Create "Loop Send Location" Task
See also code section.
Basically this task is calling "Send Location" task 30 times in a row. But first it is calling "Get Location" with a timeout of 100 seconds to get a location fix before entering in the loop.
Note that launching this task will result in a lot of popup messages (Flash actions). I noticed that Tasker is going much faster through the loop than it is displaying those "Flash" pop-up messages. In other words those pop-up messages get queued. So I recommend to remove those Flash actions in the loop once you have verified that this task is working fine.
The loop if condition (see Action 8) is also checking if the variable "StopLoopSendLocation" is not set. This variable will be set by the "Stop Loop Send Location" task (see next section).
A.2.4 Create "Stop Loop Send Location" Task
See also code section.
This task will just set a variable to stop the "Loop Send Location" task.
A.2.5 Create 3 "Task Shortcut" widgets for the created tasks
I have added 3 "Task Shortcut" widgets to the 3 tasks (see screenshot below). This way I can easily launch these tasks when needed.
In part B I will describe everything what is needed to create the Node-Red application that is able to display the location information sent by your mobile phone to the Artik cloud.
B.1. Artik Cloud Configuration for Node-Red applicationFirst you need to create a new application in the artik cloud developer dashboard. For this select "Applications" in the Dashboard and then select "+ New Application".
- Give a name to your application (I have called it "bluemix node-red"),
- select "Client credentials, auth code, implicit" as "Authorization methods"
- The "AUTH REDIRECT URL" should be set to the URL of a webpage you can host with node-red. In my case I have chosen for https://__my_node_red_url__/from_artik.
Here below you see the details of the application I have created.
Secondly you need to set the "Permissions" for your application so that it is allowed to read the messages that are sent by your mobile phone to the Artik Cloud. For this you need to Add the "Device Type" you have created for you mobile phone (see A.1.1). The screenshot below shows the permissions for my bluemix node-red application.
Then go to "App Info" of your application and then click on "show client ID & Secret" to see the client ID and Secret you will need later for connecting your node-red application to the Artik Cloud.
In this project I am using a Node-Red application that is running in the cloud (= IBM Bluemix). But you could equally well do the same thing with a Node-Red application that is running on a raspberry pi or on another device. So you do not necessarily need to create an account on the IBM Bluemix.
Note also that you can currently get the IBM bluemix services you need for this project for free (A Node-Red instance with half a GB of memory you can get for free: see pricing)!
B.2.1. Create a Node-Red application in IBM Bluemix- So you need to "Sign Up" on IBM Bluemix
- Then in the catalog choose "Node-RED Starter" or "Internet of Things Platform Starter". Give your Node-red application a name and hostname and click on "create".
- Goto "Console", select "Compute" and then select the Node-Red application you have just created (but don't click on the "Route"). Then select the restart action.
- In previous step we have installed Node-RED on Bluemix, in this step we will extend Node-RED with the following 2 nodesg. https://gps-traces.mybluemix.net/ ) which should show following screen:
In previous step we have installed Node-RED on bluemix, in this step we will extend Node-RED with the following 2 nodes
Following steps describe how this can be done:
- logon to IBM Bluemix and select Console and select Compute, then click on the Node Red application line (but don't click on the Route).
- In "Continuous Delivery" section select "Add Git Repo And Pipeli.." and click on "continue" and then "close"
- In "Continuous Delivery" section click on "Edit Code"
- Click on "package.json" in right window and add a "," just after "node-red-nodes-cf-sqldb-dashdb":"0.x" followed by the following 2 lines
"node-red-dashboard":"2.0.0",
"node-red-contrib-web-worldmap":"1.0.19"
So the contents of package.json should look like
{
"name" : "node-red-bluemix",
"version" : "0.5.0",
"dependencies": {
"when": "~3.x",
"mongodb": "~1.4.x",
"nano": "~5.11.0",
"cfenv":"~1.0.0",
"feedparser":"~0.19.2",
"redis":"~0.10.1",
"node-red": "0.x",
"node-red-bluemix-nodes":"1.x",
"node-red-node-watson":"0.x",
"node-red-node-openwhisk":"0.x",
"node-red-node-cf-cloudant":"0.x",
"node-red-contrib-scx-ibmiotapp":"0.x",
"node-red-contrib-ibmpush":"0.x",
"node-red-contrib-bluemix-hdfs":"0.x",
"node-red-nodes-cf-sqldb-dashdb":"0.x",
"node-red-dashboard":"2.0.0",
"node-red-contrib-web-worldmap":"1.0.19"
},
"scripts": {
"start": "node --max-old-space-size=384 node_modules/node-red/red.js --settings ./bluemix-settings.js -v"
},
"engines": {
"node": "4.x"
}
}
- Click on the Git Icon at the left of your screen
You will get the following screen:
- I have unchecked the changes for ".cfignore" and ".gitignore" (see also screenshot above)
- specify a comment under "Working Directory Changes" (see also screenshot above) and click on "commit".
- Push your commits into remote branch by selecting "Push" next to "Outgoing (1)" at the left.
- click on "Build & Deploy" in upper right corner. This will build and deploy the changes. This will take some time !
Once the deployment is finished it should look like this:
- To check that it has successfully installed these 2 nodes: click on the URL specified under "last execution result" and then click on "Go to your Node-RED flow editor" and then when you browse through the nodes you will indeed see the following "location" nodes and "dashboard" nodes:
The complete application is defined in the following Node-RED flow.
In the sections below this complete flow is explained in detail.
B.2.3.1 The user interface is a webpage of 2 framesThe user interface is a single webpage consisting of 2 frames:
- the left frame is showing the worldmap
- the right frame is showing the user interface dashboard.
This is realized through following 3 nodes.
So when you navigate to <Node-Red URL>/home it will return the following html
<!DOCTYPE html>
<html>
<frameset cols="80%,20%">
<frame src="worldmap">
<frame src="ui">
</frameset>
</html>
In other words:
- in the left frame you will see <Node-Red URL>/worldmap : whose content is controlled by node-red-contrib-web-worldmap
- in the right frame you will see <Node-Red URL>/ui : whose content is controlled by node-red-dashboard nodes
Here we will describe the user interface for the authentication with the Artik Cloud.
The user interface you can find in the top left corner (see screenshot below):
So the user interface shows the last retrieved "access token" and "refresh token" from the Artik Cloud. Note that the refresh token is shown but is currently not used for the authentication.
If your access token is expired you can get a new access token by clicking on the "login to artik" link. This will open the Samsung account login window in another browser window. After successful login, the Node-Red application will get a new access token and refresh token and also display these new tokens in the dashboard.
This logic is completely implemented by following part of the flow:
So the authentication user interface is made by following 3 dashboard nodes : "login url to artik", "access token", "refresh token" all 3 belonging to the dashboard group "Authenticate with Artik Cloud" (see screenshot below).
The "access token" and "refresh token" are 2 simple nodes that are displaying the access token and refresh token that are passed in the message payload.
The "login url to artik" has following content:
<div>
Click on link below to retrieve new access token.
Note that this will open in a new browser window.
<a href="https://accounts.artik.cloud/authorize?client_id=__YOUR_APPLICATION_CLIENT_ID__&response_type=code&redirect_uri=https://__YOUR_NODE_RED_URL__/from_artik&state=abcdefgh" target="_blank">login to artik</a>
</div>
The URL in this node is very important:
- You should replace "__YOUR_APPLICATION_CLIENT_ID__" by your application client ID (see screenshot at the end of B.1)
- regarding the redirect_uri: you should replace "__YOUR_NODE_RED_URL__" by your Node-RED URL (see step B.2.1). In fact https://__YOUR_NODE_RED_URL__/from_artik" must correspond to the URL you have specified in B.1 under "Auth Redirect URL".
If you click on this URL, it will show the Samsung login screen in another browser window and once you have successfully entered the password the Artik Cloud will send a "code" to the redirect_uri which will be received (Step 2) by the "artik callback" http in node as this node is listening to the "https://__YOUR_NODE_RED_URL__/from_artik" URL (see screenshot below).
The artik callback node will forward the "code" to
- the "http" out node : so this is the http response you when logging on to the samsung login screen in Step 1.
- the "construct authorization url" node.
The "construct authorization url" exists of following javascript code:
// prepare request
msg.url="https://accounts.artik.cloud/token?grant_type=authorization_code&code="+
msg.payload.code +
"&redirect_uri=__SAME_REDIRECT_URI__&state=abcdefgh";
return msg;
Replace __SAME_REDIRECT_URI__ with the same redirect URI used in "login url to artik" node (see step 1).
This url is used by an http request node with following configuration:
The Username and Password are the client ID and secret in last screenshot of B.1.
If the request is successful, we get a response string with the access token and the refresh token. The json node will convert the string to a json object.
The "store access token and refresh token in context" node has following code:
// store the access and refresh token in this flow context.
flow.set("access_token",msg.payload.access_token);
flow.set("refresh_token",msg.payload.refresh_token);
return msg;
So basically this node will store the access and refresh token into a flow context variable so that other nodes of this flow (like "return scrambled access and refresh token from context " and "prepare last messages request for artik cloud") can use these tokens.
The last part of the Authentication will scramble and display the access token and refresh token.
Here we will describe the part of the user interface to retrieve messages from Artik Cloud.
This part of the user interface is shown below.
So you first must specify how many messages you wan to retrieve from the Artik Cloud. When clicking on the "Retrieve messages" button, the specified number of messages are retrieved from the Artik Cloud and below the button the actual number of messages retrieved and also the content of the last message retrieved is displayed.
This part of the user interface is implemented by following part of the Node-RED flow:
So "specify number of messages to retrieve and plot" node is a drop down list and its output will be stored in the msg_count flow context variable.
The "retrieve messages" is a button node passing the contents of the msg_count flow context variable:
"prepare last messages request for artik cloud" node has following code.
msg.headers = { "Authorization" : "Bearer " + flow.get("access_token") };
msg.url = "https://api.artik.cloud/v1.1/messages/last?sdids="+
"4c7c90fe431b4a6e93e02d94dc6f5255&count="+msg.payload;
return msg;
Note that "4c7c90fe431b4a6e93e02d94dc6f5255" is the Artik Cloud device ID of my android phone (see A.1.3). You also see that the access token retrieved earlier is specified in the message header.
The "http request" node will issue the prepared request and its string output will be converted to a json object by the "convert to json" node.
The "store msgs in context var: "artik_msgs" " will store the retrieved artik messages in a flow context variable:
flow.set("artik_msgs",msg.payload.data);
return msg;
The text node "messages successfully retrieved: " will show the messages successfully received:
The "last message" node will return the last message retrieved as specified here below
msg.payload = msg.payload.data[msg.payload.data.length - 1];
return msg;
the "last message data retrieved" text node will display the content of the last message retrieved:
Here we will describe the part of the user interface to plot the messages retrieved from Artik Cloud (see B.2.3.3) on the worldmap.
This part of the user interface is shown below.
So there are 2 buttons:
- plot all messages: which will plot all messages that are last time retrieved on the worldmap.
- delete plotted message: which will delete all messages plotted on the worldmap.
This logic is completely implemented by following part of the flow:
So "plot all messages" and "delete plotted messages" are 2 buttons that will both return the list of artik messages from context variable and set respectively the message topic to "show" or "delete".
The "create worldmap LINE for GPS and NET coordinates" node is creating or deleting 2 tracks: one for the GPS coordinates on worldmap layer "GPStrack" and one based on the NET coordinates on worldmap layer "NETtrack". It has following javascript code:
var outputMsg = {"payload": { "name": "mytrack" , "iconColor" : "Blue" , "layer" : "GPStrack",
"deleted": ( msg.topic == "delete")}};
var outputMsg2 = {"payload": { "name": "mytrack_net", "iconColor" : "Red" , "layer" : "NETtrack",
"deleted" : ( msg.topic == "delete")}};
var line = [];
var line_net = [];
var msgs = msg.payload;
var msgcount = msgs.length;
for (var i = 0; i < msgcount; i++) {
line[i]= [ msgs[i].data.location_from_gps.latitude, msgs[i].data.location_from_gps.longitude];
line_net[i]= [ msgs[i].data.location_from_net.latitude, msgs[i].data.location_from_net.longitude];
}
outputMsg.payload.line = line;
outputMsg2.payload.line = line_net;
return [outputMsg , outputMsg2];
The "split array in individual msgs" node splits the array in individual messages and has following simple configuration.
The "create worldmap points for GPS and NET coordinates" node creates 4 points to plot on the worldmap or deletes those 4 plotted points depending on the message topic.
- GPS location with Globe icon with "info" on worldmap layer "GPS"
- GPS location with radius (based on accuracy) on worldmap layer "GPSradius"
- NET location with Globe icon with "info" on worldmap layer "NET"
- NET location with radius (based on accuracy) on worldmap layer "NETradius"
Here below an example of the "info" shown when clicking on the Globe icon of a GPS location or NET location.
Here below the javascript code for the "create worldmap points for GPS and NET coordinates" node:
var msgGPS = { "payload" : {
"name" : "msg_GPS"+ msg.parts.index,
"lat" : msg.payload.data.location_from_gps.latitude,
"lon" : msg.payload.data.location_from_gps.longitude,
"accuracy" : msg.payload.data.location_from_gps.accuracy,
"fix_time" : new Date(msg.payload.data.location_from_gps.fix_time*1000),
"time" : new Date(msg.payload.data.time_seconds*1000),
"altitude" : msg.payload.data.location_from_gps.altitude,
"speed" : msg.payload.data.location_from_gps.speed,
"icon" : "globe",
"iconColor" : "Blue",
"layer" : "GPS",
"deleted" : ( msg.topic == "delete")
}
};
var msgGPSRadius = { "payload" : {
"name" : "msg_GPSradius"+ msg.parts.index,
"lat" : msg.payload.data.location_from_gps.latitude,
"lon" : msg.payload.data.location_from_gps.longitude,
"radius" : msg.payload.data.location_from_gps.accuracy,
"iconColor" : "Blue",
"layer" : "GPSradius",
"deleted" : ( msg.topic == "delete")
}
};
var msgNet = { "payload" : {
"name" : "msg_NET"+ msg.parts.index,
"lat" : msg.payload.data.location_from_net.latitude,
"lon" : msg.payload.data.location_from_net.longitude,
"fix_time" : new Date(msg.payload.data.location_from_net.fix_time*1000),
"time" : new Date(msg.payload.data.time_seconds*1000),
"accuracy" : msg.payload.data.location_from_net.accuracy,
"icon" : "globe",
"iconColor" : "Red",
"layer" : "NET" ,
"deleted" : ( msg.topic == "delete")
}
};
var msgNetRadius = { "payload" : {
"name" : "msg_NETradius"+ msg.parts.index,
"lat" : msg.payload.data.location_from_net.latitude,
"lon" : msg.payload.data.location_from_net.longitude,
"radius" : msg.payload.data.location_from_net.accuracy,
"iconColor" : "Red",
"layer" : "NETradius" ,
"deleted" : ( msg.topic == "delete")
}
};
return [ msgGPS, msgGPSRadius, msgNet, msgNetRadius];
The worldmap node is responsible for showing the map (including the location points and tracks) on the left side of the screen. It has following configuration:
Here we will describe the part of the user interface to convert the messages retrieved from Artik Cloud (see B.2.3.3) to a gpx track that can be uploaded in openstreetmap.
This part of the user interface is shown below.
When you click on the gpx track link another window in the browser will open showing gpx track corresponding to the GPS coordinates of the last set of messages retrieved from the Artik cloud. Here below an example of such a gpxtrack.
You can copy paste this gpx track and save it into a file which you can upload in openstreetmap.
This logic is completely implemented by following part of the flow:
The "gpx url" node will show the gpxtrack link that will open in another window:
When clicking on that link it will open the page __YOUR_NODE_RED_URL__/gpxtrack which triggers the "gpx" node :
The "convert artik_msgs to gpx" node will construct the gpx track xml message for the gps locations of the last set of messages retrieved from the Artik Cloud.
var artik_msgs = flow.get("artik_msgs");
var xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<gpx version=\"1.0\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xmlns=\"http://www.topografix.com/GPX/1/0\"\n" +
" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0\n" +
" http://www.topografix.com/GPX/1/0/gpx.xsd\">\n"+
" <name>jvda gpx</name>\n" +
" <trk><name>jvda gpx</name><number>1</number><trkseg>\n";
for (var i = 0; i < artik_msgs.length; i++) {
xml += " <trkpt lat=\""+ artik_msgs[i].data.location_from_gps.latitude +
"\" lon=\"" + artik_msgs[i].data.location_from_gps.longitude +
"\"><ele>" + artik_msgs[i].data.location_from_gps.altitude + "</ele>" +
"<time>" + new Date(artik_msgs[i].data.location_from_gps.fix_time*1000).toISOString() + "</time></trkpt>\n";
}
xml += " </trkseg></trk>\n</gpx>\n";
msg.payload = xml;
return msg;
The "html page showing gpx track" node creates a html page showing the gpx track xml message:
The "http" node shows that html page as response:
-- end of story --
Comments