1
2
3
4
5
6
7
8
9 package org.locationtech.spatial4j.shape;
10
11 import org.locationtech.spatial4j.context.SpatialContext;
12 import org.locationtech.spatial4j.shape.impl.BBoxCalculator;
13
14 import java.util.*;
15
16 import static org.locationtech.spatial4j.shape.SpatialRelation.CONTAINS;
17 import static org.locationtech.spatial4j.shape.SpatialRelation.INTERSECTS;
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public class ShapeCollection<S extends Shape> extends AbstractList<S> implements Shape {
39
40 protected final SpatialContext ctx;
41 protected final List<S> shapes;
42 protected final Rectangle bbox;
43
44
45
46
47
48 public ShapeCollection(List<S> shapes, SpatialContext ctx) {
49 if (!(shapes instanceof RandomAccess))
50 throw new IllegalArgumentException("Shapes arg must implement RandomAccess: "+shapes.getClass());
51 this.shapes = shapes;
52 this.ctx = ctx;
53 this.bbox = computeBoundingBox(shapes, ctx);
54 }
55
56 protected Rectangle computeBoundingBox(Collection<? extends Shape> shapes, SpatialContext ctx) {
57 if (shapes.isEmpty())
58 return ctx.makeRectangle(Double.NaN, Double.NaN, Double.NaN, Double.NaN);
59 BBoxCalculator bboxCalc = new BBoxCalculator(ctx);
60 for (Shape geom : shapes) {
61 bboxCalc.expandRange(geom.getBoundingBox());
62 }
63 return bboxCalc.getBoundary();
64 }
65
66 public List<S> getShapes() {
67 return shapes;
68 }
69
70 @Override
71 public S get(int index) {
72 return shapes.get(index);
73 }
74
75 @Override
76 public int size() {
77 return shapes.size();
78 }
79
80 @Override
81 public Rectangle getBoundingBox() {
82 return bbox;
83 }
84
85 @Override
86 public Point getCenter() {
87 return bbox.getCenter();
88 }
89
90 @Override
91 public boolean hasArea() {
92 for (Shape geom : shapes) {
93 if( geom.hasArea() ) {
94 return true;
95 }
96 }
97 return false;
98 }
99
100 @Override
101 public ShapeCollection getBuffered(double distance, SpatialContext ctx) {
102 List<Shape> bufColl = new ArrayList<>(size());
103 for (Shape shape : shapes) {
104 bufColl.add(shape.getBuffered(distance, ctx));
105 }
106 return ctx.makeCollection(bufColl);
107 }
108
109 @Override
110 public SpatialRelation relate(Shape other) {
111 final SpatialRelation bboxSect = bbox.relate(other);
112 if (bboxSect == SpatialRelation.DISJOINT || bboxSect == SpatialRelation.WITHIN)
113 return bboxSect;
114
115 final boolean containsWillShortCircuit = (other instanceof Point) ||
116 relateContainsShortCircuits();
117 SpatialRelation sect = null;
118 for (Shape shape : shapes) {
119 SpatialRelation nextSect = shape.relate(other);
120
121 if (sect == null) {
122 sect = nextSect;
123 } else {
124 sect = sect.combine(nextSect);
125 }
126
127 if (sect == INTERSECTS)
128 return INTERSECTS;
129
130 if (sect == CONTAINS && containsWillShortCircuit)
131 return CONTAINS;
132 }
133 return sect;
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 protected boolean relateContainsShortCircuits() {
152 return true;
153 }
154
155
156
157
158
159
160
161
162 protected static boolean computeMutualDisjoint(List<? extends Shape> shapes) {
163
164
165 for (int i = 1; i < shapes.size(); i++) {
166 Shape shapeI = shapes.get(i);
167 for (int j = 0; j < i; j++) {
168 Shape shapeJ = shapes.get(j);
169 if (shapeJ.relate(shapeI).intersects())
170 return false;
171 }
172 }
173 return true;
174 }
175
176 @Override
177 public double getArea(SpatialContext ctx) {
178 double MAX_AREA = bbox.getArea(ctx);
179 double sum = 0;
180 for (Shape geom : shapes) {
181 sum += geom.getArea(ctx);
182 if (sum >= MAX_AREA)
183 return MAX_AREA;
184 }
185
186 return sum;
187 }
188
189 @Override
190 public String toString() {
191 StringBuilder buf = new StringBuilder(100);
192 buf.append("ShapeCollection(");
193 int i = 0;
194 for (Shape shape : shapes) {
195 if (i++ > 0)
196 buf.append(", ");
197 buf.append(shape);
198 if (buf.length() > 150) {
199 buf.append(" ...").append(shapes.size());
200 break;
201 }
202 }
203 buf.append(")");
204 return buf.toString();
205 }
206
207 @Override
208 public boolean equals(Object o) {
209 if (this == o) return true;
210 if (o == null || getClass() != o.getClass()) return false;
211
212 ShapeCollection that = (ShapeCollection) o;
213
214 if (!shapes.equals(that.shapes)) return false;
215
216 return true;
217 }
218
219 @Override
220 public int hashCode() {
221 return shapes.hashCode();
222 }
223
224 @Override
225 public SpatialContext getContext() {
226 return ctx;
227 }
228 }