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/src/org/openstreetmap/josm/command/SplitWayCommand.java

    r15418 r15943  
    22package org.openstreetmap.josm.command;
    33
     4import static org.openstreetmap.josm.command.SplitWayCommand.MissingMemberStrategy.GO_AHEAD_WITHOUT_DOWNLOADS;
     5import static org.openstreetmap.josm.command.SplitWayCommand.MissingMemberStrategy.GO_AHEAD_WITH_DOWNLOADS;
     6import static org.openstreetmap.josm.command.SplitWayCommand.MissingMemberStrategy.USER_ABORTED;
     7import static org.openstreetmap.josm.command.SplitWayCommand.WhenRelationOrderUncertain.ASK_USER_FOR_CONSENT_TO_DOWNLOAD;
    48import static org.openstreetmap.josm.tools.I18n.tr;
    59import static org.openstreetmap.josm.tools.I18n.trn;
     
    913import java.util.Collection;
    1014import java.util.Collections;
     15import java.util.EnumSet;
    1116import java.util.HashMap;
    1217import java.util.HashSet;
     
    2025import java.util.function.Consumer;
    2126
     27import javax.swing.JOptionPane;
     28
     29import org.openstreetmap.josm.data.osm.DataSet;
    2230import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
    2331import org.openstreetmap.josm.data.osm.Node;
     
    2735import org.openstreetmap.josm.data.osm.RelationMember;
    2836import org.openstreetmap.josm.data.osm.Way;
     37import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
     38import org.openstreetmap.josm.gui.ExceptionDialogUtil;
     39import org.openstreetmap.josm.gui.MainApplication;
     40import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
     41import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
     42import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
     43import org.openstreetmap.josm.io.OsmTransferException;
    2944import org.openstreetmap.josm.spi.preferences.Config;
    3045import org.openstreetmap.josm.tools.CheckParameterUtil;
    3146import org.openstreetmap.josm.tools.Logging;
    32 
    3347/**
    3448 * Splits a way into multiple ways (all identical except for their node list).
     
    4256
    4357    private static volatile Consumer<String> warningNotifier = Logging::warn;
     58    private static final String DOWNLOAD_MISSING_PREF_KEY = "split_way_download_missing_members";
    4459
    4560    private static final class RelationInformation {
     
    6075    private final Way originalWay;
    6176    private final List<Way> newWays;
     77
    6278    /** Map&lt;Restriction type, type to treat it as&gt; */
    6379    private static final Map<String, String> relationSpecialTypes = new HashMap<>();
     
    247263
    248264    /**
    249      * Splits the way {@code way} into chunks of {@code wayChunks} and replies
    250      * the result of this process in an instance of {@link SplitWayCommand}.
    251      * The {@link SplitWayCommand.Strategy} is used to determine which
    252      * way chunk should reuse the old id and its history.
    253      *
    254      * Note that changes are not applied to the data yet. You have to
    255      * submit the command first, i.e. {@code UndoRedoHandler.getInstance().add(result)}.
    256      *
    257      * @param way the way to split. Must not be null.
    258      * @param wayChunks the list of way chunks into the way is split. Must not be null.
    259      * @param selection The list of currently selected primitives
     265     * Splits the way {@code way} into chunks of {@code wayChunks} and replies the result of this process in an instance
     266     * of {@link SplitWayCommand}. The {@link SplitWayCommand.Strategy} is used to determine which way chunk should
     267     * reuse the old id and its history.
     268     * <p>
     269     * If the split way is part of relations, and the order of the new parts in these relations cannot be determined due
     270     * to missing relation members, the user will be asked to consent to downloading these missing members.
     271     * <p>
     272     * Note that changes are not applied to the data yet. You have to submit the command first, i.e. {@code
     273     * UndoRedoHandler.getInstance().add(result)}.
     274     *
     275     * @param way           the way to split. Must not be null.
     276     * @param wayChunks     the list of way chunks into the way is split. Must not be null.
     277     * @param selection     The list of currently selected primitives
    260278     * @param splitStrategy The strategy used to determine which way chunk should reuse the old id and its history
    261279     * @return the result from the split operation
    262280     */
    263     public static SplitWayCommand splitWay(Way way, List<List<Node>> wayChunks,
    264             Collection<? extends OsmPrimitive> selection, Strategy splitStrategy) {
     281    public static SplitWayCommand splitWay(Way way,
     282                                           List<List<Node>> wayChunks,
     283                                           Collection<? extends OsmPrimitive> selection,
     284                                           Strategy splitStrategy) {
     285
     286        // This method could be refactored to use an Optional in the future, but would need to be deprecated first
     287        // to phase out use by plugins.
     288        return splitWay(way, wayChunks, selection, splitStrategy, ASK_USER_FOR_CONSENT_TO_DOWNLOAD).orElse(null);
     289    }
     290
     291    /**
     292     * Splits the way {@code way} into chunks of {@code wayChunks} and replies the result of this process in an instance
     293     * of {@link SplitWayCommand}. The {@link SplitWayCommand.Strategy} is used to determine which way chunk should
     294     * reuse the old id and its history.
     295     * <p>
     296     * Note that changes are not applied to the data yet. You have to submit the command first, i.e. {@code
     297     * UndoRedoHandler.getInstance().add(result)}.
     298     *
     299     * @param way                        the way to split. Must not be null.
     300     * @param wayChunks                  the list of way chunks into the way is split. Must not be null.
     301     * @param selection                  The list of currently selected primitives
     302     * @param splitStrategy              The strategy used to determine which way chunk should reuse the old id and its
     303     *                                   history
     304     * @param whenRelationOrderUncertain What to do when the split way is part of relations, and the order of the new
     305     *                                   parts in the relation cannot be determined without downloading missing relation
     306     *                                   members.
     307     * @return The result from the split operation, may be an empty {@link Optional} if the operation is aborted.
     308     */
     309    public static Optional<SplitWayCommand> splitWay(Way way,
     310                                                     List<List<Node>> wayChunks,
     311                                                     Collection<? extends OsmPrimitive> selection,
     312                                                     Strategy splitStrategy,
     313                                                     WhenRelationOrderUncertain whenRelationOrderUncertain) {
    265314        // build a list of commands, and also a new selection list
    266315        final List<OsmPrimitive> newSelection = new ArrayList<>(selection.size() + wayChunks.size());
     
    273322        final Way wayToKeep = splitStrategy.determineWayToKeep(newWays);
    274323
    275         return wayToKeep != null ? doSplitWay(way, wayToKeep, newWays, newSelection) : null;
     324        return wayToKeep != null
     325                ? doSplitWay(way, wayToKeep, newWays, newSelection, whenRelationOrderUncertain)
     326                : Optional.empty();
    276327    }
    277328
     
    284335     * @param newWays potential new ways
    285336     * @param newSelection new selection list to update (optional: can be null)
     337     * @param whenRelationOrderUncertain Action to perform when the order of the new parts in relations the way is
     338     *                                   member of could not be reliably determined. See
     339     *                                   {@link WhenRelationOrderUncertain}.
    286340     * @return the {@code SplitWayCommand}
    287341     */
    288     public static SplitWayCommand doSplitWay(Way way, Way wayToKeep, List<Way> newWays, List<OsmPrimitive> newSelection) {
    289 
    290         Collection<Command> commandList = new ArrayList<>(newWays.size());
     342    public static Optional<SplitWayCommand> doSplitWay(Way way,
     343                                                       Way wayToKeep,
     344                                                       List<Way> newWays,
     345                                                       List<OsmPrimitive> newSelection,
     346                                                       WhenRelationOrderUncertain whenRelationOrderUncertain) {
     347        if (whenRelationOrderUncertain == null) whenRelationOrderUncertain = ASK_USER_FOR_CONSENT_TO_DOWNLOAD;
     348
     349        final int indexOfWayToKeep = newWays.indexOf(wayToKeep);
     350        newWays.remove(wayToKeep);
     351
     352        // Figure out the order of relation members (if any).
     353        Analysis analysis = analyseSplit(way, wayToKeep, newWays, indexOfWayToKeep);
     354
     355        // If there are relations that cannot be split properly without downloading more members,
     356        // present the user with an option to do so, or to abort the split.
     357        List<Relation> relationsNeedingMoreMembers = new ArrayList<>();
     358        Set<OsmPrimitive> incompleteMembers = new HashSet<>();
     359        for (RelationAnalysis relationAnalysis : analysis.getRelationAnalyses()) {
     360            if (!relationAnalysis.needsMoreMembers()) continue;
     361
     362            Relation relation = relationAnalysis.getRelation();
     363            int position = relationAnalysis.getPosition();
     364            int membersCount = relation.getMembersCount();
     365
     366            // Mark the neighbouring members for downloading if these are ways too.
     367            relationsNeedingMoreMembers.add(relation);
     368            RelationMember rmPrev = position == 0
     369                    ? relation.getMember(membersCount - 1)
     370                    : relation.getMember(position - 1);
     371            RelationMember rmNext = position == membersCount - 1
     372                    ? relation.getMember(0)
     373                    : relation.getMember(position + 1);
     374
     375            if (rmPrev != null && rmPrev.isWay()) {
     376                incompleteMembers.add(rmPrev.getWay());
     377            }
     378            if (rmNext != null && rmNext.isWay()) {
     379                incompleteMembers.add(rmNext.getWay());
     380            }
     381        }
     382
     383        MissingMemberStrategy missingMemberStrategy;
     384        if (relationsNeedingMoreMembers.isEmpty()) {
     385            // The split can be performed without any extra downloads.
     386            missingMemberStrategy = GO_AHEAD_WITHOUT_DOWNLOADS;
     387        } else {
     388            switch (whenRelationOrderUncertain) {
     389                case ASK_USER_FOR_CONSENT_TO_DOWNLOAD:
     390                    // If the analysis shows that for some relations missing members should be downloaded, offer the user the
     391                    // chance to consent to this.
     392
     393                    // Only ask the user about downloading missing members when they haven't consented to this before.
     394                    if (ConditionalOptionPaneUtil.getDialogReturnValue(DOWNLOAD_MISSING_PREF_KEY) == Integer.MAX_VALUE) {
     395                        // User has previously told us downloading missing relation members is fine.
     396                        missingMemberStrategy = GO_AHEAD_WITH_DOWNLOADS;
     397                    } else {
     398                        // Ask the user.
     399                        missingMemberStrategy = offerToDownloadMissingMembersIfNeeded(analysis, relationsNeedingMoreMembers);
     400                    }
     401                    break;
     402                case SPLIT_ANYWAY:
     403                    missingMemberStrategy = GO_AHEAD_WITHOUT_DOWNLOADS;
     404                    break;
     405                case DOWNLOAD_MISSING_MEMBERS:
     406                    missingMemberStrategy = GO_AHEAD_WITH_DOWNLOADS;
     407                    break;
     408                case ABORT:
     409                default:
     410                    missingMemberStrategy = USER_ABORTED;
     411                    break;
     412            }
     413        }
     414
     415        switch (missingMemberStrategy) {
     416            case GO_AHEAD_WITH_DOWNLOADS:
     417                try {
     418                    downloadMissingMembers(incompleteMembers);
     419                } catch (OsmTransferException e) {
     420                    ExceptionDialogUtil.explainException(e);
     421                    return Optional.empty();
     422                }
     423                // If missing relation members were downloaded, perform the analysis again to find the relation
     424                // member order for all relations.
     425                analysis = analyseSplit(way, wayToKeep, newWays, indexOfWayToKeep);
     426                return Optional.of(splitBasedOnAnalyses(way, newWays, newSelection, analysis, indexOfWayToKeep));
     427            case GO_AHEAD_WITHOUT_DOWNLOADS:
     428                // Proceed with the split with the information we have.
     429                // This can mean that there are no missing members we want, or that the user chooses to continue
     430                // the split without downloading them.
     431                return Optional.of(splitBasedOnAnalyses(way, newWays, newSelection, analysis, indexOfWayToKeep));
     432            case USER_ABORTED:
     433            default:
     434                return Optional.empty();
     435        }
     436    }
     437
     438    static Analysis analyseSplit(Way way,
     439                                 Way wayToKeep,
     440                                 List<Way> newWays,
     441                                 int indexOfWayToKeep) {
     442        Collection<Command> commandList = new ArrayList<>();
    291443        Collection<String> nowarnroles = Config.getPref().getList("way.split.roles.nowarn",
    292444                Arrays.asList("outer", "inner", "forward", "backward", "north", "south", "east", "west"));
     
    296448        changedWay.setNodes(wayToKeep.getNodes());
    297449        commandList.add(new ChangeCommand(way, changedWay));
    298         if (/*!isMapModeDraw &&*/ newSelection != null && !newSelection.contains(way)) {
    299             newSelection.add(way);
    300         }
    301         final int indexOfWayToKeep = newWays.indexOf(wayToKeep);
    302         newWays.remove(wayToKeep);
    303 
    304         if (/*!isMapModeDraw &&*/ newSelection != null) {
    305             newSelection.addAll(newWays);
    306         }
     450
    307451        for (Way wayToAdd : newWays) {
    308452            commandList.add(new AddCommand(way.getDataSet(), wayToAdd));
    309453        }
    310454
    311         boolean warnmerole = false;
    312         boolean warnme = false;
    313         // now copy all relations to new way also
     455        List<RelationAnalysis> relationAnalyses = new ArrayList<>();
     456        EnumSet<WarningType> warnings = EnumSet.noneOf(WarningType.class);
     457        int numberOfRelations = 0;
    314458
    315459        for (Relation r : OsmPrimitive.getParentRelations(Collections.singleton(way))) {
     
    317461                continue;
    318462            }
     463
     464            numberOfRelations++;
     465
    319466            Relation c = null;
    320467            String type = Optional.ofNullable(r.get("type")).orElse("");
     468            // Known types of ordered relations.
     469            boolean isOrderedRelation = "route".equals(type) || "multipolygon".equals(type) || "boundary".equals(type);
    321470
    322471            int ic = 0;
    323472            int ir = 0;
    324473            List<RelationMember> relationMembers = r.getMembers();
    325             for (RelationMember rm: relationMembers) {
     474            for (RelationMember rm : relationMembers) {
    326475                if (rm.isWay() && rm.getMember() == way) {
    327476                    boolean insert = true;
    328477                    if (relationSpecialTypes.containsKey(type) && "restriction".equals(relationSpecialTypes.get(type))) {
    329478                        RelationInformation rValue = treatAsRestriction(r, rm, c, newWays, way, changedWay);
    330                         warnme = rValue.warnme;
     479                        if (rValue.warnme) warnings.add(WarningType.GENERIC);
    331480                        insert = rValue.insert;
    332481                        c = rValue.relation;
    333                     } else if (!("route".equals(type)) && !("multipolygon".equals(type))) {
    334                         warnme = true;
     482                    } else if (!isOrderedRelation) {
     483                        // Warn the user when relations that are not a route or multipolygon are modified as a result
     484                        // of splitting up the way, because we can't tell if this might break anything.
     485                        warnings.add(WarningType.GENERIC);
    335486                    }
    336487                    if (c == null) {
     
    340491                    if (insert) {
    341492                        if (rm.hasRole() && !nowarnroles.contains(rm.getRole())) {
    342                             warnmerole = true;
     493                            warnings.add(WarningType.ROLE);
    343494                        }
    344495
    345                         Boolean backwards = null;
    346                         int k = 1;
    347                         while (ir - k >= 0 || ir + k < relationMembers.size()) {
    348                             if ((ir - k >= 0) && relationMembers.get(ir - k).isWay()) {
    349                                 Way w = relationMembers.get(ir - k).getWay();
    350                                 if ((w.lastNode() == way.firstNode()) || w.firstNode() == way.firstNode()) {
    351                                     backwards = Boolean.FALSE;
    352                                 } else if ((w.firstNode() == way.lastNode()) || w.lastNode() == way.lastNode()) {
    353                                     backwards = Boolean.TRUE;
     496                        // Attempt to determine the direction the ways in the relation are ordered.
     497                        Direction direction = Direction.UNKNOWN;
     498                        if (isOrderedRelation) {
     499                            if (way.lastNode() == way.firstNode()) {
     500                                // Self-closing way.
     501                                direction = Direction.IRRELEVANT;
     502                            } else {
     503                                // For ordered relations, looking beyond the nearest neighbour members is not required,
     504                                // and can even cause the wrong direction to be guessed (with closed loops).
     505                                if (ir - 1 >= 0 && relationMembers.get(ir - 1).isWay()) {
     506                                    Way w = relationMembers.get(ir - 1).getWay();
     507                                    if (w.lastNode() == way.firstNode() || w.firstNode() == way.firstNode()) {
     508                                        direction = Direction.FORWARDS;
     509                                    } else if (w.firstNode() == way.lastNode() || w.lastNode() == way.lastNode()) {
     510                                        direction = Direction.BACKWARDS;
     511                                    }
    354512                                }
    355                                 break;
     513                                if (ir + 1 < relationMembers.size() && relationMembers.get(ir + 1).isWay()) {
     514                                    Way w = relationMembers.get(ir + 1).getWay();
     515                                    if (w.lastNode() == way.firstNode() || w.firstNode() == way.firstNode()) {
     516                                        direction = Direction.BACKWARDS;
     517                                    } else if (w.firstNode() == way.lastNode() || w.lastNode() == way.lastNode()) {
     518                                        direction = Direction.FORWARDS;
     519                                    }
     520                                }
    356521                            }
    357                             if ((ir + k < relationMembers.size()) && relationMembers.get(ir + k).isWay()) {
    358                                 Way w = relationMembers.get(ir + k).getWay();
    359                                 if ((w.lastNode() == way.firstNode()) || w.firstNode() == way.firstNode()) {
    360                                     backwards = Boolean.TRUE;
    361                                 } else if ((w.firstNode() == way.lastNode()) || w.lastNode() == way.lastNode()) {
    362                                     backwards = Boolean.FALSE;
     522                        } else {
     523                            int k = 1;
     524                            while (ir - k >= 0 || ir + k < relationMembers.size()) {
     525                                if (ir - k >= 0 && relationMembers.get(ir - k).isWay()) {
     526                                    Way w = relationMembers.get(ir - k).getWay();
     527                                    if (w.lastNode() == way.firstNode() || w.firstNode() == way.firstNode()) {
     528                                        direction = Direction.FORWARDS;
     529                                    } else if (w.firstNode() == way.lastNode() || w.lastNode() == way.lastNode()) {
     530                                        direction = Direction.BACKWARDS;
     531                                    }
     532                                    break;
    363533                                }
    364                                 break;
    365                             }
    366                             k++;
    367                         }
    368 
    369                         int j = ic;
    370                         final List<Way> waysToAddBefore = newWays.subList(0, indexOfWayToKeep);
    371                         for (Way wayToAdd : waysToAddBefore) {
    372                             RelationMember em = new RelationMember(rm.getRole(), wayToAdd);
    373                             j++;
    374                             if (Boolean.TRUE.equals(backwards)) {
    375                                 c.addMember(ic + 1, em);
    376                             } else {
    377                                 c.addMember(j - 1, em);
     534                                if (ir + k < relationMembers.size() && relationMembers.get(ir + k).isWay()) {
     535                                    Way w = relationMembers.get(ir + k).getWay();
     536                                    if (w.lastNode() == way.firstNode() || w.firstNode() == way.firstNode()) {
     537                                        direction = Direction.BACKWARDS;
     538                                    } else if (w.firstNode() == way.lastNode() || w.lastNode() == way.lastNode()) {
     539                                        direction = Direction.FORWARDS;
     540                                    }
     541                                    break;
     542                                }
     543                                k++;
    378544                            }
    379545                        }
    380                         final List<Way> waysToAddAfter = newWays.subList(indexOfWayToKeep, newWays.size());
    381                         for (Way wayToAdd : waysToAddAfter) {
    382                             RelationMember em = new RelationMember(rm.getRole(), wayToAdd);
    383                             j++;
    384                             if (Boolean.TRUE.equals(backwards)) {
    385                                 c.addMember(ic, em);
    386                             } else {
    387                                 c.addMember(j, em);
    388                             }
    389                         }
    390                         ic = j;
     546
     547                        // We don't have enough information to determine the order of the new ways in this relation.
     548                        // This may cause relations to be saved with the two new way sections in reverse order.
     549                        //
     550                        // This often breaks routes.
     551                        //
     552                        // The user should be asked to download more members, or to abort the split operation.
     553                        boolean needsMoreMembers = isOrderedRelation
     554                                && direction == Direction.UNKNOWN
     555                                && relationMembers.size() > 1
     556                                && r.hasIncompleteMembers();
     557
     558                        relationAnalyses.add(new RelationAnalysis(c, rm, direction, needsMoreMembers, ic));
     559                        ic += indexOfWayToKeep;
    391560                    }
    392561                }
     
    399568            }
    400569        }
    401         if (warnmerole) {
     570
     571        return new Analysis(relationAnalyses, commandList, warnings, numberOfRelations);
     572    }
     573
     574    static class Analysis {
     575        List<RelationAnalysis> relationAnalyses;
     576        Collection<Command> commands;
     577        EnumSet<WarningType> warningTypes;
     578        private final int numberOfRelations;
     579
     580        Analysis(List<RelationAnalysis> relationAnalyses,
     581                 Collection<Command> commandList,
     582                 EnumSet<WarningType> warnings,
     583                 int numberOfRelations) {
     584            this.relationAnalyses = relationAnalyses;
     585            commands = commandList;
     586            warningTypes = warnings;
     587            this.numberOfRelations = numberOfRelations;
     588        }
     589
     590        List<RelationAnalysis> getRelationAnalyses() {
     591            return relationAnalyses;
     592        }
     593
     594        Collection<Command> getCommands() {
     595            return commands;
     596        }
     597
     598        EnumSet<WarningType> getWarningTypes() {
     599            return warningTypes;
     600        }
     601
     602        public int getNumberOfRelations() {
     603            return numberOfRelations;
     604        }
     605    }
     606
     607    static MissingMemberStrategy offerToDownloadMissingMembersIfNeeded(Analysis analysis,
     608                                                                       List<Relation> relationsNeedingMoreMembers) {
     609        String[] options = {
     610                tr("Yes, download the missing members"),
     611                tr("No, abort the split operation"),
     612                tr("No, perform the split without downloading")
     613        };
     614
     615        String msgMemberOfRelations = trn(
     616                "This way is part of a relation.",
     617                "This way is part of {0} relations.",
     618                analysis.getNumberOfRelations(),
     619                analysis.getNumberOfRelations()
     620        );
     621
     622        String msgReferToRelations;
     623        if (analysis.getNumberOfRelations() == 1) {
     624            msgReferToRelations = tr("this relation");
     625        } else if (analysis.getNumberOfRelations() == relationsNeedingMoreMembers.size()) {
     626            msgReferToRelations = tr("these relations");
     627        } else {
     628            msgReferToRelations = trn(
     629                    "one relation",
     630                    "{0} relations",
     631                    relationsNeedingMoreMembers.size(),
     632                    relationsNeedingMoreMembers.size()
     633            );
     634        }
     635
     636        String msgRelationsMissingData = tr(
     637                "For {0} the correct order of the new way parts could not be determined. " +
     638                        "To fix this, some missing relation members should be downloaded first.",
     639                msgReferToRelations
     640        );
     641
     642        JMultilineLabel msg = new JMultilineLabel(msgMemberOfRelations + " " + msgRelationsMissingData);
     643        msg.setMaxWidth(600);
     644
     645        int ret = JOptionPane.showOptionDialog(
     646                MainApplication.getMainFrame(),
     647                msg,
     648                tr("Download missing relation members?"),
     649                JOptionPane.OK_CANCEL_OPTION,
     650                JOptionPane.QUESTION_MESSAGE,
     651                null,
     652                options,
     653                options[0]
     654        );
     655
     656        switch (ret) {
     657            case JOptionPane.OK_OPTION:
     658                // Ask the user if they want to do this automatically from now on. We only ask this for the download
     659                // action, because automatically cancelling is confusing (the user can't tell why this happened), and
     660                // automatically performing the split without downloading missing members despite needing them is
     661                // likely to break a lot of routes. The user also can't tell the difference between a split that needs
     662                // no downloads at all, and this special case where downloading missing relation members will prevent
     663                // broken relations.
     664                ConditionalOptionPaneUtil.showMessageDialog(
     665                        DOWNLOAD_MISSING_PREF_KEY,
     666                        MainApplication.getMainFrame(),
     667                        tr("Missing relation members will be downloaded. Should this be done automatically from now on?"),
     668                        tr("Downloading missing relation members"),
     669                        JOptionPane.INFORMATION_MESSAGE
     670                );
     671                return GO_AHEAD_WITH_DOWNLOADS;
     672            case JOptionPane.CANCEL_OPTION:
     673                return GO_AHEAD_WITHOUT_DOWNLOADS;
     674            default:
     675                return USER_ABORTED;
     676        }
     677    }
     678
     679    static void downloadMissingMembers(Set<OsmPrimitive> incompleteMembers) throws OsmTransferException {
     680        // Download the missing members.
     681        MultiFetchServerObjectReader reader = MultiFetchServerObjectReader.create();
     682        reader.append(incompleteMembers);
     683
     684        DataSet ds = reader.parseOsm(NullProgressMonitor.INSTANCE);
     685        MainApplication.getLayerManager().getEditLayer().mergeFrom(ds);
     686    }
     687
     688    static SplitWayCommand splitBasedOnAnalyses(Way way,
     689                                                List<Way> newWays,
     690                                                List<OsmPrimitive> newSelection,
     691                                                Analysis analysis,
     692                                                int indexOfWayToKeep) {
     693        if (newSelection != null && !newSelection.contains(way)) {
     694            newSelection.add(way);
     695        }
     696
     697        if (newSelection != null) {
     698            newSelection.addAll(newWays);
     699        }
     700
     701        // Perform the split.
     702        for (RelationAnalysis relationAnalysis : analysis.getRelationAnalyses()) {
     703            RelationMember rm = relationAnalysis.getRelationMember();
     704            Relation relation = relationAnalysis.getRelation();
     705            Direction direction = relationAnalysis.getDirection();
     706            int position = relationAnalysis.getPosition();
     707
     708            int j = position;
     709            final List<Way> waysToAddBefore = newWays.subList(0, indexOfWayToKeep);
     710            for (Way wayToAdd : waysToAddBefore) {
     711                RelationMember em = new RelationMember(rm.getRole(), wayToAdd);
     712                j++;
     713                if (direction == Direction.BACKWARDS) {
     714                    relation.addMember(position + 1, em);
     715                } else {
     716                    relation.addMember(j - 1, em);
     717                }
     718            }
     719            final List<Way> waysToAddAfter = newWays.subList(indexOfWayToKeep, newWays.size());
     720            for (Way wayToAdd : waysToAddAfter) {
     721                RelationMember em = new RelationMember(rm.getRole(), wayToAdd);
     722                j++;
     723                if (direction == Direction.BACKWARDS) {
     724                    relation.addMember(position, em);
     725                } else {
     726                    relation.addMember(j, em);
     727                }
     728            }
     729        }
     730
     731        EnumSet<WarningType> warnings = analysis.getWarningTypes();
     732
     733        if (warnings.contains(WarningType.ROLE)) {
    402734            warningNotifier.accept(
    403735                    tr("A role based relation membership was copied to all new ways.<br>You should verify this and correct it when necessary."));
    404         } else if (warnme) {
     736        } else if (warnings.contains(WarningType.GENERIC)) {
    405737            warningNotifier.accept(
    406738                    tr("A relation membership was copied to all new ways.<br>You should verify this and correct it when necessary."));
     
    411743                    trn("Split way {0} into {1} part", "Split way {0} into {1} parts", newWays.size() + 1,
    412744                            way.getDisplayName(DefaultNameFormatter.getInstance()), newWays.size() + 1),
    413                     commandList,
     745                    analysis.getCommands(),
    414746                    newSelection,
    415747                    way,
     
    524856        return relationSpecialTypes;
    525857    }
     858
     859    /**
     860     * What to do when the split way is part of relations, and the order of the new parts in the relation cannot be
     861     * determined without downloading missing relation members.
     862     */
     863    public enum WhenRelationOrderUncertain {
     864        /**
     865         * Ask the user to consent to downloading the missing members. The user can abort the operation or choose to
     866         * proceed without downloading anything.
     867         */
     868        ASK_USER_FOR_CONSENT_TO_DOWNLOAD,
     869        /**
     870         * If there are relation members missing, and these are needed to determine the order of the new parts in
     871         * that relation, abort the split operation.
     872         */
     873        ABORT,
     874        /**
     875         * If there are relation members missing, and these are needed to determine the order of the new parts in
     876         * that relation, continue with the split operation anyway, without downloading anything. Caution: use this
     877         * option with care.
     878         */
     879        SPLIT_ANYWAY,
     880        /**
     881         * If there are relation members missing, and these are needed to determine the order of the new parts in
     882         * that relation, automatically download these without prompting the user.
     883         */
     884        DOWNLOAD_MISSING_MEMBERS
     885    }
     886
     887    static class RelationAnalysis {
     888        private final Relation relation;
     889        private final RelationMember relationMember;
     890        private final Direction direction;
     891        private final boolean needsMoreMembers;
     892        private final int position;
     893
     894        RelationAnalysis(Relation relation,
     895                                RelationMember relationMember,
     896                                Direction direction,
     897                                boolean needsMoreMembers,
     898                                int position) {
     899            this.relation = relation;
     900            this.relationMember = relationMember;
     901            this.direction = direction;
     902            this.needsMoreMembers = needsMoreMembers;
     903            this.position = position;
     904        }
     905
     906        RelationMember getRelationMember() {
     907            return relationMember;
     908        }
     909
     910        Direction getDirection() {
     911            return direction;
     912        }
     913
     914        boolean needsMoreMembers() {
     915            return needsMoreMembers;
     916        }
     917
     918        Relation getRelation() {
     919            return relation;
     920        }
     921
     922        int getPosition() {
     923            return position;
     924        }
     925    }
     926
     927    enum Direction {
     928        FORWARDS,
     929        BACKWARDS,
     930        UNKNOWN,
     931        IRRELEVANT
     932    }
     933
     934    enum WarningType {
     935        GENERIC,
     936        ROLE
     937    }
     938
     939    enum MissingMemberStrategy {
     940        GO_AHEAD_WITH_DOWNLOADS,
     941        GO_AHEAD_WITHOUT_DOWNLOADS,
     942        USER_ABORTED
     943    }
    526944}
Note: See TracChangeset for help on using the changeset viewer.