1
2
3
4
5
6
7
8
9 package org.locationtech.spatial4j.shape.impl;
10
11 import org.locationtech.spatial4j.context.SpatialContext;
12 import org.locationtech.spatial4j.distance.DistanceUtils;
13 import org.locationtech.spatial4j.shape.Point;
14 import org.locationtech.spatial4j.shape.Rectangle;
15 import org.locationtech.spatial4j.shape.SpatialRelation;
16
17 import java.util.Formatter;
18 import java.util.Locale;
19
20
21
22
23 public class GeoCircle extends CircleImpl {
24 private GeoCircle inverseCircle;
25 private double horizAxisY;
26
27 public GeoCircle(Point p, double radiusDEG, SpatialContext ctx) {
28 super(p, radiusDEG, ctx);
29 assert ctx.isGeo();
30 init();
31 }
32
33 @Override
34 public void reset(double x, double y, double radiusDEG) {
35 super.reset(x, y, radiusDEG);
36 init();
37 }
38
39 private void init() {
40 if (radiusDEG > 90) {
41
42 assert enclosingBox.getWidth() == 360;
43 double backDistDEG = 180 - radiusDEG;
44 if (backDistDEG > 0) {
45 double backRadius = 180 - radiusDEG;
46 double backX = DistanceUtils.normLonDEG(getCenter().getX() + 180);
47 double backY = DistanceUtils.normLatDEG(getCenter().getY() + 180);
48
49
50
51 backRadius -= Math.max(Math.ulp(Math.abs(backY)+backRadius), Math.ulp(Math.abs(backX)+backRadius));
52 if (inverseCircle != null) {
53 inverseCircle.reset(backX, backY, backRadius);
54 } else {
55 inverseCircle = new GeoCircle(ctx.makePoint(backX, backY), backRadius, ctx);
56 }
57 } else {
58 inverseCircle = null;
59 }
60 horizAxisY = getCenter().getY();
61 } else {
62 inverseCircle = null;
63 double _horizAxisY = ctx.getDistCalc().calcBoxByDistFromPt_yHorizAxisDEG(getCenter(), radiusDEG, ctx);
64
65 if (_horizAxisY > enclosingBox.getMaxY()) {
66 horizAxisY = enclosingBox.getMaxY();
67 } else if (_horizAxisY < enclosingBox.getMinY()) {
68 horizAxisY = enclosingBox.getMinY();
69 } else {
70 horizAxisY = _horizAxisY;
71 }
72
73 }
74 }
75
76 @Override
77 protected double getYAxis() {
78 return horizAxisY;
79 }
80
81
82
83
84
85
86 @Override
87 protected SpatialRelation relateRectanglePhase2(Rectangle r, SpatialRelation bboxSect) {
88
89 if (inverseCircle != null) {
90 return inverseCircle.relate(r).inverse();
91 }
92
93
94 if (enclosingBox.getWidth() == 360) {
95 return relateRectangleCircleWrapsPole(r, ctx);
96 }
97
98
99 if (!enclosingBox.getCrossesDateLine() && !r.getCrossesDateLine()) {
100 return super.relateRectanglePhase2(r, bboxSect);
101 }
102
103
104 if (r.getWidth() == 360) {
105 return SpatialRelation.INTERSECTS;
106 }
107
108
109 int cornersIntersect = numCornersIntersect(r);
110 if (cornersIntersect == 4) {
111
112 SpatialRelation xIntersect = r.relateXRange(enclosingBox.getMinX(), enclosingBox.getMaxX());
113 if (xIntersect == SpatialRelation.WITHIN)
114 return SpatialRelation.CONTAINS;
115 return SpatialRelation.INTERSECTS;
116 }
117
118
119 if (cornersIntersect > 0)
120 return SpatialRelation.INTERSECTS;
121
122
123
124
125
126 if ( r.relateYRange(getYAxis(), getYAxis()).intersects()
127 && r.relateXRange(enclosingBox.getMinX(), enclosingBox.getMaxX()).intersects() )
128 return SpatialRelation.INTERSECTS;
129
130
131 if (r.relateXRange(getXAxis(), getXAxis()).intersects()) {
132 double yTop = getCenter().getY()+ radiusDEG;
133 assert yTop <= 90;
134 double yBot = getCenter().getY()- radiusDEG;
135 assert yBot >= -90;
136 if (r.relateYRange(yBot, yTop).intersects())
137 return SpatialRelation.INTERSECTS;
138 }
139
140 return SpatialRelation.DISJOINT;
141 }
142
143 private SpatialRelation relateRectangleCircleWrapsPole(Rectangle r, SpatialContext ctx) {
144
145
146
147 if (radiusDEG == 180)
148 return SpatialRelation.CONTAINS;
149
150
151 double yTop = getCenter().getY() + radiusDEG;
152 if (yTop > 90) {
153 double yTopOverlap = yTop - 90;
154 assert yTopOverlap <= 90;
155 if (r.getMinY() >= 90 - yTopOverlap)
156 return SpatialRelation.CONTAINS;
157 } else {
158 double yBot = point.getY() - radiusDEG;
159 if (yBot < -90) {
160 double yBotOverlap = -90 - yBot;
161 assert yBotOverlap <= 90;
162 if (r.getMaxY() <= -90 + yBotOverlap)
163 return SpatialRelation.CONTAINS;
164 } else {
165
166 assert yTop == 90 || yBot == -90;
167
168 }
169 }
170
171
172 if (r.getWidth() == 360)
173 return SpatialRelation.INTERSECTS;
174
175
176 int cornersIntersect = numCornersIntersect(r);
177
178
179 double frontX = getCenter().getX();
180 if (cornersIntersect == 4) {
181 double backX = frontX <= 0 ? frontX + 180 : frontX - 180;
182 if (r.relateXRange(backX, backX).intersects())
183 return SpatialRelation.INTERSECTS;
184 else
185 return SpatialRelation.CONTAINS;
186 } else if (cornersIntersect == 0) {
187 if (r.relateXRange(frontX, frontX).intersects())
188 return SpatialRelation.INTERSECTS;
189 else
190 return SpatialRelation.DISJOINT;
191 } else
192 return SpatialRelation.INTERSECTS;
193 }
194
195
196 private int numCornersIntersect(Rectangle r) {
197
198 boolean bool;
199
200 bool = (contains(r.getMinX(),r.getMinY()));
201 if (contains(r.getMinX(),r.getMaxY())) {
202 if (!bool)
203 return 1;
204 } else {
205 if (bool)
206 return 1;
207 }
208 if (contains(r.getMaxX(),r.getMinY())) {
209 if (!bool)
210 return 1;
211 } else {
212 if (bool)
213 return 1;
214 }
215 if (contains(r.getMaxX(),r.getMaxY())) {
216 if (!bool)
217 return 1;
218 } else {
219 if (bool)
220 return 1;
221 }
222 return bool?4:0;
223 }
224
225 @Override
226 public String toString() {
227
228 double distKm = DistanceUtils.degrees2Dist(radiusDEG, DistanceUtils.EARTH_MEAN_RADIUS_KM);
229
230 String dStr = new Formatter(Locale.ROOT).format("%.1f\u00B0 %.2fkm", radiusDEG, distKm).toString();
231 return "Circle(" + point + ", d=" + dStr + ')';
232 }
233 }