Changeset 18615 in josm


Ignore:
Timestamp:
2022-12-13T22:24:53+01:00 (2 years ago)
Author:
taylor.smock
Message:

Fix #22504: Circularize multiple selected ways (patch by qeef, modified)

Location:
trunk
Files:
2 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/AlignInCircleAction.java

    r17416 r18615  
    150150     * Case 3: Only nodes are selected
    151151     * --> Align these nodes, all are fix
     152     * <p>
     153     * Case 4: Circularize selected ways
     154     * --&gt; Circularize each way of the selection.
    152155     * @param ds data set in which the command operates
    153156     * @return the resulting command to execute to perform action, or null if nothing was changed
     
    227230            fixNodes.addAll(onWay);
    228231            nodes = collectNodesAnticlockwise(ways);
     232        } else if (!ways.isEmpty() && selectedNodes.isEmpty()) {
     233            List<Command> rcmds = new LinkedList<>();
     234            for (Way w : ways) {
     235                if (!w.isDeleted() && w.isArea()) {
     236                    List<Node> wnodes = w.getNodes();
     237                    wnodes.remove(wnodes.size() - 1);
     238                    if (validateGeometry(wnodes)) {
     239                        center = Geometry.getCenter(wnodes);
     240                    }
     241                    if (center == null) {
     242                        continue;
     243                    }
     244                    boolean skipThisWay = false;
     245                    radius = 0;
     246                    for (Node n : wnodes) {
     247                        if (!n.isLatLonKnown()) {
     248                            skipThisWay = true;
     249                            break;
     250                        } else {
     251                            radius += center.distance(n.getEastNorth());
     252                        }
     253                    }
     254                    if (skipThisWay) {
     255                        continue;
     256                    } else {
     257                        radius /= wnodes.size();
     258                    }
     259                    Command c = moveNodesCommand(wnodes, Collections.emptySet(), center, radius);
     260                    if (c != null) {
     261                        rcmds.add(c);
     262                    }
     263                }
     264            }
     265            if (rcmds.isEmpty()) {
     266                throw new InvalidSelection();
     267            }
     268            return new SequenceCommand(tr("Align each Way in Circle"), rcmds);
    229269        } else {
    230270            throw new InvalidSelection();
     
    259299            radius = radius / nodes.size();
    260300        }
    261 
     301        return moveNodesCommand(nodes, fixNodes, center, radius);
     302    }
     303
     304    /**
     305     * Move each node of the list of nodes to be arranged in a circle.
     306     * @param nodes The list of nodes to be moved.
     307     * @param fixNodes The list of nodes that must not be moved.
     308     * @param center A center of the circle formed by the nodes.
     309     * @param radius A radius of the circle formed by the nodes.
     310     * @return the command that arranges the nodes in a circle.
     311     * @since 18615
     312     */
     313    public static Command moveNodesCommand(
     314            List<Node> nodes,
     315            Set<Node> fixNodes,
     316            EastNorth center,
     317            double radius) {
    262318        List<Command> cmds = new LinkedList<>();
    263319
  • trunk/test/unit/org/openstreetmap/josm/actions/AlignInCircleActionTest.java

    r17394 r18615  
    22package org.openstreetmap.josm.actions;
    33
     4import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
    45import static org.junit.jupiter.api.Assertions.assertEquals;
    56import static org.junit.jupiter.api.Assertions.assertFalse;
     
    78import static org.junit.jupiter.api.Assertions.assertNull;
    89import static org.junit.jupiter.api.Assertions.assertTrue;
    9 
     10import static org.junit.jupiter.api.Assertions.fail;
     11
     12import java.io.IOException;
     13import java.io.InputStream;
    1014import java.nio.file.Files;
    1115import java.nio.file.Paths;
     
    1822import org.openstreetmap.josm.actions.AlignInCircleAction.InvalidSelection;
    1923import org.openstreetmap.josm.command.Command;
     24import org.openstreetmap.josm.data.coor.ILatLon;
    2025import org.openstreetmap.josm.data.osm.DataSet;
    2126import org.openstreetmap.josm.data.osm.Node;
    2227import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2328import org.openstreetmap.josm.data.osm.Way;
     29import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
     30import org.openstreetmap.josm.io.IllegalDataException;
    2431import org.openstreetmap.josm.io.OsmReader;
    2532import org.openstreetmap.josm.testutils.JOSMTestRules;
     
    182189        }
    183190    }
     191
     192    /**
     193     * Test case: Circularize a batch of (two) buildings.
     194     * @throws IOException if the test file could not be read
     195     * @throws IllegalDataException if the test file has been corrupted
     196     */
     197    @Test
     198    void testMultipleWaysSelected() throws IOException, IllegalDataException {
     199        final DataSet before;
     200        try (InputStream fis = Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleBuildingsBefore.osm"))) {
     201            before = OsmReader.parseDataSet(fis, NullProgressMonitor.INSTANCE);
     202        }
     203
     204        Way firstBefore = null;
     205        Way secondBefore = null;
     206
     207        for (Way w : before.getWays()) {
     208            if ("first".equals(w.get("test"))) {
     209                firstBefore = w;
     210            } else if ("second".equals(w.get("test"))) {
     211                secondBefore = w;
     212            } else {
     213                fail("There should only be \"first\" or \"second\" values in the key \"test\"");
     214            }
     215        }
     216
     217        assertNotNull(firstBefore);
     218        assertNotNull(secondBefore);
     219
     220        before.clearSelection();
     221        before.addSelected(firstBefore);
     222        before.addSelected(secondBefore);
     223
     224        Command c = assertDoesNotThrow(() -> AlignInCircleAction.buildCommand(before));
     225        c.executeCommand();
     226
     227        final DataSet after;
     228        try (InputStream fis = Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleBuildingsAfter.osm"))) {
     229            after = OsmReader.parseDataSet(fis, NullProgressMonitor.INSTANCE);
     230        }
     231        Way firstAfter = null;
     232        Way secondAfter = null;
     233
     234        for (Way w : after.getWays()) {
     235            if ("first".equals(w.get("test"))) {
     236                firstAfter = w;
     237            } else if ("second".equals(w.get("test"))) {
     238                secondAfter = w;
     239            } else {
     240                fail("There should only be \"first\" or \"second\" values in the key \"test\"");
     241            }
     242        }
     243
     244        assertNotNull(firstAfter);
     245        assertEquals(firstAfter.getNodesCount(), firstBefore.getNodesCount());
     246        for (int i = 0; i < firstAfter.getNodesCount(); i++) {
     247            Node bn = firstBefore.getNode(i);
     248            Node an = firstAfter.getNode(i);
     249            assertEquals(bn.lat(), an.lat(), ILatLon.MAX_SERVER_PRECISION);
     250            assertEquals(bn.lon(), an.lon(), ILatLon.MAX_SERVER_PRECISION);
     251        }
     252
     253        assertNotNull(secondAfter);
     254        assertEquals(secondAfter.getNodesCount(), secondBefore.getNodesCount());
     255        for (int i = 0; i < secondAfter.getNodesCount(); i++) {
     256            Node bn = secondBefore.getNode(i);
     257            Node an = secondAfter.getNode(i);
     258            assertEquals(bn.lat(), an.lat(), ILatLon.MAX_SERVER_PRECISION);
     259            assertEquals(bn.lon(), an.lon(), ILatLon.MAX_SERVER_PRECISION);
     260        }
     261    }
    184262}
Note: See TracChangeset for help on using the changeset viewer.