├── .babelrc
├── .gitignore
├── LICENSE
├── package.json
├── js
├── tests
│ └── twobustests.js
├── userinput.js
├── app.js
├── displayroutes.js
├── router.js
└── buses.js
├── README.md
├── index.html
└── bundle.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # node.js
2 | #
3 | node_modules/
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Janith Leanage
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "BusRouteJS",
3 | "version": "3.0.0",
4 | "description": "A JS rewrite of the original bus route finder",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/janithl/BusRouteJS.git"
8 | },
9 | "license": "MIT",
10 | "homepage": "https://github.com/janithl/BusRouteJS",
11 | "main": "js/app.js",
12 | "dependencies": {
13 | "react": "^15.0.1",
14 | "react-dom": "^15.0.1",
15 | "babel-preset-es2015": "^6.6.0",
16 | "babel-preset-react": "^6.5.0",
17 | "babelify": "^7.2.0",
18 | "browserify": "^11.0.1",
19 | "browserify-shim": "^3.8.12",
20 | "watchify": "^3.4.0"
21 | },
22 | "scripts": {
23 | "build": "browserify js/app.js -t babelify -t browserify-shim | uglifyjs > bundle.js",
24 | "start": "watchify js/app.js -v -t babelify -t browserify-shim -o bundle.js",
25 | "test": "jest"
26 | },
27 | "jest": {
28 | "testRegex": "js/tests/.*\\.js$"
29 | },
30 | "browserify-shim": {
31 | "react": "global:React",
32 | "react-dom": "global:ReactDOM"
33 | },
34 | "devDependencies": {
35 | "babel-jest": "^14.0.0",
36 | "babel-polyfill": "^6.9.1"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/js/tests/twobustests.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | jest.unmock('../router.js');
4 |
5 | import Router from '../router';
6 | var router = new Router();
7 |
8 | const Nugegoda = "45";
9 | const Pepiliyana = "233";
10 | const Kohuwala = "231";
11 |
12 | describe('test2Route', () => {
13 | var fromRoutes = router.findStopRoutes(Nugegoda);
14 | it('Routes from Nugegoda contains 163', () => {
15 | expect(fromRoutes).toContain("28");
16 | });
17 |
18 | it('Routes from Nugegoda contains 176', () => {
19 | expect(fromRoutes).toContain("29");
20 | });
21 |
22 | var toRoutes = router.findStopRoutes(Pepiliyana);
23 |
24 | it('Routes from Pepiliyana contains 120s', () => {
25 | expect(toRoutes).toContain("12");
26 | expect(toRoutes).toContain("13");
27 | expect(toRoutes).toContain("14");
28 | });
29 |
30 | var fromStops = [], toStops = [];
31 | fromRoutes.forEach(function(fr) {
32 | fromStops = fromStops.concat(router.findReachableStops(fr, Nugegoda));
33 | });
34 |
35 | toRoutes.forEach(function(tr) {
36 | toStops = toStops.concat(router.findReachableStops(tr, Pepiliyana));
37 | });
38 |
39 | it('Kohuwala is reachable from Nugegoda', () => {
40 | expect(fromStops).toContain(Kohuwala);
41 | });
42 |
43 | it('Kohuwala is reachable from Pepiliyana', () => {
44 | expect(toStops).toContain(Kohuwala);
45 | });
46 |
47 | var common = router.intersect(fromStops, toStops);
48 | it('Kohuwala is reachable from both', () => {
49 | expect(common).toContain(Kohuwala);
50 | });
51 |
52 | var routes = router.findRoutes(Nugegoda, Pepiliyana, 500);
53 | it('Kohuwala is a valid changeover', () => {
54 | expect(routes[0].changes).toContain(Kohuwala);
55 | });
56 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | BusRouteJS
2 | ==========
3 |
4 | A JS rewrite of bus route finder.
5 |
6 | Currently in preliminary development, so everything is subject to change
7 | without notice.
8 |
9 | Demo
10 | ----
11 |
12 | [There's a demo here](http://janithl.github.io/BusRouteJS/). You
13 | have to turn on touch event emulation on Chrome to access the modal screens.
14 |
15 | Install
16 | -------
17 |
18 | Clone the repo and run `npm install`.
19 |
20 | API
21 | ---
22 |
23 | Creating a new Router object and calling `.findRoutes(from, to)` where `from` is
24 | the source place ID and `to` is the destination place ID (both integers) will
25 | return an array of Javascript objects like this:
26 |
27 | ```javascript
28 | var router = new Router();
29 | router.findRoutes(252, 235);
30 |
31 | [
32 | {
33 | "from":"252",
34 | "routes":[
35 | {"routes":["1","2","4","5","6","19","20"],"distance":4144},
36 | {"routes":["26"],"distance":3347}
37 | ],
38 | "changes":["53"],
39 | "to":"235",
40 | "distance":7491
41 | },
42 | {
43 | "from":"252",
44 | "routes":[
45 | {"routes":["1","2","4","5","6","19","20"],"distance":12324},
46 | {"routes":["12","13","14"],"distance":6661}
47 | ],
48 | "changes":["39"],
49 | "to":"235",
50 | "distance":18985
51 | },
52 | {
53 | "from":"252",
54 | "routes":[
55 | {"routes":["1","2","4","5","6","19","20"],"distance":12936},
56 | {"routes":["12","13","14"],"distance":7273}
57 | ],
58 | "changes":["38"],
59 | "to":"235",
60 | "distance":20209
61 | },
62 | ...
63 | ]
64 | ```
65 |
66 | The biggest improvement over the last (PHP) version is that it now
67 | handles similar (almost duplicate, but alternate - like Kottawa/Pettah and Homagama/Pettah)
68 | routes gracefully, and runs completely on the clientside, with big popping
69 | Google Maps to boot.
70 |
71 | License
72 | -------
73 |
74 | Released under a permissive MIT license.
75 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Bus Route Finder
11 |
12 |
13 |
14 |
15 |
19 |
20 |
21 |
22 |
42 |
43 |
44 |
45 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/js/userinput.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 |
5 | class LocateMeButton extends Component {
6 | constructor() {
7 | super();
8 | this.geolocate = this.geolocate.bind(this);
9 | }
10 |
11 | /** geolocate code from MDN: https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/Using_geolocation */
12 | geolocate() {
13 | var _props = this.props;
14 | if (!navigator.geolocation) {
15 | _props.setError('Sorry! Your browser does not support Geolocation.');
16 | return;
17 | }
18 |
19 | navigator.geolocation.getCurrentPosition(function(position) {
20 | _props.setUserLoc(position.coords.latitude, position.coords.longitude); /** on success */
21 | }, function() {
22 | _props.setError('Sorry! There was an error in getting your location.'); /** on error */
23 | });
24 | }
25 |
26 | render() {
27 | return (
28 |
31 | );
32 | }
33 | }
34 |
35 | class Suggestion extends Component {
36 | constructor() {
37 | super();
38 | this.setSuggestion = this.setSuggestion.bind(this);
39 | }
40 |
41 | setSuggestion() {
42 | this.props.setSuggestion(this.props.suggestion.id, this.props.suggestion.name);
43 | }
44 |
45 | render() {
46 | return {this.props.suggestion.name};
47 | }
48 | }
49 |
50 | class SuggestionsList extends Component {
51 | render() {
52 | if(this.props.suggestions.length == 0) {
53 | return
54 | }
55 | else {
56 | var _props = this.props;
57 | return (
58 |
59 | {this.props.suggestions.map(function(sug, index) {
60 | return ;
61 | })}
62 |
63 | );
64 | }
65 | }
66 | }
67 |
68 | class SourceInput extends Component {
69 | constructor() {
70 | super();
71 | this.handleInput = this.handleInput.bind(this);
72 | }
73 |
74 | handleInput(event) {
75 | this.props.setSource(null, event.target.value);
76 | }
77 |
78 | render() {
79 | return (
80 |
81 |
83 |
84 |
85 |
86 |
87 | );
88 | }
89 | }
90 |
91 | class DestinationInput extends Component {
92 | constructor() {
93 | super();
94 | this.handleInput = this.handleInput.bind(this);
95 | }
96 |
97 | handleInput(event) {
98 | this.props.setDestination(null, event.target.value);
99 | }
100 |
101 | render() {
102 | return (
103 |
104 |
106 |
107 |
110 |
111 |
112 | );
113 | }
114 | }
115 |
116 | class UserInput extends Component {
117 | render() {
118 | return (
119 |
120 |
Enter where you are and where you want to go below:
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | );
136 | }
137 | }
138 |
139 | export default UserInput;
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import ReactDOM from 'react-dom';
5 |
6 | import Router from './router';
7 | import UserInput from './userinput';
8 | import DisplayRoutes from './displayroutes';
9 |
10 | var router = new Router();
11 |
12 | class DisplayAlert extends Component {
13 | render() {
14 | if(this.props.message) {
15 | var cssclass = 'alert alert-dismissible alert-' + this.props.cssclass;
16 | return (
17 |
18 |
19 |
{this.props.title}
20 |
{this.props.message}
21 |
22 | );
23 | }
24 | else {
25 | return ;
26 | }
27 | }
28 | }
29 |
30 | class App extends Component {
31 | constructor() {
32 | super();
33 | this.state = {
34 | userLat: null,
35 | userLon: null,
36 |
37 | source: { id: null, name: '' },
38 | sourceSug: [],
39 |
40 | destination: { id: null, name: '' },
41 | destinationSug: [],
42 |
43 | locations: router.getAllPlaces(),
44 | routes: [],
45 |
46 | error: null,
47 | warning: null
48 | };
49 |
50 | this.setUserLoc = this.setUserLoc.bind(this);
51 | this.setError = this.setError.bind(this);
52 | this.findRoutes = this.findRoutes.bind(this);
53 |
54 | this.setSource = this.setSource.bind(this);
55 | this.setDestination = this.setDestination.bind(this);
56 | this.readHash = this.readHash.bind(this);
57 | this.setHash = this.setHash.bind(this);
58 |
59 | this.filterSuggestions = this.filterSuggestions.bind(this);
60 |
61 | setTimeout(this.readHash, 500);
62 | }
63 |
64 | readHash() {
65 | if(window.location.hash.length > 1) {
66 | var hash = window.location.hash.substr(1).split('-');
67 | if(hash.length == 2) {
68 | var from = router.getPlaceDetails(hash[0]);
69 | var to = router.getPlaceDetails(hash[1]);
70 | if(from && to) {
71 | this.setSource(hash[0], from.name);
72 | this.setDestination(hash[1], to.name);
73 | setTimeout(this.findRoutes, 500);
74 | }
75 | }
76 | }
77 | }
78 |
79 | setHash() {
80 | if(this.state.source.id && this.state.destination.id) {
81 | window.location.hash = '#' + this.state.source.id + '-' + this.state.destination.id;
82 | }
83 | }
84 |
85 | setError(error) {
86 | this.setState({error});
87 | }
88 |
89 | setUserLoc(userLat, userLon) {
90 | this.setState({userLat, userLon});
91 |
92 | var nearest = router.getNearestPlace(userLat, userLon);
93 | if(nearest) {
94 | this.setSource(nearest.id, nearest.name);
95 |
96 | if(nearest.distance > 1000) {
97 | this.setState({
98 | warning: 'You might have to walk ' + (nearest.distance / 1000.0).toFixed(2) + 'km'
99 | });
100 | }
101 | }
102 | }
103 |
104 | setSource(id, name) {
105 | this.setState({
106 | source: { id: id, name: name },
107 | sourceSug: id ? [] : this.filterSuggestions(name)
108 | });
109 | if(id) { setTimeout(this.setHash, 500); }
110 | }
111 |
112 | setDestination(id, name) {
113 | this.setState({
114 | destination: { id: id, name: name },
115 | destinationSug: id ? [] : this.filterSuggestions(name)
116 | });
117 | if(id) { setTimeout(this.setHash, 500); }
118 | }
119 |
120 | filterSuggestions(term) {
121 | if(term.length > 0) {
122 | var regexp = new RegExp(term, 'i');
123 | return this.state.locations.filter(function(elem) { return regexp.test(elem.name); }).slice(0,4);
124 | }
125 | return [];
126 | }
127 |
128 | findRoutes() {
129 | this.setState({ routes: [] });
130 | if(this.state.source.id && this.state.destination.id) {
131 | if(this.state.source.id === this.state.destination.id) {
132 | this.setError("Source and destination same");
133 | }
134 | else {
135 | var routes = router.findRoutes(this.state.source.id, this.state.destination.id);
136 | if(routes && routes.length > 0) {
137 | this.setState({ routes: routes, error: null });
138 | }
139 | else {
140 | this.setError("Sorry! No buses were found.");
141 | }
142 | }
143 | }
144 | else {
145 | this.setError("You haven't entered where you are and/or where you want to go!");
146 | }
147 | }
148 |
149 | render() {
150 | return (
151 |
152 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | );
166 | }
167 | }
168 |
169 | ReactDOM.render(
170 | ,
171 | document.getElementById('app')
172 | );
173 |
174 |
--------------------------------------------------------------------------------
/js/displayroutes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 |
5 | class MapLink extends Component {
6 | constructor() {
7 | super();
8 | this.renderMap = this.renderMap.bind(this);
9 | }
10 |
11 | renderMap() {
12 | $('#map-canvas').html(null);
13 | $('#map-title').html(this.props.link.name);
14 |
15 | setTimeout(function() {
16 | /** Google Map code stolen off http://stackoverflow.com/a/26410438 */
17 | var centre = new google.maps.LatLng(this.props.link.lat, this.props.link.lon);
18 | var marker = new google.maps.Marker({
19 | position: centre
20 | });
21 |
22 | var mapProp = {
23 | center: centre,
24 | zoom: 16,
25 | mapTypeId: google.maps.MapTypeId.ROADMAP
26 | };
27 |
28 | var map = new google.maps.Map(document.getElementById('map-canvas'), mapProp);
29 | marker.setMap(map);
30 | }.bind(this), 200);
31 | }
32 |
33 | render() {
34 | return(
35 | {this.props.link.name}
36 | );
37 | }
38 | }
39 |
40 | class RenderRoute extends Component {
41 | render() {
42 | var from = this.props.router.getPlaceDetails(this.props.from);
43 | var to = this.props.router.getPlaceDetails(this.props.to);
44 |
45 | var details, busmarkup = this.props.route.routes.map(function(r, index) {
46 | details = this.props.router.getRouteDetails(r);
47 | if(details) {
48 | return #{details.routeno} ({details.from} - {details.to})
49 | }
50 | }.bind(this));
51 |
52 | if(from && to) {
53 | return (
54 |
55 |
56 |
57 | to ({(this.props.route.distance / 1000.0).toFixed(2)} km)
58 |
59 |
60 |
{busmarkup}
61 |
62 | );
63 | }
64 | else {
65 | return ;
66 | }
67 | }
68 | }
69 |
70 | class RenderOption extends Component {
71 | render() {
72 | var cssclass = this.props.first ? 'panel panel-warning' : 'panel panel-default';
73 |
74 | var recommended = ;
75 | if(this.props.first) {
76 | recommended =
77 |
78 | Recommended Route
79 |
80 | ;
81 | }
82 |
83 | var components = ;
84 | if(this.props.route.changes.length == 0) {
85 | components = ;
94 | }
95 | else if(this.props.route.changes.length == 1) {
96 | components =
97 |
98 |
103 |
104 |
105 |
110 |
111 |
;
112 | }
113 | else if(this.props.route.changes.length == 2) {
114 | components =
115 |
116 |
121 |
122 |
123 |
128 |
129 |
130 |
135 |
136 |
;
137 | }
138 |
139 | return (
140 |
141 | {recommended}
142 | {components}
143 |
144 | Total Distance: {(this.props.route.distance / 1000.0).toFixed(2)} km
145 |
146 |
147 | );
148 | }
149 | }
150 |
151 | class DisplayRoutes extends Component {
152 | render() {
153 | var routes = ;
154 | if(this.props.routes && this.props.routes.length > 0) {
155 | routes = this.props.routes.map(function(r, index) {
156 | return
157 | }.bind(this));
158 | }
159 | return (
160 | {routes}
161 | );
162 | }
163 | }
164 |
165 | export default DisplayRoutes;
--------------------------------------------------------------------------------
/js/router.js:
--------------------------------------------------------------------------------
1 | import Buses from './buses';
2 |
3 | var Router = function () {
4 | this.buses = Buses;
5 | this.penalty = 5000; /** 5km penalty for changing buses */
6 | };
7 |
8 | /** stolen off http://stackoverflow.com/a/14438954 */
9 | Router.prototype.unique = function(array) {
10 | return array.filter(function(item, pos) {
11 | return array.indexOf(item) == pos;
12 | });
13 | };
14 |
15 | /** stolen off http://stackoverflow.com/a/1885569 */
16 | Router.prototype.intersect = function(array1, array2) {
17 | return this.unique(array1.filter(function(n) {
18 | return array2.indexOf(n) != -1;
19 | }));
20 | };
21 |
22 | /** get distance from start node to end node on a route */
23 | Router.prototype.getDistance = function(route, from, to) {
24 | if(!this.buses.routes.hasOwnProperty(route)) {
25 | return 1/0;
26 | }
27 |
28 | var fromdist = this.buses.routes[route].stopsfrom[to] - this.buses.routes[route].stopsfrom[from];
29 | var todist = this.buses.routes[route].stopsto[to] - this.buses.routes[route].stopsto[from];
30 |
31 | return fromdist > 0 ? fromdist : (todist > 0 ? todist : 1/0);
32 | };
33 |
34 | /**
35 | see if an end node is reachable from a start node on a given route.
36 | distance of the end node should be greater than the start node
37 | */
38 | Router.prototype.reachable = function(route, from, to) {
39 | return this.buses.routes.hasOwnProperty(route) &&
40 | (this.buses.routes[route].stopsfrom[to] > this.buses.routes[route].stopsfrom[from] ||
41 | this.buses.routes[route].stopsto[to] > this.buses.routes[route].stopsto[from]);
42 | };
43 |
44 |
45 | /** find stops that can be reached from a given stop on a given route */
46 | Router.prototype.findReachableStops = function(route, stop) {
47 | var stops = [];
48 | if(this.buses.routes.hasOwnProperty(route) &&
49 | this.buses.routes[route].stopsfrom.hasOwnProperty(stop)) {
50 | var startdist = this.buses.routes[route].stopsfrom[stop];
51 | for(var s in this.buses.routes[route].stopsfrom) {
52 | if(this.buses.routes[route].stopsfrom[s] > startdist) {
53 | stops.push(s);
54 | }
55 | }
56 | }
57 |
58 | if(this.buses.routes.hasOwnProperty(route) &&
59 | this.buses.routes[route].stopsto.hasOwnProperty(stop)) {
60 | var startdist = this.buses.routes[route].stopsto[stop];
61 | for(var s in this.buses.routes[route].stopsto) {
62 | if(this.buses.routes[route].stopsto[s] > startdist) {
63 | stops.push(s);
64 | }
65 | }
66 | }
67 |
68 | return this.unique(stops);
69 | };
70 |
71 | /** find routes that pass through the given stop */
72 | Router.prototype.findStopRoutes = function(stop) {
73 | var routes = [];
74 | for(var r in this.buses.routes) {
75 | if(this.buses.routes[r].stopsfrom.hasOwnProperty(stop) ||
76 | this.buses.routes[r].stopsto.hasOwnProperty(stop)) {
77 | routes.push(r);
78 | }
79 | }
80 |
81 | return this.unique(routes);
82 | };
83 |
84 | /** find single bus routes between two nodes */
85 | Router.prototype.findSingleRoutes = function(from, to) {
86 | var _self = this;
87 |
88 | var fromRoutes = this.findStopRoutes(from); /** bus routes passing through start node */
89 | var toRoutes = this.findStopRoutes(to); /** bus routes passing through end node */
90 |
91 | return this.intersect(fromRoutes, toRoutes).filter(function(r) { return _self.reachable(r, from, to); });
92 | };
93 |
94 | /** find routes between two nodes */
95 | Router.prototype.findRoutes = function(from, to, limit = 5) {
96 | var fromRoutes = this.findStopRoutes(from); /** bus routes passing through start node */
97 | var toRoutes = this.findStopRoutes(to); /** bus routes passing through end node */
98 |
99 | /** try to find a single route from start node to end node, and return */
100 | var singleRoute = this.findSingleRoutes(from, to);
101 |
102 | if(singleRoute.length > 0) {
103 | singleRoute = singleRoute.sort(function(a, b) {
104 | return a.distance - b.distance;
105 | });
106 |
107 | var distance = this.getDistance(singleRoute[0], from, to);
108 | return [{
109 | from : from,
110 | routes : [
111 | { routes: singleRoute.slice(0, 5), distance: distance }
112 | ],
113 | changes : [],
114 | to : to,
115 | distance: distance
116 | }];
117 | }
118 | else {
119 | /**
120 | if not found, try to find reachable nodes from the first route, and
121 | from the second route, and any intersecting stops
122 | */
123 | var fromStops = [], toStops = [], distances, multiRoutes = [], _self = this;
124 | fromRoutes.forEach(function(fr) {
125 | fromStops = fromStops.concat(_self.findReachableStops(fr, from));
126 | });
127 |
128 | toRoutes.forEach(function(tr) {
129 | toStops = toStops.concat(_self.findReachableStops(tr, to));
130 | });
131 |
132 | /**
133 | 2 bus: find intersecting stops, and routes to take you from your starting node
134 | to your intersection, and from the intersection to the end stop
135 | */
136 | var common = this.intersect(fromStops, toStops);
137 | common.forEach(function(c) {
138 | var toc = _self.findSingleRoutes(from, c);
139 | var fromc = _self.findSingleRoutes(c, to);
140 |
141 | if(toc.length > 0 && fromc.length > 0) {
142 | distances = [
143 | _self.getDistance(toc[0], from, c),
144 | _self.getDistance(fromc[0], c, to)
145 | ];
146 |
147 | multiRoutes.push({
148 | from : from,
149 | routes : [
150 | { routes: toc, distance: distances[0] },
151 | { routes: fromc, distance: distances[1] }
152 | ],
153 | changes : [c],
154 | to : to,
155 | distance: distances[0] + distances[1]
156 | });
157 | }
158 | });
159 |
160 | if(common.length < 3) {
161 | /**
162 | 3 bus: find reachable stops from start and end nodes, and find
163 | routes connecting some nodes within those two sets
164 | */
165 | fromStops.forEach(function(fs) {
166 | toStops.forEach(function(ts) {
167 | var tots = _self.findSingleRoutes(fs, ts);
168 | if(tots.length > 0) {
169 | var tofs = _self.findSingleRoutes(from, fs);
170 | var fromts = _self.findSingleRoutes(ts, to);
171 |
172 | if(tofs.length > 0 && fromts.length > 0) {
173 | distances = [
174 | _self.getDistance(tofs[0], from, fs),
175 | _self.getDistance(tots[0], fs, ts),
176 | _self.getDistance(fromts[0], ts, to)
177 | ];
178 |
179 | multiRoutes.push({
180 | from : from,
181 | routes : [
182 | { routes: tofs, distance: distances[0] },
183 | { routes: tots, distance: distances[1] },
184 | { routes: fromts, distance: distances[2] }
185 | ],
186 | changes : [fs, ts],
187 | to : to,
188 | distance: distances[0] + distances[1] + distances[2]
189 | });
190 | }
191 | }
192 | });
193 | });
194 | }
195 |
196 | if(multiRoutes.length > 0) {
197 | multiRoutes = multiRoutes.filter(function(n) {
198 | return !Number.isNaN(n.distance);
199 | }).sort(function(a, b) {
200 | return (a.routes.length - b.routes.length) * _self.penalty + (a.distance - b.distance);
201 | });
202 | return multiRoutes.slice(0, limit);
203 | }
204 | }
205 | };
206 |
207 | Router.prototype.getPlaceDetails = function(pid) {
208 | return this.buses.places[pid];
209 | };
210 |
211 | Router.prototype.getRouteDetails = function(id) {
212 | return this.buses.routes[id];
213 | };
214 |
215 | /** get a list of all places, for autosuggest */
216 | Router.prototype.getAllPlaces = function() {
217 | var places = [];
218 | for(var p in this.buses.places) {
219 | places.push({ id: p, name: this.buses.places[p].name });
220 | }
221 | return places;
222 | };
223 |
224 | /** Haversine formula, stolen off http://stackoverflow.com/a/27943 */
225 | Router.prototype.deg2rad = function(deg) {
226 | return deg * (Math.PI/180)
227 | }
228 |
229 | Router.prototype.haversine = function(lat1, lon1, lat2, lon2) {
230 | var R = 6371; // Radius of the earth in km
231 | var dLat = this.deg2rad(lat2 - lat1); // deg2rad below
232 | var dLon = this.deg2rad(lon2 - lon1);
233 | var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
234 | Math.cos(this.deg2rad(lat1)) * Math.cos(this.deg2rad(lat2)) *
235 | Math.sin(dLon/2) * Math.sin(dLon/2);
236 | var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
237 | return Math.floor(R * c * 1000); // Distance in metres
238 | }
239 |
240 | /** get the stop nearest to a geolocation */
241 | Router.prototype.getNearestPlace = function(lat, lon) {
242 | var nearby = [], place;
243 | for(var p in this.buses.places) {
244 | if(Math.abs(this.buses.places[p].lat - lat) < 0.1 &&
245 | Math.abs(this.buses.places[p].lon - lon) < 0.1) {
246 | place = this.buses.places[p];
247 | place.id = p;
248 | place.distance = this.haversine(place.lat, place.lon, lat, lon);
249 | nearby.push(place);
250 | }
251 | }
252 | return nearby.sort(function(a, b) {
253 | return b.distance - a.distance;
254 | }).pop();
255 | };
256 |
257 | export default Router;
--------------------------------------------------------------------------------
/bundle.js:
--------------------------------------------------------------------------------
1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o1){var hash=window.location.hash.substr(1).split("-");if(hash.length==2){var from=router.getPlaceDetails(hash[0]);var to=router.getPlaceDetails(hash[1]);if(from&&to){this.setSource(hash[0],from.name);this.setDestination(hash[1],to.name);setTimeout(this.findRoutes,500)}}}}},{key:"setHash",value:function setHash(){if(this.state.source.id&&this.state.destination.id){window.location.hash="#"+this.state.source.id+"-"+this.state.destination.id}}},{key:"setError",value:function setError(error){this.setState({error:error})}},{key:"setUserLoc",value:function setUserLoc(userLat,userLon){this.setState({userLat:userLat,userLon:userLon});var nearest=router.getNearestPlace(userLat,userLon);if(nearest){this.setSource(nearest.id,nearest.name);if(nearest.distance>1e3){this.setState({warning:"You might have to walk "+(nearest.distance/1e3).toFixed(2)+"km"})}}}},{key:"setSource",value:function setSource(id,name){this.setState({source:{id:id,name:name},sourceSug:id?[]:this.filterSuggestions(name)});if(id){setTimeout(this.setHash,500)}}},{key:"setDestination",value:function setDestination(id,name){this.setState({destination:{id:id,name:name},destinationSug:id?[]:this.filterSuggestions(name)});if(id){setTimeout(this.setHash,500)}}},{key:"filterSuggestions",value:function filterSuggestions(term){if(term.length>0){var regexp=new RegExp(term,"i");return this.state.locations.filter(function(elem){return regexp.test(elem.name)}).slice(0,4)}return[]}},{key:"findRoutes",value:function findRoutes(){this.setState({routes:[]});if(this.state.source.id&&this.state.destination.id){if(this.state.source.id===this.state.destination.id){this.setError("Source and destination same")}else{var routes=router.findRoutes(this.state.source.id,this.state.destination.id);if(routes&&routes.length>0){this.setState({routes:routes,error:null})}else{this.setError("Sorry! No buses were found.")}}}else{this.setError("You haven't entered where you are and/or where you want to go!")}}},{key:"render",value:function render(){return _react2.default.createElement("div",null,_react2.default.createElement(_userinput2.default,{appState:this.state,setUserLoc:this.setUserLoc,setError:this.setError,setSource:this.setSource,setDestination:this.setDestination,findRoutes:this.findRoutes}),_react2.default.createElement(DisplayAlert,{cssclass:"danger",title:"Error!",message:this.state.error}),_react2.default.createElement(DisplayAlert,{cssclass:"warning",title:"Warning!",message:this.state.warning}),_react2.default.createElement(_displayroutes2.default,{routes:this.state.routes,router:router}))}}]);return App}(_react.Component);_reactDom2.default.render(_react2.default.createElement(App,null),document.getElementById("app"))}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./displayroutes":3,"./router":4,"./userinput":5}],2:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var Buses={places:{1:{name:"Pettah",lat:6.93321,lon:79.8554},2:{name:"Fort Railway Station",lat:6.93408,lon:79.8502},3:{name:"Lotus Road",lat:6.93398,lon:79.847},4:{name:"Galle Face Green",lat:6.92196,lon:79.8461},5:{name:"Kollupitiya Junction",lat:6.91116,lon:79.8497},6:{name:"McDonalds, Kollupitiya",lat:6.90317,lon:79.8523},7:{name:"Bambalapitiya Junction",lat:6.89369,lon:79.8555},8:{name:"Holy Family Convent, Bambalapitiya",lat:6.88781,lon:79.8573},9:{name:"Savoy Cinema, Wellawatte",lat:6.88027,lon:79.8596},10:{name:"Wellawatte Junction",lat:6.87534,lon:79.861},16:{name:"Lake House",lat:6.93261,lon:79.848},19:{name:"Gamini Hall Junction",lat:6.92653,lon:79.8618},20:{name:"Darley Road/Excel World",lat:6.92166,lon:79.8622},21:{name:"Gangarama",lat:6.91811,lon:79.8543},22:{name:"Bishop's College",lat:6.91459,lon:79.8559},23:{name:"Mahanama College",lat:6.90568,lon:79.8536},24:{name:"British Council",lat:6.89983,lon:79.8555},25:{name:"Regal Cinema",lat:6.93128,lon:79.847},26:{name:"Slave Island",lat:6.9233,lon:79.8516},27:{name:"Town Hall",lat:6.9168,lon:79.8634},28:{name:"Public Library",lat:6.91287,lon:79.8579},29:{name:"St. Bridget's Convent",lat:6.90965,lon:79.8637},30:{name:"Race Course Grounds",lat:6.90618,lon:79.8637},31:{name:"Campus (Arts Faculty)",lat:6.90258,lon:79.8622},32:{name:"Campus (near Wycherley)",lat:6.90002,lon:79.8595},33:{name:"Thurstan College",lat:6.90502,lon:79.8583},34:{name:"Glass House",lat:6.91094,lon:79.8583},35:{name:"Thunmulla",lat:6.89619,lon:79.8603},36:{name:"Police Park",lat:6.89288,lon:79.862},37:{name:"Thimbirigasyaya",lat:6.88998,lon:79.8638},38:{name:"BRC Grounds",lat:6.88753,lon:79.8645},39:{name:"Redimola Junction",lat:6.88363,lon:79.8683},40:{name:"Maya Ave.",lat:6.88077,lon:79.8704},41:{name:"Kirulapone Junction",lat:6.87858,lon:79.8746},42:{name:"Kirulapone South",lat:6.87772,lon:79.8786},43:{name:"Balapokuna Road",lat:6.87384,lon:79.8819},44:{name:"Anula Vidyalaya",lat:6.87195,lon:79.8843},45:{name:"Nugegoda",lat:6.86932,lon:79.8896},46:{name:"Seventh Mile Post",lat:6.86686,lon:79.8933},47:{name:"Gansabha Junction",lat:6.86511,lon:79.8966},48:{name:"Delkanda",lat:6.86282,lon:79.9018},49:{name:"Wijerama",lat:6.85764,lon:79.9086},50:{name:"Navinna",lat:6.8551,lon:79.9134},51:{name:"Arpico M'gama",lat:6.85243,lon:79.917},52:{name:"Wattegedara Junction",lat:6.85123,lon:79.9211},53:{name:"Maharagama",lat:6.84608,lon:79.9281},74:{name:"Sugathadasa Stadium",lat:6.94666,lon:79.8691},75:{name:"Armour Street",lat:6.94343,lon:79.8643},76:{name:"Ananda College",lat:6.9245,lon:79.868},77:{name:"Maradana",lat:6.92824,lon:79.8646},78:{name:"Castle Street Hospital",lat:6.91097,lon:79.8852},79:{name:"Devi Balika Vidyalaya",lat:6.91102,lon:79.8821},80:{name:"Alwis Place, Kollupitiya",lat:6.91264,lon:79.8539},81:{name:"Arts Fac. Horton Place",lat:6.91155,lon:79.8687},82:{name:"Green Path",lat:6.91173,lon:79.862},83:{name:"Stratford Avenue",lat:6.87761,lon:79.873},84:{name:"Kirulapone Ave.",lat:6.88269,lon:79.8756},85:{name:"Suwisuddharamaya",lat:6.88013,lon:79.8696},86:{name:"Sapphire Halt",lat:6.8767,lon:79.8664},87:{name:"Pamankada",lat:6.87698,lon:79.8697},88:{name:"Vijaya Kumaratunge Mw.",lat:6.88553,lon:79.8767},89:{name:"Apollo Hospital",lat:6.88922,lon:79.8769},90:{name:"Narahenpita Junction",lat:6.89189,lon:79.877},94:{name:"Ibbanwela Junction",lat:6.91879,lon:79.8613},95:{name:"Ward Place",lat:6.91721,lon:79.8662},100:{name:"Borella",lat:6.91474,lon:79.8776},101:{name:"Horton Place - Baseline Junction",lat:6.91126,lon:79.8775},102:{name:"Borella Cemetery Junction",lat:6.90859,lon:79.8773},103:{name:"Sarana Road",lat:6.90558,lon:79.8735},104:{name:"Maitland Place",lat:6.9029,lon:79.8705},201:{name:"Delmon Hospital",lat:6.87071,lon:79.8621},202:{name:"Ramakrishna Road",lat:6.8659,lon:79.863},203:{name:"William Grinding Mills",lat:6.86251,lon:79.8638},204:{name:"Dehiwala Municipal Council",lat:6.86103,lon:79.8641},205:{name:"St. Mary's Church, Dehiwala",lat:6.85918,lon:79.8645},206:{name:"Holy Family Convent, Dehiwala",lat:6.85717,lon:79.865},207:{name:"Dehiwala Junction",lat:6.85082,lon:79.866},208:{name:"Dehiwala Cemetery",lat:6.84493,lon:79.8662},209:{name:"Hotel Road, Mt. Lavinia",lat:6.84197,lon:79.8669},210:{name:"S. Thomas' College, Mt. Lavinia",lat:6.83775,lon:79.8674},211:{name:"Mount Lavinia Junction",lat:6.83296,lon:79.8673},212:{name:"Maliban Junction",lat:6.81943,lon:79.8737},213:{name:"Belekkade Junc., Rathmalana",lat:6.81474,lon:79.8788},214:{name:"Rathmalana Airport",lat:6.81244,lon:79.8811},215:{name:"Soysapura",lat:6.80462,lon:79.8867},216:{name:"Katubedda Junction",lat:6.79733,lon:79.8885},217:{name:"Rawatawatta",lat:6.78803,lon:79.8851},218:{name:"Moratuwa",lat:6.77449,lon:79.8824},219:{name:"Panadura",lat:6.71138,lon:79.9076},220:{name:"Angulana",lat:6.79819,lon:79.873},230:{name:"Dutugemunu Street",lat:6.87059,lon:79.8774},231:{name:"Kohuwala",lat:6.86707,lon:79.8846},232:{name:"Woodlands",lat:6.8623,lon:79.8884},233:{name:"Pepiliyana",lat:6.85666,lon:79.8906},234:{name:"Raththanapitiya",lat:6.84834,lon:79.8976},235:{name:"Boralesgamuwa",lat:6.84124,lon:79.9016},236:{name:"Werahera",lat:6.82972,lon:79.9114},237:{name:"Bokundara",lat:6.81862,lon:79.9177},238:{name:"Piliyandala",lat:6.80122,lon:79.9233},239:{name:"Kesbewa",lat:6.79552,lon:79.9408},240:{name:"Polgasovita",lat:6.78698,lon:79.9649},241:{name:"Kahathuduwa",lat:6.78346,lon:79.9834},242:{name:"Horana",lat:6.71662,lon:80.0638},244:{name:"Panagoda",lat:6.84827,lon:80.0188},245:{name:"Godagama",lat:6.85148,lon:80.0316},246:{name:"Migoda",lat:6.84423,lon:80.0467},247:{name:"Padukka",lat:6.84146,lon:80.0908},248:{name:"Handapangoda",lat:6.79234,lon:80.1426},249:{name:"Ingiriya",lat:6.74399,lon:80.1766},250:{name:"Teachers' Training College",lat:6.84471,lon:79.9327},251:{name:"Pannipitiya",lat:6.8462,lon:79.9497},252:{name:"Kottawa",lat:6.84167,lon:79.964},253:{name:"Makumbura",lat:6.83862,lon:79.98},254:{name:"Homagama",lat:6.84113,lon:80.002},255:{name:"Kottawa Railway Station",lat:6.84401,lon:79.9682},256:{name:"Rukmalgama",lat:6.85692,lon:79.9859},257:{name:"Walgama Junc., Athurugiriya",lat:6.86495,lon:79.9958},258:{name:"Athurugiriya Junction",lat:6.87745,lon:79.9895},259:{name:"Pinhena Junction",lat:6.83278,lon:79.9647},260:{name:"Mattegoda",lat:6.81158,lon:79.9751},271:{name:"Open University, Nawala",lat:6.88302,lon:79.8868},272:{name:"Nawala Junction",lat:6.88703,lon:79.8872},273:{name:"Koswatta, Nawala",lat:6.90003,lon:79.894},274:{name:"Bellanthota Junction",lat:6.84831,lon:79.8854},275:{name:"Nandimala",lat:6.84986,lon:79.8794},276:{name:"Karagampitiya",lat:6.85025,lon:79.872},277:{name:"Kalubowila Hospital",lat:6.86647,lon:79.8773},278:{name:"Nugegoda Supermarket",lat:6.87347,lon:79.8914},279:{name:"Pita Kotte",lat:6.8839,lon:79.9019},280:{name:"Impala Cinema, Rajagiriya",lat:6.91097,lon:79.8907},281:{name:"Rajagiriya Junction",lat:6.91012,lon:79.8944},282:{name:"Ethul Kotte Junction",lat:6.90551,lon:79.9052},283:{name:"Sethsiripaya",lat:6.90244,lon:79.9143},284:{name:"Battaramulla",lat:6.90209,lon:79.9181},285:{name:"Thalangama",lat:6.90597,lon:79.9262},286:{name:"Thalahena",lat:6.90801,lon:79.945},287:{name:"Malabe",lat:6.90408,lon:79.9544},288:{name:"Pittugala",lat:6.90869,lon:79.9693},289:{name:"SLIIT, Malabe",lat:6.91471,lon:79.9722},290:{name:"Kaduwela",lat:6.9357,lon:79.9842},291:{name:"Thalawathugoda",lat:6.87628,lon:79.9353},292:{name:"Pelawatta/Isurupaya",lat:6.8906,lon:79.9289},295:{name:"Peliyagoda Junction",lat:6.95576,lon:79.8831},296:{name:"Nawaloka Junction",lat:6.96068,lon:79.8808},297:{name:"Wattala",lat:6.98125,lon:79.888},298:{name:"Handala",lat:6.99069,lon:79.8931},299:{name:"Welisara",lat:7.02178,lon:79.8996},300:{name:"Kandana",lat:7.04783,lon:79.897},301:{name:"Kapuwatta",lat:7.06401,lon:79.8932},302:{name:"Ja-Ela",lat:7.07824,lon:79.8905},303:{name:"Katunayake Airport",lat:7.16564,lon:79.8841}},routes:{1:{routeno:"138",from:"Pettah",to:"Kottawa",stopsfrom:{1:0,2:609,25:2503,26:4056,27:6664,28:8217,29:9130,30:9570,31:10021,34:8440,35:10805,36:11226,37:11614,38:11898,39:12510,40:12907,41:13517,42:13969,43:14564,44:14911,45:15567,46:16073,47:16517,48:17149,49:18114,50:18724,51:19238,52:19717,53:21116,250:22627,251:24548,252:26231},stopsto:{1:22556,2:21962,25:20875,26:19802,27:18232,28:16762,32:14544,33:15110,34:16002,35:14029,36:13608,37:13220,38:12936,39:12324,40:11918,41:11308,42:10856,43:10261,44:9914,45:9258,46:8752,47:8308,48:7676,49:6711,50:6101,51:5587,52:5108,53:4144,250:3604,251:1683,252:0}},2:{routeno:"138",from:"Pettah",to:"Homagama",stopsfrom:{1:0,2:609,25:2503,26:4056,27:6664,28:8217,29:9130,30:9570,31:10021,34:8440,35:10805,36:11226,37:11614,38:11898,39:12510,40:12907,41:13517,42:13969,43:14564,44:14911,45:15567,46:16073,47:16517,48:17149,49:18114,50:18724,51:19238,52:19717,53:21116,250:22627,251:24548,252:26231,253:28080,254:30597},stopsto:{1:26922,2:26328,25:25241,26:24168,27:22598,28:21128,32:18910,33:19476,34:20368,35:18395,36:17974,37:17586,38:17302,39:16690,40:16284,41:15674,42:15222,43:14627,44:14280,45:13624,46:13118,47:12674,48:12042,49:11077,50:10467,51:9953,52:9474,53:8510,250:7970,251:6049,252:4366,253:2517,254:0}},3:{routeno:"138",from:"Pettah",to:"Maharagama",stopsfrom:{1:0,2:609,25:2503,26:4056,27:6664,28:8217,29:9130,30:9570,31:10021,34:8440,35:10805,36:11226,37:11614,38:11898,39:12510,40:12907,41:13517,42:13969,43:14564,44:14911,45:15567,46:16073,47:16517,48:17149,49:18114,50:18724,51:19238,52:19717,53:21116},stopsto:{1:18412,2:17818,25:16731,26:15658,27:14088,28:12618,32:10400,33:10966,34:11858,35:9885,36:9464,37:9076,38:8792,39:8180,40:7774,41:7164,42:6712,43:6117,44:5770,45:5114,46:4608,47:4164,48:3532,49:2567,50:1957,51:1443,52:964,53:0}},4:{routeno:"138/2",from:"Pettah",to:"Mattegoda",stopsfrom:{1:0,2:609,25:2503,26:4056,27:6664,28:8217,29:9130,30:9570,31:10021,34:8440,35:10805,36:11226,37:11614,38:11898,39:12510,40:12907,41:13517,42:13969,43:14564,44:14911,45:15567,46:16073,47:16517,48:17149,49:18114,50:18724,51:19238,52:19717,53:21116,250:22627,251:24548,252:26231,259:27340,260:30130},stopsto:{1:26455,2:25861,25:24774,26:23701,27:22131,28:20661,32:18443,33:19009,34:19901,35:17928,36:17507,37:17119,38:16835,39:16223,40:15817,41:15207,42:14755,43:14160,44:13813,45:13157,46:12651,47:12207,48:11575,49:10610,50:1e4,51:9486,52:9007,53:8043,250:7503,251:5582,252:3899,259:2790,260:0}},5:{routeno:"138/4",from:"Pettah",to:"Athurugiriya",stopsfrom:{1:0,2:609,25:2503,26:4056,27:6664,28:9086,29:9999,30:10439,31:10890,34:8326,35:11674,36:12095,37:12483,38:12767,39:13379,40:13776,41:14386,42:14838,43:15433,44:15780,45:16436,46:16942,47:17386,48:18018,49:18983,50:19593,51:20107,52:20586,53:21985,250:23496,251:25417,252:27100,255:27650,256:30454,257:31999,258:33750},stopsto:{1:29206,2:28612,25:27525,26:26452,27:24882,28:23412,32:21194,33:21760,34:22652,35:20679,36:20258,37:19870,38:19586,39:18974,40:18568,41:17958,42:17506,43:16911,44:16564,45:15908,46:15402,47:14958,48:14326,49:13361,50:12751,51:12237,52:11758,53:10794,250:10254,251:8333,252:6650,255:6100,256:3296,257:1751,258:0}},6:{routeno:"138/3",from:"Pettah",to:"Rukmalgama",stopsfrom:{1:0,2:609,25:2503,26:4056,27:6664,28:8217,29:9130,30:9570,31:10021,34:8440,35:10805,36:11226,37:11614,38:11898,39:12510,40:12907,41:13517,42:13969,43:14564,44:14911,45:15567,46:16073,47:16517,48:17149,49:18114,50:18724,51:19238,52:19717,53:21116,250:22627,251:24548,252:26231,255:26781,256:29585},stopsto:{1:25910,2:25316,25:24229,26:23156,27:21586,28:20116,32:17898,33:18464,34:19356,35:17383,36:16962,37:16574,38:16290,39:15678,40:15272,41:14662,42:14210,43:13615,44:13268,45:12612,46:12106,47:11662,48:11030,49:10065,50:9455,51:8941,52:8462,53:7498,250:6958,251:5037,252:3354,255:2804,256:0}},7:{routeno:"100",from:"Pettah",to:"Panadura",stopsfrom:{1:0,2:609,3:964,4:3048,5:5304,7:7872,8:9380,9:10656,10:11225,23:6312,24:6995,201:11755,202:12299,203:12686,204:12855,205:13065,206:13296,207:14012,208:14668,209:15007,210:15486,211:16019,212:17703,213:18479,214:18838,215:19914,216:20767,217:21871,218:23505,219:31585},stopsto:{1:29615,2:29021,3:28666,4:26417,5:25135,6:24202,7:22646,8:21737,9:20860,10:20291,201:19761,202:19217,203:18830,204:18661,205:18451,206:18220,207:17504,208:16848,209:16509,210:16030,211:15497,212:13813,213:13037,214:12678,215:11602,216:10749,217:9645,218:8060,219:0}},8:{routeno:"100",from:"Pettah",to:"Moratuwa",stopsfrom:{1:0,2:609,3:964,4:3048,5:5304,7:7872,8:9380,9:10656,10:11225,23:6312,24:6995,201:11755,202:12299,203:12686,204:12855,205:13065,206:13296,207:14012,208:14668,209:15007,210:15486,211:16019,212:17703,213:18479,214:18838,215:19914,216:20767,217:21871,218:23505},stopsto:{1:21555,2:20961,3:20606,4:18357,5:17075,6:16142,7:14586,8:13677,9:12800,10:12231,201:11701,202:11157,203:10770,204:10601,205:10391,206:10160,207:9444,208:8788,209:8449,210:7970,211:7437,212:5753,213:4977,214:4618,215:3542,216:2689,217:1585,218:0}},9:{routeno:"101",from:"Pettah",to:"Moratuwa",stopsfrom:{1:0,2:609,5:7694,7:10262,8:11770,9:13046,10:13615,21:5065,22:5513,23:8702,24:9385,25:2503,26:4056,201:14145,202:14689,203:15076,204:15245,205:15455,206:15686,207:16402,208:17058,209:17397,210:17876,211:18409,212:20093,213:20869,214:21228,215:22304,216:23157,217:24261,218:25895},stopsto:{1:21882,2:21288,5:17075,6:16142,7:14586,8:13677,9:12800,10:12231,21:18456,22:18008,25:20201,26:19128,201:11701,202:11157,203:10770,204:10601,205:10391,206:10160,207:9444,208:8788,209:8449,210:7970,211:7437,212:5753,213:4977,214:4618,215:3542,216:2689,217:1585,218:0}},10:{routeno:"154",from:"Kiribathgoda",to:"Angulana",stopsfrom:{7:4627,8:6135,9:7411,10:7980,31:2970,35:3754,100:0,101:386,102:683,103:1242,104:1688,201:8510,202:9054,203:9441,204:9610,205:9820,206:10051,207:10767,208:11423,209:11762,210:12241,211:12774,212:14458,213:15234,214:15593,220:18383},stopsto:{7:0,8:25647,9:26923,10:27492,24:30770,30:33289,32:34432,33:34998,35:36629,100:39880,101:40266,102:40563,103:41122,104:22437,201:17276,202:16732,203:4042,204:4211,205:4421,206:4652,207:5368,208:6024,209:6363,210:6842,211:7375,212:9059,213:9835,214:10194,220:56255}},11:{routeno:"154",from:"Kadawatha",to:"Bambalapitiya",stopsfrom:{7:4627,31:2970,35:3754,100:0,101:386,102:683,103:1242,104:1688},stopsto:{7:0,24:14461,30:12325,32:10401,33:8861,35:7780,100:4624,101:4238,102:3830,103:3231,104:16677}},12:{routeno:"120",from:"Pettah",to:"Horana",stopsfrom:{1:0,2:609,16:1385,19:3208,20:3752,27:5070,29:6191,30:6631,31:7082,35:7866,36:8287,37:8675,38:8959,39:9571,40:9968,83:10480,230:11622,231:12597,232:13307,233:14020,234:15296,235:16232,236:17997,237:19432,238:21554,239:23759,240:26657,241:28879,242:42291},stopsto:{1:43822,2:43228,16:42069,19:40317,20:39773,27:39056,29:36278,32:34800,33:35366,35:34285,36:33864,37:33476,38:33192,39:32580,85:32138,87:31776,230:30597,231:29677,232:28983,233:28270,234:26994,235:26058,236:24294,237:22859,238:20737,239:18532,240:15634,241:13412,242:0}},13:{routeno:"120",from:"Pettah",to:"Kesbewa",stopsfrom:{1:0,2:609,16:1385,19:3208,20:3752,27:5070,29:6191,30:6631,31:7082,35:7866,36:8287,37:8675,38:8959,39:9571,40:9968,83:10480,230:11622,231:12597,232:13307,233:14020,234:15296,235:16232,236:17997,237:19432,238:21554,239:23759},stopsto:{1:25290,2:24696,16:23537,19:21785,20:21241,27:20524,29:17746,32:16268,33:16834,35:15753,36:15332,37:14944,38:14660,39:14048,85:13606,87:13244,230:12065,231:11145,232:10451,233:9738,234:8462,235:7526,236:5762,237:4327,238:2205,239:0}},14:{routeno:"120",from:"Pettah",to:"Piliyandala",stopsfrom:{1:0,2:609,16:1385,19:3208,20:3752,27:5070,29:6191,30:6631,31:7082,35:7866,36:8287,37:8675,38:8959,39:9571,40:9968,83:10480,230:11622,231:12597,232:13307,233:14020,234:15296,235:16232,236:17997,237:19432,238:21554},stopsto:{1:23085,2:22491,16:21332,19:19580,20:19036,27:18319,29:15541,32:14063,33:14629,35:13548,36:13127,37:12739,38:12455,39:11843,85:11401,87:11039,230:9860,231:8940,232:8246,233:7533,234:6257,235:5321,236:3557,237:2122,238:0}},15:{routeno:"141",from:"Narahenpita",to:"Wellawatte",stopsfrom:{90:0,89:297,88:721,41:1755,87:3098,86:3489,10:4121},stopsto:{10:0,86:804,87:1169,85:1451,40:1685,41:2219,84:2809,88:3212,89:3620,90:3864}},16:{routeno:"135",from:"Kohuwala",to:"Kelaniya",stopsfrom:{37:3799,38:3515,39:2903,85:2461,87:2099,90:6019,100:9098,101:8712,102:8304,230:920,231:0},stopsto:{37:0,38:17396,39:16784,40:16378,83:14646,90:12574,100:10032,101:9646,102:9238,230:2947,231:21467}},17:{routeno:"122",from:"Pettah",to:"Avissawella",stopsfrom:{},stopsto:{}},18:{routeno:"122",from:"Pettah",to:"Rathnapura",stopsfrom:{},stopsto:{}},19:{routeno:"125",from:"Pettah",to:"Padukka",stopsfrom:{1:0,2:609,16:1385,19:3208,20:3752,27:5070,28:6623,29:7536,30:7976,31:8427,35:9211,36:9632,37:10020,38:10304,39:10916,40:11313,41:11923,42:12375,43:12970,44:13317,45:13973,46:14479,47:14923,48:15555,49:16520,50:17130,51:17644,52:18123,53:19522,244:31478,245:37986,246:39907,247:45709,250:21033,251:22954,252:24637,253:26486,254:33954},stopsto:{1:39687,2:39093,16:37934,19:36182,20:35638,27:34921,29:32143,32:30665,33:31231,35:30150,36:29729,37:29341,38:29057,39:28445,40:28039,41:27429,42:26977,43:26382,44:26035,45:25379,46:24873,47:24429,48:23797,49:22832,50:22222,51:21708,52:21229,53:20265,244:9279,245:7723,246:5802,247:0,250:19725,251:17804,252:16121,254:11755}},20:{routeno:"125",from:"Pettah",to:"Ingiriya",stopsfrom:{1:0,2:609,16:1385,19:3208,20:3752,27:5070,28:6623,29:7536,30:7976,31:8427,35:9211,36:9632,37:10020,38:10304,39:10916,40:11313,41:11923,42:12375,43:12970,44:13317,45:13973,46:14479,47:14923,48:15555,49:16520,50:17130,51:17644,52:18123,53:19522,244:31479,245:33035,246:34956,247:40758,248:50596,249:57743,250:21033,251:22954,252:24637,253:26486,254:29003},stopsto:{1:57091,2:56497,16:55338,19:53586,20:53042,27:52325,29:49547,32:48069,33:48635,35:47554,36:47133,37:46745,38:46461,39:45849,40:45443,41:44833,42:44381,43:43786,44:43439,45:42783,46:42277,47:41833,48:41201,49:40236,50:39626,51:39112,52:38633,53:37669,244:26683,245:25127,246:23206,247:17404,248:7147,249:0,250:37129,251:35208,252:33525,254:29159}},21:{routeno:"177",from:"Kollupitiya",to:"Kaduwela",stopsfrom:{5:0,28:1076,29:1989,34:1299,78:5913,79:5570,80:550,81:4087,101:5057,280:6546,281:6977,282:8381,283:9532,284:9954,285:10989,286:13321,287:14506,288:16400,289:17218,290:20186},stopsto:{5:19102,34:17500,78:14405,79:14748,81:16231,82:16989,101:15261,280:13769,281:13338,282:11805,283:10654,284:10232,285:9197,286:6865,287:5680,288:3786,289:2968,290:0}},22:{routeno:"155",from:"Soysapura",to:"Mattakkuliya",stopsfrom:{7:11044,8:10135,9:9258,10:8689,19:19631,20:19087,24:12637,27:18370,29:15592,32:14108,35:13593,201:8159,202:7615,203:7228,204:7059,205:6849,206:6618,207:5902,208:5246,209:4907,210:4428,211:3895,212:2211,213:1435,214:1076,215:0},stopsto:{7:5531,8:7039,9:8315,10:8884,19:0,20:544,27:1862,29:2983,30:3423,31:3874,35:4658,201:9414,202:9958,203:10345,204:10514,205:10724,206:10955,207:11671,208:12327,209:12666,210:13145,211:13678,212:15362,213:16138,214:16497,215:17573}},23:{routeno:"155",from:"Mount Lavinia",to:"Mattakkuliya",stopsfrom:{7:7149,8:6240,9:5363,10:4794,19:15736,20:15192,24:8742,27:14475,29:11697,32:10213,35:9698,201:4264,202:3720,203:3333,204:3164,205:2954,206:2723,207:2007,208:1351,209:1012,210:533,211:0},stopsto:{7:5531,8:7039,9:8315,10:8884,19:0,20:544,27:1862,29:2983,30:3423,31:3874,35:4658,201:9414,202:9958,203:10345,204:10514,205:10724,206:10955,207:11671,208:12327,209:12666,210:13145,211:13678}},24:{routeno:"140",from:"Kollupitiya",to:"Wellampitiya",stopsfrom:{5:0,27:2546,28:1076,77:4366,80:550},stopsto:{5:5520,27:2296,28:3849,77:0}},25:{routeno:"175",from:"Kollupitiya",to:"Kohilawatte",stopsfrom:{5:0,27:2546,28:1076,80:550,100:5403},stopsto:{5:6081,27:2857,28:4410,100:0}},26:{routeno:"119",from:"Dehiwala",to:"Maharagama",stopsfrom:{53:0,207:8242,235:3347,274:5610,275:6310,276:7134},stopsto:{53:8242,207:0,235:4895,274:2632,275:1932,276:1108}},27:{routeno:"174",from:"Kottawa",to:"Borella",stopsfrom:{100:14467,251:1683,252:0,280:12882,281:12451,282:11047,283:9896,284:9474,291:5620,292:7493},stopsto:{100:0,251:12784,252:14467,280:1585,281:2016,282:3420,283:4571,284:4993,291:8847,292:6974}},28:{routeno:"163",from:"Dehiwala",to:"Battaramulla",stopsfrom:{45:4792,203:1326,204:1157,205:947,206:716,207:0,231:3756,277:2929,278:5708,279:7485,282:10193,283:11344,284:11766},stopsto:{45:7195,203:10781,204:10950,205:11160,206:11391,207:12107,231:8301,277:9178,278:6037,279:4260,282:1573,283:422,284:0}},29:{routeno:"176",from:"Karagampitiya",to:"Hettiyawatte",stopsfrom:{45:5222,76:15565,77:16125,78:12791,79:13134,100:14033,101:13647,231:4116,271:7771,272:8222,273:9983,276:0,277:3239,278:6380,280:12158,281:11727},stopsto:{45:10903,76:560,77:0,78:3334,79:2991,100:2092,101:2478,231:12009,271:8354,272:7903,273:6142,276:16125,277:12886,278:9745,280:3967,281:4398}},30:{routeno:"103",from:"Narahenpita",to:"Fort",stopsfrom:{1:7565,2:8174,76:4611,77:5459,90:0,100:3079,101:2693,102:2285},stopsto:{1:594,2:0,76:2405,77:1845,90:6477,100:3937,101:4323,102:4620}},31:{routeno:"187",from:"Fort",to:"Airport",stopsfrom:{2:0,74:2890,75:2221,295:4893,296:5568,297:8207,298:9455,299:13190,300:16190,301:18135,302:19828,303:31121},stopsto:{2:31121,74:28231,75:28900,295:26228,296:25553,297:22914,298:21666,299:17931,300:14931,301:12986,302:11293,303:0}},32:{routeno:"190",from:"Pettah",to:"Meegoda",stopsfrom:{1:0,2:609,16:1385,19:3208,20:3752,27:5070,100:7011,245:28050,246:29971,258:21811,280:8596,281:9027,282:10431,283:11582,284:12004,285:13039,286:15371,287:16556},stopsto:{1:30706,2:30112,16:28953,19:27201,20:26657,27:25940,100:23083,245:1921,246:0,258:8160,280:21504,281:21073,282:19540,283:18389,284:17967,285:16932,286:14600,287:13415}},33:{routeno:"336",from:"Kottawa",to:"Malabe",stopsfrom:{252:0,287:8700},stopsto:{252:8700,287:0}},34:{routeno:"17",from:"Kandy",to:"Panadura",stopsfrom:{45:17427,203:21013,204:21182,205:21392,206:21623,207:22339,208:22995,209:23334,210:23813,211:24346,212:26030,213:26806,214:27165,215:28241,216:29094,217:30198,218:31832,219:39912,231:18533,277:19410,278:16269,279:14492,282:11805,283:10654,284:10232,285:9197,286:6865,287:5680,288:3786,289:2968,290:0},stopsto:{45:22296,203:18830,204:18661,205:18451,206:18220,207:17504,208:16848,209:16509,210:16030,211:15497,212:13813,213:13037,214:12678,215:11602,216:10749,217:9645,218:8060,219:0,231:21260,277:20433,278:23212,279:24989,282:27697,283:28848,284:29270,285:30305,286:32637,287:33822,288:35716,289:36534,290:39502}}},groups:{1:{routes:["1","2","3","4","5","6"],stops:[1,2,25,26,27,28,29,30,31,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,250,251,252,32,33,253,254,259,260,255,256,257,258]},2:{routes:["7","8"],stops:[1,2,3,4,5,7,8,9,10,23,24,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,6]},3:{routes:["9"],stops:[1,2,5,7,8,9,10,21,22,23,24,25,26,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,6]},4:{routes:["10","11"],stops:[7,8,9,10,31,35,100,101,102,103,104,201,202,203,204,205,206,207,208,209,210,211,212,213,214,220,24,30,32,33]},5:{routes:["12","13","14"],stops:[1,2,16,19,20,27,29,30,31,35,36,37,38,39,40,83,230,231,232,233,234,235,236,237,238,239,240,241,242,32,33,85,87]},6:{routes:["15"],stops:[10,41,86,87,88,89,90,40,84,85]},7:{routes:["16"],stops:[37,38,39,85,87,90,100,101,102,230,231,40,83]},8:{routes:["17","18","19","20"],stops:[1,2,16,19,20,27,28,29,30,31,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,244,245,246,247,250,251,252,253,254,32,33,248,249]},9:{routes:["21"],stops:[5,28,29,34,78,79,80,81,101,280,281,282,283,284,285,286,287,288,289,290,82]},10:{routes:["22","23"],stops:[7,8,9,10,19,20,24,27,29,32,35,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,30,31]},11:{routes:["24"],stops:[5,27,28,77,80]},12:{routes:["25"],stops:[5,27,28,80,100]},13:{routes:["26"],stops:[53,207,235,274,275,276]},14:{routes:["27"],stops:[100,251,252,280,281,282,283,284,291,292]},15:{routes:["28"],stops:[45,203,204,205,206,207,231,277,278,279,282,283,284]},16:{routes:["29"],stops:[45,76,77,78,79,100,101,231,271,272,273,276,277,278,280,281]},17:{routes:["30"],stops:[1,2,76,77,90,100,101,102]},18:{routes:["31"],stops:[2,74,75,295,296,297,298,299,300,301,302,303]},19:{routes:["32"],stops:[1,2,16,19,20,27,100,245,246,258,280,281,282,283,284,285,286,287]},20:{routes:["33"],stops:[252,287]},21:{routes:["34"],stops:[45,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,231,277,278,279,282,283,284,285,286,287,288,289,290]}}};exports.default=Buses},{}],3:[function(require,module,exports){(function(global){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i0){routes=this.props.routes.map(function(r,index){return _react2.default.createElement(RenderOption,{key:index,first:index===0,route:r,router:this.props.router})}.bind(this))}return _react2.default.createElement("div",null,routes)}}]);return DisplayRoutes}(_react.Component);exports.default=DisplayRoutes}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],4:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _buses=require("./buses");var _buses2=_interopRequireDefault(_buses);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{"default":obj}}var Router=function Router(){this.buses=_buses2.default;this.penalty=5e3};Router.prototype.unique=function(array){return array.filter(function(item,pos){return array.indexOf(item)==pos})};Router.prototype.intersect=function(array1,array2){return this.unique(array1.filter(function(n){return array2.indexOf(n)!=-1}))};Router.prototype.getDistance=function(route,from,to){if(!this.buses.routes.hasOwnProperty(route)){return 1/0}var fromdist=this.buses.routes[route].stopsfrom[to]-this.buses.routes[route].stopsfrom[from];var todist=this.buses.routes[route].stopsto[to]-this.buses.routes[route].stopsto[from];return fromdist>0?fromdist:todist>0?todist:1/0};Router.prototype.reachable=function(route,from,to){return this.buses.routes.hasOwnProperty(route)&&(this.buses.routes[route].stopsfrom[to]>this.buses.routes[route].stopsfrom[from]||this.buses.routes[route].stopsto[to]>this.buses.routes[route].stopsto[from])};Router.prototype.findReachableStops=function(route,stop){var stops=[];if(this.buses.routes.hasOwnProperty(route)&&this.buses.routes[route].stopsfrom.hasOwnProperty(stop)){var startdist=this.buses.routes[route].stopsfrom[stop];for(var s in this.buses.routes[route].stopsfrom){if(this.buses.routes[route].stopsfrom[s]>startdist){stops.push(s)}}}if(this.buses.routes.hasOwnProperty(route)&&this.buses.routes[route].stopsto.hasOwnProperty(stop)){var startdist=this.buses.routes[route].stopsto[stop];for(var s in this.buses.routes[route].stopsto){if(this.buses.routes[route].stopsto[s]>startdist){stops.push(s)}}}return this.unique(stops)};Router.prototype.findStopRoutes=function(stop){var routes=[];for(var r in this.buses.routes){if(this.buses.routes[r].stopsfrom.hasOwnProperty(stop)||this.buses.routes[r].stopsto.hasOwnProperty(stop)){routes.push(r)}}return this.unique(routes)};Router.prototype.findSingleRoutes=function(from,to){var _self=this;var fromRoutes=this.findStopRoutes(from);var toRoutes=this.findStopRoutes(to);return this.intersect(fromRoutes,toRoutes).filter(function(r){return _self.reachable(r,from,to)})};Router.prototype.findRoutes=function(from,to){var limit=arguments.length<=2||arguments[2]===undefined?5:arguments[2];var fromRoutes=this.findStopRoutes(from);var toRoutes=this.findStopRoutes(to);var singleRoute=this.findSingleRoutes(from,to);if(singleRoute.length>0){singleRoute=singleRoute.sort(function(a,b){return a.distance-b.distance});var distance=this.getDistance(singleRoute[0],from,to);return[{from:from,routes:[{routes:singleRoute.slice(0,5),distance:distance}],changes:[],to:to,distance:distance}]}else{var fromStops=[],toStops=[],distances,multiRoutes=[],_self=this;fromRoutes.forEach(function(fr){fromStops=fromStops.concat(_self.findReachableStops(fr,from))});toRoutes.forEach(function(tr){toStops=toStops.concat(_self.findReachableStops(tr,to))});var common=this.intersect(fromStops,toStops);common.forEach(function(c){var toc=_self.findSingleRoutes(from,c);var fromc=_self.findSingleRoutes(c,to);if(toc.length>0&&fromc.length>0){distances=[_self.getDistance(toc[0],from,c),_self.getDistance(fromc[0],c,to)];multiRoutes.push({from:from,routes:[{routes:toc,distance:distances[0]},{routes:fromc,distance:distances[1]}],changes:[c],to:to,distance:distances[0]+distances[1]})}});if(common.length<3){fromStops.forEach(function(fs){toStops.forEach(function(ts){var tots=_self.findSingleRoutes(fs,ts);if(tots.length>0){var tofs=_self.findSingleRoutes(from,fs);var fromts=_self.findSingleRoutes(ts,to);if(tofs.length>0&&fromts.length>0){distances=[_self.getDistance(tofs[0],from,fs),_self.getDistance(tots[0],fs,ts),_self.getDistance(fromts[0],ts,to)];multiRoutes.push({from:from,routes:[{routes:tofs,distance:distances[0]},{routes:tots,distance:distances[1]},{routes:fromts,distance:distances[2]}],changes:[fs,ts],to:to,distance:distances[0]+distances[1]+distances[2]})}}})})}if(multiRoutes.length>0){multiRoutes=multiRoutes.filter(function(n){return!Number.isNaN(n.distance)}).sort(function(a,b){return(a.routes.length-b.routes.length)*_self.penalty+(a.distance-b.distance)});return multiRoutes.slice(0,limit)}}};Router.prototype.getPlaceDetails=function(pid){return this.buses.places[pid]};Router.prototype.getRouteDetails=function(id){return this.buses.routes[id]};Router.prototype.getAllPlaces=function(){var places=[];for(var p in this.buses.places){places.push({id:p,name:this.buses.places[p].name})}return places};Router.prototype.deg2rad=function(deg){return deg*(Math.PI/180)};Router.prototype.haversine=function(lat1,lon1,lat2,lon2){var R=6371;var dLat=this.deg2rad(lat2-lat1);var dLon=this.deg2rad(lon2-lon1);var a=Math.sin(dLat/2)*Math.sin(dLat/2)+Math.cos(this.deg2rad(lat1))*Math.cos(this.deg2rad(lat2))*Math.sin(dLon/2)*Math.sin(dLon/2);var c=2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));return Math.floor(R*c*1e3)};Router.prototype.getNearestPlace=function(lat,lon){var nearby=[],place;for(var p in this.buses.places){if(Math.abs(this.buses.places[p].lat-lat)<.1&&Math.abs(this.buses.places[p].lon-lon)<.1){place=this.buses.places[p];place.id=p;place.distance=this.haversine(place.lat,place.lon,lat,lon);nearby.push(place)}}return nearby.sort(function(a,b){return b.distance-a.distance}).pop()};exports.default=Router},{"./buses":2}],5:[function(require,module,exports){(function(global){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i