Skip to content

Commit 3e9d3b2

Browse files
committed
Add ST_Contains etc.
1 parent f0b3e7a commit 3e9d3b2

File tree

4 files changed

+397
-50
lines changed

4 files changed

+397
-50
lines changed

core/src/main/java/org/apache/calcite/runtime/GeoFunctions.java

Lines changed: 221 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,18 @@
2222
import org.apache.calcite.linq4j.function.Strict;
2323
import org.apache.calcite.util.Util;
2424

25+
import com.esri.core.geometry.Envelope;
2526
import com.esri.core.geometry.Geometry;
2627
import com.esri.core.geometry.GeometryEngine;
28+
import com.esri.core.geometry.Line;
2729
import com.esri.core.geometry.MapGeometry;
30+
import com.esri.core.geometry.Operator;
31+
import com.esri.core.geometry.OperatorBoundary;
32+
import com.esri.core.geometry.OperatorFactoryLocal;
33+
import com.esri.core.geometry.OperatorIntersects;
2834
import com.esri.core.geometry.Point;
2935
import com.esri.core.geometry.Polygon;
36+
import com.esri.core.geometry.Polyline;
3037
import com.esri.core.geometry.SpatialReference;
3138
import com.esri.core.geometry.WktExportFlags;
3239
import com.esri.core.geometry.WktImportFlags;
@@ -46,8 +53,13 @@
4653
* <li>Should we create aliases for functions in upper-case?
4754
* Without ST_ prefix?
4855
* <li>Consider adding spatial literals, e.g. `GEOMETRY 'POINT (30 10)'`
49-
* <li>Integer arugments, e.g. SELECT ST_MakePoint(1, 2, 1.5)
56+
* <li>Integer arguments, e.g. SELECT ST_MakePoint(1, 2, 1.5),
57+
* ST_MakePoint(1, 2)
5058
* <li>Are GEOMETRY values comparable? If so add ORDER BY test
59+
* <li>We have to add 'Z' to create 3D objects. This is inconsistent with
60+
* PostGIS. Who is right? At least document the difference.
61+
* <li>Should add GeometryEngine.intersects; similar to disjoint etc.
62+
* <li>Make {@link #ST_MakeLine(Geom, Geom)} varargs</li>
5163
* </ul>
5264
*/
5365
@SuppressWarnings({"UnnecessaryUnboxing", "WeakerAccess", "unused"})
@@ -168,27 +180,76 @@ public static Geom ST_MPolyFromText(String wkt, int srid) {
168180

169181
// Geometry creation functions ==============================================
170182

171-
/** Constructs a 2D point from coordinates. */
172-
public static Geom ST_MakePoint(BigDecimal x, BigDecimal y) {
173-
// NOTE: Combine the double and BigDecimal variants of this function
174-
if (x == null || y == null) {
175-
return null; // TODO: strictness should make this check unnecessary
176-
}
177-
return ST_MakePoint(x.doubleValue(), y.doubleValue());
183+
/** Creates a line-string from the given POINTs (or MULTIPOINTs). */
184+
public static Geom ST_MakeLine(Geom geom1, Geom geom2) {
185+
return makeLine(geom1, geom2);
178186
}
179187

180-
private static Geom ST_MakePoint(double x, double y) {
181-
final Geometry g = new Point(x, y);
188+
public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3) {
189+
return makeLine(geom1, geom2, geom3);
190+
}
191+
192+
public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3,
193+
Geom geom4) {
194+
return makeLine(geom1, geom2, geom3, geom4);
195+
}
196+
197+
public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3,
198+
Geom geom4, Geom geom5) {
199+
return makeLine(geom1, geom2, geom3, geom4, geom5);
200+
}
201+
202+
public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3,
203+
Geom geom4, Geom geom5, Geom geom6) {
204+
return makeLine(geom1, geom2, geom3, geom4, geom5, geom6);
205+
}
206+
207+
private static Geom makeLine(Geom... geoms) {
208+
final Polyline g = new Polyline();
209+
Point p = null;
210+
for (Geom geom : geoms) {
211+
if (geom.g() instanceof Point) {
212+
final Point prev = p;
213+
p = (Point) geom.g();
214+
if (prev != null) {
215+
final Line line = new Line();
216+
line.setStart(prev);
217+
line.setEnd(p);
218+
g.addSegment(line, false);
219+
}
220+
}
221+
}
182222
return new SimpleGeom(g);
183223
}
184224

185-
/** Constructs a 3D point from coordinates. */
225+
/** Alias for {@link #ST_Point(BigDecimal, BigDecimal)}. */
226+
public static Geom ST_MakePoint(BigDecimal x, BigDecimal y) {
227+
return ST_Point(x, y);
228+
}
229+
230+
/** Alias for {@link #ST_Point(BigDecimal, BigDecimal, BigDecimal)}. */
186231
public static Geom ST_MakePoint(BigDecimal x, BigDecimal y, BigDecimal z) {
232+
return ST_Point(x, y, z);
233+
}
234+
235+
/** Constructs a 2D point from coordinates. */
236+
public static Geom ST_Point(BigDecimal x, BigDecimal y) {
237+
// NOTE: Combine the double and BigDecimal variants of this function
238+
return point(x.doubleValue(), y.doubleValue());
239+
}
240+
241+
/** Constructs a 3D point from coordinates. */
242+
public static Geom ST_Point(BigDecimal x, BigDecimal y, BigDecimal z) {
187243
final Geometry g = new Point(x.doubleValue(), y.doubleValue(),
188244
z.doubleValue());
189245
return new SimpleGeom(g);
190246
}
191247

248+
private static Geom point(double x, double y) {
249+
final Geometry g = new Point(x, y);
250+
return new SimpleGeom(g);
251+
}
252+
192253
// Geometry properties (2D and 3D) ==========================================
193254

194255
/** Returns whether {@code geom} has at least one z-coordinate. */
@@ -202,13 +263,141 @@ public static Double ST_Z(Geom geom) {
202263
? ((Point) geom.g()).getZ() : null;
203264
}
204265

266+
/** Returns the boundary of {@code geom}. */
267+
public static Geom ST_Boundary(Geom geom) {
268+
OperatorBoundary op = OperatorBoundary.local();
269+
Geometry result = op.execute(geom.g(), null);
270+
return geom.wrap(result);
271+
}
272+
205273
/** Returns the distance between {@code geom1} and {@code geom2}. */
206274
public static double ST_Distance(Geom geom1, Geom geom2) {
207275
return GeometryEngine.distance(geom1.g(), geom2.g(), geom1.sr());
208276
}
209277

278+
/** Returns the type of {@code geom}. */
279+
public static String ST_GeometryType(Geom geom) {
280+
return type(geom.g()).name();
281+
}
282+
283+
/** Returns the OGC SFS type code of {@code geom}. */
284+
public static int ST_GeometryTypeCode(Geom geom) {
285+
return type(geom.g()).code;
286+
}
287+
288+
/** Returns the OGC type of a geometry. */
289+
private static Type type(Geometry g) {
290+
switch (g.getType()) {
291+
case Point:
292+
return Type.POINT;
293+
case Polyline:
294+
return Type.LINESTRING;
295+
case Polygon:
296+
return Type.POLYGON;
297+
case MultiPoint:
298+
return Type.MULTIPOINT;
299+
case Envelope:
300+
return Type.POLYGON;
301+
case Line:
302+
return Type.LINESTRING;
303+
case Unknown:
304+
return Type.Geometry;
305+
default:
306+
throw new AssertionError(g);
307+
}
308+
}
309+
310+
/** Returns the minimum bounding box of {@code geom} (which may be a
311+
* GEOMETRYCOLLECTION). */
312+
public static Geom ST_Envelope(Geom geom) {
313+
final Envelope env = envelope(geom.g());
314+
return geom.wrap(env);
315+
}
316+
317+
private static Envelope envelope(Geometry g) {
318+
final Envelope env = new Envelope();
319+
g.queryEnvelope(env);
320+
return env;
321+
}
322+
210323
// Geometry predicates ======================================================
211324

325+
/** Returns whether {@code geom1} contains {@code geom2}. */
326+
public static boolean ST_Contains(Geom geom1, Geom geom2) {
327+
return GeometryEngine.contains(geom1.g(), geom2.g(), geom1.sr());
328+
}
329+
330+
/** Returns whether {@code geom1} contains {@code geom2} but does not
331+
* intersect its boundary. */
332+
public static boolean ST_ContainsProperly(Geom geom1, Geom geom2) {
333+
return GeometryEngine.contains(geom1.g(), geom2.g(), geom1.sr())
334+
&& !GeometryEngine.crosses(geom1.g(), geom2.g(), geom1.sr());
335+
}
336+
337+
/** Returns whether no point in {@code geom2} is outside {@code geom1}. */
338+
private static boolean ST_Covers(Geom geom1, Geom geom2) {
339+
throw todo();
340+
}
341+
342+
/** Returns whether {@code geom1} crosses {@code geom2}. */
343+
public static boolean ST_Crosses(Geom geom1, Geom geom2) {
344+
return GeometryEngine.crosses(geom1.g(), geom2.g(), geom1.sr());
345+
}
346+
347+
/** Returns whether {@code geom1} and {@code geom2} are disjoint. */
348+
public static boolean ST_Disjoint(Geom geom1, Geom geom2) {
349+
return GeometryEngine.disjoint(geom1.g(), geom2.g(), geom1.sr());
350+
}
351+
352+
/** Returns whether the envelope of {@code geom1} intersects the envelope of
353+
* {@code geom2}. */
354+
public static boolean ST_EnvelopesIntersect(Geom geom1, Geom geom2) {
355+
final Geometry e1 = envelope(geom1.g());
356+
final Geometry e2 = envelope(geom2.g());
357+
return intersects(e1, e2, geom1.sr());
358+
}
359+
360+
/** Returns whether {@code geom1} equals {@code geom2}. */
361+
public static boolean ST_Equals(Geom geom1, Geom geom2) {
362+
return GeometryEngine.equals(geom1.g(), geom2.g(), geom1.sr());
363+
}
364+
365+
/** Returns whether {@code geom1} intersects {@code geom2}. */
366+
public static boolean ST_Intersects(Geom geom1, Geom geom2) {
367+
final Geometry g1 = geom1.g();
368+
final Geometry g2 = geom2.g();
369+
final SpatialReference sr = geom1.sr();
370+
return intersects(g1, g2, sr);
371+
}
372+
373+
private static boolean intersects(Geometry g1, Geometry g2,
374+
SpatialReference sr) {
375+
final OperatorIntersects op = (OperatorIntersects) OperatorFactoryLocal
376+
.getInstance().getOperator(Operator.Type.Intersects);
377+
return op.execute(g1, g2, sr, null);
378+
}
379+
380+
/** Returns whether {@code geom1} equals {@code geom2} and their coordinates
381+
* and component Geometries are listed in the same order. */
382+
public static boolean ST_OrderingEquals(Geom geom1, Geom geom2) {
383+
return GeometryEngine.equals(geom1.g(), geom2.g(), geom1.sr());
384+
}
385+
386+
/** Returns {@code geom1} overlaps {@code geom2}. */
387+
public static boolean ST_Overlaps(Geom geom1, Geom geom2) {
388+
return GeometryEngine.overlaps(geom1.g(), geom2.g(), geom1.sr());
389+
}
390+
391+
/** Returns whether {@code geom1} touches {@code geom2}. */
392+
public static boolean ST_Touches(Geom geom1, Geom geom2) {
393+
return GeometryEngine.touches(geom1.g(), geom2.g(), geom1.sr());
394+
}
395+
396+
/** Returns whether {@code geom1} is within {@code geom2}. */
397+
public static boolean ST_Within(Geom geom1, Geom geom2) {
398+
return GeometryEngine.within(geom1.g(), geom2.g(), geom1.sr());
399+
}
400+
212401
/** Returns whether {@code geom1} and {@code geom2} are within
213402
* {@code distance} of each other. */
214403
public static boolean ST_DWithin(Geom geom1, Geom geom2, double distance) {
@@ -436,6 +625,27 @@ public Geom wrap(Geometry g) {
436625
return bind(g, this.mg.getSpatialReference());
437626
}
438627
}
628+
629+
/** Geometry types, with the names and codes assigned by OGC. */
630+
enum Type {
631+
Geometry(0),
632+
POINT(1),
633+
LINESTRING(2),
634+
POLYGON(3),
635+
MULTIPOINT(4),
636+
MULTILINESTRING(5),
637+
MULTIPOLYGON(6),
638+
GEOMCOLLECTION(7),
639+
CURVE(13),
640+
SURFACE(14),
641+
POLYHEDRALSURFACE(15);
642+
643+
final int code;
644+
645+
Type(int code) {
646+
this.code = code;
647+
}
648+
}
439649
}
440650

441651
// End GeoFunctions.java

0 commit comments

Comments
 (0)