| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
 | class Chouette::Route < Chouette::TridentActiveRecord
  include RouteRestrictions
  include ChecksumSupport
  extend Enumerize
  extend ActiveModel::Naming
  enumerize :direction, in: %i(straight_forward backward clockwise counter_clockwise north north_west west south_west south south_east east north_east)
  enumerize :wayback, in: %i(straight_forward backward), default: :straight_forward
  # FIXME http://jira.codehaus.org/browse/JRUBY-6358
  self.primary_key = "id"
  def self.nullable_attributes
    [:published_name, :comment, :number, :name, :direction, :wayback]
  end
  belongs_to :line
  belongs_to :opposite_route, :class_name => 'Chouette::Route', :foreign_key => :opposite_route_id
  has_many :routing_constraint_zones
  has_many :journey_patterns, :dependent => :destroy
  has_many :vehicle_journeys, :dependent => :destroy do
    def timeless
      Chouette::Route.vehicle_journeys_timeless(proxy_association.owner.journey_patterns.pluck( :departure_stop_point_id))
    end
  end
  has_many :vehicle_journey_frequencies, :dependent => :destroy do
    # Todo : I think there is a better way to do this.
    def timeless
      Chouette::Route.vehicle_journeys_timeless(proxy_association.owner.journey_patterns.pluck( :departure_stop_point_id))
    end
  end
  has_many :stop_points, -> { order("position") }, :dependent => :destroy do
    def find_by_stop_area(stop_area)
      stop_area_ids = Integer === stop_area ? [stop_area] : (stop_area.children_in_depth + [stop_area]).map(&:id)
      where( :stop_area_id => stop_area_ids).first or
        raise ActiveRecord::RecordNotFound.new("Can't find a StopArea #{stop_area.inspect} in Route #{proxy_owner.id.inspect}'s StopPoints")
    end
    def between(departure, arrival)
      between_positions = [departure, arrival].collect do |endpoint|
        case endpoint
        when Chouette::StopArea
          find_by_stop_area(endpoint).position
        when  Chouette::StopPoint
          endpoint.position
        when Integer
          endpoint
        else
          raise ActiveRecord::RecordNotFound.new("Can't determine position in route #{proxy_owner.id} with #{departure.inspect}")
        end
      end
      where(" position between ? and ? ", between_positions.first, between_positions.last)
    end
  end
  has_many :stop_areas, -> { order('stop_points.position ASC') }, :through => :stop_points do
    def between(departure, arrival)
      departure, arrival = [departure, arrival].map do |endpoint|
        String === endpoint ? Chouette::StopArea.find_by_objectid(endpoint) : endpoint
      end
      proxy_owner.stop_points.between(departure, arrival).includes(:stop_area).collect(&:stop_area)
    end
  end
  accepts_nested_attributes_for :stop_points, :allow_destroy => :true
  validates_presence_of :name
  validates_presence_of :published_name
  validates_presence_of :line
  # validates_presence_of :direction
  # validates_presence_of :wayback
  validates :wayback, inclusion: { in: self.wayback.values }
  after_commit :journey_patterns_control_route_sections
  def duplicate
    keys_for_create = attributes.keys - %w{id objectid created_at updated_at}
    atts_for_create = attributes
      .slice(*keys_for_create)
      .merge('object_version' => object_version - 1)
    new_route = self.class.create!(atts_for_create)
    duplicate_stop_points(for_route: new_route)
    new_route
  end
  def duplicate_stop_points(for_route:)
    stop_points.each(&duplicate_stop_point(for_route: for_route))
  end
  def duplicate_stop_point(for_route:)
    -> stop_point do
      stop_point.duplicate(for_route: for_route)
    end
  end
  def geometry_presenter
    Chouette::Geometry::RoutePresenter.new self
  end
  @@opposite_waybacks = { straight_forward: :backward, backward: :straight_forward}
  def opposite_wayback
    @@opposite_waybacks[wayback.to_sym]
  end
  def opposite_route_candidates
    if opposite_wayback
      line.routes.where(opposite_route: [nil, self], wayback: opposite_wayback)
    else
      self.class.none
    end
  end
  validate :check_opposite_route
  def check_opposite_route
    return unless opposite_route && opposite_wayback
    unless opposite_route_candidates.include?(opposite_route)
      errors.add(:opposite_route_id, :invalid)
    end
  end
  def checksum_attributes
    values = self.slice(*['name', 'published_name', 'wayback']).values
    values.tap do |attrs|
      attrs << self.stop_points.map{|sp| "#{sp.stop_area.user_objectid}#{sp.for_boarding}#{sp.for_alighting}" }.join
      attrs << self.routing_constraint_zones.map(&:checksum)
    end
  end
  def geometry
    points = stop_areas.map(&:to_lat_lng).compact.map do |loc|
      [loc.lng, loc.lat]
    end
    GeoRuby::SimpleFeatures::LineString.from_coordinates( points, 4326)
  end
  def time_tables
    vehicle_journeys.joins(:time_tables).map(&:"time_tables").flatten.uniq
  end
  def sorted_vehicle_journeys(journey_category_model)
    send(journey_category_model)
        .joins(:journey_pattern, :vehicle_journey_at_stops)
        .joins('LEFT JOIN "time_tables_vehicle_journeys" ON "time_tables_vehicle_journeys"."vehicle_journey_id" = "vehicle_journeys"."id" LEFT JOIN "time_tables" ON "time_tables"."id" = "time_tables_vehicle_journeys"."time_table_id"')
        .where("vehicle_journey_at_stops.stop_point_id=journey_patterns.departure_stop_point_id")
        .order("vehicle_journey_at_stops.departure_time")
  end
  def stop_point_permutation?( stop_point_ids)
    stop_points.map(&:id).map(&:to_s).sort == stop_point_ids.map(&:to_s).sort
  end
  def reorder!( stop_point_ids)
    return false unless stop_point_permutation?( stop_point_ids)
    stop_area_id_by_stop_point_id = {}
    stop_points.each do |sp|
      stop_area_id_by_stop_point_id.merge!( sp.id => sp.stop_area_id)
    end
    reordered_stop_area_ids = []
    stop_point_ids.each do |stop_point_id|
      reordered_stop_area_ids << stop_area_id_by_stop_point_id[ stop_point_id.to_i]
    end
    stop_points.each_with_index do |sp, index|
      if sp.stop_area_id.to_s != reordered_stop_area_ids[ index].to_s
        #result = sp.update_attributes( :stop_area_id => reordered_stop_area_ids[ index])
        sp.stop_area_id = reordered_stop_area_ids[ index]
        result = sp.save!
      end
    end
    return true
  end
  def journey_patterns_control_route_sections
    self.journey_patterns.each do |jp|
      jp.control_route_sections
    end
  end
  protected
  def self.vehicle_journeys_timeless(stop_point_id)
    all( :conditions => ['vehicle_journeys.id NOT IN (?)', Chouette::VehicleJourneyAtStop.where(stop_point_id: stop_point_id).pluck(:vehicle_journey_id)] )
  end
end
 |