Last active
July 20, 2022 20:20
-
-
Save JohnB/055e126e4b581de802acb451691a1a4f to your computer and use it in GitHub Desktop.
Example code to improve the MapMyWalk altitude graph
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule YAxis do | |
@moduledoc """ | |
The YAxis module attempts to find "reasonable" values for the MapMyWalk | |
altitude graph. For example, my MapMyWalk graph (as of 7/2022) offers | |
these altitude values when my workout goes from 138 to 198 feet: | |
20.4, 79.5, 138.5, 197.6, 256.6, 315.7 | |
Which I find to be much less readable/understandable than: | |
120, 140, 160, 180, 200, 220 | |
""" | |
@deltas [ | |
5, 10, 20, 25, | |
50, 100, 200, 250, | |
500, 1_000, 2_000, 2_500, | |
5_000, 10_000, 20_000, 25_000, | |
50_000, 100_000, 200_000, 250_000 | |
] | |
@doc ~S""" | |
Expands the span of numbers to ones that are divisible by 5 | |
and then expands it more to be easily divisible by the | |
number of labels, with each label being a multiple of 5 | |
## Examples | |
iex> YAxis.labels(138, 198, 6) | |
{:ok, [120, 140, 160, 180, 200, 220]} | |
iex> YAxis.labels(11, 22, 2) | |
{:ok, [10, 30]} | |
iex> YAxis.labels(138, 168, 6) | |
{:ok, [130, 140, 150, 160, 170, 180]} | |
iex> YAxis.labels(133, 206, 7) | |
{:ok, [120, 140, 160, 180, 200, 220, 240]} | |
iex> YAxis.labels(95, 261, 6) | |
{:ok, [50, 100, 150, 200, 250, 300]} | |
iex> YAxis.labels(123, 5206, 7) | |
{:ok, [0, 1000, 2000, 3000, 4000, 5000, 6000]} | |
iex> YAxis.labels(17, 523_456, 7) | |
{:ok, [0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000]} | |
iex> YAxis.labels(5, 3, 7) | |
{:error, "min value must be less than max value"} | |
iex> YAxis.labels(11, 22, 1) | |
{:error, "must specify at least 2 labels"} | |
""" | |
def labels(min, max, _num_labels) when min >= max do | |
{:error, "min value must be less than max value"} | |
end | |
def labels(_min, _max, num_labels) when num_labels < 2 do | |
{:error, "must specify at least 2 labels"} | |
end | |
def labels(min, max, num_labels) do | |
min = lower_multiple_of_five(min) | |
max = higher_multiple_of_five(max) | |
table_rows = num_labels - 1 | |
step = | |
Integer.floor_div(max - min, table_rows) | |
|> next_higher_delta() | |
# Attempt to force onto a step boundary while still containing min & max | |
updated_min = min - rem(abs(min), step) | |
updated_top = updated_min + step * table_rows | |
values = case updated_top >= max do | |
true -> Range.new(updated_min, updated_top, step) | |
_ -> Range.new(min, min + step * table_rows, step) | |
end |> Enum.to_list | |
{:ok, values} | |
end | |
defp next_higher_delta(num) do | |
Enum.find(@deltas, fn n -> n >= num end) || List.last(@deltas) | |
end | |
defp lower_multiple_of_five(num) when rem(num, 5) == 0 do | |
num | |
end | |
defp lower_multiple_of_five(num) do | |
num - rem(num, 5) | |
end | |
defp higher_multiple_of_five(num) when rem(num, 5) == 0 do | |
num | |
end | |
defp higher_multiple_of_five(num) do | |
num + 5 - rem(num, 5) | |
end | |
end | |
# TO RUN THE DOC TESTS: | |
# 0) Install elixir and generate a Phoenix app | |
# 1) Add a y_axis_test.exs file with these contents: | |
# defmodule YAxisTest do | |
# use ExUnit.Case, async: true | |
# doctest YAxis | |
# end | |
# 2) Run `mix test y_axis_test.exs` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment