Skip to content

Instantly share code, notes, and snippets.

@JohnB
Last active July 20, 2022 20:20
Show Gist options
  • Save JohnB/055e126e4b581de802acb451691a1a4f to your computer and use it in GitHub Desktop.
Save JohnB/055e126e4b581de802acb451691a1a4f to your computer and use it in GitHub Desktop.
Example code to improve the MapMyWalk altitude graph
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