defmodule MicrowavepropWeb.WeatherTileController do use MicrowavepropWeb, :controller alias Microwaveprop.Weather alias Microwaveprop.Weather.MapLayers alias Microwaveprop.Weather.TileRenderer # Fields shipped to the client. The client recolors locally on layer # switch from this same payload, so include every field any layer # might need. @cell_fields ~w( temperature dewpoint_depression surface_rh surface_pressure_mb surface_refractivity refractivity_gradient bl_height pwat temp_850mb dewpoint_850mb temp_700mb dewpoint_700mb lapse_rate mid_lapse_rate inversion_strength inversion_base_m ducting duct_base_m duct_strength )a @spec show(Plug.Conn.t(), map()) :: Plug.Conn.t() def show(conn, %{"z" => z, "x" => x, "y" => y, "layer" => layer_id, "time" => time_param}) do with true <- MapLayers.valid_id?(layer_id), {z, ""} <- Integer.parse(z), {x, ""} <- Integer.parse(x), {y, ""} <- Integer.parse(y), {:ok, valid_time, _offset} <- DateTime.from_iso8601(time_param) do body = TileRenderer.render_svg(valid_time, layer_id, z, x, y) conn |> put_resp_content_type("image/svg+xml") |> put_resp_header("cache-control", "public, max-age=60") |> send_resp(200, body) else _ -> conn |> put_resp_content_type("image/svg+xml") |> put_resp_header("cache-control", "public, max-age=5") |> send_resp(200, TileRenderer.empty_svg()) end end @spec cells(Plug.Conn.t(), map()) :: Plug.Conn.t() def cells(conn, %{"time" => time_param} = params) do with {:ok, valid_time, _offset} <- DateTime.from_iso8601(time_param), {:ok, bounds} <- parse_bounds(params) do rows = Weather.weather_grid_at(valid_time, bounds) conn |> put_resp_header("cache-control", "public, max-age=60") |> json(%{ valid_time: DateTime.to_iso8601(valid_time), cells: Enum.map(rows, &cell_payload/1) }) else _ -> bad_request(conn) end end def cells(conn, _params), do: bad_request(conn) defp bad_request(conn) do conn |> put_status(400) |> json(%{error: "invalid params"}) end defp parse_bounds(%{"south" => s, "north" => n, "west" => w, "east" => e}) do with {:ok, south} <- parse_float(s), {:ok, north} <- parse_float(n), {:ok, west} <- parse_float(w), {:ok, east} <- parse_float(e) do {:ok, %{"south" => south, "north" => north, "west" => west, "east" => east}} end end defp parse_bounds(_), do: :error defp parse_float(value) when is_binary(value) do case Float.parse(value) do {f, ""} -> {:ok, f} _ -> :error end end defp parse_float(_), do: :error defp cell_payload(row) do Enum.reduce(@cell_fields, %{lat: row.lat, lon: row.lon}, fn field, acc -> Map.put(acc, field, Map.get(row, field)) end) end end