lib/gpx/gpx.ex

defmodule LocationSimulator.Gpx do
  @moduledoc """
  `Gpx` is a module that provides functions for parsing GPX file.
  """

  import SweetXml

  ## Public functions ##
  def read_gpx(file) do
    parse_gpx(file)
  end

  def read_string_gpx(content) do
    parse_gpx_content(content)
  end

  ## Private functions ##

  defp parse_gpx(file) do
    case File.read(file) do
      {:ok, content} ->
        parse_gpx_content(content)
      {:error, reason} ->
        raise "cannot read file #{inspect file}, reason: #{inspect reason}"
    end
  end

  defp parse_gpx_content(content) do
    content
    |> parse()
    |> xpath(~x"//trkseg/trkpt"l)
    |> Enum.map(&parse_gpx_point/1)
  end

  defp parse_gpx_point(xml_el) do
    # ele should be 0 in case xml file has no 'ele' tag
    ele =
      try do
        xml_el |> xpath(~x"./ele/text()"f)
      catch
        :error, {:badmatch, _} -> 0
      end

    %LocationSimulator.Gpx.GpsPoint{
      lat: xml_el |> xpath(~x"./@lat"f) ,
      lon: xml_el |> xpath(~x"./@lon"f),
      ele: ele,
      time: xml_el |> xpath(~x"./time/text()"s) |> parse_datetime()
    }
  end

  defp parse_datetime(str) do
    case NaiveDateTime.from_iso8601(str) do
      {:ok, datetime} -> datetime
      {:error, _seasion} -> nil
    end
  end
end