@Override
    public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) {
        PointList pointList = way.getTag("point_list", null);
        if (pointList != null) {
            if (pointList.isEmpty() || !pointList.is3D()) {
                if (maxSlopeEnc != null)
                    maxSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0);
                if (averageSlopeEnc != null)
                    averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0);
                return;
            }
            // Calculate 2d distance, although pointList might be 3D.
            // This calculation is a bit expensive and edge_distance is available already, but this would be in 3D
            double distance2D = DistanceCalcEarth.calcDistance(pointList, false);
            if (distance2D < MIN_LENGTH) {
                if (averageSlopeEnc != null)
                    // default is minimum of average_slope is negative so we have to explicitly set it to 0
                    averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0);
                return;
            }

            double towerNodeSlope = calcSlope(pointList.getEle(pointList.size() - 1) - pointList.getEle(0), distance2D);
            if (Double.isNaN(towerNodeSlope))
                throw new IllegalArgumentException("average_slope was NaN for OSM way ID " + way.getId());

            if (averageSlopeEnc != null) {
                if (towerNodeSlope >= 0)
                    averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, Math.min(towerNodeSlope, averageSlopeEnc.getMaxStorableDecimal()));
                else
                    averageSlopeEnc.setDecimal(true, edgeId, edgeIntAccess, Math.min(Math.abs(towerNodeSlope), averageSlopeEnc.getMaxStorableDecimal()));
            }

            if (maxSlopeEnc != null) {
                // max_slope is more error-prone as the shorter distances increase the fluctuation
                // so apply some more filtering (here we use the average elevation delta of the previous two points)
                double maxSlope = 0, prevDist = 0, prevLat = pointList.getLat(0), prevLon = pointList.getLon(0);
                for (int i = 1; i < pointList.size(); i++) {
                    double pillarDistance2D = DistanceCalcEarth.DIST_EARTH.calcDist(prevLat, prevLon, pointList.getLat(i), pointList.getLon(i));
                    if (i > 1 && prevDist > MIN_LENGTH) {
                        double averagedPrevEle = (pointList.getEle(i - 1) + pointList.getEle(i - 2)) / 2;
                        double tmpSlope = calcSlope(pointList.getEle(i) - averagedPrevEle, pillarDistance2D + prevDist / 2);
                        maxSlope = Math.abs(tmpSlope) > Math.abs(maxSlope) ? tmpSlope : maxSlope;
                    }
                    prevDist = pillarDistance2D;
                    prevLat = pointList.getLat(i);
                    prevLon = pointList.getLon(i);
                }

                // For tunnels and bridges we cannot trust the pillar node elevation and ignore all changes.
                // Probably we should somehow recalculate even the average_slope after elevation interpolation? See EdgeElevationInterpolator
                if (way.hasTag("tunnel", "yes") || way.hasTag("bridge", "yes") || way.hasTag("highway", "steps"))
                    maxSlope = towerNodeSlope;
                else
                    maxSlope = Math.abs(towerNodeSlope) > Math.abs(maxSlope) ? towerNodeSlope : maxSlope;

                if (Double.isNaN(maxSlope))
                    throw new IllegalArgumentException("max_slope was NaN for OSM way ID " + way.getId());

                double val = Math.max(maxSlope, maxSlopeEnc.getMinStorableDecimal());
                maxSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, Math.min(maxSlopeEnc.getMaxStorableDecimal(), val));
            }
        }
    }
@Test
    public void testMaxSlopeLargerThanMaxStorableDecimal() {
        PointList pointList = new PointList(5, true);
        pointList.add(47.7281561, 11.9993135, 1163.0);
        pointList.add(47.7282782, 11.9991944, 1163.0);
        pointList.add(47.7283135, 11.9991135, 1178.0);
        ReaderWay way = new ReaderWay(1);
        way.setTag("point_list", pointList);
        ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(1);

        DecimalEncodedValue averageEnc = AverageSlope.create();
        DecimalEncodedValue maxEnc = MaxSlope.create();
        new EncodingManager.Builder().add(averageEnc).add(maxEnc).build();
        SlopeCalculator creator = new SlopeCalculator(maxEnc, averageEnc);
        int edgeId = 0;
        creator.handleWayTags(edgeId, intAccess, way, IntsRef.EMPTY);
        assertEquals(31, maxEnc.getDecimal(false, edgeId, intAccess), 1e-3);
        assertEquals(-31, averageEnc.getDecimal(true, edgeId, intAccess), 1e-3);
    }