diff --git a/Parse/src/main/java/com/parse/OfflineQueryLogic.java b/Parse/src/main/java/com/parse/OfflineQueryLogic.java index dd626cb7c..3e0c52e44 100644 --- a/Parse/src/main/java/com/parse/OfflineQueryLogic.java +++ b/Parse/src/main/java/com/parse/OfflineQueryLogic.java @@ -480,6 +480,23 @@ private static boolean matchesGeoIntersectsConstraint(Object constraint, Object return target.containsPoint(point); } + /** + * Matches $geoWithin constraints. + */ + private static boolean matchesGeoWithinConstraint(Object constraint, Object value) + throws ParseException { + if (value == null || value == JSONObject.NULL) { + return false; + } + + @SuppressWarnings("unchecked") + HashMap> constraintMap = + (HashMap>) constraint; + List points = constraintMap.get("$polygon"); + ParsePolygon polygon = new ParsePolygon(points); + ParseGeoPoint point = (ParseGeoPoint) value; + return polygon.containsPoint(point); + } /** * Returns true iff the given value matches the given operator and constraint. * @@ -535,6 +552,9 @@ private static boolean matchesStatelessConstraint(String operator, Object constr case "$within": return matchesWithinConstraint(constraint, value); + case "$geoWithin": + return matchesGeoWithinConstraint(constraint, value); + case "$geoIntersects": return matchesGeoIntersectsConstraint(constraint, value); diff --git a/Parse/src/main/java/com/parse/ParseQuery.java b/Parse/src/main/java/com/parse/ParseQuery.java index 652cb3363..6b72945bd 100644 --- a/Parse/src/main/java/com/parse/ParseQuery.java +++ b/Parse/src/main/java/com/parse/ParseQuery.java @@ -468,6 +468,12 @@ public Builder whereWithin(String key, ParseGeoPoint southwest, ParseGeoPoint return addCondition(key, "$within", dictionary); } + public Builder whereGeoWithin(String key, List points) { + Map> dictionary = new HashMap<>(); + dictionary.put("$polygon", points); + return addCondition(key, "$geoWithin", dictionary); + } + public Builder whereGeoIntersects(String key, ParseGeoPoint point) { Map dictionary = new HashMap<>(); dictionary.put("$point", point); @@ -1848,6 +1854,28 @@ public ParseQuery whereWithinGeoBox( return this; } + /** + * Adds a constraint to the query that requires a particular key's + * coordinates be contained within and on the bounds of a given polygon. + * Supports closed and open (last point is connected to first) paths + * + * Polygon must have at least 3 points + * + * @param key + * The key to be constrained. + * @param value + * List or ParsePolygon + * @return this, so you can chain this call. + */ + public ParseQuery whereWithinPolygon(String key, List points) { + builder.whereGeoWithin(key, points); + return this; + } + + public ParseQuery whereWithinPolygon(String key, ParsePolygon polygon) { + return whereWithinPolygon(key, polygon.getCoordinates()); + } + /** * Add a constraint to the query that requires a particular key's * coordinates that contains a {@link ParseGeoPoint}s diff --git a/Parse/src/test/java/com/parse/OfflineQueryLogicTest.java b/Parse/src/test/java/com/parse/OfflineQueryLogicTest.java index 7a77cdad4..f7d84c04f 100644 --- a/Parse/src/test/java/com/parse/OfflineQueryLogicTest.java +++ b/Parse/src/test/java/com/parse/OfflineQueryLogicTest.java @@ -579,6 +579,44 @@ public void testMatchesGeoIntersects() throws ParseException { assertFalse(matches(logic, query, object)); } + @Test + public void testMatchesGeoWithin() throws ParseException { + List smallBox = new ArrayList(); + smallBox.add(new ParseGeoPoint(0,0)); + smallBox.add(new ParseGeoPoint(0,1)); + smallBox.add(new ParseGeoPoint(1,1)); + smallBox.add(new ParseGeoPoint(1,0)); + + List largeBox = new ArrayList(); + largeBox.add(new ParseGeoPoint(0,0)); + largeBox.add(new ParseGeoPoint(0,10)); + largeBox.add(new ParseGeoPoint(10,10)); + largeBox.add(new ParseGeoPoint(10,0)); + + ParseGeoPoint point = new ParseGeoPoint(5,5); + + //ParsePolygon polygon = new ParsePolygon(points); + + ParseObject object = new ParseObject("TestObject"); + object.put("point", point); + + ParseQuery.State query; + OfflineQueryLogic logic = new OfflineQueryLogic(null); + query = new ParseQuery.State.Builder<>("TestObject") + .whereGeoWithin("point", largeBox) + .build(); + assertTrue(matches(logic, query, object)); + + query = new ParseQuery.State.Builder<>("TestObject") + .whereGeoWithin("point", smallBox) + .build(); + assertFalse(matches(logic, query, object)); + + // Non-existant key + object = new ParseObject("TestObject"); + assertFalse(matches(logic, query, object)); + } + //endregion //region compare diff --git a/Parse/src/test/java/com/parse/ParseQueryTest.java b/Parse/src/test/java/com/parse/ParseQueryTest.java index 7177543fb..066576afa 100644 --- a/Parse/src/test/java/com/parse/ParseQueryTest.java +++ b/Parse/src/test/java/com/parse/ParseQueryTest.java @@ -530,6 +530,50 @@ public void testWhereWithinGeoBox() throws Exception { assertTrue(list.contains(pointAgain)); } + @Test + public void testWhereWithinPolygon() throws Exception { + ParseQuery query = new ParseQuery<>("Test"); + ParseGeoPoint point1 = new ParseGeoPoint(10, 10); + ParseGeoPoint point2 = new ParseGeoPoint(20, 20); + ParseGeoPoint point3 = new ParseGeoPoint(30, 30); + + List points = Arrays.asList(point1, point2, point3); + query.whereWithinPolygon("key", points); + + // We generate a state to verify the content of the builder + ParseQuery.State state = query.getBuilder().build(); + ParseQuery.QueryConstraints queryConstraints = state.constraints(); + ParseQuery.KeyConstraints keyConstraints = (ParseQuery.KeyConstraints) queryConstraints.get("key"); + Map map = (Map) keyConstraints.get("$geoWithin"); + List list = (List) map.get("$polygon"); + assertEquals(3, list.size()); + assertTrue(list.contains(point1)); + assertTrue(list.contains(point2)); + assertTrue(list.contains(point3)); + } + + @Test + public void testWhereWithinPolygonWithPolygon() throws Exception { + ParseQuery query = new ParseQuery<>("Test"); + ParseGeoPoint point1 = new ParseGeoPoint(10, 10); + ParseGeoPoint point2 = new ParseGeoPoint(20, 20); + ParseGeoPoint point3 = new ParseGeoPoint(30, 30); + + List points = Arrays.asList(point1, point2, point3); + query.whereWithinPolygon("key", new ParsePolygon(points)); + + // We generate a state to verify the content of the builder + ParseQuery.State state = query.getBuilder().build(); + ParseQuery.QueryConstraints queryConstraints = state.constraints(); + ParseQuery.KeyConstraints keyConstraints = (ParseQuery.KeyConstraints) queryConstraints.get("key"); + Map map = (Map) keyConstraints.get("$geoWithin"); + List list = (List) map.get("$polygon"); + assertEquals(3, list.size()); + assertTrue(list.contains(point1)); + assertTrue(list.contains(point2)); + assertTrue(list.contains(point3)); + } + @Test public void testWherePolygonContains() throws Exception { ParseQuery query = new ParseQuery<>("Test");