Skip to content

Commit

Permalink
Merge pull request #374 from Freika/fix/gpx-geojson-speed-recording
Browse files Browse the repository at this point in the history
Fix/gpx geojson speed recording
  • Loading branch information
Freika authored Nov 8, 2024
2 parents fb799eb + 3880d51 commit 657f69d
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .app_version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.16.0
0.16.1
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

# 0.16.1 - 2024-11-08

### Fixed

- Speed is now being recorded into points when a GPX file is being imported. Previously, the speed was not being recorded.
- GeoJSON file from GPSLogger now can be imported to Dawarich. Previously, the import was failing due to incorrect parsing of the file.

### Changed

- The Vists suggestion job is disabled. It will be re-enabled in the future with a new approach to the visit suggestion process.

# 0.16.0 - 2024-11-07

## The Websockets release
Expand Down
30 changes: 23 additions & 7 deletions app/services/geojson/params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,30 @@ def build_point(feature)
battery: battery_level(feature[:properties][:battery_level]),
timestamp: timestamp(feature),
altitude: altitude(feature),
velocity: feature[:properties][:speed],
velocity: speed(feature),
tracker_id: feature[:properties][:device_id],
ssid: feature[:properties][:wifi],
accuracy: feature[:properties][:horizontal_accuracy],
accuracy: accuracy(feature),
vertical_accuracy: feature[:properties][:vertical_accuracy],
raw_data: feature
}
end

def build_line(feature)
feature[:geometry][:coordinates].map do |point|
build_line_point(feature, point)
build_line_point(point)
end
end

def build_multi_line(feature)
feature[:geometry][:coordinates].map do |line|
line.map do |point|
build_line_point(feature, point)
build_line_point(point)
end
end
end

def build_line_point(feature, point)
def build_line_point(point)
{
latitude: point[1],
longitude: point[0],
Expand All @@ -84,7 +84,23 @@ def altitude(feature)
def timestamp(feature)
return Time.zone.at(feature[3]) if feature.is_a?(Array)

value = feature.dig(:properties, :timestamp) || feature.dig(:geometry, :coordinates, 3)
Time.zone.at(value)
value = feature.dig(:properties, :timestamp) ||
feature.dig(:geometry, :coordinates, 3)

return Time.zone.at(value.to_i) if value.is_a?(Numeric)

### GPSLogger for Android case ###
time = feature.dig(:properties, :time)

Time.zone.parse(time).to_i if time.present?
### /GPSLogger for Android case ###
end

def speed(feature)
feature.dig(:properties, :speed).to_f.round(1)
end

def accuracy(feature)
feature.dig(:properties, :accuracy) || feature.dig(:properties, :horizontal_accuracy)
end
end
9 changes: 9 additions & 0 deletions app/services/gpx/track_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def create_point(point, index)
altitude: point['ele'].to_i,
timestamp: Time.parse(point['time']).to_i,
import_id: import.id,
velocity: speed(point),
raw_data: point,
user_id:
)
Expand All @@ -54,4 +55,12 @@ def point_exists?(point)
user_id:
)
end

def speed(point)
return if point['extensions'].blank?

(
point.dig('extensions', 'speed') || point.dig('extensions', 'TrackPointExtension', 'speed')
).to_f.round(1)
end
end
9 changes: 5 additions & 4 deletions config/schedule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ area_visits_calculation_scheduling_job:
class: "AreaVisitsCalculationSchedulingJob"
queue: visit_suggesting

visit_suggesting_job:
cron: "0 1 * * *" # every day at 1:00
class: "VisitSuggestingJob"
queue: visit_suggesting
# Disabled until fixed
# visit_suggesting_job:
# cron: "0 1 * * *" # every day at 1:00
# class: "VisitSuggestingJob"
# queue: visit_suggesting

watcher_job:
cron: "0 */1 * * *" # every 1 hour
Expand Down
23 changes: 23 additions & 0 deletions spec/fixtures/files/geojson/gpslogger_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"features": [
{
"geometry": {
"coordinates": [
106.64234449272531,
10.758321212464024
],
"type": "Point"
},
"properties": {
"accuracy": 4.7551565,
"altitude": 17.634344400269068,
"provider": "gps",
"speed": 1.2,
"time": "2024-11-03T16:30:11.331+07:00",
"time_long": 1730626211331
},
"type": "Feature"
}
],
"type": "FeatureCollection"
}
31 changes: 31 additions & 0 deletions spec/fixtures/files/gpx/garmin_example.gpx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="GPSLogger 131 - http://gpslogger.mendhak.com/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v2"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd
http://www.garmin.com/xmlschemas/TrackPointExtension/v2 https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd
">
<metadata>
<time>2024-11-03T16:30:11.331+07:00</time>
</metadata>
<trk>
<name>20241103</name>
<trkseg>
<trkpt lat="10.758321212464024" lon="106.64234449272531">
<ele>17.634344400269068</ele>
<time>2024-11-03T16:30:11.331+07:00</time>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:speed>2.8</gpxtpx:speed>
</gpxtpx:TrackPointExtension>
</extensions>
<geoidheight>-1.6</geoidheight>
<src>gps</src>
<sat>3</sat>
<hdop>1.9</hdop>
<vdop>8.6</vdop>
<pdop>8.8</pdop>
</trkpt>
</trkseg>
</trk>
</gpx>
108 changes: 108 additions & 0 deletions spec/services/geojson/params_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe Geojson::Params do
describe '#call' do
let(:file_path) { Rails.root.join('spec/fixtures/files/geojson/export.json') }
let(:file) { File.read(file_path) }
let(:json) { JSON.parse(file) }
let(:params) { described_class.new(json) }

subject { params.call }

it 'returns an array of points' do
expect(subject).to be_an_instance_of(Array)
expect(subject.first).to be_an_instance_of(Hash)
end

it 'returns the correct data for each point' do
expect(subject.first).to eq(
latitude: '0.0',
longitude: '0.0',
battery_status: nil,
battery: nil,
timestamp: Time.zone.at(1_609_459_201),
altitude: 1,
velocity: 0,
tracker_id: nil,
ssid: nil,
accuracy: 1,
vertical_accuracy: 1,
raw_data: {
'type' => 'Feature',
'geometry' => {
'type' => 'Point',
'coordinates' => [
'0.0',
'0.0'
]
},
'properties' => {
'battery_status' => 'unplugged',
'ping' => 'MyString',
'battery' => 1,
'tracker_id' => 'MyString',
'topic' => 'MyString',
'altitude' => 1,
'longitude' => '0.1',
'velocity' => 'MyString',
'trigger' => 'background_event',
'bssid' => 'MyString',
'ssid' => 'MyString',
'connection' => 'wifi',
'vertical_accuracy' => 1,
'accuracy' => 1,
'timestamp' => 1_609_459_201,
'latitude' => '0.1',
'mode' => 1,
'inrids' => [],
'in_regions' => [],
'raw_data' => '',
'city' => nil,
'country' => nil,
'geodata' => {}
}
}
)
end

context 'when the json is exported from GPSLogger' do
let(:file_path) { Rails.root.join('spec/fixtures/files/geojson/gpslogger_example.json') }

it 'returns the correct data for each point' do
expect(subject.first).to eq(
latitude: 10.758321212464024,
longitude: 106.64234449272531,
battery_status: nil,
battery: nil,
timestamp: Time.parse('2024-11-03T16:30:11.331+07:00').to_i,
altitude: 17.634344400269068,
velocity: 1.2,
tracker_id: nil,
ssid: nil,
accuracy: 4.7551565,
vertical_accuracy: nil,
raw_data: {
'geometry' => {
'coordinates' => [
106.64234449272531,
10.758321212464024
],
'type' => 'Point'
},
'properties' => {
'accuracy' => 4.7551565,
'altitude' => 17.634344400269068,
'provider' => 'gps',
'speed' => 1.2,
'time' => '2024-11-03T16:30:11.331+07:00',
'time_long' => 1_730_626_211_331
},
'type' => 'Feature'
}
)
end
end
end
end
58 changes: 40 additions & 18 deletions spec/services/gpx/track_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,29 @@
let(:raw_data) { Hash.from_xml(File.read(file_path)) }
let(:import) { create(:import, user:, name: 'gpx_track.gpx', raw_data:) }

context 'when file exists' do
context 'when file has a single segment' do
it 'creates points' do
expect { parser }.to change { Point.count }.by(301)
end
context 'when file has a single segment' do
it 'creates points' do
expect { parser }.to change { Point.count }.by(301)
end

it 'broadcasts importing progress' do
expect_any_instance_of(Imports::Broadcaster).to receive(:broadcast_import_progress).exactly(301).times
it 'broadcasts importing progress' do
expect_any_instance_of(Imports::Broadcaster).to receive(:broadcast_import_progress).exactly(301).times

parser
end
parser
end
end

context 'when file has multiple segments' do
let(:file_path) { Rails.root.join('spec/fixtures/files/gpx/gpx_track_multiple_segments.gpx') }
context 'when file has multiple segments' do
let(:file_path) { Rails.root.join('spec/fixtures/files/gpx/gpx_track_multiple_segments.gpx') }

it 'creates points' do
expect { parser }.to change { Point.count }.by(558)
end
it 'creates points' do
expect { parser }.to change { Point.count }.by(558)
end

it 'broadcasts importing progress' do
expect_any_instance_of(Imports::Broadcaster).to receive(:broadcast_import_progress).exactly(558).times
it 'broadcasts importing progress' do
expect_any_instance_of(Imports::Broadcaster).to receive(:broadcast_import_progress).exactly(558).times

parser
end
parser
end
end

Expand All @@ -51,6 +49,30 @@

parser
end

it 'creates points with correct data' do
parser

expect(Point.first.latitude).to eq(37.17221.to_d)
expect(Point.first.longitude).to eq(-3.55468.to_d)
expect(Point.first.altitude).to eq(1066)
expect(Point.first.timestamp).to eq(Time.zone.parse('2024-04-21T10:19:55Z').to_i)
expect(Point.first.velocity).to eq('2.9')
end
end

context 'when file exported from Garmin' do
let(:file_path) { Rails.root.join('spec/fixtures/files/gpx/garmin_example.gpx') }

it 'creates points with correct data' do
parser

expect(Point.first.latitude).to eq(10.758321.to_d)
expect(Point.first.longitude).to eq(106.642344.to_d)
expect(Point.first.altitude).to eq(17)
expect(Point.first.timestamp).to eq(1_730_626_211)
expect(Point.first.velocity).to eq('2.8')
end
end
end
end

0 comments on commit 657f69d

Please sign in to comment.