Ticket #20716: josm_20716_power_v3.patch

File josm_20716_power_v3.patch, 12.4 KB (added by gaben, 3 years ago)
  • src/org/openstreetmap/josm/data/validation/tests/OverlappingWays.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/data/validation/tests/OverlappingWays.java b/src/org/openstreetmap/josm/data/validation/tests/OverlappingWays.java
    a b  
    4747    /** Bag of all way segments */
    4848    private MultiMap<Pair<Node, Node>, WaySegment> nodePairs;
    4949
    50     private boolean onlyKnwonLinear;
     50    private boolean onlyKnownLinear;
    5151    private boolean includeOther;
    5252    private boolean ignoreLayer;
    5353
     
    8383        super.startTest(monitor);
    8484        nodePairs = new MultiMap<>(1000);
    8585        includeOther = isBeforeUpload ? ValidatorPrefHelper.PREF_OTHER_UPLOAD.get() : ValidatorPrefHelper.PREF_OTHER.get();
    86         onlyKnwonLinear = Config.getPref().getBoolean("overlapping-ways.only-known-linear", true);
     86        onlyKnownLinear = Config.getPref().getBoolean("overlapping-ways.only-known-linear", true);
    8787        ignoreLayer = Config.getPref().getBoolean("overlapping-ways.ignore-layer", false);
    8888    }
    8989
     
    192192                errortype = tr("Waterway shares segment with linear way");
    193193                type = OVERLAPPING_WATERWAY_LINEAR_WAY;
    194194                severity = Severity.WARNING;
    195             } else if (!includeOther || onlyKnwonLinear) {
     195            } else if (!includeOther || onlyKnownLinear) {
    196196                return;
    197197            } else if (countHighway > 0) {
    198198                errortype = tr("Highway shares segment with other way");
     
    269269        if (IGNORED.test(w))
    270270            return;
    271271
    272         if (onlyKnwonLinear && (w.concernsArea() || w.getInterestingTags().isEmpty()))
     272        if (onlyKnownLinear && (w.concernsArea() || w.getInterestingTags().isEmpty()))
    273273            return;
    274274
    275275        Node lastN = null;
  • src/org/openstreetmap/josm/data/validation/tests/PowerLines.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/data/validation/tests/PowerLines.java b/src/org/openstreetmap/josm/data/validation/tests/PowerLines.java
    a b  
    1111import java.util.Set;
    1212
    1313import org.openstreetmap.josm.data.osm.Node;
     14import org.openstreetmap.josm.data.osm.NodePair;
    1415import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1516import org.openstreetmap.josm.data.osm.Relation;
    1617import org.openstreetmap.josm.data.osm.RelationMember;
     
    2526import org.openstreetmap.josm.tools.Geometry;
    2627
    2728/**
    28  * Checks for nodes in power lines/minor_lines that do not have a power=tower/pole tag.<br>
    29  * See #7812 for discussions about this test.
     29 * Checks for nodes in power lines/minor_lines that do not have a power=tower/pole/portal tag and also for ways where
     30 * are unusually long segments without line support feature.<br>
     31 * See #7812 and #20716 for discussions about this test.
    3032 */
    3133public class PowerLines extends Test {
    3234
    3335    /** Test identifier */
    3436    protected static final int POWER_LINES = 2501;
    3537    protected static final int POWER_CONNECTION = 2502;
     38    protected static final int POWER_SEGMENT_LENGTH = 2503;
    3639
    3740    /** Values for {@code power} key interpreted as power lines */
    3841    static final Collection<String> POWER_LINE_TAGS = Arrays.asList("line", "minor_line");
    3942    /** Values for {@code power} key interpreted as power towers */
    40     static final Collection<String> POWER_TOWER_TAGS = Arrays.asList("tower", "pole");
     43    static final Collection<String> POWER_TOWER_TAGS = Arrays.asList("tower", "pole", "portal", "catenary_mast");
    4144    /** Values for {@code power} key interpreted as power stations */
    42     static final Collection<String> POWER_STATION_TAGS = Arrays.asList("station", "sub_station", "substation", "plant", "generator");
     45    static final Collection<String> POWER_STATION_TAGS = Arrays.asList("generator", "plant", "substation");
    4346    /** Values for {@code building} key interpreted as power stations */
    4447    static final Collection<String> BUILDING_STATION_TAGS = Arrays.asList("transformer_tower");
    4548    /** Values for {@code power} key interpreted as allowed power items */
    46     static final Collection<String> POWER_ALLOWED_TAGS = Arrays.asList("switch", "transformer", "busbar", "generator", "switchgear",
    47             "portal", "terminal", "insulator");
     49    static final Collection<String> POWER_INFRASTRUCTURE_TAGS = Arrays.asList("compensator", "converter",
     50            "generator", "insulator", "switch", "switchgear", "terminal", "transformer");
    4851
    4952    private final Set<Node> badConnections = new LinkedHashSet<>();
    50     private final Set<Node> missingTowerOrPole = new LinkedHashSet<>();
     53    private final Set<Node> missingTag = new LinkedHashSet<>();
     54    private final Set<NodePair> missingNode = new LinkedHashSet<>();
    5155
    5256    private final List<OsmPrimitive> powerStations = new ArrayList<>();
    5357
     
    5862        super(tr("Power lines"), tr("Checks for nodes in power lines that do not have a power=tower/pole tag."));
    5963    }
    6064
    61     @Override
    62     public void visit(Way w) {
    63         if (w.isUsable()) {
    64             if (isPowerLine(w) && !w.hasTag("location", "underground")) {
    65                 for (Node n : w.getNodes()) {
    66                     if (!isPowerTower(n) && !isPowerAllowed(n) && IN_DOWNLOADED_AREA.test(n)
    67                         && (!w.isFirstLastNode(n) || !isPowerStation(n))) {
    68                         missingTowerOrPole.add(n);
    69                     }
    70                 }
    71             } else if (w.isClosed() && isPowerStation(w)) {
    72                 powerStations.add(w);
    73             }
    74         }
    75     }
    76 
    7765    @Override
    7866    public void visit(Node n) {
    7967        boolean nodeInLineOrCable = false;
     
    8977            badConnections.add(n);
    9078    }
    9179
    92     private static boolean isRelatedToPower(Way way) {
    93         if (way.hasTag("power") || way.hasTag("building"))
    94             return true;
    95         for (OsmPrimitive ref : way.getReferrers()) {
    96             if (ref instanceof Relation && ref.isMultipolygon() && (ref.hasTag("power") || ref.hasTag("building"))) {
    97                 for (RelationMember rm : ((Relation) ref).getMembers()) {
    98                     if (way == rm.getMember())
    99                         return true;
     80    @Override
     81    public void visit(Way w) {
     82        if (w.isUsable()) {
     83            if (isPowerLine(w) && !w.hasKey("line") && !w.hasTag("location", "underground")) {
     84                float avgWayLength = (float) (w.getLength() / w.getRealNodesCount());
     85                Node prevNode = w.firstNode();
     86
     87                for (Node n : w.getNodes()) {
     88                    // handle power station power line connections (power=line + line=*)
     89                    boolean skip = false;
     90                    for (OsmPrimitive p : n.getReferrers()) {
     91                        if (p instanceof Way && !p.equals(w) && isPowerLine((Way) p) && p.hasKey("line")) {
     92                            skip = true;
     93                            break;
     94                        }
     95                    }
     96                    if (skip) {
     97                        prevNode = n;
     98                        continue;
     99                    }
     100
     101                    // handle missing tags
     102                    if (!isPowerTower(n) && !isPowerInfrastructure(n) && IN_DOWNLOADED_AREA.test(n)
     103                            && (!w.isFirstLastNode(n) || !isPowerStation(n))) {
     104                        missingTag.add(n);
     105                    }
     106
     107                    // handle missing nodes
     108                    double distance = n.getCoor().greatCircleDistance(prevNode.getCoor());
     109                    double threshold = w.hasTag("power", "line") ? 1.6 : 1.8;
     110                    if (w.getRealNodesCount() > 4 && distance > avgWayLength * threshold && !isPowerInfrastructure(n)) {
     111                        missingNode.add(new NodePair(prevNode, n));
     112                    }
     113                    prevNode = n;
    100114                }
     115            } else if (w.isClosed() && isPowerStation(w)) {
     116                powerStations.add(w);
    101117            }
    102118        }
    103         return false;
    104119    }
    105120
    106121    @Override
     
    118133
    119134    @Override
    120135    public void endTest() {
    121         for (Node n : missingTowerOrPole) {
     136        for (Node n : missingTag) {
    122137            if (!isInPowerStation(n)) {
    123138                errors.add(TestError.builder(this, Severity.WARNING, POWER_LINES)
    124                         .message(tr("Missing power tower/pole within power line"))
     139                        .message(tr("Missing power line support tag from node"))
    125140                        .primitives(n)
    126141                        .build());
    127142            }
     
    130145        for (Node n : badConnections) {
    131146            errors.add(TestError.builder(this, Severity.WARNING, POWER_CONNECTION)
    132147                    .message(tr("Node connects a power line or cable with an object "
    133                             + "which is not related to the power infrastructure."))
    134                     .primitives(n).build());
     148                            + "which is not related to the power infrastructure"))
     149                    .primitives(n)
     150                    .build());
    135151        }
     152
     153        for (NodePair pair : missingNode) {
     154            errors.add(TestError.builder(this, Severity.WARNING, POWER_SEGMENT_LENGTH)
     155                    .message(tr("Missing line support node within power line"))
     156                    .primitives(pair.getA(), pair.getB())
     157                    .build());
     158        }
     159
    136160        clearCollections();
    137161        super.endTest();
    138162    }
    139163
     164    private static boolean isRelatedToPower(Way way) {
     165        if (way.hasTag("power") || way.hasTag("building"))
     166            return true;
     167        for (OsmPrimitive ref : way.getReferrers()) {
     168            if (ref instanceof Relation && ref.isMultipolygon() && (ref.hasTag("power") || ref.hasTag("building"))) {
     169                for (RelationMember rm : ((Relation) ref).getMembers()) {
     170                    if (way == rm.getMember())
     171                        return true;
     172                }
     173            }
     174        }
     175        return false;
     176    }
     177
    140178    protected final boolean isInPowerStation(Node n) {
    141179        for (OsmPrimitive station : powerStations) {
    142180            List<List<Node>> nodesLists = new ArrayList<>();
     
    171209    /**
    172210     * Determines if the specified primitive denotes a power station.
    173211     * @param p The primitive to be tested
    174      * @return {@code true} if power key is set and equal to station/sub_station/plant
     212     * @return {@code true} if power key is set and equal to generator/substation/plant
    175213     */
    176214    protected static final boolean isPowerStation(OsmPrimitive p) {
    177215        return isPowerIn(p, POWER_STATION_TAGS) || isBuildingIn(p, BUILDING_STATION_TAGS);
    178216    }
    179217
    180218    /**
    181      * Determines if the specified node denotes a power tower/pole.
     219     * Determines if the specified node denotes a power tower/pole/portal/catenary_mast.
    182220     * @param n The node to be tested
    183      * @return {@code true} if power key is set and equal to tower/pole
     221     * @return {@code true} if power key is set and equal to tower/pole/portal/catenary_mast
    184222     */
    185223    protected static final boolean isPowerTower(Node n) {
    186224        return isPowerIn(n, POWER_TOWER_TAGS);
     
    189227    /**
    190228     * Determines if the specified node denotes a power infrastructure allowed on a power line.
    191229     * @param n The node to be tested
    192      * @return True if power key is set and equal to switch/tranformer/busbar/generator
     230     * @return True if power key is set and equal to compensator/converter/generator/insulator
     231     * /switch/switchgear/terminal/transformer
    193232     */
    194     protected static final boolean isPowerAllowed(Node n) {
    195         return isPowerIn(n, POWER_ALLOWED_TAGS);
     233    protected static final boolean isPowerInfrastructure(Node n) {
     234        return isPowerIn(n, POWER_INFRASTRUCTURE_TAGS);
    196235    }
    197236
    198237    /**
     
    218257    private void clearCollections() {
    219258        powerStations.clear();
    220259        badConnections.clear();
    221         missingTowerOrPole.clear();
     260        missingTag.clear();
     261        missingNode.clear();
    222262    }
    223263}