Ticket #24851: 24851-powerlines-v3.patch

File 24851-powerlines-v3.patch, 7.6 KB (added by gaben, 5 weeks ago)
  • src/org/openstreetmap/josm/data/validation/tests/PowerLines.java

     
    9898
    9999    @Override
    100100    public void visit(Node n) {
    101         boolean nodeInLineOrCable = false;
    102         boolean connectedToUnrelated = false;
    103         for (Way parent : n.getParentWays()) {
    104             if (parent.hasTag(POWER, "line", MINOR_LINE, "cable"))
    105                 nodeInLineOrCable = true;
    106             else if (!isRelatedToPower(parent))
    107                 connectedToUnrelated = true;
     101        if (!n.isConnectionNode() || n.referrers(Way.class).noneMatch(PowerLines::isPowerLineOrCable))
     102            return;
     103
     104        boolean connectedToUnrelated = n.referrers(Way.class).anyMatch(w -> !isPowerLineOrCable(w) && !isRelatedToPower(w));
     105        if (connectedToUnrelated) {
     106            badConnections.add(n);
    108107        }
    109         if (nodeInLineOrCable && connectedToUnrelated)
    110             badConnections.add(n);
    111108    }
    112109
    113110    @Override
    … …  
    164161        // Then return the errors
    165162        for (Node n : missingTags) {
    166163            if (!isInPowerStation(n)) {
     164                List<OsmPrimitive> primitives = new ArrayList<>();
     165                primitives.add(n);
     166                n.referrers(Way.class).filter(PowerLines::isPowerLine).forEach(primitives::add);
     167
    167168                errors.add(TestError.builder(this, Severity.WARNING, POWER_SUPPORT)
    168169                        // the "missing tag" grouping can become broken if the MapCSS message get reworded
    169170                        .message(tr("missing tag"), tr("node without power=*"))
    170                         .primitives(n)
     171                        .primitives(primitives)
     172                        .highlight(n)
    171173                        .build());
    172174            }
    173175        }
    174176
    175177        for (Node n : badConnections) {
     178            List<OsmPrimitive> primitives = new ArrayList<>();
     179            primitives.add(n);
     180            n.referrers(Way.class).filter(w -> !isPowerLineOrCable(w) && !isRelatedToPower(w)).forEach(primitives::add);
     181            n.referrers(Way.class).filter(PowerLines::isPowerLineOrCable).forEach(primitives::add);
     182
    176183            errors.add(TestError.builder(this, Severity.WARNING, POWER_CONNECTION)
    177184                    .message(tr("Node connects a power line or cable with an object "
    178185                            + "which is not related to the power infrastructure"))
    179                     .primitives(n)
     186                    .primitives(primitives)
     187                    .highlight(n)
    180188                    .build());
    181189        }
    182190
    … …  
    669677    }
    670678
    671679    /**
     680     * Determines if the specified way denotes a power line or cable.
     681     * @param w The way to be tested
     682     * @return {@code true} if power key is set and equal to line, minor_line or cable
     683     */
     684    protected static boolean isPowerLineOrCable(Way w) {
     685        return isPowerIn(w, Arrays.asList("line", MINOR_LINE, "cable"));
     686    }
     687
     688    /**
    672689     * Determines if the specified primitive denotes a power station.
    673690     * @param p The primitive to be tested
    674691     * @return {@code true} if power key is set and equal to generator/substation/plant
  • test/unit/org/openstreetmap/josm/data/validation/tests/PowerLinesTest.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.validation.tests;
    33
    4 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
    5 import static org.junit.jupiter.api.Assertions.assertFalse;
    6 import static org.junit.jupiter.api.Assertions.assertTrue;
    7 
    8 import java.util.ArrayList;
    9 
    104import org.junit.jupiter.api.BeforeEach;
    115import org.junit.jupiter.api.Test;
    126import org.openstreetmap.josm.TestUtils;
    … …  
    1711import org.openstreetmap.josm.data.osm.RelationMember;
    1812import org.openstreetmap.josm.data.osm.TagMap;
    1913import org.openstreetmap.josm.data.osm.Way;
     14import org.openstreetmap.josm.data.validation.TestError;
    2015import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
    2116import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
    2217import org.openstreetmap.josm.testutils.annotations.Projection;
    2318
     19import java.util.ArrayList;
     20
     21import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
     22import static org.junit.jupiter.api.Assertions.assertFalse;
     23import static org.junit.jupiter.api.Assertions.assertTrue;
     24
    2425/**
    2526 * Test class for {@link PowerLines}
    2627 * @since 18553
    … …  
    159160        this.powerLines.endTest();
    160161        assertTrue(this.powerLines.getErrors().isEmpty());
    161162    }
    162 }
     163
     164    /**
     165     * Test for ticket #24851.
     166     * Simulates connecting a power line to an existing highway node without power tags.
     167     * Validates that the resulting error contains both the Node AND the Way, so it
     168     * doesn't get filtered out during partial validation on upload.
     169     */
     170    @Test
     171    void testTicket24851_ReportExistingNonPowerNodes() {
     172        Node sharedNode = new Node(new LatLon(0, 0)); // no power tag attached
     173
     174        // unrelated highway way
     175        Way highway = TestUtils.newWay("highway=unclassified",
     176                sharedNode, new Node(new LatLon(0.1, 0)));
     177
     178        // power line way
     179        Way powerline = TestUtils.newWay("power=line",
     180                sharedNode, new Node(new LatLon(0, 0.1)));
     181
     182        // second node has a valid tag
     183        powerline.getNode(1).put("power", "tower");
     184
     185        ds.addPrimitiveRecursive(highway);
     186        ds.addPrimitiveRecursive(powerline);
     187
     188        powerLines.startTest(NullProgressMonitor.INSTANCE);
     189        for (Way w : ds.getWays()) {
     190            powerLines.visit(w);
     191        }
     192        for (Node n : ds.getNodes()) {
     193            powerLines.visit(n);
     194        }
     195        powerLines.endTest();
     196
     197        assertFalse(powerLines.getErrors().isEmpty(), "Errors should be generated for the missing tag and bad connection");
     198
     199        boolean foundSupportError = false;
     200        boolean foundConnectionError = false;
     201
     202        for (TestError error : powerLines.getErrors()) {
     203            // verify POWER_SUPPORT behavior (missing tag)
     204            if (error.getCode() == PowerLines.POWER_SUPPORT && error.getPrimitives().contains(sharedNode)) {
     205                foundSupportError = true;
     206                assertTrue(error.getPrimitives().contains(powerline),
     207                        "MUST contain the parent powerline way. This prevents JOSM from discarding the error " +
     208                                "during partial validation if the node itself was unmodified.");
     209            }
     210            // verify POWER_CONNECTION behavior (bad connection)
     211            if (error.getCode() == PowerLines.POWER_CONNECTION && error.getPrimitives().contains(sharedNode)) {
     212                foundConnectionError = true;
     213                assertTrue(error.getPrimitives().contains(highway),
     214                        "MUST contain the unrelated parent way. This prevents JOSM from discarding the error" +
     215                                "during partial validation if the node itself was unmodified.");
     216            }
     217        }
     218
     219        assertTrue(foundSupportError, "Should report missing power tag on shared node");
     220        assertTrue(foundConnectionError, "Should report bad connection on shared node");
     221    }
     222}
     223 No newline at end of file