Skip to content

Commit

Permalink
Add alternative routes support
Browse files Browse the repository at this point in the history
1 additional route created for route from A to B without waypoints
that is below 100km
- has to be enabled in the settings
- active route can be selected using route geometry or sidebar entry

- sharing an alternative route is currently not possible.
  this is a user setting that would have to be overwritten by the permalink
  which is not intended.

closes #319
  • Loading branch information
TheGreatRefrigerator committed Mar 26, 2020
1 parent 260a82f commit edafb6f
Show file tree
Hide file tree
Showing 33 changed files with 424 additions and 147 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- deviation tolerance during route import ([#325](https://github.com/GIScience/openrouteservice-app/issues/325))
- set lower value for more waypoints/accuracy
- alternative route feature ([#319](https://github.com/GIScience/openrouteservice-app/issues/319))
- activate in settings, click route or sidebar entry to switch route
- only for routes with 2 points (start & end) below 100km
- default values for `weight-factor` and `share-factor` used

### Changed
- maxZoom of all basemaps to 18 ([#311](https://github.com/GIScience/openrouteservice-app/issues/311))
Expand Down
27 changes: 17 additions & 10 deletions app/components/ors-header/ors-header.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ angular.module("orsApp.ors-header", []).component("orsHeader", {
// ctrl.showSettings = ctrl.showDev = ctrl.editEndpoints = true;

/* initialize endpoint urls from cookies */
ctrl.currentOptions.env = orsCookiesFactory.getCookies().env;
let cookies = orsCookiesFactory.getCookies();
ctrl.currentOptions.env = cookies.env;
ctrl.currentOptions.alternativeRoutes = cookies.alternativeRoutes; //TODO set from cookies/permalink
ctrl.setENV();
ctrl.envBase = ctrl.currentOptions.env.directions
.split("/")
Expand All @@ -38,6 +40,7 @@ angular.module("orsApp.ors-header", []).component("orsHeader", {
waytype: true,
surface: true
};
ctrl.saveENV = false;
ctrl.lists_extra_info = lists.extra_info;
ctrl.getActiveProfile = orsSettingsFactory.getActiveProfile;
ctrl.optionList = lists.userOptions;
Expand Down Expand Up @@ -97,12 +100,16 @@ angular.module("orsApp.ors-header", []).component("orsHeader", {
* Writes the endpoint settings to app/js/config.js to take immediate effect
*/
ctrl.setENV = () => {
ENV.directions = ctrl.currentOptions.env.directions;
ENV.isochrones = ctrl.currentOptions.env.isochrones;
ENV.geocode = ctrl.currentOptions.env.geocode;
ENV.matrix = ctrl.currentOptions.env.matrix;
ENV.pois = ctrl.currentOptions.env.pois;
ENV.fuel = ctrl.currentOptions.env.fuel;
for (let ep of [
"directions",
"isochrones",
"geocode",
"matrix",
"pois",
"fuel"
]) {
ENV[ep] = ctrl.currentOptions.env[ep];
}
};
/**
* Informs the user about changed Endpoints
Expand All @@ -120,7 +127,7 @@ fuel: ${ENV.fuel}`);
* Save Endpoints to cookies
*/
ctrl.saveEndpoints = () => {
if (ctrl.saveCookies)
if (ctrl.saveENV)
orsCookiesFactory.setCookieUserOptions(ctrl.currentOptions);
};
/**
Expand All @@ -132,7 +139,7 @@ fuel: ${ENV.fuel}`);
.split("/")
.slice(0, 3)
.join("/");
if (ctrl.saveCookies)
if (ctrl.saveENV)
orsCookiesFactory.setCookieUserOptions(ctrl.currentOptions);
};

Expand All @@ -155,7 +162,7 @@ fuel: ${ENV.fuel}`);
$translate.use(ctrl.currentOptions.language);
orsSettingsFactory.setUserOptions(ctrl.currentOptions);
// if endpoints should not be saved to cookies pass current options without them
if (!ctrl.saveCookies) {
if (!ctrl.saveENV) {
let withoutEnv = JSON.parse(JSON.stringify(ctrl.currentOptions));
delete withoutEnv.env;
orsCookiesFactory.setCookieUserOptions(withoutEnv);
Expand Down
193 changes: 175 additions & 18 deletions app/components/ors-map/ors-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ angular.module("orsApp").directive("orsMap", () => {
transparent: true,
attribution: '<a href="http://srtm.csi.cgiar.org/">SRTM</a>; ASTER GDEM is a product of <a href="http://www.meti.go.jp/english/press/data/20090626_03.html">METI</a> and <a href="https://lpdaac.usgs.gov/products/aster_policies">NASA</a>',
});*/
$scope.heightGraphData = [];
$scope.geofeatures = {
layerLocationMarker: L.featureGroup(),
layerRoutePoints: L.featureGroup(),
Expand Down Expand Up @@ -171,6 +172,56 @@ angular.module("orsApp").directive("orsMap", () => {
weight: 6
}
});
$scope.alternativeRouteLayers = {};
// separated hoverLine is only used for active route
$scope.hoverRouteLayer = L.featureGroup
.subGroup($scope.mapModel.geofeatures.layerRouteLines)
.addTo($scope.mapModel.map);
$scope.$on("activeRouteChanged", (f, idx) => {
if (orsRouteService.data) {
// set/switch layer order and style to (de)activate routes
for (let [key, value] of Object.entries(
$scope.alternativeRouteLayers
)) {
if (key === idx.toString()) {
value.bringToFront();
Object.values(value._layers)[1]
.setStyle(lists.layerStyles.route())
.closeTooltip();
} else {
Object.values(value._layers)[1]
.setStyle(lists.layerStyles.routeAlternative())
.closeTooltip();
}
}
// create hover line for active route
let currentRoute = orsRouteService.data.features[idx];
const routeHover = orsObjectsFactory.createMapAction(
41,
lists.layers[1],
currentRoute.geometry,
undefined,
lists.layerStyles.routeHovering(),
{
pointInformation: currentRoute.point_information
}
);
orsMapFactory.mapServiceSubject.onNext(routeHover);
// add heightgraph for active route
if ($scope.heightGraphData.length === 0) {
let data = orsRouteService.data;
for (let route of data.features) {
if (route.geometry[0].length === 3) {
$scope.heightGraphData.push(
orsRouteService.processHeightgraphData(route)
);
}
}
}
orsRouteService.DeColor();
orsRouteService.addHeightgraph($scope.heightGraphData[idx]);
}
});

/**
* Listens to changeOptions event from ors-header.js
Expand Down Expand Up @@ -213,13 +264,15 @@ angular.module("orsApp").directive("orsMap", () => {
) {
if (orsRouteService.data.features.length > 0) {
let data = orsRouteService.data;
let route = data.features[idx];
if (route.geometry[0][0].length === 3) {
const hgGeojson = orsRouteService.processHeightgraphData(
route
);
orsRouteService.addHeightgraph(hgGeojson);
$scope.heightGraphData = [];
for (let route of data.features) {
if (route.geometry[0].length === 3) {
$scope.heightGraphData.push(
orsRouteService.processHeightgraphData(route)
);
}
}
orsRouteService.addHeightgraph($scope.heightGraphData[idx]);
}
} else {
$scope.hg.remove();
Expand All @@ -232,7 +285,7 @@ angular.module("orsApp").directive("orsMap", () => {
}
if (setting === "distanceMarkers") {
// get Leaflet route object
let lines = $scope.mapModel.geofeatures.layerRouteLines["_layers"];
let lines = $scope.mapModel.geofeatures.layerRouteLines._layers;
let route = lines[Object.keys(lines)[0]];

if (options.distanceMarkers === true) {
Expand Down Expand Up @@ -567,7 +620,6 @@ angular.module("orsApp").directive("orsMap", () => {
//$scope.mapModel.map.on('overlayadd', emitMapChangeOverlay);
//$scope.mapModel.map.on('overlayremove', emitMapChangeOverlay);
$scope.mapModel.map.on("zoomend", e => {
let layerRouteLines = $scope.mapModel.geofeatures.layerRouteLines;
const currentZoom = $scope.mapModel.map.getZoom();
if (currentZoom >= 15) {
d3.select($scope.mapModel.map.getPanes().overlayPane).style(
Expand Down Expand Up @@ -1072,20 +1124,58 @@ angular.module("orsApp").directive("orsMap", () => {
offset: 1000,
cssClass: "ors-marker-dist",
iconSize: [18, 18]
}
},
currentRouteIndex: actionPackage.currentRouteIndex
});
polyLine.on("addDistanceMarkers", () => {
polyLine.addDistanceMarkers();
});
polyLine.on("removeDistanceMarkers", polyLine.removeDistanceMarkers);
polyLine.addTo($scope.mapModel.geofeatures[actionPackage.layerCode]);
// add route lines to dynamic subgroup which hold maximum 2 layers (white padding, colored route)
if (actionPackage.layerCode === "layerRouteLines") {
if (
!Object.keys($scope.alternativeRouteLayers).includes(
actionPackage.currentRouteIndex.toString()
)
) {
$scope.alternativeRouteLayers[
actionPackage.currentRouteIndex
] = L.featureGroup.subGroup(
$scope.mapModel.geofeatures[actionPackage.layerCode]
);
$scope.alternativeRouteLayers[
actionPackage.currentRouteIndex
].addTo($scope.mapModel.map);
}
// clear subgroup if there are the 2 layers from the last routing request
if (
$scope.alternativeRouteLayers[
actionPackage.currentRouteIndex
].getLayers().length === 2
) {
$scope.alternativeRouteLayers[
actionPackage.currentRouteIndex
].clearLayers();
}
$scope.alternativeRouteLayers[
actionPackage.currentRouteIndex
].addLayer(polyLine);
} else {
// default: add to general layer
polyLine.addTo(
$scope.mapModel.geofeatures[actionPackage.layerCode]
);
}
polyLine.setStyle(actionPackage.style);
};
/**
* adds interactive route
* @param {Object} actionPackage - The action actionPackage
*/
$scope.addPolyline = actionPackage => {
if (actionPackage.currentRouteIndex === 0) {
$scope.heightGraphData = [];
}
$scope.mapModel.map.closePopup();
const polyLine = L.polyline(actionPackage.geometry, {
index:
Expand All @@ -1095,16 +1185,82 @@ angular.module("orsApp").directive("orsMap", () => {
interactive: true,
distanceMarkers: {
lazy: true
},
currentRouteIndex: actionPackage.currentRouteIndex
});
polyLine.bubblingMouseEvents = false;
// click inactive route geomgetry to activate
polyLine.on("click", e => {
orsRouteService.setCurrentRouteIdx(
polyLine.options.currentRouteIndex
);
$rootScope.$broadcast(
"activeRouteChanged",
polyLine.options.currentRouteIndex
);
L.DomEvent.stopPropagation(e);
});
// distance and duration popup comparing to 1. route
polyLine.on("mouseover", e => {
let idx = polyLine.options.currentRouteIndex;
let {
properties: {
summary: {
duration: dur,
distance: dis,
durationDelta: durD,
distanceDelta: disD
}
}
} = orsRouteService.data.features[idx];
let popupContent = `<div>
<span>
<i class="fa fa-clock-o">
</i>
</span>
<span>
${$filter("duration")(dur)}
</span>`;
if (durD) {
popupContent += `<span>
(${durD > 0 ? "+ " : "- "}${$filter("duration")(durD)})
</span>`;
}
//bubblingMouseEvents: true
}).addTo($scope.mapModel.geofeatures[actionPackage.layerCode]);
popupContent += `</div>
<div>
<span>
<i class="fa fa-arrows-h">
</i>
</span>
<span>
${$filter("distance")(dis)}
</span>`;
if (disD) {
popupContent += `<span>
(${disD > 0 ? "+" : ""}${$filter("distance")(disD)})
</span>`;
}
popupContent += "</div>";
polyLine
.bindTooltip(popupContent, {
direction: "right",
offset: [10, 0]
})
.openTooltip(e.latlng);
});
polyLine.on("mouseout mouseup", () => {
polyLine.unbindTooltip(); // needs unbind instead of close or popup will remain opened
});
$scope.alternativeRouteLayers[
actionPackage.currentRouteIndex
].addLayer(polyLine);
polyLine.setStyle(actionPackage.style);
};
/* copied from https://github.com/makinacorpus/Leaflet.GeometryUtil/blob/master/src/leaflet.geometryutil.js
@param {L.PolyLine} polyline Polyline on which the latlng will be search
@param {L.LatLng} latlng The position to search
*/
$scope.locateOnLineCopiedFromGeometryUtil = (map, polyline, latlng) => {
$scope.locateClosestPointOnLine = (map, polyline, latlng) => {
const latlngs = polyline.getLatLngs();
if (latlng.equals(latlngs[0])) return 0.0;
if (latlng.equals(latlngs[latlngs.length - 1])) return 1.0;
Expand Down Expand Up @@ -1138,12 +1294,13 @@ angular.module("orsApp").directive("orsMap", () => {
};
};
$scope.addPolylineHover = actionPackage => {
$scope.hoverRouteLayer.clearLayers();
$scope.mapModel.map.closePopup();
$scope.polylineZone = L.polyline(actionPackage.geometry, {
distanceMarkers: {
lazy: true
}
}).addTo($scope.mapModel.geofeatures[actionPackage.layerCode]);
}).addTo($scope.hoverRouteLayer);
$scope.polylineZone.setStyle({
color: "#FFF",
weight: 100,
Expand All @@ -1160,7 +1317,7 @@ angular.module("orsApp").directive("orsMap", () => {
distanceMarkers: {
lazy: true
}
}).addTo($scope.mapModel.geofeatures[actionPackage.layerCode]);
}).addTo($scope.hoverRouteLayer);
$scope.hoverPolyLine.setStyle(actionPackage.style);
$scope.pointList = actionPackage.extraInformation.pointInformation;
$scope.hoverPolyLine.on("mousemove", e => {
Expand All @@ -1181,7 +1338,7 @@ angular.module("orsApp").directive("orsMap", () => {
$scope.hoverPoint.removeFrom(
$scope.mapModel.geofeatures.layerRouteDrag
);
let snappedPosition = $scope.locateOnLineCopiedFromGeometryUtil(
let snappedPosition = $scope.locateClosestPointOnLine(
mapModel.map,
hoverPolyLine,
latlng
Expand Down Expand Up @@ -1228,7 +1385,7 @@ angular.module("orsApp").directive("orsMap", () => {
})
.on("click", e => {
$scope.mapModel.map.closePopup();
const snappedPosition = $scope.locateOnLineCopiedFromGeometryUtil(
const snappedPosition = $scope.locateClosestPointOnLine(
mapModel.map,
hoverPolyLine,
e.latlng
Expand Down Expand Up @@ -1420,7 +1577,7 @@ angular.module("orsApp").directive("orsMap", () => {
orsSettingsFactory.subscribeToNgRoute(function onNext(route) {
//let svg = d3.select($scope.mapModel.map.getPanes().overlayPane);
$scope.clearMap(true);
$scope.routing = route == "directions";
$scope.routing = route === "directions";
//if ($scope.routing) svg.style("opacity", 1);
});
orsSettingsFactory.subscribeToWaypoints(function onNext(d) {
Expand Down

0 comments on commit edafb6f

Please sign in to comment.