brouter-web/js/router/BRouter.js

231 lines
No EOL
6.7 KiB
JavaScript

L.BRouter = L.Class.extend({
statics: {
// /brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml
URL_TEMPLATE: BR.conf.host + '/brouter?lonlats={lonlats}&nogos={nogos}&profile={profile}&alternativeidx={alternativeidx}&format={format}',
URL_PROFILE_UPLOAD: BR.conf.host + '/brouter/profile',
PRECISION: 6,
NUMBER_SEPARATOR: ',',
GROUP_SEPARATOR: '|',
ABORTED_ERROR: 'aborted'
},
options: {
},
format: 'geojson',
initialize: function (options) {
L.setOptions(this, options);
this.queue = async.queue(L.bind(function (task, callback) {
this.getRoute(task.segment, callback);
}, this), 1);
// patch to call callbacks on kill for cleanup (loadingTrailer)
this.queue.kill = function () {
var aborted = this.tasks;
this.drain = null;
this.tasks = [];
aborted.forEach(function(task) {
task.callback(L.BRouter.ABORTED_ERROR);
});
};
},
setOptions: function(options) {
L.setOptions(this, options);
},
getUrlParams: function(latLngs, format) {
return {
lonlats: this._getLonLatsString(latLngs),
nogos: this._getNogosString(this.options.nogos),
profile: this.options.profile,
alternativeidx: this.options.alternative,
format: format || this.format
};
},
parseUrlParams: function(params) {
var opts = {};
if (params.lonlats) {
opts.lonlats = this._parseLonLats(params.lonlats);
}
if (params.nogos) {
opts.nogos = this._parseNogos(params.nogos);
}
if (params.alternativeidx) {
opts.alternative = params.alternativeidx;
}
if (params.profile) {
opts.profile = params.profile;
}
return opts;
},
getUrl: function(latLngs, format) {
var urlParams = this.getUrlParams(latLngs, format);
var url = L.Util.template(L.BRouter.URL_TEMPLATE, urlParams);
return url;
},
_getError: function(xhr) {
var msg = 'no response from server';
if (xhr.responseText) {
msg = xhr.responseText;
} else if (this.status || this.statusText) {
msg = this.status + ': ' + this.statusText;
}
return new Error(msg);
},
getRoute: function(latLngs, cb) {
var url = this.getUrl(latLngs),
xhr = new XMLHttpRequest();
if (!url) {
return cb(new Error('Error getting route URL'));
}
xhr.open('GET', url, true);
xhr.onload = L.bind(this._handleRouteResponse, this, xhr, cb);
xhr.onerror = L.bind(function(xhr, cb) {
cb(this._getError(xhr));
}, this, xhr, cb);
xhr.send();
},
_handleRouteResponse: function(xhr, cb) {
var layer,
geojson;
if (xhr.status === 200
&& xhr.responseText
// application error when not GeoJSON format (text/plain for errors)
&& xhr.getResponseHeader('Content-Type').split(';')[0] === 'application/vnd.geo+json') {
// leaflet.spin
//gpxLayer.fire('data:loaded');
geojson = JSON.parse(xhr.responseText);
layer = L.geoJson(geojson).getLayers()[0];
return cb(null, layer);
} else {
cb(this._getError(xhr));
}
},
getRouteSegment: function(l1, l2, cb) {
this.queue.push({ segment: [l1, l2] }, cb);
},
uploadProfile: function(profileId, profileText, cb) {
var url = L.BRouter.URL_PROFILE_UPLOAD;
xhr = new XMLHttpRequest();
// reuse existing profile file
if (profileId) {
url += '/' + profileId;
}
xhr.open('POST', url, true);
xhr.onload = L.bind(this._handleProfileResponse, this, xhr, cb);
xhr.onerror = function(evt) {
var xhr = this;
cb('Upload error: ' + xhr.statusText);
};
// send profile text only, as text/plain;charset=UTF-8
xhr.send(profileText);
},
_handleProfileResponse: function(xhr, cb) {
var response;
if (xhr.status === 200 && xhr.responseText && xhr.responseText.length > 0) {
response = JSON.parse(xhr.responseText);
cb(response.error, response.profileid);
} else {
cb('Profile error: no or empty response from server');
}
},
_getLonLatsString: function(latLngs) {
var s = '';
for (var i = 0; i < latLngs.length; i++) {
s += this._formatLatLng(latLngs[i]);
if (i < (latLngs.length - 1)) {
s += L.BRouter.GROUP_SEPARATOR;
}
}
return s;
},
_parseLonLats: function(s) {
var groups,
numbers,
lonlats = [];
if (!s) {
return lonlats;
}
groups = s.split(L.BRouter.GROUP_SEPARATOR);
for (var i = 0; i < groups.length; i++) {
// lng,lat
numbers = groups[i].split(L.BRouter.NUMBER_SEPARATOR);
lonlats.push(L.latLng(numbers[1], numbers[0]));
}
return lonlats;
},
_getNogosString: function(nogos) {
var s = '';
for (var i = 0, circle; i < nogos.length; i++) {
circle = nogos[i];
s += this._formatLatLng(circle.getLatLng());
s += L.BRouter.NUMBER_SEPARATOR;
s += Math.round(circle.getRadius());
if (i < (nogos.length - 1)) {
s += L.BRouter.GROUP_SEPARATOR;
}
}
return s;
},
_parseNogos: function(s) {
var groups,
numbers,
nogos = [];
if (!s) {
return nogos;
}
groups = s.split(L.BRouter.GROUP_SEPARATOR);
for (var i = 0; i < groups.length; i++) {
// lng,lat,radius
numbers = groups[i].split(L.BRouter.NUMBER_SEPARATOR);
// TODO refactor: pass simple obj, create circle in NogoAreas; use shapeOptions of instance
// [lat,lng],radius
nogos.push(L.circle([numbers[1], numbers[0]], numbers[2], L.Draw.Circle.prototype.options.shapeOptions));
}
return nogos;
},
// formats L.LatLng object as lng,lat string
_formatLatLng: function(latLng) {
var s = '';
s += L.Util.formatNum(latLng.lng, L.BRouter.PRECISION);
s += L.BRouter.NUMBER_SEPARATOR;
s += L.Util.formatNum(latLng.lat, L.BRouter.PRECISION);
return s;
}
});
L.bRouter = function (options) {
return new L.BRouter(options);
};