Skip to content

Commit

Permalink
Merge pull request #39 from ibikecph/develop
Browse files Browse the repository at this point in the history
Added Travel planner(route breaking) feature
  • Loading branch information
viktorshamal committed Dec 9, 2015
2 parents 3eac819 + 49bf176 commit f0ec2b0
Show file tree
Hide file tree
Showing 17 changed files with 405 additions and 12 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ logfile
*~
\#*\#
.\#*
config/database.yml
config/secrets.yml
.gitignore
config/application.yml
.idea
7 changes: 6 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ gem 'fog'
gem 'delayed_job_active_record'
gem 'simple-navigation'
gem 'will_paginate'
gem 'thin'
#gem 'puma'
gem 'unicorn'
gem 'unicorn-rails'
gem 'auto_html', git: 'git://github.com/ibikecph/auto_html.git', branch: 'master'
gem 'rails_autolink'
gem 'acts-as-taggable-on', '~> 3.4'
Expand All @@ -34,6 +36,9 @@ gem 'uglifier'
gem 'eco'
gem 'recipient_interceptor'
gem 'koala'
gem 'httparty'

gem 'polylines'

group :development do
gem 'quiet_assets'
Expand Down
24 changes: 17 additions & 7 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.9.1.1)
daemons (1.2.3)
database_cleaner (1.5.0)
delayed_job (4.0.6)
activesupport (>= 3.0, < 5.0)
Expand All @@ -105,7 +104,6 @@ GEM
execjs
eco-source (1.1.0.rc.1)
erubis (2.7.0)
eventmachine (1.0.8)
exception_notification (4.1.1)
actionmailer (>= 3.0.4)
activesupport (>= 3.0.4)
Expand Down Expand Up @@ -233,6 +231,9 @@ GEM
excon (~> 0.44)
multi_json (~> 1.8)
highline (1.7.6)
httparty (0.13.7)
json (~> 1.8)
multi_xml (>= 0.5.2)
i18n (0.7.0)
i18n-js (3.0.0.rc11)
i18n (~> 0.6)
Expand All @@ -247,6 +248,7 @@ GEM
thor (>= 0.14, < 2.0)
json (1.8.3)
jwt (1.5.1)
kgio (2.10.0)
koala (2.2.0)
addressable
faraday
Expand Down Expand Up @@ -284,6 +286,7 @@ GEM
omniauth (~> 1.2)
orm_adapter (0.5.0)
pg (0.18.3)
polylines (0.3.0)
quiet_assets (1.1.0)
railties (>= 3.1, < 5.0)
rack (1.6.4)
Expand Down Expand Up @@ -323,6 +326,7 @@ GEM
activesupport (= 4.2.4)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
raindrops (0.15.0)
rake (10.4.2)
recipient_interceptor (0.1.2)
mail
Expand Down Expand Up @@ -376,10 +380,6 @@ GEM
sprockets (>= 2.8, < 4.0)
temple (0.7.6)
terminal-table (1.4.5)
thin (1.6.4)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (~> 1.0)
thor (0.19.1)
thread_safe (0.3.5)
tilt (2.0.1)
Expand All @@ -388,6 +388,13 @@ GEM
uglifier (2.7.2)
execjs (>= 0.3.0)
json (>= 1.8.0)
unicorn (5.0.1)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
unicorn-rails (2.2.0)
rack
unicorn
warden (1.2.3)
rack (>= 1.0)
will_paginate (3.0.7)
Expand Down Expand Up @@ -421,6 +428,7 @@ DEPENDENCIES
figaro
fog
google-analytics-rails
httparty
i18n-js (~> 3.0.0.rc8)
jbuilder
jquery-rails
Expand All @@ -430,6 +438,7 @@ DEPENDENCIES
omniauth
omniauth-facebook
pg
polylines
quiet_assets
rails (= 4.2.4)
rails-timeago
Expand All @@ -439,8 +448,9 @@ DEPENDENCIES
rspec-rails (~> 3.0)
sass-rails
simple-navigation
thin
uglifier
unicorn
unicorn-rails
will_paginate
workless (~> 1.2.2)

Expand Down
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
web: bundle exec thin start -p $PORT -e $RAILS_ENV
web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
19 changes: 19 additions & 0 deletions app/controllers/api/v1/journey_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Api::V1::JourneyController < Api::V1::BaseController
skip_before_filter :check_auth_token!

rescue_from TravelPlanner::Error, with: :travel_planner_message
rescue_from StandardError, with: :standard_message

def show
render json: TravelPlanner.get_journey(params[:loc])
end

def travel_planner_message(e)
render json: {error: e.message}, status: 422
end

def standard_message(e)
ExceptionNotifier.notify_exception(e, env: request.env)
render json: {error: 'An unexpected error occurred.'}, status: 500
end
end
22 changes: 22 additions & 0 deletions app/lib/travel_planner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module TravelPlanner
require 'travel_planner/errors'

include HTTParty
default_timeout 15
disable_rails_query_string_format # for ibike routing server
base_uri ENV['REJSEPLANEN_API_URL']

def self.get_journey(loc)
Journey.new(options(loc)).trips
end

def self.options(loc)
{ loc: loc.map {|coord| coord.split(',')}.flatten,
originCoordName: '\0',
destCoordName: '\0',
useBicycle: 1,
maxCyclingDistanceDep: 5000,
maxCyclingDistanceDest: 5000,
format: 'json' }
end
end
44 changes: 44 additions & 0 deletions app/lib/travel_planner/coord_set.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
class TravelPlanner::CoordSet
# This class gathers all the logic needed to format coordinates, so we can use them in different services.
# It takes coordinate data in the form of [origin_lat,origin_lng,dest_lat,dest_lng].

def initialize(coord_data)
raise TravelPlanner::InvalidCoordsError unless coord_data.is_a?(Array) and coord_data.length == 4
@coords = coord_data
end

def coords
@coords
end

def origin
[coords[0],coords[1]]
end

def destination
[coords[2],coords[3]]
end

def as_via_points
[[coords[0],coords[1]], [coords[2],coords[3]]]
end

def for_travel
travel_coords = coords.flatten.map { |x| (x.to_f * (10**6)).to_i }
{
originCoordX: travel_coords[1],
originCoordY: travel_coords[0],
destCoordX: travel_coords[3],
destCoordY: travel_coords[2]
}
end

def for_ibike
[ coords[0].to_s+','+coords[1].to_s, coords[2].to_s+','+coords[3].to_s ]
end

def for_polyline
poly_coords = coords.map {|coord| coord.to_f}
[[poly_coords[0],poly_coords[1]], [poly_coords[2],poly_coords[3]]]
end
end
20 changes: 20 additions & 0 deletions app/lib/travel_planner/errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module TravelPlanner
#todo improve and translate error messages
class Error < StandardError
def message
'An unexpected error occurred.'
end
end

class ConnectionError < Error
def message
'The routing server could not be reached.'
end
end

class InvalidCoordsError < Error
def message
'Supplied coordinates are invalid.'
end
end
end
90 changes: 90 additions & 0 deletions app/lib/travel_planner/journey.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
class TravelPlanner::Journey
def initialize(options)
@coords = TravelPlanner::CoordSet.new options[:loc]
@journey_data = fetch_journey_data options.merge(@coords.for_travel)
end

def trips
format_journeys @journey_data
end

private
def fetch_journey_data(query)
response = TravelPlanner.get('/trips/', query: query)
response ? response['TripList']['Trip'] : raise(TravelPlanner::ConnectionError)
end

def format_journeys(journey_data)
journey_data.first(3).map {|journey| format_legs(journey) }
end

def format_legs(journey_data)
total_time = total_distance = total_bike_distance = 0

journey = journey_data['Leg'].map do |leg_data|
leg = TravelPlanner::Leg.new leg_data, @coords

formatted_leg = leg.type=='BIKE' ? format_bike(leg) : format_public(leg)

total_time += formatted_leg['route_summary']['total_time']
total_distance += formatted_leg['route_summary']['total_distance']
total_bike_distance += formatted_leg['route_summary']['total_distance'] if leg.type == 'BIKE'

formatted_leg
end

{
journey_summary:{
total_time: total_time,
total_distance: total_distance,
total_bike_distance: total_bike_distance
},
journey:journey
}
end

# We're formatting the response so it mirrors our OSRM-routers bike response.
def format_public(leg)
{ route_name: [
leg.origin['name'],
leg.destination['name']
],

route_summary: {
start_point: leg.origin['name'],
end_point: leg.destination['name'],
total_time: leg.total_time,
total_distance: leg.distance,
type: leg.type,
name: leg.name,
departure_time: leg.departure_time,
arrival_time: leg.arrival_time
},

route_instructions: leg.route_instructions,
route_geometry: leg.route_geometry,

via_points: leg.coords.as_via_points
}.with_indifferent_access
end

def format_bike(leg)
options = {
loc: leg.coords.for_ibike,
z: 18,
alt: false,
instructions:true
}

response = TravelPlanner.get('http://routes.ibikecph.dk/v1.1/fast/viaroute', query: options)

raise TravelPlanner::ConnectionError unless response['status'] == 0

response['route_summary'].merge!({
'type': leg.type,
'departure_time': leg.departure_time,
'arrival_time': leg.arrival_time
})
response
end
end
Loading

0 comments on commit f0ec2b0

Please sign in to comment.