UNPKG

leaflet-routing-machine

Version:
351 lines (294 loc) 8.83 kB
(function() { 'use strict'; var L = require('leaflet'); var Itinerary = require('./itinerary'); var Line = require('./line'); var Plan = require('./plan'); var OSRMv1 = require('./osrm-v1'); module.exports = Itinerary.extend({ options: { fitSelectedRoutes: 'smart', routeLine: function(route, options) { return new Line(route, options); }, autoRoute: true, routeWhileDragging: false, routeDragInterval: 500, waypointMode: 'connect', showAlternatives: false, defaultErrorHandler: function(e) { console.error('Routing error:', e.error); } }, initialize: function(options) { L.Util.setOptions(this, options); this._router = this.options.router || new OSRMv1(options); this._plan = this.options.plan || new Plan(this.options.waypoints, options); this._requestCount = 0; Itinerary.prototype.initialize.call(this, options); this.on('routeselected', this._routeSelected, this); if (this.options.defaultErrorHandler) { this.on('routingerror', this.options.defaultErrorHandler); } this._plan.on('waypointschanged', this._onWaypointsChanged, this); if (options.routeWhileDragging) { this._setupRouteDragging(); } }, _onZoomEnd: function() { if (!this._selectedRoute || !this._router.requiresMoreDetail) { return; } var map = this._map; if (this._router.requiresMoreDetail(this._selectedRoute, map.getZoom(), map.getBounds())) { this.route({ callback: L.bind(function(err, routes) { var i; if (!err) { for (i = 0; i < routes.length; i++) { this._routes[i].properties = routes[i].properties; } this._updateLineCallback(err, routes); } }, this), simplifyGeometry: false, geometryOnly: true }); } }, onAdd: function(map) { if (this.options.autoRoute) { this.route(); } var container = Itinerary.prototype.onAdd.call(this, map); this._map = map; this._map.addLayer(this._plan); this._map.on('zoomend', this._onZoomEnd, this); if (this._plan.options.geocoder) { container.insertBefore(this._plan.createGeocoders(), container.firstChild); } return container; }, onRemove: function(map) { map.off('zoomend', this._onZoomEnd, this); if (this._line) { map.removeLayer(this._line); } map.removeLayer(this._plan); if (this._alternatives && this._alternatives.length > 0) { for (var i = 0, len = this._alternatives.length; i < len; i++) { map.removeLayer(this._alternatives[i]); } } return Itinerary.prototype.onRemove.call(this, map); }, getWaypoints: function() { return this._plan.getWaypoints(); }, setWaypoints: function(waypoints) { this._plan.setWaypoints(waypoints); return this; }, spliceWaypoints: function() { var removed = this._plan.spliceWaypoints.apply(this._plan, arguments); return removed; }, getPlan: function() { return this._plan; }, getRouter: function() { return this._router; }, _routeSelected: function(e) { var route = this._selectedRoute = e.route, alternatives = this.options.showAlternatives && e.alternatives, fitMode = this.options.fitSelectedRoutes, fitBounds = (fitMode === 'smart' && !this._waypointsVisible()) || (fitMode !== 'smart' && fitMode); this._updateLines({route: route, alternatives: alternatives}); if (fitBounds) { this._map.fitBounds(this._line.getBounds()); } if (this.options.waypointMode === 'snap') { this._plan.off('waypointschanged', this._onWaypointsChanged, this); this.setWaypoints(route.waypoints); this._plan.on('waypointschanged', this._onWaypointsChanged, this); } }, _waypointsVisible: function() { var wps = this.getWaypoints(), mapSize, bounds, boundsSize, i, p; try { mapSize = this._map.getSize(); for (i = 0; i < wps.length; i++) { p = this._map.latLngToLayerPoint(wps[i].latLng); if (bounds) { bounds.extend(p); } else { bounds = L.bounds([p]); } } boundsSize = bounds.getSize(); return (boundsSize.x > mapSize.x / 5 || boundsSize.y > mapSize.y / 5) && this._waypointsInViewport(); } catch (e) { return false; } }, _waypointsInViewport: function() { var wps = this.getWaypoints(), mapBounds, i; try { mapBounds = this._map.getBounds(); } catch (e) { return false; } for (i = 0; i < wps.length; i++) { if (mapBounds.contains(wps[i].latLng)) { return true; } } return false; }, _updateLines: function(routes) { var addWaypoints = this.options.addWaypoints !== undefined ? this.options.addWaypoints : true; this._clearLines(); // add alternatives first so they lie below the main route this._alternatives = []; if (routes.alternatives) routes.alternatives.forEach(function(alt, i) { this._alternatives[i] = this.options.routeLine(alt, L.extend({ isAlternative: true }, this.options.altLineOptions || this.options.lineOptions)); this._alternatives[i].addTo(this._map); this._hookAltEvents(this._alternatives[i]); }, this); this._line = this.options.routeLine(routes.route, L.extend({ addWaypoints: addWaypoints, extendToWaypoints: this.options.waypointMode === 'connect' }, this.options.lineOptions)); this._line.addTo(this._map); this._hookEvents(this._line); }, _hookEvents: function(l) { l.on('linetouched', function(e) { this._plan.dragNewWaypoint(e); }, this); }, _hookAltEvents: function(l) { l.on('linetouched', function(e) { var alts = this._routes.slice(); var selected = alts.splice(e.target._route.routesIndex, 1)[0]; this.fire('routeselected', {route: selected, alternatives: alts}); }, this); }, _onWaypointsChanged: function(e) { if (this.options.autoRoute) { this.route({}); } if (!this._plan.isReady()) { this._clearLines(); this._clearAlts(); } this.fire('waypointschanged', {waypoints: e.waypoints}); }, _setupRouteDragging: function() { var timer = 0, waypoints; this._plan.on('waypointdrag', L.bind(function(e) { waypoints = e.waypoints; if (!timer) { timer = setTimeout(L.bind(function() { this.route({ waypoints: waypoints, geometryOnly: true, callback: L.bind(this._updateLineCallback, this) }); timer = undefined; }, this), this.options.routeDragInterval); } }, this)); this._plan.on('waypointdragend', function() { if (timer) { clearTimeout(timer); timer = undefined; } this.route(); }, this); }, _updateLineCallback: function(err, routes) { if (!err) { routes = routes.slice(); var selected = routes.splice(this._selectedRoute.routesIndex, 1)[0]; this._updateLines({ route: selected, alternatives: this.options.showAlternatives ? routes : [] }); } else if (err.type !== 'abort') { this._clearLines(); } }, route: function(options) { var ts = ++this._requestCount, wps; if (this._pendingRequest && this._pendingRequest.abort) { this._pendingRequest.abort(); this._pendingRequest = null; } options = options || {}; if (this._plan.isReady()) { if (this.options.useZoomParameter) { options.z = this._map && this._map.getZoom(); } wps = options && options.waypoints || this._plan.getWaypoints(); this.fire('routingstart', {waypoints: wps}); this._pendingRequest = this._router.route(wps, function(err, routes) { this._pendingRequest = null; if (options.callback) { return options.callback.call(this, err, routes); } // Prevent race among multiple requests, // by checking the current request's count // against the last request's; ignore result if // this isn't the last request. if (ts === this._requestCount) { this._clearLines(); this._clearAlts(); if (err && err.type !== 'abort') { this.fire('routingerror', {error: err}); return; } routes.forEach(function(route, i) { route.routesIndex = i; }); if (!options.geometryOnly) { this.fire('routesfound', {waypoints: wps, routes: routes}); this.setAlternatives(routes); } else { var selectedRoute = routes.splice(0,1)[0]; this._routeSelected({route: selectedRoute, alternatives: routes}); } } }, this, options); } }, _clearLines: function() { if (this._line) { this._map.removeLayer(this._line); delete this._line; } if (this._alternatives && this._alternatives.length) { for (var i in this._alternatives) { this._map.removeLayer(this._alternatives[i]); } this._alternatives = []; } } }); })();