Module: GitFit::Elevation::TerrainCoeff
- Defined in:
- lib/git_fit/elevation/terrain_coeff.rb,
lib/git_fit/elevation/terrain_lookup.rb
Defined Under Namespace
Modules: Lookup
Constant Summary collapse
- MODEL_CACHE_DIR =
ENV.fetch('GIT_FIT_DEM_MODEL_DIR', 'data/cache/dem_fit')
- BUNDLED_MODEL_DIR =
File.('model', __dir__)
- BUNDLED_MODEL =
File.join(BUNDLED_MODEL_DIR, 'dem_model.msgpack')
- MODEL_FILE =
'dem_model.msgpack'- GRID =
0.0003- DEM_SLICE_INTERVAL =
OpenTopography free tier allows 1 request/second — pacing below that guarantees a 429 on every cold-cache slice (observed in workouts CI).
1.05
Class Method Summary collapse
- .dem_spacing(gain_per_km:, range_per_km:, seg_density:) ⇒ Object
- .elevations_for(locations) ⇒ Object
- .find_model ⇒ Object
- .grid_key(lat, lng) ⇒ Object
- .hysteresis_threshold(m_val) ⇒ Object
- .lookup ⇒ Object
- .model ⇒ Object
- .model_path ⇒ Object
- .reset! ⇒ Object
- .terrain_m(gain_per_km:, range_per_km:, seg_density:) ⇒ Object
- .uniquify_locations(locations) ⇒ Object
Class Method Details
.dem_spacing(gain_per_km:, range_per_km:, seg_density:) ⇒ Object
59 60 61 62 63 64 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 59 def dem_spacing(gain_per_km:, range_per_km:, seg_density:) Lookup.nearest_neighbor_spacing(lookup, gain_per_km, range_per_km, seg_density) rescue StandardError => e warn "TerrainCoeff: dem_spacing fallback to 181m (#{e.})" 181 end |
.elevations_for(locations) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 66 def elevations_for(locations) uniq_vals = uniquify_locations(locations) elevs = {} last_api = nil uniq_vals.each_slice(100).with_index do |slice, bi| cached = DemCache.load_batch(slice) elevs.merge!(cached) missing = DemCache.missing_keys(slice, cached) next if missing.empty? # Pace API→API independently of interleaved cache-hit slices: # cache processing is free, only consecutive fetches must respect # the provider's 1 req/s limit. if last_api elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - last_api sleep(DEM_SLICE_INTERVAL - elapsed) if elapsed < DEM_SLICE_INTERVAL end missing_locations = missing.map { |lat, lng| [lat, lng] } batch = {} DemFetch.fetch_batch(missing_locations, bi, batch, method(:grid_key)) elevs.merge!(batch) DemCache.save_batch(batch) last_api = Process.clock_gettime(Process::CLOCK_MONOTONIC) end locations.map { |lat, lng| elevs[grid_key(lat, lng)] } end |
.find_model ⇒ Object
31 32 33 34 35 36 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 31 def find_model cache_path = File.join(MODEL_CACHE_DIR, MODEL_FILE) return cache_path if File.exist?(cache_path) return BUNDLED_MODEL if File.exist?(BUNDLED_MODEL) raise "No #{MODEL_FILE} found in cache (#{MODEL_CACHE_DIR}) or bundled (#{BUNDLED_MODEL})" end |
.grid_key(lat, lng) ⇒ Object
99 100 101 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 99 def grid_key(lat, lng) "#{(lat / GRID).round}/#{(lng / GRID).round}" end |
.hysteresis_threshold(m_val) ⇒ Object
55 56 57 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 55 def hysteresis_threshold(m_val) Lookup.hysteresis_threshold(model['pav_h'], m_val) end |
.lookup ⇒ Object
103 104 105 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 103 def lookup model['lookup'] end |
.model ⇒ Object
27 28 29 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 27 def model @model ||= MessagePack.unpack(File.read(model_path)) end |
.model_path ⇒ Object
23 24 25 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 23 def model_path @model_path ||= find_model end |
.reset! ⇒ Object
38 39 40 41 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 38 def reset! @model_path = nil @model = nil end |
.terrain_m(gain_per_km:, range_per_km:, seg_density:) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 43 def terrain_m(gain_per_km:, range_per_km:, seg_density:) g = gain_per_km.to_f r = range_per_km.to_f s = seg_density.to_f norms = model['norms'] g_n = g / norms[0] r_n = r / norms[1] s_n = s / norms[2] w = model['ridge_h'] g_n * w[0] + r_n * w[1] + s_n * w[2] end |
.uniquify_locations(locations) ⇒ Object
93 94 95 96 97 |
# File 'lib/git_fit/elevation/terrain_coeff.rb', line 93 def uniquify_locations(locations) uniq = {} locations.each { |lat, lng| uniq[grid_key(lat, lng)] ||= [lat, lng] } uniq.values end |