Compute a distance matrix (Gleam)
Compute an origins-to-destinations matrix via thinwrap_location's matrix/2 in Gleam — one call shape for Mapbox, Google, HERE, ESRI, TomTom and OSRM.
1. Install
gleam add thinwrap_location gleam_erlang envoy2. Send the call
import envoy
import gleam/dynamic.{type Dynamic}
import gleam/dynamic/decode
import gleam/erlang/atom
import gleam/float
import gleam/int
import gleam/io
import gleam/list
import gleam/string
@external(erlang, "thinwrap_location", "matrix")
fn matrix(config: Dynamic, input: Dynamic) -> Result(Dynamic, Dynamic)
pub fn main() -> Nil {
let assert Ok(token) = envoy.get("MAPBOX_ACCESS_TOKEN")
let config =
obj([
#("provider", atom("mapbox")),
#("access_token", dynamic.string(token)),
])
// Providers bill per origin x destination pair — keep probe matrices small.
let input =
obj([
#("origins", dynamic.list([at(52.52, 13.405)])),
#(
"destinations",
dynamic.list([at(52.5163, 13.3777), at(52.5096, 13.3765)]),
),
#("travel_mode", atom("driving")),
])
case matrix(config, input) {
Ok(res) -> {
let cell = {
use o <- decode.field(key("origin_index"), decode.int)
use d <- decode.field(key("destination_index"), decode.int)
use m <- decode.field(key("distance_meters"), decode.float)
use s <- decode.field(key("duration_seconds"), decode.float)
decode.success([
int.to_string(o),
int.to_string(d),
float.to_string(m),
float.to_string(s),
])
}
let assert Ok(cells) =
decode.run(res, decode.at([key("cells")], decode.list(cell)))
list.each(cells, fn(c) { io.println(string.join(c, " ")) })
}
Error(err) -> io.println(describe(err))
}
}
// The library speaks atom-keyed Erlang maps; these helpers build and read them.
fn key(name: String) -> atom.Atom {
atom.create(name)
}
fn atom(name: String) -> Dynamic {
atom.to_dynamic(atom.create(name))
}
fn obj(fields: List(#(String, Dynamic))) -> Dynamic {
dynamic.properties(list.map(fields, fn(f) { #(atom(f.0), f.1) }))
}
fn at(lat: Float, lng: Float) -> Dynamic {
obj([#("lat", dynamic.float(lat)), #("lng", dynamic.float(lng))])
}
fn describe(err: Dynamic) -> String {
let error = {
use code <- decode.field(key("code"), atom.decoder())
use message <- decode.field(key("message"), decode.string)
decode.success(atom.to_string(code) <> ": " <> message)
}
let assert Ok(line) = decode.run(err, error)
line
}
Same code works for all supported providers — just change the provider ID.