Skip to content

Commit

Permalink
Merge pull request #53 from dougfales/nk-procs_to_decorate_gpx
Browse files Browse the repository at this point in the history
Decorate GeoJSON -> GPX conversion with procs
  • Loading branch information
niborg authored Jul 27, 2024
2 parents a424436 + d0e93b6 commit 154cea5
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 18 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ gpx_file = GPX::GeoJSON.convert_to_gpx(geojson_file: 'mygeojsonfile.json')
# Converting from a string
data = JSON.generate(my_geojson_hash)
gpx_file = GPX::GeoJSON.convert_to_gpx(geojson_data: data)

# The above won't transfer anything but coordinate values. If you want to
# transfer ad hoc "properties" information, you can specify an object that
# responds to `call` to manipulate GPX data structures as follows:
gpx_file = GPX::GeoJSON.convert_to_gpx(
geojson_data: data,
line_string_feature_to_segment: ->(ls, seg) { seg.distance = ls["properties"]["distance"] },
multi_line_string_feature_to_track: lambda { |mls, track|
track.name = mls["properties"]["name"]
},
point_feature_to_waypoint: ->(pt, wpt) { wpt.name = pt["properties"]["name"] }
multi_point_feature_to_waypoint: ->(mpt, wpt) { wpt.sym = mpt["properties"]["icon"] }
)
```

Exporting an ActiveRecord to GPXFile (as Waypoints)
Expand Down
46 changes: 30 additions & 16 deletions lib/gpx/geo_json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ class << self
def convert_to_gpx(opts = {})
geojson = geojson_data_from(opts)
gpx_file = GPX::GPXFile.new
add_tracks_to(gpx_file, geojson)
add_waypoints_to(gpx_file, geojson)
add_tracks_to(gpx_file, geojson, opts)
add_waypoints_to(gpx_file, geojson, opts)
gpx_file
end

Expand All @@ -61,32 +61,35 @@ def parse_geojson_data(data)
JSON.parse(data)
end

def add_tracks_to(gpx_file, geojson)
tracks = [line_strings_to_track(geojson)] +
multi_line_strings_to_tracks(geojson)
def add_tracks_to(gpx_file, geojson, opts)
tracks = [line_strings_to_track(geojson, opts)] +
multi_line_strings_to_tracks(geojson, opts)
tracks.compact!
gpx_file.tracks += tracks
gpx_file.tracks.each { |t| gpx_file.update_meta_data(t) }
end

def add_waypoints_to(gpx_file, geojson)
def add_waypoints_to(gpx_file, geojson, opts)
gpx_file.waypoints +=
points_to_waypoints(geojson, gpx_file) +
multi_points_to_waypoints(geojson, gpx_file)
points_to_waypoints(geojson, gpx_file, opts) +
multi_points_to_waypoints(geojson, gpx_file, opts)
end

# Converts GeoJSON 'LineString' features.
# Current strategy is to convert each LineString into a
# Track Segment, returning a Track for all LineStrings.
#
def line_strings_to_track(geojson)
def line_strings_to_track(geojson, opts)
line_strings = line_strings_in(geojson)
return nil unless line_strings.any?

track = GPX::Track.new
line_strings.each do |ls|
coords = ls['geometry']['coordinates']
track.append_segment(coords_to_segment(coords))
segment = coords_to_segment(coords)

opts[:line_string_feature_to_segment]&.call(ls, segment)
track.append_segment(segment)
end
track
end
Expand All @@ -96,15 +99,18 @@ def line_strings_to_track(geojson)
# into a Track, with each set of LineString coordinates
# within a MultiLineString a Track Segment.
#
def multi_line_strings_to_tracks(geojson)
def multi_line_strings_to_tracks(geojson, opts)
tracks = []
multi_line_strings_in(geojson).each do |mls|
track = GPX::Track.new
mls['geometry']['coordinates'].each do |coords|
seg = coords_to_segment(coords)
seg.track = track

track.append_segment(seg)
end

opts[:multi_line_string_feature_to_track]&.call(mls, track)
tracks << track
end
tracks
Expand All @@ -114,10 +120,14 @@ def multi_line_strings_to_tracks(geojson)
# Current strategy is to convert each Point
# feature into a GPX waypoint.
#
def points_to_waypoints(geojson, gpx_file)
def points_to_waypoints(geojson, gpx_file, opts)
points_in(geojson).reduce([]) do |acc, pt|
coords = pt['geometry']['coordinates']
acc << point_to_waypoint(coords, gpx_file)
waypoint = point_to_waypoint(coords, gpx_file)

opts[:point_feature_to_waypoint]&.call(pt, waypoint)

acc << waypoint
end
end

Expand All @@ -132,10 +142,14 @@ def points_to_waypoints(geojson, gpx_file)
# series of turn points leading to a destination."
# See http://www.topografix.com/gpx/1/1/#type_rteType
#
def multi_points_to_waypoints(geojson, gpx_file)
multi_points_in(geojson).reduce([]) do |acc, mpt|
def multi_points_to_waypoints(geojson, gpx_file, opts)
multi_points_in(geojson).each_with_object([]) do |mpt, acc|
mpt['geometry']['coordinates'].each do |coords|
acc << point_to_waypoint(coords, gpx_file)
waypoint = point_to_waypoint(coords, gpx_file)

opts[:multi_point_feature_to_waypoint]&.call(mpt, waypoint)

acc << waypoint
end
end
end
Expand Down
13 changes: 11 additions & 2 deletions tests/geojson_files/line_string_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
[-118.41506, 34.06301, 80],
[-118.415, 34.06305, 80],
[-118.41494, 34.06309, 80]
]
}
]
},
"properties": {
"distance": 10
}
}, {
"type": "Feature",
"geometry": {
Expand All @@ -49,6 +52,9 @@
[-118.36894, 34.05818, 40],
[-118.369, 34.05806, 40]
]
},
"properties": {
"distance": 100
}
}, {
"type": "Feature",
Expand Down Expand Up @@ -78,6 +84,9 @@
[-118.35046, 34.03779, 30],
[-118.3505, 34.03768, 30]
]
},
"properties": {
"distance": 1000
}
}]
}
3 changes: 3 additions & 0 deletions tests/geojson_files/multi_line_string_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@
[-118.35046, 34.03779, 30],
[-118.3505, 34.03768, 30]
]]
},
"properties": {
"name": "Foo"
}
}]
}
3 changes: 3 additions & 0 deletions tests/geojson_files/multi_point_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
[-118.4158, 34.06256, 80],
[-118.41575, 34.06259, 80]
]
},
"properties": {
"name": "Foo"
}
}]
}
9 changes: 9 additions & 0 deletions tests/geojson_files/point_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,27 @@
"geometry": {
"type": "Point",
"coordinates": [-118.41585, 34.06253, 80]
},
"properties": {
"name": "Foo"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-118.36855, 34.05844, 40]
},
"properties": {
"name": "Bar"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-118.35043, 34.0392, 30]
},
"properties": {
"name": "Baz"
}
}]
}
63 changes: 63 additions & 0 deletions tests/geojson_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ def test_line_string_functionality
assert_equal(58, pts_size)
end

def test_line_string_functionality_with_lambda
file = File.join(File.dirname(__FILE__), 'geojson_files/line_string_data.json')
gpx_file = GPX::GeoJSON.convert_to_gpx(
geojson_file: file,
line_string_feature_to_segment: lambda { |line_string, segment|
segment.points << GPX::Point.new(
{
lat: line_string['geometry']['coordinates'][0][1],
lon: line_string['geometry']['coordinates'][0][0]
}
)
}
)

assert_equal(1, gpx_file.tracks.size)
assert_equal(3, gpx_file.tracks.first.segments.size)
pts_size = gpx_file.tracks.first.segments[0].points.size +
gpx_file.tracks.first.segments[1].points.size +
gpx_file.tracks.first.segments[2].points.size
assert_equal(61, pts_size)
end

def test_multi_line_string_functionality
file = File.join(File.dirname(__FILE__), 'geojson_files/multi_line_string_data.json')
gpx_file = GPX::GeoJSON.convert_to_gpx(geojson_file: file)
Expand All @@ -65,18 +87,59 @@ def test_multi_line_string_functionality
assert_equal(58, pts_size)
end

def test_multi_line_string_functionality_with_lambda
file = File.join(File.dirname(__FILE__), 'geojson_files/multi_line_string_data.json')
gpx_file = GPX::GeoJSON.convert_to_gpx(
geojson_file: file,
multi_line_string_feature_to_track: lambda { |multi_line_string, segment|
segment.name = multi_line_string['properties']['name']
}
)
assert_equal(1, gpx_file.tracks.size)
assert_equal(3, gpx_file.tracks.first.segments.size)
pts_size = gpx_file.tracks.first.segments[0].points.size +
gpx_file.tracks.first.segments[1].points.size +
gpx_file.tracks.first.segments[2].points.size
assert_equal(58, pts_size)
assert_equal("Foo", gpx_file.tracks[0].name)
end

def test_point_functionality
file = File.join(File.dirname(__FILE__), 'geojson_files/point_data.json')
gpx_file = GPX::GeoJSON.convert_to_gpx(geojson_file: file)
assert_equal(3, gpx_file.waypoints.size)
end

def test_point_functionality_with_proc
file = File.join(File.dirname(__FILE__), 'geojson_files/point_data.json')
gpx_file = GPX::GeoJSON.convert_to_gpx(
geojson_file: file,
point_feature_to_waypoint: ->(point, waypoint) { waypoint.name = point['properties']['name'] }
)
assert_equal(3, gpx_file.waypoints.size)
assert_equal('Foo', gpx_file.waypoints[0].name)
assert_equal('Bar', gpx_file.waypoints[1].name)
assert_equal('Baz', gpx_file.waypoints[2].name)
end

def test_multi_point_functionality
file = File.join(File.dirname(__FILE__), 'geojson_files/multi_point_data.json')
gpx_file = GPX::GeoJSON.convert_to_gpx(geojson_file: file)
assert_equal(3, gpx_file.waypoints.size)
end

def test_multi_point_functionality_with_proc
file = File.join(File.dirname(__FILE__), 'geojson_files/multi_point_data.json')
gpx_file = GPX::GeoJSON.convert_to_gpx(
geojson_file: file,
multi_point_feature_to_waypoint: ->(multi_point, waypoint) { waypoint.name = multi_point['properties']['name'] }
)
assert_equal(3, gpx_file.waypoints.size)
assert_equal('Foo', gpx_file.waypoints[0].name)
assert_equal('Foo', gpx_file.waypoints[1].name)
assert_equal('Foo', gpx_file.waypoints[2].name)
end

def test_combined_functionality
file = File.join(File.dirname(__FILE__), 'geojson_files/combined_data.json')
gpx_file = GPX::GeoJSON.convert_to_gpx(geojson_file: file)
Expand Down

0 comments on commit 154cea5

Please sign in to comment.