Ignore:
Timestamp:
2020-02-26T20:09:08+01:00 (4 years ago)
Author:
GerdP
Message:

see #18596 Fix relation ordering after split-way
Patch by JeroenHoek, slightly modified

  • download needed relation members if wanted
  • improve member ordering of route relations

TODO:

  • download parents if not known
  • fix also ordering of members in multipolygon relations
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/test/unit/org/openstreetmap/josm/command/SplitWayCommandTest.java

    r15362 r15943  
    33
    44import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertFalse;
    56import static org.junit.Assert.assertTrue;
    67
     8import java.io.IOException;
     9import java.io.InputStream;
    710import java.util.ArrayList;
    811import java.util.Arrays;
    912import java.util.Collections;
    1013import java.util.Iterator;
     14import java.util.Optional;
    1115
    1216import org.junit.Rule;
    1317import org.junit.Test;
     18import org.openstreetmap.josm.TestUtils;
    1419import org.openstreetmap.josm.command.SplitWayCommand.Strategy;
    1520import org.openstreetmap.josm.data.UndoRedoHandler;
     
    1823import org.openstreetmap.josm.data.osm.Node;
    1924import org.openstreetmap.josm.data.osm.OsmPrimitive;
     25import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    2026import org.openstreetmap.josm.data.osm.Relation;
    2127import org.openstreetmap.josm.data.osm.RelationMember;
    2228import org.openstreetmap.josm.data.osm.Way;
     29import org.openstreetmap.josm.io.IllegalDataException;
     30import org.openstreetmap.josm.io.OsmReader;
    2331import org.openstreetmap.josm.testutils.JOSMTestRules;
    2432
     
    3543    @Rule
    3644    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
    37     public JOSMTestRules test = new JOSMTestRules().main().projection();
     45    public JOSMTestRules test = new JOSMTestRules().main().projection().preferences();
    3846
    3947    /**
     
    113121
    114122        final Strategy strategy = wayChunks -> {
    115                 final Iterator<Way> it = wayChunks.iterator();
    116                 for (int i = 0; i < indexOfWayToKeep; i++) {
    117                     it.next();
    118                 }
    119                 return it.next();
    120             };
     123            final Iterator<Way> it = wayChunks.iterator();
     124            for (int i = 0; i < indexOfWayToKeep; i++) {
     125                it.next();
     126            }
     127            return it.next();
     128        };
    121129        final SplitWayCommand result = SplitWayCommand.splitWay(
    122                 w2, SplitWayCommand.buildSplitChunks(w2, Arrays.asList(n3, n4, n5)), new ArrayList<OsmPrimitive>(), strategy);
     130                w2, SplitWayCommand.buildSplitChunks(w2, Arrays.asList(n3, n4, n5)), new ArrayList<>(), strategy);
    123131        UndoRedoHandler.getInstance().add(result);
    124132
     
    140148    }
    141149
     150    @Test
     151    public void testOneMemberOrderedRelationShowsWarningTest() {
     152        final DataSet dataSet = new DataSet();
     153
     154        // Positive IDs to mark that these ways are incomplete (i.e., no nodes loaded).
     155        final Way w1 = new Way(1);
     156        final Way w3 = new Way(3);
     157
     158        // The way we are going to split is complete of course.
     159        final Node n1 = new Node(new LatLon(1, 0));
     160        final Node n2 = new Node(new LatLon(2, 0));
     161        final Node n3 = new Node(new LatLon(3, 0));
     162        final Way w2 = new Way();
     163
     164        final Relation route = new Relation();
     165        for (OsmPrimitive p : Arrays.asList(n1, n2, n3, w1, w2, w3, route)) {
     166            dataSet.addPrimitive(p);
     167        }
     168        w2.setNodes(Arrays.asList(n1, n2, n3));
     169
     170        route.put("type", "route");
     171        route.addMember(new RelationMember("", w1));
     172        route.addMember(new RelationMember("", w2));
     173        route.addMember(new RelationMember("", w3));
     174        dataSet.setSelected(Arrays.asList(w2, n2));
     175
     176        // This split cannot be safely performed without downloading extra relation members.
     177        // Here we ask the split method to abort if it needs more information.
     178        final Optional<SplitWayCommand> result = SplitWayCommand.splitWay(
     179                w2,
     180                SplitWayCommand.buildSplitChunks(w2, Collections.singletonList(n2)),
     181                new ArrayList<>(),
     182                Strategy.keepLongestChunk(),
     183                SplitWayCommand.WhenRelationOrderUncertain.ABORT
     184        );
     185
     186        assertFalse(result.isPresent());
     187    }
     188
     189    @Test
     190    public void testDoIncompleteMembersOrderedRelationCorrectOrderTest() {
     191        for (int i = 0; i < 2; i++) {
     192            // All these permutations should result in a split that keeps the new parts in order.
     193            doIncompleteMembersOrderedRelationCorrectOrderTest(false, false, i);
     194            doIncompleteMembersOrderedRelationCorrectOrderTest(true, false, i);
     195            doIncompleteMembersOrderedRelationCorrectOrderTest(true, true, i);
     196            doIncompleteMembersOrderedRelationCorrectOrderTest(false, true, i);
     197        }
     198    }
     199
     200    private void doIncompleteMembersOrderedRelationCorrectOrderTest(final boolean reverseWayOne,
     201                                                                    final boolean reverseWayTwo,
     202                                                                    final int indexOfWayToKeep) {
     203        final DataSet dataSet = new DataSet();
     204
     205        // Positive IDs to mark that these ways are incomplete (i.e., no nodes loaded).
     206        final Way w1 = new Way(1);
     207        final Way w4 = new Way(3);
     208
     209        // The ways we are going to split are complete of course.
     210        final Node n1 = new Node(new LatLon(1, 0));
     211        final Node n2 = new Node(new LatLon(2, 0));
     212        final Node n3 = new Node(new LatLon(3, 0));
     213        final Node n4 = new Node(new LatLon(4, 0));
     214        final Node n5 = new Node(new LatLon(5, 0));
     215        final Way w2 = new Way();
     216        final Way w3 = new Way();
     217
     218        final Relation route = new Relation();
     219        for (OsmPrimitive p : Arrays.asList(n1, n2, n3, n4, n5, w1, w2, w3, w4, route)) {
     220            dataSet.addPrimitive(p);
     221        }
     222        w2.setNodes(reverseWayOne ? Arrays.asList(n3, n2, n1) : Arrays.asList(n1, n2, n3));
     223        w3.setNodes(reverseWayTwo ? Arrays.asList(n5, n4, n3) : Arrays.asList(n3, n4, n5));
     224
     225        route.put("type", "route");
     226        route.addMember(new RelationMember("", w1));
     227        route.addMember(new RelationMember("", w2));
     228        route.addMember(new RelationMember("", w3));
     229        route.addMember(new RelationMember("", w4));
     230
     231        Way splitWay = indexOfWayToKeep == 0 ? w2 : w3;
     232        Node splitNode = indexOfWayToKeep == 0 ? n2 : n4;
     233
     234        dataSet.setSelected(Arrays.asList(splitWay, splitNode));
     235
     236        final SplitWayCommand result = SplitWayCommand.splitWay(
     237                splitWay, SplitWayCommand.buildSplitChunks(splitWay, Collections.singletonList(splitNode)), new ArrayList<>());
     238        UndoRedoHandler.getInstance().add(result);
     239
     240        assertEquals(5, route.getMembersCount());
     241        assertConnectedAtEnds(route.getMember(1).getWay(), route.getMember(2).getWay());
     242        assertConnectedAtEnds(route.getMember(2).getWay(), route.getMember(3).getWay());
     243    }
     244
    142245    static void assertFirstLastNodeIs(Way way, Node node) {
    143246        assertTrue("First/last node of " + way + " should be " + node, node.equals(way.firstNode()) || node.equals(way.lastNode()));
    144247    }
     248
     249    static void assertConnectedAtEnds(Way one, Way two) {
     250        Node first1 = one.firstNode();
     251        Node last1 = one.lastNode();
     252        Node first2 = two.firstNode();
     253        Node last2 = two.lastNode();
     254
     255        assertTrue("Ways expected to be connected at their ends.",
     256                first1 == first2 || first1 == last2 || last1 == first2 || last1 == last2);
     257    }
     258
     259    /**
     260     * Non-regression test for patch #18596 (Fix relation ordering after split-way)
     261     * @throws IOException if any I/O error occurs
     262     * @throws IllegalDataException if OSM parsing fails
     263     */
     264    @Test
     265    public void testTicket18596() throws IOException, IllegalDataException {
     266        try (InputStream is = TestUtils.getRegressionDataStream(18596, "data.osm")) {
     267            DataSet ds = OsmReader.parseDataSet(is, null);
     268
     269            Way splitWay = (Way) ds.getPrimitiveById(5, OsmPrimitiveType.WAY);
     270            Node splitNode = (Node) ds.getPrimitiveById(100002, OsmPrimitiveType.NODE);
     271
     272            final SplitWayCommand result = SplitWayCommand.splitWay(
     273                    splitWay,
     274                    SplitWayCommand.buildSplitChunks(splitWay, Collections.singletonList(splitNode)),
     275                    new ArrayList<>()
     276            );
     277
     278            UndoRedoHandler.getInstance().add(result);
     279
     280            Relation relation = (Relation) ds.getPrimitiveById(8888, OsmPrimitiveType.RELATION);
     281
     282            assertEquals(relation.getMembersCount(), 8);
     283
     284            // Before the patch introduced in #18596, these asserts would fail. The two parts of
     285            // way '5' would be in the wrong order, breaking the boundary relation in this test.
     286            assertConnectedAtEnds(relation.getMember(4).getWay(), relation.getMember(5).getWay());
     287            assertConnectedAtEnds(relation.getMember(5).getWay(), relation.getMember(6).getWay());
     288        }
     289    }
     290
     291    /**
     292     * Non-regression test for issue #17400 ( Warn when splitting way in not fully downloaded region)
     293     *
     294     * Bus route 190 gets broken when the split occurs, because the two new way parts are inserted in the relation in
     295     * the wrong order.
     296     *
     297     * @throws IOException if any I/O error occurs
     298     * @throws IllegalDataException if OSM parsing fails
     299     */
     300    @Test
     301    public void testTicket17400() throws IOException, IllegalDataException {
     302        try (InputStream is = TestUtils.getRegressionDataStream(17400, "data.osm")) {
     303            DataSet ds = OsmReader.parseDataSet(is, null);
     304
     305            Way splitWay = (Way) ds.getPrimitiveById(253731928, OsmPrimitiveType.WAY);
     306            Node splitNode = (Node) ds.getPrimitiveById(29830834, OsmPrimitiveType.NODE);
     307
     308            final Optional<SplitWayCommand> result = SplitWayCommand.splitWay(
     309                    splitWay,
     310                    SplitWayCommand.buildSplitChunks(splitWay, Collections.singletonList(splitNode)),
     311                    new ArrayList<>(),
     312                    Strategy.keepLongestChunk(),
     313                    // This split requires no additional downloads.
     314                    SplitWayCommand.WhenRelationOrderUncertain.ABORT
     315            );
     316
     317            assertTrue(result.isPresent());
     318
     319            UndoRedoHandler.getInstance().add(result.get());
     320
     321            // 190 Hormersdorf-Thalheim-Stollberg.
     322            Relation relation = (Relation) ds.getPrimitiveById(2873422, OsmPrimitiveType.RELATION);
     323
     324            // One more than the original 161.
     325            assertEquals(relation.getMembersCount(), 162);
     326
     327            // Before the patch introduced in #18596, these asserts would fail. The new parts of
     328            // the Hauptstraße would be in the wrong order, breaking the bus route relation.
     329            // These parts should be connected, in their relation sequence: 74---75---76.
     330            // Before #18596 this would have been a broken connection: 74---75-x-76.
     331            assertConnectedAtEnds(relation.getMember(74).getWay(), relation.getMember(75).getWay());
     332            assertConnectedAtEnds(relation.getMember(75).getWay(), relation.getMember(76).getWay());
     333        }
     334    }
    145335}
Note: See TracChangeset for help on using the changeset viewer.