How to import Nokia Sportstracker GPS data with Ruby on Rails
Posted by Lars | Posted in Geocoding, Rails, Ruby, Tutorials | Posted on 08-05-2009
2
In this tutorial i will explain how to import GPS data from Nokia mobile phones with Ruby on Rails and displaying them as route on a google map.
The first part will solve the data import of GPX formated data files. The second part will show you how to display the routes on google map.
As data source i use a sample *.GPX file. It is provided from Marton who asked me to write this tutorial. The full description of xml structure you can find at http://www.topografix.com/GPX/1/1/
I will write a sample Rails Application - navtracker - covering following points of Ruby on Rails programming:
- Upload a *.GPX file
- Parse a XML Data
- Google Maps Api access to display the route
Alright, lets start writing the application. All code examples are based on Rails 2.3.2.
rake db:create:all
For file upload we need the paperclip plugin.
Now we create the “Track”, “Tracksegment” and “Point” resources. One track has many tracksegments has many points.
Tragsegment is needed if you loss your GPS connection. The trackersoftware will create a new tracksegment if your GPS connections is etablished again.
gpx_content_type:string gpx_file_size:integer
longitude:float elevation:float description:string
point_created_at:datetime tracksegment:references
Note: I broke the lines for better readabillity. Script/generate commands have to be written in single line.
After the command
we can write the associations into our models.
1 2 3 4 | class Track < ActiveRecord::Base has_many :tracksegments, :dependent => :destroy has_attached_file :gpx end |
1 2 3 4 | class Tracksegment < ActiveRecord::Base belongs_to :track has_many :points, :dependent => :destroy end |
1 2 3 | class Point < ActiveRecord::Base belongs_to :tracksegment end |
Upload a GPX file
For uploading a GPX file we modify our view/tracks/new.html.erb following way:
1 2 3 4 5 6 7 8 9 10 11 | <% form_for(@track, :html => { :multipart => true }) do |f| %> <%= f.error_messages %> <p> <%= f.file_field :gpx %> </p> <p> <%= f.submit 'Upload GPX' %> </p> <% end %> <%= link_to 'Back', tracks_path %> |
Upload a GPX file for testing before we start with with next section.
Parsing the GPX file
Let’s start with parsing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Track < ActiveRecord::Base has_many :tracksegments has_attached_file :gpx def save_attached_files_with_parse_file save_attached_files_without_parse_file parse_file end alias_method_chain :save_attached_files, :parse_file def parse_file ... end end |
We like to parse the uploaded GPX file directly after upload. That tricky because the paperclip pluging uses the after_create callback. If we use after_create it will be executed before paperclip plugin upload. It will cause an error “File not found”. We workaround with alias chain method.
We need following steps to parse an xml file:
- Open the uploaded GPX file
- Parse the GPX file
- Close GPX file
Open GPX file and prepare for parsing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Track < ActiveRecord::Base has_many :tracksegments has_attached_file :gpx attr_accessor :file_handler, :gpx_document def save_attached_files_with_parse_file save_attached_files_without_parse_file parse_file end alias_method_chain :save_attached_files, :parse_file def parse_file prepare_gpx_file parse_xml cleanup_gpx_file end ... end |
In the first step we prepare the file for parsing. We need a file handler to open the gpx xml file with REXML. For this reason i added two attributes to the model in line 5.
1 2 3 | def parse_xml ... end |
Parse_xml executes the parsing. I will explain it later.
1 2 3 4 5 | def prepare_gpx_file logger.info "Prepare GPX file ..." make_file_handler open_gpx_xml end |
Prepare_gpx_file calls functions to make a file_handler and
and gpx_xml handler.
1 2 3 4 5 | def make_file_handler logger.info "Make file handler ..." file_handler = File.new(File.join(RAILS_ROOT,'public', gpx.url.split("?")[0]),"r") end |
make_file_handler assigns the file_handler. The file open is a little bit tricky. We have to remove the parameters after ? from gpx.url. So we use a simple “split” and take the first array element from it.
1 2 3 4 | def open_gpx_xml logger.info "Open GPX XML file ..." gpx_document = REXML::Document.new file_handler end |
open_gpx_xml assigns the xml handler. We use REXML to parse the file.
1 2 3 | def cleanup_gpx_file file_handler.close end |
cleanup_gpx_file closes the file afterwards.
Parse the GPX file
1 | attr_accessor ..., :tmp_segment, :tmp_point |
We need two more attributes: tmp_segment and tmp_point. This attributes contain the temporary data while parsing.
1 2 3 4 5 | def parse_xml self.gpx_document.root.each_element do |node| parse_tracks(node) end end |
parse_xml loops the root level for each node it can find.
For each node we start the function parse_tracks with the current node as parameter.
1 2 3 4 5 6 7 | def parse_tracks(node) if node.name.eql? "trk" node.each_element do |node| parse_track_segments(node) end end end |
parse_tracks function checks the node name. If the name is “trk” then we go a level deeper and loop all nodes from the current “trk” node. For each node we call
parse_track_segments(node).
1 2 3 4 5 6 7 8 9 | def parse_track_segments(node) if node.name.eql? "trkseg" tmp_segment = Tracksegment.new self.tracksegments << tmp_segment node.each_element do |node| parse_points(node,tmp_segment) end end end |
In parse_track_segments we check for the name “trkseg”. If so then create a new segment. We go a level deeper now and loop all nodes of “trkseg” node. For each of this node we start parse_points.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def parse_points(node,tmp_segment) if node.name.eql? "trkpt" tmp_point = Point.new node.attributes.each do |key,value| tmp_point.latitude = value if key.eql? 'lat' tmp_point.longitude = value if key.eql? 'lon' end node.each_element do |node| tmp_point.name = node.text.to_s if node.name.eql? 'name' tmp_point.elevation = node.text.to_s if node.name.eql? 'ele' tmp_point.description = node.text.to_s if node.name.eql? 'desc' tmp_point.point_created_at = node.text.to_s if node.name.eql? 'time' end tmp_segment.points << tmp_point end end ... |
In parse_points the real action is starting. We check for node name “trkpt”. If so we create a new point object and read this node attributes lat and lon. We save it in the point object attributes latitude and longitude.
Next we start with looping all elements of current node “trkpt”. Inside loop we check for name, ele, desc and time. If one of this node names match we assign it to the corresponding attribute of point. At last we add the temporary point to the tmp_segment.
Comming next
In next tutorial we are going to care about display the route in google maps with Ruby on Rails.











Good morning from Málaga (Spain).
I wrote this document looking for an answer and how to: import KML or GPX to Nokia Sportstracker. Y try to convert gpx to data file of my Nokia 5800 for import a route of Google.Is it possible?
Sorry my poor english.
Nice day. Antonio
Hi Antonio,
thank you for your comment. Unfourtunally i can’t help you with the opposite way to import google data into your Nokia 5800.
cu Lars