source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java@ 3671

Last change on this file since 3671 was 3671, checked in by bastiK, 13 years ago

adapt coding style (to some degree); there shouldn't be any semantic changes in this commit

  • Property svn:eol-style set to native
File size: 9.7 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.geom.GeneralPath;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.List;
11
12import org.openstreetmap.josm.Main;
13import org.openstreetmap.josm.data.osm.Node;
14import org.openstreetmap.josm.data.osm.OsmPrimitive;
15import org.openstreetmap.josm.data.osm.Relation;
16import org.openstreetmap.josm.data.osm.RelationMember;
17import org.openstreetmap.josm.data.osm.Way;
18import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
19import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
20import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData.Intersection;
21import org.openstreetmap.josm.data.validation.Severity;
22import org.openstreetmap.josm.data.validation.Test;
23import org.openstreetmap.josm.data.validation.TestError;
24import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
25import org.openstreetmap.josm.gui.mappaint.ElemStyle;
26import org.openstreetmap.josm.gui.mappaint.ElemStyles;
27import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
28
29public class MultipolygonTest extends Test {
30
31 protected static final int WRONG_MEMBER_TYPE = 1601;
32 protected static final int WRONG_MEMBER_ROLE = 1602;
33 protected static final int NON_CLOSED_WAY = 1603;
34 protected static final int MISSING_OUTER_WAY = 1604;
35 protected static final int INNER_WAY_OUTSIDE = 1605;
36 protected static final int CROSSING_WAYS = 1606;
37 protected static final int OUTER_STYLE_MISMATCH = 1607;
38 protected static final int INNER_STYLE_MISMATCH = 1608;
39 protected static final int NOT_CLOSED = 1609;
40 protected static final int NO_STYLE = 1610;
41 protected static final int NO_STYLE_POLYGON = 1611;
42
43 private static ElemStyles.StyleSet styles;
44
45 private final List<List<Node>> nonClosedWays = new ArrayList<List<Node>>();
46
47 public MultipolygonTest() {
48 super(tr("Multipolygon"),
49 tr("This test checks if multipolygons are valid"));
50 }
51
52 @Override
53 public void initialize() throws Exception {
54 styles = MapPaintStyles.getStyles().getStyleSet();
55 }
56
57 private List<List<Node>> joinWays(Collection<Way> ways) {
58 List<List<Node>> result = new ArrayList<List<Node>>();
59 List<Way> waysToJoin = new ArrayList<Way>();
60 for (Way way : ways) {
61 if (way.isClosed()) {
62 result.add(way.getNodes());
63 } else {
64 waysToJoin.add(way);
65 }
66 }
67
68 for (JoinedWay jw : Multipolygon.joinWays(waysToJoin)) {
69 if (!jw.isClosed()) {
70 nonClosedWays.add(jw.getNodes());
71 } else {
72 result.add(jw.getNodes());
73 }
74 }
75 return result;
76 }
77
78 private GeneralPath createPath(List<Node> nodes) {
79 GeneralPath result = new GeneralPath();
80 result.moveTo((float) nodes.get(0).getCoor().lat(), (float) nodes.get(0).getCoor().lon());
81 for (int i=1; i<nodes.size(); i++) {
82 Node n = nodes.get(i);
83 result.lineTo((float )n.getCoor().lat(), (float) n.getCoor().lon());
84 }
85 return result;
86 }
87
88 private List<GeneralPath> createPolygons(List<List<Node>> joinedWays) {
89 List<GeneralPath> result = new ArrayList<GeneralPath>();
90 for (List<Node> way : joinedWays) {
91 result.add(createPath(way));
92 }
93 return result;
94 }
95
96 private Intersection getPolygonIntersection(GeneralPath outer, List<Node> inner) {
97 boolean inside = false;
98 boolean outside = false;
99
100 for (Node n : inner) {
101 boolean contains = outer.contains(n.getCoor().lat(), n.getCoor().lon());
102 inside = inside | contains;
103 outside = outside | !contains;
104 if (inside & outside) {
105 return Intersection.CROSSING;
106 }
107 }
108
109 return inside ? Intersection.INSIDE : Intersection.OUTSIDE;
110 }
111
112 @Override
113 public void visit(Way w) {
114 if (styles != null && !w.isClosed()) {
115 ElemStyle e = styles.getArea(w);
116 if (e instanceof AreaElemStyle && !((AreaElemStyle)e).closed) {
117 errors.add( new TestError(this, Severity.WARNING, tr("Area style way is not closed"), NOT_CLOSED, w));
118 }
119 }
120 }
121
122 @Override
123 public void visit(Relation r) {
124 nonClosedWays.clear();
125 if ("multipolygon".equals(r.get("type"))) {
126 checkMembersAndRoles(r);
127
128 Multipolygon polygon = new Multipolygon(Main.map.mapView);
129 polygon.load(r);
130
131 if (polygon.getOuterWays().isEmpty()) {
132 errors.add( new TestError(this, Severity.WARNING, tr("No outer way for multipolygon"), MISSING_OUTER_WAY, r));
133 }
134
135 for (RelationMember rm : r.getMembers()) {
136 if (!rm.getMember().isUsable())
137 return; // Rest of checks is only for complete multipolygons
138 }
139
140 List<List<Node>> innerWays = joinWays(polygon.getInnerWays()); // Side effect - sets nonClosedWays
141 List<List<Node>> outerWays = joinWays(polygon.getOuterWays());
142
143 if (styles != null) {
144 ElemStyle wayStyle = styles.get(r);
145
146 // If area style was not found for relation then use style of ways
147 if (!(wayStyle instanceof AreaElemStyle)) {
148 errors.add( new TestError(this, Severity.OTHER, tr("No style in multipolygon relation"),
149 NO_STYLE_POLYGON, r));
150 for (Way w : polygon.getOuterWays()) {
151 wayStyle = styles.getArea(w);
152 if(wayStyle != null) {
153 break;
154 }
155 }
156 }
157
158 if (wayStyle instanceof AreaElemStyle) {
159 for (Way wInner : polygon.getInnerWays()) {
160 ElemStyle innerStyle = styles.get(wInner);
161 if (wayStyle != null && wayStyle.equals(innerStyle)) {
162 List<OsmPrimitive> l = new ArrayList<OsmPrimitive>();
163 l.add(r);
164 l.add(wInner);
165 errors.add( new TestError(this, Severity.WARNING, tr("Style for inner way equals multipolygon"),
166 INNER_STYLE_MISMATCH, l, Collections.singletonList(wInner)));
167 }
168 }
169 for (Way wOuter : polygon.getOuterWays()) {
170 ElemStyle outerStyle = styles.get(wOuter);
171 if (outerStyle instanceof AreaElemStyle && !wayStyle.equals(outerStyle)) {
172 List<OsmPrimitive> l = new ArrayList<OsmPrimitive>();
173 l.add(r);
174 l.add(wOuter);
175 errors.add(new TestError(this, Severity.WARNING, tr("Style for outer way mismatches"),
176 OUTER_STYLE_MISMATCH, l, Collections.singletonList(wOuter)));
177 }
178 }
179 }
180 else {
181 errors.add(new TestError(this, Severity.OTHER, tr("No style for multipolygon"), NO_STYLE, r));
182 }
183 }
184
185 if (!nonClosedWays.isEmpty()) {
186 errors.add(new TestError(this, Severity.WARNING, tr("Multipolygon is not closed"), NON_CLOSED_WAY, Collections.singletonList(r), nonClosedWays));
187 }
188
189 // For painting is used Polygon class which works with ints only. For validation we need more precision
190 List<GeneralPath> outerPolygons = createPolygons(outerWays);
191 for (List<Node> pdInner : innerWays) {
192 boolean outside = true;
193 boolean crossing = false;
194 List<Node> outerWay = null;
195 for (int i=0; i<outerWays.size(); i++) {
196 GeneralPath outer = outerPolygons.get(i);
197 Intersection intersection = getPolygonIntersection(outer, pdInner);
198 outside = outside & intersection == Intersection.OUTSIDE;
199 if (intersection == Intersection.CROSSING) {
200 crossing = true;
201 outerWay = outerWays.get(i);
202 }
203 }
204 if (outside || crossing) {
205 List<List<Node>> highlights = new ArrayList<List<Node>>();
206 highlights.add(pdInner);
207 if (outside) {
208 errors.add(new TestError(this, Severity.WARNING, tr("Multipolygon inner way is outside"), INNER_WAY_OUTSIDE, Collections.singletonList(r), highlights));
209 } else if (crossing) {
210 highlights.add(outerWay);
211 errors.add(new TestError(this, Severity.WARNING, tr("Intersection between multipolygon ways"), CROSSING_WAYS, Collections.singletonList(r), highlights));
212 }
213 }
214 }
215 }
216 }
217
218 private void checkMembersAndRoles(Relation r) {
219 for (RelationMember rm : r.getMembers()) {
220 if (rm.isWay()) {
221 if (!("inner".equals(rm.getRole()) || "outer".equals(rm.getRole()) || !rm.hasRole())) {
222 errors.add(new TestError(this, Severity.WARNING, tr("No useful role for multipolygon member"), WRONG_MEMBER_ROLE, rm.getMember()));
223 }
224 } else {
225 errors.add(new TestError(this, Severity.WARNING, tr("Non-Way in multipolygon"), WRONG_MEMBER_TYPE, rm.getMember()));
226 }
227 }
228 }
229}
Note: See TracBrowser for help on using the repository browser.