public static PointList simplify(ResponsePath responsePath, RamerDouglasPeucker ramerDouglasPeucker, boolean enableInstructions) {
        final PointList pointList = responsePath.getPoints();
        List<Partition> partitions = new ArrayList<>();

        // make sure all waypoints are retained in the simplified point list
        // we copy the waypoint indices into temporary intervals where they will be mutated by the simplification,
        // afterwards we need to update the way point indices accordingly.
        List<Interval> intervals = new ArrayList<>();
        for (int i = 0; i < responsePath.getWaypointIndices().size() - 1; i++)
            intervals.add(new Interval(responsePath.getWaypointIndices().get(i), responsePath.getWaypointIndices().get(i + 1)));
        partitions.add(new Partition() {
            @Override
            public int size() {
                return intervals.size();
            }

            @Override
            public int getIntervalLength(int index) {
                return intervals.get(index).end - intervals.get(index).start;
            }

            @Override
            public void setInterval(int index, int start, int end) {
                intervals.get(index).start = start;
                intervals.get(index).end = end;
            }
        });

        // todo: maybe this code can be simplified if path details and instructions would be merged, see #1121
        if (enableInstructions) {
            final InstructionList instructions = responsePath.getInstructions();
            partitions.add(new Partition() {
                @Override
                public int size() {
                    return instructions.size();
                }

                @Override
                public int getIntervalLength(int index) {
                    return instructions.get(index).getLength();
                }

                @Override
                public void setInterval(int index, int start, int end) {
                    Instruction instruction = instructions.get(index);
                    if (instruction instanceof ViaInstruction || instruction instanceof FinishInstruction) {
                        if (start != end) {
                            throw new IllegalStateException("via- and finish-instructions are expected to have zero length");
                        }
                        // have to make sure that via instructions and finish instructions contain a single point
                        // even though their 'instruction length' is zero.
                        end++;
                    }
                    instruction.setPoints(pointList.shallowCopy(start, end, false));
                }
            });
        }

        for (final Map.Entry<String, List<PathDetail>> entry : responsePath.getPathDetails().entrySet()) {
            // If the pointList only contains one point, PathDetails have to be empty because 1 point => 0 edges
            final List<PathDetail> detail = entry.getValue();
            if (detail.isEmpty() && pointList.size() > 1)
                throw new IllegalStateException("PathDetails " + entry.getKey() + " must not be empty");

            partitions.add(new Partition() {
                @Override
                public int size() {
                    return detail.size();
                }

                @Override
                public int getIntervalLength(int index) {
                    return detail.get(index).getLength();
                }

                @Override
                public void setInterval(int index, int start, int end) {
                    PathDetail pd = detail.get(index);
                    pd.setFirst(start);
                    pd.setLast(end);
                }
            });
        }

        simplify(responsePath.getPoints(), partitions, ramerDouglasPeucker);

        List<Integer> simplifiedWaypointIndices = new ArrayList<>();
        simplifiedWaypointIndices.add(intervals.get(0).start);
        for (Interval interval : intervals)
            simplifiedWaypointIndices.add(interval.end);
        responsePath.setWaypointIndices(simplifiedWaypointIndices);

        assertConsistencyOfPathDetails(responsePath.getPathDetails());
        if (enableInstructions)
            assertConsistencyOfInstructions(responsePath.getInstructions(), responsePath.getPoints().size());
        return pointList;
    }
@Test
    public void testMultiplePartitions() {
        // points are chosen such that DP will remove those marked with an x
        // got this data from running a request like this:
        // http://localhost:8989/maps/?point=48.891273%2C9.325418&point=48.891005%2C9.322865&point=48.889877%2C9.32102&point=48.88975%2C9.31999&profile=car&weighting=fastest&elevation=true&debug=true&details=max_speed&details=street_name&
        PointList points = new PointList(20, true);
        points.add(48.89089, 9.32538, 270.0); // 0    -> 0
        points.add(48.89090, 9.32527, 269.0); // 1 x
        points.add(48.89091, 9.32439, 267.0); // 2 x
        points.add(48.89091, 9.32403, 267.0); // 3    -> 1
        points.add(48.89090, 9.32324, 267.0); // 4    -> 2
        points.add(48.89088, 9.32296, 267.0); // 5 x
        points.add(48.89088, 9.32288, 266.0); // 6    -> 3
        points.add(48.89081, 9.32208, 265.0); // 7    -> 4
        points.add(48.89056, 9.32217, 265.0); // 8    -> 5
        points.add(48.89047, 9.32218, 265.0); // 9    -> 6
        points.add(48.89037, 9.32215, 265.0); // 10   -> 7
        points.add(48.89026, 9.32157, 265.0); // 11   -> 8
        points.add(48.89023, 9.32101, 264.0); // 12   -> 9
        points.add(48.89027, 9.32038, 261.0); // 13 x
        points.add(48.89030, 9.32006, 261.0); // 14   -> 10
        points.add(48.88989, 9.31965, 261.0); // 15   -> 11

        PointList origPoints = points.clone(false);
        // from instructions
        TestPartition partition1 = TestPartition.start()
                .add(0, 6)
                .add(6, 6) // via
                .add(6, 7)
                .add(7, 10)
                .add(10, 12)
                .add(12, 12) // via
                .add(12, 14)
                .add(14, 15)
                .add(15, 15); // end

        // from max_speed detail
        TestPartition partition2 = TestPartition.start()
                .add(0, 3)
                .add(3, 7)
                .add(7, 15);

        // from street_name detail
        TestPartition partition3 = TestPartition.start()
                .add(0, 7)
                .add(7, 14)
                .add(14, 15);

        List<PathSimplification.Partition> partitions = new ArrayList<>();
        partitions.add(partition1);
        partitions.add(partition2);
        partitions.add(partition3);
        PathSimplification.simplify(points, partitions, new RamerDouglasPeucker());

        // check points were modified correctly
        assertEquals(12, points.size());
        origPoints.set(1, Double.NaN, Double.NaN, Double.NaN);
        origPoints.set(2, Double.NaN, Double.NaN, Double.NaN);
        origPoints.set(5, Double.NaN, Double.NaN, Double.NaN);
        origPoints.set(13, Double.NaN, Double.NaN, Double.NaN);
        RamerDouglasPeucker.removeNaN(origPoints);
        assertEquals(origPoints, points);

        // check partitions were modified correctly
        TestPartition expected1 = TestPartition.start()
                .add(0, 3)
                .add(3, 3) // via
                .add(3, 4)
                .add(4, 7)
                .add(7, 9)
                .add(9, 9) // via
                .add(9, 10)
                .add(10, 11)
                .add(11, 11); // end

        TestPartition expected2 = TestPartition.start()
                .add(0, 1)
                .add(1, 4)
                .add(4, 11);

        TestPartition expected3 = TestPartition.start()
                .add(0, 4)
                .add(4, 10)
                .add(10, 11);

        assertEquals(expected1.intervals, partition1.intervals);
        assertEquals(expected2.intervals, partition2.intervals);
        assertEquals(expected3.intervals, partition3.intervals);
    }