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

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

add validator plugin to josm core. Original author: Francisco R. Santos (frsantos); major contributions by bilbo, daeron, delta_foxtrot, imi, jttt, jrreid, gabriel, guggis, pieren, rrankin, skela, stoecker, stotz and others

  • 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 {
55 styles = MapPaintStyles.getStyles().getStyleSet();
56 }
57
58 private List<List<Node>> joinWays(Collection<Way> ways) {
59 List<List<Node>> result = new ArrayList<List<Node>>();
60 List<Way> waysToJoin = new ArrayList<Way>();
61 for (Way way: ways) {
62 if (way.isClosed()) {
63 result.add(way.getNodes());
64 } else {
65 waysToJoin.add(way);
66 }
67 }
68
69 for (JoinedWay jw: Multipolygon.joinWays(waysToJoin)) {
70 if (!jw.isClosed()) {
71 nonClosedWays.add(jw.getNodes());
72 } else {
73 result.add(jw.getNodes());
74 }
75 }
76 return result;
77 }
78
79 private GeneralPath createPath(List<Node> nodes) {
80 GeneralPath result = new GeneralPath();
81 result.moveTo((float)nodes.get(0).getCoor().lat(), (float)nodes.get(0).getCoor().lon());
82 for (int i=1; i<nodes.size(); i++) {
83 Node n = nodes.get(i);
84 result.lineTo((float)n.getCoor().lat(), (float)n.getCoor().lon());
85 }
86 return result;
87 }
88
89 private List<GeneralPath> createPolygons(List<List<Node>> joinedWays) {
90 List<GeneralPath> result = new ArrayList<GeneralPath>();
91 for (List<Node> way: joinedWays) {
92 result.add(createPath(way));
93 }
94 return result;
95 }
96
97 private Intersection getPolygonIntersection(GeneralPath outer, List<Node> inner) {
98 boolean inside = false;
99 boolean outside = false;
100
101 for (Node n: inner) {
102 boolean contains = outer.contains(n.getCoor().lat(), n.getCoor().lon());
103 inside = inside | contains;
104 outside = outside | !contains;
105 if (inside & outside) {
106 return Intersection.CROSSING;
107 }
108 }
109
110 return inside?Intersection.INSIDE:Intersection.OUTSIDE;
111 }
112
113 @Override
114 public void visit(Way w) {
115 if (styles != null && !w.isClosed())
116 {
117 ElemStyle e = styles.getArea(w);
118 if(e instanceof AreaElemStyle && !((AreaElemStyle)e).closed)
119 errors.add( new TestError(this, Severity.WARNING, tr("Area style way is not closed"), NOT_CLOSED, w));
120 }
121 }
122
123 @Override
124 public void visit(Relation r) {
125 nonClosedWays.clear();
126 if ("multipolygon".equals(r.get("type"))) {
127 checkMembersAndRoles(r);
128
129 Multipolygon polygon = new Multipolygon(Main.map.mapView);
130 polygon.load(r);
131
132 if (polygon.getOuterWays().isEmpty()) {
133 errors.add( new TestError(this, Severity.WARNING, tr("No outer way for multipolygon"), MISSING_OUTER_WAY, r));
134 }
135
136 for (RelationMember rm: r.getMembers()) {
137 if (!rm.getMember().isUsable()) {
138 return; // Rest of checks is only for complete multipolygons
139 }
140 }
141
142 List<List<Node>> innerWays = joinWays(polygon.getInnerWays()); // Side effect - sets nonClosedWays
143 List<List<Node>> outerWays = joinWays(polygon.getOuterWays());
144
145 if(styles != null) {
146 ElemStyle wayStyle = styles.get(r);
147
148 // If area style was not found for relation then use style of ways
149 if(!(wayStyle instanceof AreaElemStyle)) {
150 errors.add( new TestError(this, Severity.OTHER, tr("No style in multipolygon relation"),
151 NO_STYLE_POLYGON, r));
152 for (Way w : polygon.getOuterWays()) {
153 wayStyle = styles.getArea(w);
154 if(wayStyle != null) {
155 break;
156 }
157 }
158 }
159
160 if (wayStyle instanceof AreaElemStyle) {
161 for (Way wInner : polygon.getInnerWays())
162 {
163 ElemStyle innerStyle = styles.get(wInner);
164 if(wayStyle != null && wayStyle.equals(innerStyle)) {
165 List<OsmPrimitive> l = new ArrayList<OsmPrimitive>();
166 l.add(r);
167 l.add(wInner);
168 errors.add( new TestError(this, Severity.WARNING, tr("Style for inner way equals multipolygon"),
169 INNER_STYLE_MISMATCH, l, Collections.singletonList(wInner)));
170 }
171 }
172 for (Way wOuter : polygon.getOuterWays())
173 {
174 ElemStyle outerStyle = styles.get(wOuter);
175 if(outerStyle instanceof AreaElemStyle && !wayStyle.equals(outerStyle)) {
176 List<OsmPrimitive> l = new ArrayList<OsmPrimitive>();
177 l.add(r);
178 l.add(wOuter);
179 errors.add( new TestError(this, Severity.WARNING, tr("Style for outer way mismatches"),
180 OUTER_STYLE_MISMATCH, l, Collections.singletonList(wOuter)));
181 }
182 }
183 }
184 else
185 errors.add( new TestError(this, Severity.OTHER, tr("No style for multipolygon"),
186 NO_STYLE, r));
187 }
188
189 if (!nonClosedWays.isEmpty()) {
190 errors.add( new TestError(this, Severity.WARNING, tr("Multipolygon is not closed"), NON_CLOSED_WAY, Collections.singletonList(r), nonClosedWays));
191 }
192
193 // For painting is used Polygon class which works with ints only. For validation we need more precision
194 List<GeneralPath> outerPolygons = createPolygons(outerWays);
195 for (List<Node> pdInner: innerWays) {
196 boolean outside = true;
197 boolean crossing = false;
198 List<Node> outerWay = null;
199 for (int i=0; i<outerWays.size(); i++) {
200 GeneralPath outer = outerPolygons.get(i);
201 Intersection intersection = getPolygonIntersection(outer, pdInner);
202 outside = outside & intersection == Intersection.OUTSIDE;
203 if (intersection == Intersection.CROSSING) {
204 crossing = true;
205 outerWay = outerWays.get(i);
206 }
207 }
208 if (outside || crossing) {
209 List<List<Node>> highlights = new ArrayList<List<Node>>();
210 highlights.add(pdInner);
211 if (outside) {
212 errors.add(new TestError(this, Severity.WARNING, tr("Multipolygon inner way is outside"), INNER_WAY_OUTSIDE, Collections.singletonList(r), highlights));
213 } else if (crossing) {
214 highlights.add(outerWay);
215 errors.add(new TestError(this, Severity.WARNING, tr("Intersection between multipolygon ways"), CROSSING_WAYS, Collections.singletonList(r), highlights));
216 }
217 }
218 }
219 }
220 }
221
222 private void checkMembersAndRoles(Relation r) {
223 for (RelationMember rm: r.getMembers()) {
224 if (rm.isWay()) {
225 if (!("inner".equals(rm.getRole()) || "outer".equals(rm.getRole()) || !rm.hasRole())) {
226 errors.add( new TestError(this, Severity.WARNING, tr("No useful role for multipolygon member"), WRONG_MEMBER_ROLE, rm.getMember()));
227 }
228 } else {
229 errors.add( new TestError(this, Severity.WARNING, tr("Non-Way in multipolygon"), WRONG_MEMBER_TYPE, rm.getMember()));
230 }
231 }
232 }
233
234
235}
Note: See TracBrowser for help on using the repository browser.