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


Ignore:
Timestamp:
2016-01-23T14:47:56+01:00 (8 years ago)
Author:
Don-vip
Message:

see #12377 - code refactoring to cleanup multipolygon test

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java

    r9553 r9582  
    156156        if (r.isMultipolygon()) {
    157157            checkMembersAndRoles(r);
    158 
    159             Multipolygon polygon = MultipolygonCache.getInstance().get(Main.map.mapView, r);
    160 
    161             boolean hasOuterWay = false;
    162             for (RelationMember m : r.getMembers()) {
    163                 if ("outer".equals(m.getRole())) {
    164                     hasOuterWay = true;
    165                     break;
    166                 }
    167             }
    168             if (!hasOuterWay) {
    169                 addError(r, new TestError(this, Severity.WARNING, tr("No outer way for multipolygon"), MISSING_OUTER_WAY, r));
    170             }
    171 
    172             if (r.hasIncompleteMembers()) {
    173                 return; // Rest of checks is only for complete multipolygons
    174             }
    175 
    176             // Create new multipolygon using the logics from CreateMultipolygonAction and see if roles match.
    177             final Pair<Relation, Relation> newMP = CreateMultipolygonAction.createMultipolygonRelation(r.getMemberPrimitives(Way.class), false);
    178             if (newMP != null) {
    179                 for (RelationMember member : r.getMembers()) {
    180                     final Collection<RelationMember> memberInNewMP = newMP.b.getMembersFor(Collections.singleton(member.getMember()));
    181                     if (memberInNewMP != null && !memberInNewMP.isEmpty()) {
    182                         final String roleInNewMP = memberInNewMP.iterator().next().getRole();
    183                         if (!member.getRole().equals(roleInNewMP)) {
    184                             List<OsmPrimitive> l = new ArrayList<>();
    185                             l.add(r);
    186                             l.add(member.getMember());
    187                             addError(r, new TestError(this, Severity.WARNING, RelationChecker.ROLE_VERIF_PROBLEM_MSG,
    188                                     tr("Role for ''{0}'' should be ''{1}''",
    189                                             member.getMember().getDisplayName(DefaultNameFormatter.getInstance()), roleInNewMP),
    190                                     MessageFormat.format("Role for ''{0}'' should be ''{1}''",
    191                                             member.getMember().getDisplayName(DefaultNameFormatter.getInstance()), roleInNewMP),
    192                                     WRONG_MEMBER_ROLE, l, Collections.singleton(member.getMember())));
     158            checkOuterWay(r);
     159
     160            // Rest of checks is only for complete multipolygons
     161            if (!r.hasIncompleteMembers()) {
     162                Multipolygon polygon = MultipolygonCache.getInstance().get(Main.map.mapView, r);
     163
     164                // Create new multipolygon using the logics from CreateMultipolygonAction and see if roles match.
     165                checkMemberRoleCorrectness(r);
     166                checkStyleConsistency(r, polygon);
     167                checkGeometry(r, polygon);
     168            }
     169        }
     170    }
     171
     172    /**
     173     * Checks that multipolygon has at least an outer way:<ul>
     174     * <li>{@link #MISSING_OUTER_WAY}: No outer way for multipolygon</li>
     175     * </ul>
     176     * @param r relation
     177     */
     178    private void checkOuterWay(Relation r) {
     179        boolean hasOuterWay = false;
     180        for (RelationMember m : r.getMembers()) {
     181            if ("outer".equals(m.getRole())) {
     182                hasOuterWay = true;
     183                break;
     184            }
     185        }
     186        if (!hasOuterWay) {
     187            addError(r, new TestError(this, Severity.WARNING, tr("No outer way for multipolygon"), MISSING_OUTER_WAY, r));
     188        }
     189    }
     190
     191    /**
     192     * Create new multipolygon using the logics from CreateMultipolygonAction and see if roles match:<ul>
     193     * <li>{@link #WRONG_MEMBER_ROLE}: Role for ''{0}'' should be ''{1}''</li>
     194     * </ul>
     195     * @param r relation
     196     */
     197    private void checkMemberRoleCorrectness(Relation r) {
     198        final Pair<Relation, Relation> newMP = CreateMultipolygonAction.createMultipolygonRelation(r.getMemberPrimitives(Way.class), false);
     199        if (newMP != null) {
     200            for (RelationMember member : r.getMembers()) {
     201                final Collection<RelationMember> memberInNewMP = newMP.b.getMembersFor(Collections.singleton(member.getMember()));
     202                if (memberInNewMP != null && !memberInNewMP.isEmpty()) {
     203                    final String roleInNewMP = memberInNewMP.iterator().next().getRole();
     204                    if (!member.getRole().equals(roleInNewMP)) {
     205                        List<OsmPrimitive> l = new ArrayList<>();
     206                        l.add(r);
     207                        l.add(member.getMember());
     208                        addError(r, new TestError(this, Severity.WARNING, RelationChecker.ROLE_VERIF_PROBLEM_MSG,
     209                                tr("Role for ''{0}'' should be ''{1}''",
     210                                        member.getMember().getDisplayName(DefaultNameFormatter.getInstance()), roleInNewMP),
     211                                MessageFormat.format("Role for ''{0}'' should be ''{1}''",
     212                                        member.getMember().getDisplayName(DefaultNameFormatter.getInstance()), roleInNewMP),
     213                                WRONG_MEMBER_ROLE, l, Collections.singleton(member.getMember())));
     214                    }
     215                }
     216            }
     217        }
     218    }
     219
     220    /**
     221     * Various style-related checks:<ul>
     222     * <li>{@link #NO_STYLE_POLYGON}: Multipolygon relation should be tagged with area tags and not the outer way</li>
     223     * <li>{@link #INNER_STYLE_MISMATCH}: With the currently used mappaint style the style for inner way equals the multipolygon style</li>
     224     * <li>{@link #OUTER_STYLE_MISMATCH}: Style for outer way mismatches</li>
     225     * <li>{@link #OUTER_STYLE}: Area style on outer way</li>
     226     * </ul>
     227     * @param r relation
     228     * @param polygon multipolygon
     229     */
     230    private void checkStyleConsistency(Relation r, Multipolygon polygon) {
     231        if (styles != null && !"boundary".equals(r.get("type"))) {
     232            AreaElement area = ElemStyles.getAreaElemStyle(r, false);
     233            boolean areaStyle = area != null;
     234            // If area style was not found for relation then use style of ways
     235            if (area == null) {
     236                for (Way w : polygon.getOuterWays()) {
     237                    area = ElemStyles.getAreaElemStyle(w, true);
     238                    if (area != null) {
     239                        break;
     240                    }
     241                }
     242                if (area == null) {
     243                    addError(r, new TestError(this, Severity.OTHER, tr("No area style for multipolygon"), NO_STYLE, r));
     244                } else {
     245                    /* old style multipolygon - solve: copy tags from outer way to multipolygon */
     246                    addError(r, new TestError(this, Severity.WARNING,
     247                            trn("Multipolygon relation should be tagged with area tags and not the outer way",
     248                                    "Multipolygon relation should be tagged with area tags and not the outer ways",
     249                                    polygon.getOuterWays().size()),
     250                       NO_STYLE_POLYGON, r));
     251                }
     252            }
     253
     254            if (area != null) {
     255                for (Way wInner : polygon.getInnerWays()) {
     256                    AreaElement areaInner = ElemStyles.getAreaElemStyle(wInner, false);
     257
     258                    if (areaInner != null && area.equals(areaInner)) {
     259                        List<OsmPrimitive> l = new ArrayList<>();
     260                        l.add(r);
     261                        l.add(wInner);
     262                        addError(r, new TestError(this, Severity.OTHER,
     263                                tr("With the currently used mappaint style the style for inner way equals the multipolygon style"),
     264                                INNER_STYLE_MISMATCH, l, Collections.singletonList(wInner)));
     265                    }
     266                }
     267                for (Way wOuter : polygon.getOuterWays()) {
     268                    AreaElement areaOuter = ElemStyles.getAreaElemStyle(wOuter, false);
     269                    if (areaOuter != null) {
     270                        List<OsmPrimitive> l = new ArrayList<>();
     271                        l.add(r);
     272                        l.add(wOuter);
     273                        if (!area.equals(areaOuter)) {
     274                            addError(r, new TestError(this, Severity.WARNING, !areaStyle ? tr("Style for outer way mismatches")
     275                            : tr("With the currently used mappaint style(s) the style for outer way mismatches polygon"),
     276                            OUTER_STYLE_MISMATCH, l, Collections.singletonList(wOuter)));
     277                        } else if (areaStyle) { /* style on outer way of multipolygon, but equal to polygon */
     278                            addError(r, new TestError(this, Severity.WARNING, tr("Area style on outer way"), OUTER_STYLE,
     279                            l, Collections.singletonList(wOuter)));
    193280                        }
    194281                    }
    195282                }
    196283            }
    197 
    198             if (styles != null && !"boundary".equals(r.get("type"))) {
    199                 AreaElement area = ElemStyles.getAreaElemStyle(r, false);
    200                 boolean areaStyle = area != null;
    201                 // If area style was not found for relation then use style of ways
    202                 if (area == null) {
    203                     for (Way w : polygon.getOuterWays()) {
    204                         area = ElemStyles.getAreaElemStyle(w, true);
    205                         if (area != null) {
    206                             break;
    207                         }
    208                     }
    209                     if (area == null) {
    210                         addError(r, new TestError(this, Severity.OTHER, tr("No area style for multipolygon"), NO_STYLE, r));
    211                     } else {
    212                         /* old style multipolygon - solve: copy tags from outer way to multipolygon */
    213                         addError(r, new TestError(this, Severity.WARNING,
    214                                 trn("Multipolygon relation should be tagged with area tags and not the outer way",
    215                                         "Multipolygon relation should be tagged with area tags and not the outer ways",
    216                                         polygon.getOuterWays().size()),
    217                            NO_STYLE_POLYGON, r));
    218                     }
    219                 }
    220 
    221                 if (area != null) {
    222                     for (Way wInner : polygon.getInnerWays()) {
    223                         AreaElement areaInner = ElemStyles.getAreaElemStyle(wInner, false);
    224 
    225                         if (areaInner != null && area.equals(areaInner)) {
    226                             List<OsmPrimitive> l = new ArrayList<>();
    227                             l.add(r);
    228                             l.add(wInner);
    229                             addError(r, new TestError(this, Severity.OTHER,
    230                                     tr("With the currently used mappaint style the style for inner way equals the multipolygon style"),
    231                                     INNER_STYLE_MISMATCH, l, Collections.singletonList(wInner)));
    232                         }
    233                     }
    234                     for (Way wOuter : polygon.getOuterWays()) {
    235                         AreaElement areaOuter = ElemStyles.getAreaElemStyle(wOuter, false);
    236                         if (areaOuter != null) {
    237                             List<OsmPrimitive> l = new ArrayList<>();
    238                             l.add(r);
    239                             l.add(wOuter);
    240                             if (!area.equals(areaOuter)) {
    241                                 addError(r, new TestError(this, Severity.WARNING, !areaStyle ? tr("Style for outer way mismatches")
    242                                 : tr("With the currently used mappaint style(s) the style for outer way mismatches polygon"),
    243                                 OUTER_STYLE_MISMATCH, l, Collections.singletonList(wOuter)));
    244                             } else if (areaStyle) { /* style on outer way of multipolygon, but equal to polygon */
    245                                 addError(r, new TestError(this, Severity.WARNING, tr("Area style on outer way"), OUTER_STYLE,
    246                                 l, Collections.singletonList(wOuter)));
    247                             }
    248                         }
    249                     }
    250                 }
    251             }
    252 
    253             List<Node> openNodes = polygon.getOpenEnds();
    254             if (!openNodes.isEmpty()) {
    255                 List<OsmPrimitive> primitives = new LinkedList<>();
    256                 primitives.add(r);
    257                 primitives.addAll(openNodes);
    258                 Arrays.asList(openNodes, r);
    259                 addError(r, new TestError(this, Severity.WARNING, tr("Multipolygon is not closed"), NON_CLOSED_WAY,
    260                         primitives, openNodes));
    261             }
    262 
    263             // For painting is used Polygon class which works with ints only. For validation we need more precision
    264             List<GeneralPath> outerPolygons = createPolygons(polygon.getOuterPolygons());
    265             for (Multipolygon.PolyData pdInner : polygon.getInnerPolygons()) {
    266                 boolean outside = true;
    267                 boolean crossing = false;
    268                 Multipolygon.PolyData outerWay = null;
    269                 for (int i = 0; i < polygon.getOuterPolygons().size(); i++) {
    270                     GeneralPath outer = outerPolygons.get(i);
    271                     Intersection intersection = getPolygonIntersection(outer, pdInner.getNodes());
    272                     outside = outside & intersection == Intersection.OUTSIDE;
    273                     if (intersection == Intersection.CROSSING) {
    274                         crossing = true;
    275                         outerWay = polygon.getOuterPolygons().get(i);
    276                     }
    277                 }
    278                 if (outside || crossing) {
    279                     List<List<Node>> highlights = new ArrayList<>();
    280                     highlights.add(pdInner.getNodes());
    281                     if (outside) {
    282                         addError(r, new TestError(this, Severity.WARNING, tr("Multipolygon inner way is outside"),
    283                                 INNER_WAY_OUTSIDE, Collections.singletonList(r), highlights));
    284                     } else if (outerWay != null) {
    285                         highlights.add(outerWay.getNodes());
    286                         addError(r, new TestError(this, Severity.WARNING, tr("Intersection between multipolygon ways"),
    287                                 CROSSING_WAYS, Collections.singletonList(r), highlights));
    288                     }
    289                 }
    290             }
    291         }
    292     }
    293 
     284        }
     285    }
     286
     287    /**
     288     * Various geometry-related checks:<ul>
     289     * <li>{@link #NON_CLOSED_WAY}: Multipolygon is not closed</li>
     290     * <li>{@link #INNER_WAY_OUTSIDE}: Multipolygon inner way is outside</li>
     291     * <li>{@link #CROSSING_WAYS}: Intersection between multipolygon ways</li>
     292     * </ul>
     293     * @param r relation
     294     * @param polygon multipolygon
     295     */
     296    private void checkGeometry(Relation r, Multipolygon polygon) {
     297        List<Node> openNodes = polygon.getOpenEnds();
     298        if (!openNodes.isEmpty()) {
     299            List<OsmPrimitive> primitives = new LinkedList<>();
     300            primitives.add(r);
     301            primitives.addAll(openNodes);
     302            Arrays.asList(openNodes, r);
     303            addError(r, new TestError(this, Severity.WARNING, tr("Multipolygon is not closed"), NON_CLOSED_WAY,
     304                    primitives, openNodes));
     305        }
     306
     307        // For painting is used Polygon class which works with ints only. For validation we need more precision
     308        List<GeneralPath> outerPolygons = createPolygons(polygon.getOuterPolygons());
     309        for (Multipolygon.PolyData pdInner : polygon.getInnerPolygons()) {
     310            boolean outside = true;
     311            boolean crossing = false;
     312            Multipolygon.PolyData outerWay = null;
     313            for (int i = 0; i < polygon.getOuterPolygons().size(); i++) {
     314                GeneralPath outer = outerPolygons.get(i);
     315                Intersection intersection = getPolygonIntersection(outer, pdInner.getNodes());
     316                outside = outside & intersection == Intersection.OUTSIDE;
     317                if (intersection == Intersection.CROSSING) {
     318                    crossing = true;
     319                    outerWay = polygon.getOuterPolygons().get(i);
     320                }
     321            }
     322            if (outside || crossing) {
     323                List<List<Node>> highlights = new ArrayList<>();
     324                highlights.add(pdInner.getNodes());
     325                if (outside) {
     326                    addError(r, new TestError(this, Severity.WARNING, tr("Multipolygon inner way is outside"),
     327                            INNER_WAY_OUTSIDE, Collections.singletonList(r), highlights));
     328                } else if (outerWay != null) {
     329                    highlights.add(outerWay.getNodes());
     330                    addError(r, new TestError(this, Severity.WARNING, tr("Intersection between multipolygon ways"),
     331                            CROSSING_WAYS, Collections.singletonList(r), highlights));
     332                }
     333            }
     334        }
     335    }
     336
     337    /**
     338     * Check for:<ul>
     339     * <li>{@link #WRONG_MEMBER_ROLE}: No useful role for multipolygon member</li>
     340     * <li>{@link #WRONG_MEMBER_TYPE}: Non-Way in multipolygon</li>
     341     * </ul>
     342     * @param r relation
     343     */
    294344    private void checkMembersAndRoles(Relation r) {
    295345        for (RelationMember rm : r.getMembers()) {
Note: See TracChangeset for help on using the changeset viewer.