diff --git a/app/controllers/midways_controller.rb b/app/controllers/midways_controller.rb index c776854..57fa3bb 100644 --- a/app/controllers/midways_controller.rb +++ b/app/controllers/midways_controller.rb @@ -88,16 +88,24 @@ def create #accesses the venue type and any keywords the user wants @midway.venue_type = params[:midway][:venue_type].downcase @midway.keyword = params[:midway][:keyword].downcase + @midway.choice = params[:midway][:choice].downcase #assigns the midpoint midpoint_service = MidpointService.new(addresses: participants_locations, time_option: time_option, future_datetime: future_datetime) - midpoint_coordinates = midpoint_service.calculate[0] + if @midway.choice == "efficient" + results = midpoint_service.calculate_efficient + midpoint_coordinates = results[0] + durations = results[1] + else + results = midpoint_service.calculate_equal + midpoint_coordinates = results[0] + durations = results[1] + end @midpoint = "#{midpoint_coordinates[:lat]},#{midpoint_coordinates[:lng]}" @midway.midpoint = @midpoint @midway.save! #assigns a duration to each Midway Participant - durations = midpoint_service.calculate[1] participants.each_with_index do |participant, index| participant.duration_to_midpoint = durations[index] participant.save! @@ -229,7 +237,7 @@ def show private def midway_params - params.require(:midway).permit(:friends, :time_option, :future_time, :future_date, :future_datetime, :venue_type, :venue, :keyword) + params.require(:midway).permit(:friends, :time_option, :future_time, :future_date, :future_datetime, :venue_type, :venue, :keyword, :choice) end def venue_params diff --git a/app/services/midpoint_service.rb b/app/services/midpoint_service.rb index 6054d62..3c09f85 100644 --- a/app/services/midpoint_service.rb +++ b/app/services/midpoint_service.rb @@ -11,7 +11,7 @@ def initialize(attributes = {}) @midpoint = { lat: 0, lng: 0 } end - def calculate + def calculate_efficient # find the average lat and long of all user locations (set this to variable D) - this is the first attempt at the midpoint (based purely off geographical equidistance) @@ -33,7 +33,8 @@ def calculate p address_query candidates = [] - 5.times do + user_candidates = [] + 3.times do url = "https://api.distancematrix.ai/maps/api/distancematrix/json?units=imperial&origins=#{address_query}&destinations=#{@midpoint[:lat]},#{@midpoint[:lng]}&transit_mode=subway&mode=transit{url_time}&key=#{ENV["DISTANCE_MATRIX_KEY"]}" @@ -48,6 +49,7 @@ def calculate durations = @addresses.map { |address| address[:duration] } max_duration = durations.max md_index = durations.index(max_duration) + min_duration = durations.min durations_text = @addresses.map { |address| address[:duration_text] } @@ -57,15 +59,38 @@ def calculate percentage = avg / max_duration @midpoint[:lat] = lerp(@addresses[md_index][:lat], @midpoint[:lat], percentage) @midpoint[:lng] = lerp(@addresses[md_index][:lng], @midpoint[:lng], percentage) - min_duration = durations.min # stores as candidate_duration the discrepancy between the largest/smallest travel time candidate_duration = (max_duration - min_duration).abs - candidates.push([@midpoint, candidate_duration, durations_text]) + candidates.push([@midpoint, candidate_duration, durations_text, avg]) + end + + # also puts as candidate midpoints the home locations of each of the users + + @addresses.each do |address| + url = "https://api.distancematrix.ai/maps/api/distancematrix/json?units=imperial&origins=#{address_query}&destinations=#{address[:lat]},#{address[:lng]}&transit_mode=subway&mode=transit{url_time}&key=#{ENV["DISTANCE_MATRIX_KEY"]}" + + @matrix = JSON.parse(open(url).read, symbolize_names: true) + + @addresses.each_with_index do |address, index| + address[:duration] = @matrix[:rows][index+1][:elements][0][:duration][:value] + address[:duration_text] = @matrix[:rows][index+1][:elements][0][:duration][:text] + end + + # determines the longest route between a user and @midpoint + durations = @addresses.map { |address| address[:duration] } + max_duration = durations.max + md_index = durations.index(max_duration) + min_duration = durations.min + durations_text = @addresses.map { |address| address[:duration_text] } + avg = (durations.sum / durations.count).to_f + + candidate_duration = (max_duration - min_duration).abs + user_candidates.push([address, candidate_duration, durations_text, avg]) end - # of the 5 attempts, selects the midpoint with the smallest discrepancy (candidate_duration) + # of all the 5 attempts, selects the midpoint with the smallest discrepancy (candidate_duration) # returns the chosen midpoint candidate_durations = candidates.map { |candidate| candidate[1] } @@ -73,12 +98,107 @@ def calculate mcd_index = candidate_durations.index(minimum_candidate_duration) candidate_midpoints = candidates.map { |candidate| candidate[0] } @midpoint = candidate_midpoints[mcd_index] + p "Candidates:" p candidates - p candidate_durations + p "_________" + # p candidate_durations + p "User Candidates" + p user_candidates + p "_________" p @midpoint + # then it checks that this midpoint does not have an average duration greater than any of the user locations + # if it does it reassigns + + candidate_averages = candidates.map { |candidate| candidate[3] } + midpoint_average = candidate_averages[mcd_index] + p midpoint_average + user_candidate_midpoints = user_candidates.map { |candidate| candidate[0] } + user_candidate_averages = user_candidates.map { |candidate| candidate[3] } + min_user_candidate_average = user_candidate_averages.min + p min_user_candidate_average + mucd_index = user_candidate_averages.index(min_user_candidate_average) + if midpoint_average > user_candidate_averages.min + @midpoint = user_candidate_midpoints[mucd_index] + user_candidate_durations_text = user_candidates.map { |candidate| candidate[2] } + @durations_text = user_candidate_durations_text[mucd_index] + else + candidate_durations_text = candidates.map { |candidate| candidate[2] } + @durations_text = candidate_durations_text[mcd_index] + end + p @midpoint + + # p "Durations: #{@durations_text}" + return [@midpoint, @durations_text] + end + + def calculate_equal + + # find the average lat and long of all user locations (set this to variable D) - this is the first attempt at the midpoint (based purely off geographical equidistance) + + lat_sum = @addresses.map { |address| address[:lat] }.sum + lng_sum = @addresses.map { |address| address[:lng] }.sum + @midpoint[:lat] = lat_sum / @addresses.count + @midpoint[:lng] = lng_sum / @addresses.count + + # make the API calls to find the distances and duration between each user location and attempted midpoint + + @condition = false + url_time = @time_option == 1 ? "arrival_time=#{@future_datetime}" : "departure_time=#{@future_datetime}" + + address_query="" + @addresses.each do |address| + iaq = "|#{address[:lat]},#{address[:lng]}" + address_query = address_query + iaq + end + p address_query + + candidates = [] + user_candidates = [] + 3.times do + + url = "https://api.distancematrix.ai/maps/api/distancematrix/json?units=imperial&origins=#{address_query}&destinations=#{@midpoint[:lat]},#{@midpoint[:lng]}&transit_mode=subway&mode=transit{url_time}&key=#{ENV["DISTANCE_MATRIX_KEY"]}" + + @matrix = JSON.parse(open(url).read, symbolize_names: true) + + @addresses.each_with_index do |address, index| + address[:duration] = @matrix[:rows][index+1][:elements][0][:duration][:value] + address[:duration_text] = @matrix[:rows][index+1][:elements][0][:duration][:text] + end + + # determines the longest route between a user and @midpoint + durations = @addresses.map { |address| address[:duration] } + max_duration = durations.max + md_index = durations.index(max_duration) + min_duration = durations.min + + durations_text = @addresses.map { |address| address[:duration_text] } + + # reassigns the midpoint to a new location (skewed to be closer to the user who previously would have had to travel the longest) + + avg = (durations.sum / durations.count).to_f + percentage = avg / max_duration + @midpoint[:lat] = lerp(@addresses[md_index][:lat], @midpoint[:lat], percentage) + @midpoint[:lng] = lerp(@addresses[md_index][:lng], @midpoint[:lng], percentage) + + # stores as candidate_duration the discrepancy between the largest/smallest travel time + + candidate_duration = (max_duration - min_duration).abs + candidates.push([@midpoint, candidate_duration, durations_text, avg]) + end + + # of all the 5 attempts, selects the midpoint with the smallest discrepancy (candidate_duration) + # returns the chosen midpoint + + candidate_durations = candidates.map { |candidate| candidate[1] } + minimum_candidate_duration = candidate_durations.min + mcd_index = candidate_durations.index(minimum_candidate_duration) + candidate_midpoints = candidates.map { |candidate| candidate[0] } + @midpoint = candidate_midpoints[mcd_index] + candidate_durations_text = candidates.map { |candidate| candidate[2] } @durations_text = candidate_durations_text[mcd_index] + # p "Durations: #{@durations_text}" return [@midpoint, @durations_text] end diff --git a/app/views/midways/new.html.erb b/app/views/midways/new.html.erb index 1e6dc41..b76b9d9 100644 --- a/app/views/midways/new.html.erb +++ b/app/views/midways/new.html.erb @@ -160,6 +160,11 @@ + +