mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-09 06:28:06 -05:00
Update gpx serializer
This commit is contained in:
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
## Changed
|
||||
|
||||
- If user already have import with the same name, it will be appended with timestamp during the import process.
|
||||
- Export to GPX now adds adds speed and course to each point if they are available.
|
||||
|
||||
## Fixed
|
||||
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Simple wrapper class that acts like GPX::GPXFile but preserves enhanced XML
|
||||
class EnhancedGpxFile < GPX::GPXFile
|
||||
def initialize(name, xml_string)
|
||||
super(name: name)
|
||||
@enhanced_xml = xml_string
|
||||
end
|
||||
|
||||
def to_s
|
||||
@enhanced_xml
|
||||
end
|
||||
end
|
||||
|
||||
class Points::GpxSerializer
|
||||
def initialize(points, name)
|
||||
@points = points
|
||||
@@ -7,30 +19,92 @@ class Points::GpxSerializer
|
||||
end
|
||||
|
||||
def call
|
||||
gpx_file = GPX::GPXFile.new(name: "dawarich_#{name}")
|
||||
track = GPX::Track.new(name: "dawarich_#{name}")
|
||||
gpx_file = create_base_gpx_file
|
||||
add_track_points_to_gpx(gpx_file)
|
||||
xml_string = enhance_gpx_with_speed_and_course(gpx_file.to_s)
|
||||
|
||||
gpx_file.tracks << track
|
||||
|
||||
track_segment = GPX::Segment.new
|
||||
track.segments << track_segment
|
||||
|
||||
points.each do |point|
|
||||
track_segment.points << GPX::TrackPoint.new(
|
||||
lat: point.lat,
|
||||
lon: point.lon,
|
||||
elevation: point.altitude.to_f,
|
||||
time: point.recorded_at
|
||||
)
|
||||
end
|
||||
|
||||
GPX::GPXFile.new(
|
||||
name: "dawarich_#{name}",
|
||||
gpx_data: gpx_file.to_s.sub('<gpx', '<gpx xmlns="http://www.topografix.com/GPX/1/1"')
|
||||
)
|
||||
EnhancedGpxFile.new("dawarich_#{name}", xml_string)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :points, :name
|
||||
|
||||
def create_base_gpx_file
|
||||
gpx_file = GPX::GPXFile.new(name: "dawarich_#{name}")
|
||||
track = GPX::Track.new(name: "dawarich_#{name}")
|
||||
gpx_file.tracks << track
|
||||
|
||||
track_segment = GPX::Segment.new
|
||||
track.segments << track_segment
|
||||
|
||||
gpx_file
|
||||
end
|
||||
|
||||
def add_track_points_to_gpx(gpx_file)
|
||||
track_segment = gpx_file.tracks.first.segments.first
|
||||
|
||||
points.each do |point|
|
||||
track_point = create_track_point(point)
|
||||
track_segment.points << track_point
|
||||
end
|
||||
end
|
||||
|
||||
def create_track_point(point)
|
||||
track_point_attrs = build_track_point_attributes(point)
|
||||
GPX::TrackPoint.new(**track_point_attrs)
|
||||
end
|
||||
|
||||
def build_track_point_attributes(point)
|
||||
{
|
||||
lat: point.lat,
|
||||
lon: point.lon,
|
||||
elevation: point.altitude.to_f,
|
||||
time: point.recorded_at
|
||||
}
|
||||
end
|
||||
|
||||
def enhance_gpx_with_speed_and_course(gpx_xml)
|
||||
xml_string = add_gpx_namespace(gpx_xml)
|
||||
enhance_trackpoints_with_speed_and_course(xml_string)
|
||||
end
|
||||
|
||||
def add_gpx_namespace(gpx_xml)
|
||||
gpx_xml.sub('<gpx', '<gpx xmlns="http://www.topografix.com/GPX/1/1"')
|
||||
end
|
||||
|
||||
def enhance_trackpoints_with_speed_and_course(xml_string)
|
||||
trkpt_count = 0
|
||||
xml_string.gsub(/(<trkpt[^>]*>.*?<\/trkpt>)/m) do |trkpt_xml|
|
||||
point = points[trkpt_count]
|
||||
trkpt_count += 1
|
||||
enhance_single_trackpoint(trkpt_xml, point)
|
||||
end
|
||||
end
|
||||
|
||||
def enhance_single_trackpoint(trkpt_xml, point)
|
||||
enhanced_trkpt = add_speed_to_trackpoint(trkpt_xml, point)
|
||||
add_course_to_trackpoint(enhanced_trkpt, point)
|
||||
end
|
||||
|
||||
def add_speed_to_trackpoint(trkpt_xml, point)
|
||||
return trkpt_xml unless should_include_speed?(point)
|
||||
|
||||
trkpt_xml.sub(/(<ele>[^<]*<\/ele>)/, "\\1\n <speed>#{point.velocity.to_f}</speed>")
|
||||
end
|
||||
|
||||
def add_course_to_trackpoint(trkpt_xml, point)
|
||||
return trkpt_xml unless should_include_course?(point)
|
||||
|
||||
extensions_xml = "\n <extensions>\n <course>#{point.course.to_f}</course>\n </extensions>"
|
||||
trkpt_xml.sub(/\n <\/trkpt>/, "#{extensions_xml}\n </trkpt>")
|
||||
end
|
||||
|
||||
def should_include_speed?(point)
|
||||
point.velocity.present? && point.velocity.to_f > 0
|
||||
end
|
||||
|
||||
def should_include_course?(point)
|
||||
point.course.present?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,7 @@ RSpec.describe Points::GpxSerializer do
|
||||
|
||||
let(:points) do
|
||||
(1..3).map do |i|
|
||||
create(:point, timestamp: 1.day.ago + i.minutes)
|
||||
create(:point, timestamp: 1.day.ago + i.minutes, velocity: i * 10.5, course: i * 45.2)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,17 +16,55 @@ RSpec.describe Points::GpxSerializer do
|
||||
expect(serializer).to be_a(GPX::GPXFile)
|
||||
end
|
||||
|
||||
it 'includes waypoints' do
|
||||
expect(serializer.tracks[0].points.size).to eq(3)
|
||||
it 'includes waypoints in XML output' do
|
||||
gpx_xml = serializer.to_s
|
||||
|
||||
# Check that all 3 points are included in XML
|
||||
expect(gpx_xml.scan(/<trkpt/).size).to eq(3)
|
||||
|
||||
# Check that basic point data is included
|
||||
points.each do |point|
|
||||
expect(gpx_xml).to include("lat=\"#{point.lat}\"")
|
||||
expect(gpx_xml).to include("lon=\"#{point.lon}\"")
|
||||
expect(gpx_xml).to include("<ele>#{point.altitude.to_f}</ele>")
|
||||
end
|
||||
end
|
||||
|
||||
it 'includes speed and course data in the GPX XML output' do
|
||||
gpx_xml = serializer.to_s
|
||||
|
||||
# Check that speed is included in XML for points with velocity
|
||||
expect(gpx_xml).to include('<speed>10.5</speed>')
|
||||
expect(gpx_xml).to include('<speed>21.0</speed>')
|
||||
expect(gpx_xml).to include('<speed>31.5</speed>')
|
||||
|
||||
# Check that course is included in extensions for points with course data
|
||||
expect(gpx_xml).to include('<course>45.2</course>')
|
||||
expect(gpx_xml).to include('<course>90.4</course>')
|
||||
expect(gpx_xml).to include('<course>135.6</course>')
|
||||
end
|
||||
|
||||
it 'includes waypoints with correct attributes' do
|
||||
serializer.tracks[0].points.each_with_index do |track_point, index|
|
||||
point = points[index]
|
||||
context 'when points have nil velocity or course' do
|
||||
let(:points) do
|
||||
[
|
||||
create(:point, timestamp: 1.day.ago, velocity: nil, course: nil),
|
||||
create(:point, timestamp: 1.day.ago + 1.minute, velocity: 15.5, course: nil),
|
||||
create(:point, timestamp: 1.day.ago + 2.minutes, velocity: nil, course: 90.0)
|
||||
]
|
||||
end
|
||||
|
||||
expect(track_point.lat.to_s).to eq(point.lat.to_s)
|
||||
expect(track_point.lon.to_s).to eq(point.lon.to_s)
|
||||
expect(track_point.time).to eq(point.recorded_at)
|
||||
it 'handles nil values gracefully in XML output' do
|
||||
gpx_xml = serializer.to_s
|
||||
|
||||
# Should only include speed for the point with velocity
|
||||
expect(gpx_xml).to include('<speed>15.5</speed>')
|
||||
expect(gpx_xml).not_to include('<speed>0</speed>') # Should not include zero/nil speeds
|
||||
|
||||
# Should only include course for the point with course data
|
||||
expect(gpx_xml).to include('<course>90.0</course>')
|
||||
|
||||
# Should have 3 track points total
|
||||
expect(gpx_xml.scan(/<trkpt/).size).to eq(3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user