source: josm/trunk/src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java@ 15390

Last change on this file since 15390 was 15390, checked in by Don-vip, 5 years ago

remove deprecated code

  • Property svn:eol-style set to native
File size: 8.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.geom.Area;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14import java.util.stream.Collectors;
15
16import org.openstreetmap.josm.data.validation.tests.MultipolygonTest;
17import org.openstreetmap.josm.tools.CheckParameterUtil;
18import org.openstreetmap.josm.tools.Geometry;
19import org.openstreetmap.josm.tools.MultiMap;
20import org.openstreetmap.josm.tools.Pair;
21
22/**
23 * Helper class to build multipolygons from multiple ways.
24 * @author viesturs
25 * @since 7392 (rename)
26 * @since 3704
27 */
28public class MultipolygonBuilder {
29
30 /**
31 * Represents one polygon that consists of multiple ways.
32 */
33 public static class JoinedPolygon {
34 /** list of ways building this polygon */
35 public final List<Way> ways;
36 /** list of flags that indicate if the nodes of the way in the same position where reversed */
37 public final List<Boolean> reversed;
38 /** the nodes of the polygon, first node is not duplicated as last node. */
39 public final List<Node> nodes;
40 /** the area in east/north space */
41 public final Area area;
42
43 /**
44 * Constructs a new {@code JoinedPolygon} from given list of ways.
45 * @param ways The ways used to build joined polygon
46 * @param reversed list of reversed states
47 */
48 public JoinedPolygon(List<Way> ways, List<Boolean> reversed) {
49 this.ways = ways;
50 this.reversed = reversed;
51 this.nodes = this.getNodes();
52 this.area = Geometry.getArea(nodes);
53 }
54
55 /**
56 * Creates a polygon from single way.
57 * @param way the way to form the polygon
58 */
59 public JoinedPolygon(Way way) {
60 this(Collections.singletonList(way), Collections.singletonList(Boolean.FALSE));
61 }
62
63 /**
64 * Builds a list of nodes for this polygon. First node is not duplicated as last node.
65 * @return list of nodes
66 */
67 public List<Node> getNodes() {
68 List<Node> ringNodes = new ArrayList<>();
69
70 for (int waypos = 0; waypos < this.ways.size(); waypos++) {
71 Way way = this.ways.get(waypos);
72
73 if (!this.reversed.get(waypos)) {
74 for (int pos = 0; pos < way.getNodesCount() - 1; pos++) {
75 ringNodes.add(way.getNode(pos));
76 }
77 } else {
78 for (int pos = way.getNodesCount() - 1; pos > 0; pos--) {
79 ringNodes.add(way.getNode(pos));
80 }
81 }
82 }
83
84 return ringNodes;
85 }
86 }
87
88 /** List of outer ways **/
89 public final List<JoinedPolygon> outerWays;
90 /** List of inner ways **/
91 public final List<JoinedPolygon> innerWays;
92
93 /**
94 * Constructs a new {@code MultipolygonBuilder} initialized with given ways.
95 * @param outerWays The outer ways
96 * @param innerWays The inner ways
97 */
98 public MultipolygonBuilder(List<JoinedPolygon> outerWays, List<JoinedPolygon> innerWays) {
99 this.outerWays = outerWays;
100 this.innerWays = innerWays;
101 }
102
103 /**
104 * Constructs a new empty {@code MultipolygonBuilder}.
105 */
106 public MultipolygonBuilder() {
107 this.outerWays = new ArrayList<>(0);
108 this.innerWays = new ArrayList<>(0);
109 }
110
111 /**
112 * Splits ways into inner and outer JoinedWays. Sets {@link #innerWays} and {@link #outerWays} to the result.
113 * Calculation is done in {@link MultipolygonTest#makeFromWays(Collection)} to ensure that the result is a valid multipolygon.
114 * @param ways ways to analyze
115 * @return error description if the ways cannot be split, {@code null} if all fine.
116 */
117 public String makeFromWays(Collection<Way> ways) {
118 MultipolygonTest mpTest = new MultipolygonTest();
119 Relation calculated = mpTest.makeFromWays(ways);
120 if (!mpTest.getErrors().isEmpty()) {
121 return mpTest.getErrors().iterator().next().getMessage();
122 }
123 Pair<List<JoinedPolygon>, List<JoinedPolygon>> outerInner = joinWays(calculated);
124 this.outerWays.clear();
125 this.innerWays.clear();
126 this.outerWays.addAll(outerInner.a);
127 this.innerWays.addAll(outerInner.b);
128 return null;
129 }
130
131 /**
132 * An exception indicating an error while joining ways to multipolygon rings.
133 */
134 public static class JoinedPolygonCreationException extends RuntimeException {
135 /**
136 * Constructs a new {@code JoinedPolygonCreationException}.
137 * @param message the detail message. The detail message is saved for
138 * later retrieval by the {@link #getMessage()} method
139 */
140 public JoinedPolygonCreationException(String message) {
141 super(message);
142 }
143 }
144
145 /**
146 * Joins the given {@code multipolygon} to a pair of outer and inner multipolygon rings.
147 *
148 * @param multipolygon the multipolygon to join.
149 * @return a pair of outer and inner multipolygon rings.
150 * @throws JoinedPolygonCreationException if the creation fails.
151 */
152 public static Pair<List<JoinedPolygon>, List<JoinedPolygon>> joinWays(Relation multipolygon) {
153 CheckParameterUtil.ensureThat(multipolygon.isMultipolygon(), "multipolygon.isMultipolygon");
154 final Map<String, Set<Way>> members = multipolygon.getMembers().stream()
155 .filter(RelationMember::isWay)
156 .collect(Collectors.groupingBy(RelationMember::getRole, Collectors.mapping(RelationMember::getWay, Collectors.toSet())));
157 final List<JoinedPolygon> outerRings = joinWays(members.getOrDefault("outer", Collections.emptySet()));
158 final List<JoinedPolygon> innerRings = joinWays(members.getOrDefault("inner", Collections.emptySet()));
159 return Pair.create(outerRings, innerRings);
160 }
161
162 /**
163 * Joins the given {@code ways} to multipolygon rings.
164 * @param ways the ways to join.
165 * @return a list of multipolygon rings.
166 * @throws JoinedPolygonCreationException if the creation fails.
167 */
168 public static List<JoinedPolygon> joinWays(Collection<Way> ways) {
169 List<JoinedPolygon> joinedWays = new ArrayList<>();
170
171 //collect ways connecting to each node.
172 MultiMap<Node, Way> nodesWithConnectedWays = new MultiMap<>();
173 Set<Way> usedWays = new HashSet<>();
174
175 for (Way w: ways) {
176 if (w.getNodesCount() < 2) {
177 throw new JoinedPolygonCreationException(tr("Cannot add a way with only {0} nodes.", w.getNodesCount()));
178 }
179
180 if (w.isClosed()) {
181 //closed way, add as is.
182 JoinedPolygon jw = new JoinedPolygon(w);
183 joinedWays.add(jw);
184 usedWays.add(w);
185 } else {
186 nodesWithConnectedWays.put(w.lastNode(), w);
187 nodesWithConnectedWays.put(w.firstNode(), w);
188 }
189 }
190
191 //process unclosed ways
192 for (Way startWay: ways) {
193 if (usedWays.contains(startWay)) {
194 continue;
195 }
196
197 Node startNode = startWay.firstNode();
198 List<Way> collectedWays = new ArrayList<>();
199 List<Boolean> collectedWaysReverse = new ArrayList<>();
200 Way curWay = startWay;
201 Node prevNode = startNode;
202
203 //find polygon ways
204 while (true) {
205 boolean curWayReverse = prevNode == curWay.lastNode();
206 Node nextNode = curWayReverse ? curWay.firstNode() : curWay.lastNode();
207
208 //add cur way to the list
209 collectedWays.add(curWay);
210 collectedWaysReverse.add(Boolean.valueOf(curWayReverse));
211
212 if (nextNode == startNode) {
213 //way finished
214 break;
215 }
216
217 //find next way
218 Collection<Way> adjacentWays = nodesWithConnectedWays.get(nextNode);
219
220 if (adjacentWays.size() != 2) {
221 throw new JoinedPolygonCreationException(tr("Each node must connect exactly 2 ways"));
222 }
223
224 Way nextWay = null;
225 for (Way way: adjacentWays) {
226 if (way != curWay) {
227 nextWay = way;
228 }
229 }
230
231 //move to the next way
232 curWay = nextWay;
233 prevNode = nextNode;
234 }
235
236 usedWays.addAll(collectedWays);
237 joinedWays.add(new JoinedPolygon(collectedWays, collectedWaysReverse));
238 }
239
240 return joinedWays;
241 }
242}
Note: See TracBrowser for help on using the repository browser.