Changeset 19197 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2024-08-16T15:58:12+02:00 (3 months ago)
Author:
taylor.smock
Message:

Fix #21856: Split way: Wrong position of new member in PTv2 relation splitting a loop

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/command/SplitWayCommand.java

    r18539 r19197  
    4646import org.openstreetmap.josm.tools.CheckParameterUtil;
    4747import org.openstreetmap.josm.tools.Logging;
     48
    4849/**
    4950 * Splits a way into multiple ways (all identical except for their node list).
    50  *
     51 * <p>
    5152 * Ways are just split at the selected nodes.  The nodes remain in their
    5253 * original order.  Selected nodes at the end of a way are ignored.
     
    7778    private final List<Way> newWays;
    7879
     80    private static final String RESTRICTION = "restriction";
    7981    /** Map&lt;Restriction type, type to treat it as&gt; */
    8082    private static final Map<String, String> relationSpecialTypes = new HashMap<>();
    8183    static {
    82         relationSpecialTypes.put("restriction", "restriction");
    83         relationSpecialTypes.put("destination_sign", "restriction");
    84         relationSpecialTypes.put("connectivity", "restriction");
     84        relationSpecialTypes.put(RESTRICTION, RESTRICTION);
     85        relationSpecialTypes.put("destination_sign", RESTRICTION);
     86        relationSpecialTypes.put("connectivity", RESTRICTION);
    8587    }
    8688
     
    167169     * Splits the nodes of {@code wayToSplit} into a list of node sequences
    168170     * which are separated at the nodes in {@code splitPoints}.
    169      *
     171     * <p>
    170172     * This method displays warning messages if {@code wayToSplit} and/or
    171173     * {@code splitPoints} aren't consistent.
    172      *
     174     * <p>
    173175     * Returns null, if building the split chunks fails.
    174176     *
     
    250252     * Splits the way {@code way} into chunks of {@code wayChunks} and replies
    251253     * the result of this process in an instance of {@link SplitWayCommand}.
    252      *
     254     * <p>
    253255     * Note that changes are not applied to the data yet. You have to
    254256     * submit the command first, i.e. {@code UndoRedoHandler.getInstance().add(result)}.
     
    463465                if (rm.getMember() == way) {
    464466                    boolean insert = true;
    465                     if (relationSpecialTypes.containsKey(type) && "restriction".equals(relationSpecialTypes.get(type))) {
     467                    if (relationSpecialTypes.containsKey(type) && RESTRICTION.equals(relationSpecialTypes.get(type))) {
    466468                        RelationInformation rValue = treatAsRestriction(r, rm, c, newWays, way, changedWayNodes);
    467469                        if (rValue.warnme) warnings.add(WarningType.GENERIC);
     
    715717            Direction direction = relationAnalysis.getDirection();
    716718
    717             int position = -1;
    718719            for (int i = 0; i < relation.getMembersCount(); i++) {
    719720                // search for identical member (can't use indexOf() as it uses equals()
    720721                if (rm == relation.getMember(i)) {
    721                     position = i;
     722                    addSortedWays(i, indexOfWayToKeep, direction, newWays, relation);
    722723                    break;
    723                 }
    724             }
    725 
    726             // sanity check
    727             if (position < 0) {
    728                 throw new AssertionError("Relation member not found");
    729             }
    730 
    731             int j = position;
    732             final List<Way> waysToAddBefore = newWays.subList(0, indexOfWayToKeep);
    733             for (Way wayToAdd : waysToAddBefore) {
    734                 RelationMember em = new RelationMember(rm.getRole(), wayToAdd);
    735                 j++;
    736                 if (direction == Direction.BACKWARDS) {
    737                     relation.addMember(position + 1, em);
    738                 } else {
    739                     relation.addMember(j - 1, em);
    740                 }
    741             }
    742             final List<Way> waysToAddAfter = newWays.subList(indexOfWayToKeep, newWays.size());
    743             for (Way wayToAdd : waysToAddAfter) {
    744                 RelationMember em = new RelationMember(rm.getRole(), wayToAdd);
    745                 j++;
    746                 if (direction == Direction.BACKWARDS) {
    747                     relation.addMember(position, em);
    748                 } else {
    749                     relation.addMember(j, em);
    750724                }
    751725            }
     
    779753                    newWays
    780754            );
     755    }
     756
     757    /**
     758     * Ad ways in a sorted manner
     759     * @param position The position of the relation member we are operating on
     760     * @param indexOfWayToKeep The index of the way that is keeping history if it were in {@code newWays}
     761     * @param direction The direction of the ways
     762     * @param newWays The ways that are being added to the relation
     763     * @param relation The relation we are operating on
     764     */
     765    private static void addSortedWays(final int position, final int indexOfWayToKeep, final Direction direction,
     766                                      final List<Way> newWays, final Relation relation) {
     767        // sanity check
     768        if (position < 0) {
     769            throw new AssertionError("Relation member not found");
     770        }
     771        final RelationMember rm = relation.getMember(position);
     772        final boolean reverse = direction == Direction.BACKWARDS || needToReverseSplit(position, indexOfWayToKeep, relation, newWays);
     773        final List<Way> waysToAddBefore = newWays.subList(0, indexOfWayToKeep);
     774        int j = position;
     775        for (Way wayToAdd : waysToAddBefore) {
     776            RelationMember em = new RelationMember(rm.getRole(), wayToAdd);
     777            j++;
     778            if (reverse) {
     779                relation.addMember(position + 1, em);
     780            } else {
     781                relation.addMember(j - 1, em);
     782            }
     783        }
     784        final List<Way> waysToAddAfter = newWays.subList(indexOfWayToKeep, newWays.size());
     785        for (Way wayToAdd : waysToAddAfter) {
     786            RelationMember em = new RelationMember(rm.getRole(), wayToAdd);
     787            j++;
     788            if (reverse) {
     789                relation.addMember(position, em);
     790            } else {
     791                relation.addMember(j, em);
     792            }
     793        }
     794    }
     795
     796    /**
     797     * This is only strictly necessary when we are splitting a route where it starts to loop back.
     798     * Example: way1 -> way2 -> way2 -> way1
     799     *
     800     * @param position         The position of the original way in the relation
     801     * @param indexOfWayToKeep The index of the way to keep in relation to {@code newWays}
     802     * @param relation         The relation we are working on
     803     * @param newWays          The ways that are being added
     804     * @return {@code true} if we need to reverse the direction of the ways
     805     */
     806    private static boolean needToReverseSplit(final int position, int indexOfWayToKeep, final Relation relation, final List<Way> newWays) {
     807        final RelationMember rm = relation.getMember(position);
     808        if (!rm.isWay()) {
     809            return false;
     810        }
     811        final RelationMember previous = position <= 0 ? null : relation.getMember(position - 1);
     812        final RelationMember next = position + 1 >= relation.getMembersCount() ? null : relation.getMember(position + 1);
     813        final Way first = indexOfWayToKeep == 0 ? rm.getWay() : newWays.get(0);
     814        final Way last = indexOfWayToKeep == newWays.size() ? rm.getWay() : newWays.get(newWays.size() - 1);
     815        if (previous != null && previous.isWay() && previous.getWay().isUsable()) {
     816            final Way compare = previous.getWay();
     817            if (!(compare.isFirstLastNode(first.firstNode()) || compare.isFirstLastNode(first.lastNode()))) {
     818                return true;
     819            }
     820        }
     821        if (next != null && next.isWay() && next.getWay().isUsable()) {
     822            final Way compare = next.getWay();
     823            return !(compare.isFirstLastNode(last.firstNode()) || compare.isFirstLastNode(last.lastNode()));
     824        }
     825        return false;
    781826    }
    782827
     
    833878            switch (type) {
    834879            case "connectivity":
    835             case "restriction":
     880            case RESTRICTION:
    836881                return r.findRelationMembers("via");
    837882            case "destination_sign":
     
    849894     * Splits the way {@code way} at the nodes in {@code atNodes} and replies
    850895     * the result of this process in an instance of {@link SplitWayCommand}.
    851      *
     896     * <p>
    852897     * Note that changes are not applied to the data yet. You have to
    853898     * submit the command first, i.e. {@code UndoRedoHandler.getInstance().add(result)}.
    854      *
     899     * <p>
    855900     * Replies null if the way couldn't be split at the given nodes.
    856901     *
Note: See TracChangeset for help on using the changeset viewer.