~ihabunek/triglav

8ca2eb11424daca5e3379c80d2f11ec1c4ad71c6 — Ivan Habunek 9 months ago d268be2
Match gtfs route variants with osm relations
M TODO.md => TODO.md +1 -0
@@ 13,3 13,4 @@ Reading:
Errors:

* Ways with a role other than "platform"
* Platforms in bus relations should be nodes, not ways

M assets/css/app.scss => assets/css/app.scss +10 -1
@@ 3,6 3,7 @@
// Palette

$red: FireBrick;
$orange: DarkOrange;
$green: ForestGreen;

html {


@@ 102,8 103,16 @@ summary {
    margin-bottom: 0;
  }

  &.error {
    background-color: rgba($red, 0.3);
  }

  &.warning {
    background-color: rgba($red, 0.2);
    background-color: rgba($orange, 0.3);
  }

  &.success {
    background-color: rgba($green, 0.2);
  }
}


M lib/mix/tasks/triglav/validate_routes.ex => lib/mix/tasks/triglav/validate_routes.ex +3 -0
@@ 11,7 11,10 @@ defmodule Mix.Tasks.Triglav.ValidateRoutes do
  def run(_args) do
    Application.put_env(:triglav, :repo_only, true)
    {:ok, _} = Application.ensure_all_started(:triglav)
    validate()
  end

  def validate() do
    multi = Multi.new() |> Multi.delete_all(:delete, Error)

    Validator.validate_all_routes()

M lib/triglav/zet/osm.ex => lib/triglav/zet/osm.ex +16 -0
@@ 1,5 1,6 @@
defmodule Triglav.Zet.Osm do
  alias Triglav.Repo
  alias Triglav.Schemas.Osm.Point
  alias Triglav.Schemas.Osm.Relation
  alias Triglav.Schemas.Osm.Way
  import Ecto.Query


@@ 31,6 32,21 @@ defmodule Triglav.Zet.Osm do
    |> Repo.all()
  end

  @spec list_platform_nodes([Relation.t()]) :: [Point.t()]
  def list_platform_nodes(relations) do
    ids =
      relations
      |> Enum.map(fn r ->
        r.members
        |> Enum.filter(&(&1.type == :node and String.starts_with?(&1.role, "platform")))
        |> Enum.map(& &1.id)
      end)
      |> List.flatten()

    from(p in Point, where: p.id in ^ids)
    |> Repo.all()
  end

  # Private

  defp pt_relations() do

M lib/triglav_web/controllers/zet/routes_controller.ex => lib/triglav_web/controllers/zet/routes_controller.ex +26 -1
@@ 38,8 38,13 @@ defmodule TriglavWeb.Zet.RoutesController do
    route = Gtfs.get_route(id)
    relations = Osm.list_public_transport_relations(ref: id)
    hierarchy = make_hierarchy(relations)
    platforms = Osm.list_platform_nodes(relations) |> Map.new(&{&1.id, &1})
    {errors, rel_errors} = Gtfs.get_errors(id) |> group_errors_by_relation()
    trips = Gtfs.fetch_distinct_trips(route) |> Enum.group_by(& &1["direction_id"])

    trips =
      Gtfs.fetch_distinct_trips(route)
      |> match_trips_with_relations(relations, platforms)
      |> Enum.group_by(fn {trip, _} -> trip["direction_id"] end)

    # JSON encode stops for drawing on the map
    stops_json =


@@ 60,6 65,26 @@ defmodule TriglavWeb.Zet.RoutesController do
    )
  end

  defp match_trips_with_relations(trips, relations, platforms) do
    route_relations = Enum.filter(relations, &Relation.is_route/1)

    relations_stop_ids =
      for relation <- route_relations do
        relation.members
        |> Enum.filter(&(String.starts_with?(&1.role, "platform") and &1.type == :node))
        |> Enum.map(&Map.get(platforms, &1.id))
        |> Enum.map(&Map.get(&1.tags, "gtfs:stop_id"))
      end

    for trip <- trips do
      stop_ids = Enum.map(trip["stops"], &elem(&1, 0))
      index = Enum.find_index(relations_stop_ids, &(&1 == stop_ids))
      matched_relation = if index, do: Enum.at(route_relations, index)

      {trip, matched_relation}
    end
  end

  def group_errors_by_relation(errors) do
    {errors, rel_errors} = Enum.split_with(errors, &is_nil(&1.relation_id))


M lib/triglav_web/templates/zet/routes/detail.html.eex => lib/triglav_web/templates/zet/routes/detail.html.eex +12 -1
@@ 65,7 65,7 @@
    <h3>Direction #<%= direction_id %></h3>

    <div style="display: flex; flex-direction: row;">
      <%= for trip <- trips do %>
      <%= for {trip, matched_relation} <- trips do %>
        <div style="margin-left: 1rem;">
          <p><b><%= trip["trip_count"] %> departures</b></p>
          <table>


@@ 88,6 88,17 @@
              <% end %>
            </tbody>
          </table>

          <%= if matched_relation do %>
            <div class="callout">
              <span class="green">✔ Matched relation</span><br />
              <%= osm_link(matched_relation, [josm: true]) %>
            </div>
          <% else %>
            <div class="callout error">
              ✖ No relation matched.
            </div>
          <% end %>
        </div>
      <% end %>
    </div>