class Bike2WeightFlagEncoder extends BikeFlagEncoder {

    @Override
    public void applyWayTags(ReaderWay way, EdgeIteratorState edge) {
        PointList pl = edge.fetchWayGeometry(3);
        if (!pl.is3D())
            throw new IllegalStateException(toString() + " requires elevation data to improve speed calculation based on it. Please enable it in config via e.g. graph.elevation.provider: srtm");

        IntsRef intsRef = edge.getFlags();
        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 loosing 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.getLatitude(0), prevLon = pl.getLongitude(0);
        double prevEle = pl.getElevation(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.getElevation(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
        double fwdIncline = incDist2DSum > 1 ? incEleSum / incDist2DSum : 0;
        double fwdDecline = decDist2DSum > 1 ? decEleSum / decDist2DSum : 0;
        double restDist2D = fullDist2D - incDist2DSum - decDist2DSum;
        double maxSpeed = getHighwaySpeed("cycleway");
        if (accessEnc.getBool(false, intsRef)) {
            // use weighted mean so that longer incline influences speed more than shorter
            double speed = getSpeed(false, intsRef);
            double fwdFaster = 1 + 2 * keepIn(fwdDecline, 0, 0.2);
            fwdFaster = fwdFaster * fwdFaster;
            double fwdSlower = 1 - 5 * keepIn(fwdIncline, 0, 0.2);
            fwdSlower = fwdSlower * fwdSlower;
            speed = speed * (fwdSlower * incDist2DSum + fwdFaster * decDist2DSum + 1 * restDist2D) / fullDist2D;
            setSpeed(false, intsRef, keepIn(speed, PUSHING_SECTION_SPEED / 2.0, maxSpeed));
        }

        if (accessEnc.getBool(true, intsRef)) {
            double speedReverse = getSpeed(true, intsRef);
            double bwFaster = 1 + 2 * keepIn(fwdIncline, 0, 0.2);
            bwFaster = bwFaster * bwFaster;
            double bwSlower = 1 - 5 * keepIn(fwdDecline, 0, 0.2);
            bwSlower = bwSlower * bwSlower;
            speedReverse = speedReverse * (bwFaster * incDist2DSum + bwSlower * decDist2DSum + 1 * restDist2D) / fullDist2D;
            setSpeed(true, intsRef, keepIn(speedReverse, PUSHING_SECTION_SPEED / 2.0, maxSpeed));
        }
        edge.setFlags(intsRef);
    }

    public  Bike2WeightFlagEncoder();
    public  Bike2WeightFlagEncoder(String propertiesStr);
    public  Bike2WeightFlagEncoder(PMap properties);
    public  Bike2WeightFlagEncoder(int speedBits, double speedFactor, int maxTurnCosts);

}