Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

High res net elevation gain and loss calculation for Bike2FlagEncoder #92

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bay-area/config-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ graphhopper:
profiles:
- name: bike2
vehicle: bike2
weighting: fastest
weighting: hill_penalty
use_ferries: false
- name: foot
vehicle: foot
Expand Down
2 changes: 1 addition & 1 deletion bay-area/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ graphhopper:
profiles:
- name: bike2
vehicle: bike2
weighting: fastest
weighting: hill_penalty
turn_costs: true
u_turn_costs: 10
use_ferries: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ else if (encoder.supports(PriorityWeighting.class))

} else if ("short_fastest".equalsIgnoreCase(weightingStr)) {
weighting = new ShortFastestWeighting(encoder, hints, turnCostProvider);
} else if ("hill_penalty".equalsIgnoreCase(weightingStr)) {
weighting = new HillPenaltyWeighting(encoder, hints, turnCostProvider);
}

if (weighting == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,48 @@
*/
package com.graphhopper.routing.util;

import static com.graphhopper.routing.util.EncodingManager.getKey;

import com.graphhopper.reader.ReaderWay;
import com.graphhopper.routing.ev.*;
import com.graphhopper.storage.IntsRef;
import com.graphhopper.util.DistancePlaneProjection;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.FetchMode;
import com.graphhopper.util.PMap;
import com.graphhopper.util.PointList;

import static com.graphhopper.util.Helper.keepIn;

import java.util.*;

/**
* Stores two speed values into an edge to support avoiding too much incline
*
* @author Peter Karich
*/
public class Bike2WeightFlagEncoder extends BikeFlagEncoder {
protected IntEncodedValue netElevationGainEncoder;
protected IntEncodedValue avgGradeEncoder;


public Bike2WeightFlagEncoder() {
this(new PMap());
this.netElevationGainEncoder = new IntEncodedValueImpl(getKey(this, "ele_gain"), 16, 0, false ,true);
this.avgGradeEncoder = new IntEncodedValueImpl(getKey(this, "avg_grade"), 16, -200, false, true);
}

public Bike2WeightFlagEncoder(PMap properties) {
super(new PMap(properties).putObject("speed_two_directions", true).putObject("name", properties.getString("name", "bike2")));
this.netElevationGainEncoder = new IntEncodedValueImpl(getKey(this, "ele_gain"), 16, 0, false, true);
this.avgGradeEncoder = new IntEncodedValueImpl(getKey(this, "avg_grade"), 16, 0, true, false);
}

@Override
public void createEncodedValues(List<EncodedValue> registerNewEncodedValue) {
super.createEncodedValues(registerNewEncodedValue);
registerNewEncodedValue.add(this.netElevationGainEncoder);
registerNewEncodedValue.add(this.avgGradeEncoder);
}

@Override
Expand All @@ -47,33 +67,51 @@ public void applyWayTags(ReaderWay way, EdgeIteratorState edge) {
if (!pl.is3D())
throw new IllegalStateException(getName() + " requires elevation data to improve speed calculation based on it. Please enable it in config via e.g. graph.elevation.provider: srtm");

// Decrease the speed for ele increase (incline), and decrease the speed for ele decrease (decline). The speed-decrease
// has to be bigger (compared to the speed-increase) for the same elevation difference to simulate losing energy and avoiding hills.
// For the reverse speed this has to be the opposite but again keeping in mind that up+down difference.
double incEleSum = 0, incDist2DSum = 0, decEleSum = 0, decDist2DSum = 0;
double fullDist2D = edge.getDistance();

for(int i=1; i < pl.size(); i++) {
double prevLat = pl.getLat(i-1);
double prevLon = pl.getLon(i-1);
double prevEle = pl.getEle(i-1);
double nextLat = pl.getLat(i);
double nextLon = pl.getLon(i);
double nextEle = pl.getEle(i);
double eleDelta = nextEle - prevEle;
if (eleDelta > 0.1) {
incEleSum += eleDelta;
incDist2DSum += DistancePlaneProjection.DIST_PLANE.calcDist3D(prevLat, prevLon, prevEle, nextLat, nextLon, nextEle);
} else if (eleDelta < -0.1) {
decEleSum -= eleDelta;
decDist2DSum += DistancePlaneProjection.DIST_PLANE.calcDist3D(prevLat, prevLon, prevEle, nextLat, nextLon, nextEle);;
}
}


IntsRef intsRef = edge.getFlags();
netElevationGainEncoder.setInt(false, intsRef, (int) Math.round(incEleSum));
netElevationGainEncoder.setInt(true, intsRef, (int) Math.round(decEleSum));
double endEle = pl.getEle(pl.size() - 1);
double startEle = pl.getEle(0);
int grade = fullDist2D > 5.0 ? (int) Math.round((endEle - startEle) * 100/fullDist2D) : 0;
if (grade > 100) {
throw new IllegalStateException(grade + " is > 100%");
}

avgGradeEncoder.setInt(false, intsRef, grade);
if (way.hasTag("tunnel", "yes") || way.hasTag("bridge", "yes") || way.hasTag("highway", "steps"))
// do not change speed
// note: although tunnel can have a difference in elevation it is very unlikely that the elevation data is correct for a tunnel
return;

// Decrease the speed for ele increase (incline), and decrease the speed for ele decrease (decline). The speed-decrease
// has to be bigger (compared to the speed-increase) for the same elevation difference to simulate losing energy and avoiding hills.
// For the reverse speed this has to be the opposite but again keeping in mind that up+down difference.
double incEleSum = 0, incDist2DSum = 0, decEleSum = 0, decDist2DSum = 0;
// double prevLat = pl.getLat(0), prevLon = pl.getLon(0);
double prevEle = pl.getEle(0);
double fullDist2D = edge.getDistance();

// for short edges an incline makes no sense and for 0 distances could lead to NaN values for speed, see #432
if (fullDist2D < 2)
return;

double eleDelta = pl.getEle(pl.size() - 1) - prevEle;
if (eleDelta > 0.1) {
incEleSum = eleDelta;
incDist2DSum = fullDist2D;
} else if (eleDelta < -0.1) {
decEleSum = -eleDelta;
decDist2DSum = fullDist2D;
}

// Calculate slop via tan(asin(height/distance)) but for rather smallish angles where we can assume tan a=a and sin a=a.
// Then calculate a factor which decreases or increases the speed.
// Do this via a simple quadratic equation where y(0)=1 and y(0.3)=1/4 for incline and y(0.3)=2 for decline
Expand Down Expand Up @@ -103,5 +141,4 @@ public void applyWayTags(ReaderWay way, EdgeIteratorState edge) {
}
edge.setFlags(intsRef);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Licensed to GraphHopper GmbH under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper GmbH licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.graphhopper.routing.weighting;

import com.graphhopper.routing.ev.EnumEncodedValue;
import com.graphhopper.routing.ev.IntEncodedValue;
import com.graphhopper.routing.ev.RoadAccess;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.FetchMode;
import com.graphhopper.util.PMap;
import com.graphhopper.util.PointList;
import com.graphhopper.util.Parameters.Routing;

import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER;

/**
* Adds penalty to the weight of an edge based on net elevation gain and grade.
* <p>
*
* @author Peter Karich
*/
public class HillPenaltyWeighting extends FastestWeighting {
private final IntEncodedValue gradeEnc;

public HillPenaltyWeighting(FlagEncoder encoder, PMap map, TurnCostProvider turnCostProvider) {
super(encoder, map, turnCostProvider);
this.gradeEnc = encoder.getIntEncodedValue(EncodingManager.getKey(encoder, "avg_grade"));
}


public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) {
double weight = super.calcEdgeWeight(edgeState, reverse);
PointList pl = edgeState.fetchWayGeometry(FetchMode.TOWER_ONLY);
int sign = reverse ? -1 : 1;
double dist = edgeState.getDistance();
int grade = sign * (dist > 20.0 ? (int)Math.round((pl.getEle(pl.size() - 1) - pl.getEle(0)) * 100 / dist) : 0);
if (grade >= 0 && grade < 10) {
weight *= 1.3;
} else if (grade >= 10 && grade < 40) {
weight *= 2.0;
} else if (grade >=40 && grade < 80) {
weight *= 4.0;
} else if (grade >= 80) {
weight *= 8.0;
}

return weight;
}

@Override
public String getName() {
return "hill_penalty";
}
}