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 }