From 0ee21be7285afc66c53b6e0f0541fb72899b52e4 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Mon, 3 Dec 2018 19:44:43 -0600 Subject: [PATCH] Add sort option to GeoQueries --- src/Parse/ParseQuery.php | 38 ++++++---- tests/Parse/ParseGeoPointTest.php | 113 ++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 14 deletions(-) diff --git a/src/Parse/ParseQuery.php b/src/Parse/ParseQuery.php index bd90b3e6..8c9cd156 100755 --- a/src/Parse/ParseQuery.php +++ b/src/Parse/ParseQuery.php @@ -770,13 +770,27 @@ public function near($key, $point) * @param string $key The key of the ParseGeoPoint * @param ParseGeoPoint $point The ParseGeoPoint that is used. * @param int $maxDistance Maximum distance (in radians) + * @param bool $sort Return objects sorted by distance * * @return ParseQuery Returns this query, so you can chain this call. */ - public function withinRadians($key, $point, $maxDistance) + public function withinRadians($key, $point, $maxDistance, $sort = true) { - $this->near($key, $point); - $this->addCondition($key, '$maxDistance', $maxDistance); + if ($sort) { + $this->near($key, $point); + $this->addCondition($key, '$maxDistance', $maxDistance); + } else { + $this->addCondition( + $key, + '$geoWithin', + [ + '$centerSphere' => [ + [$point->getLongitude(), $point->getLatitude()], + $maxDistance + ] + ] + ); + } return $this; } @@ -784,20 +798,18 @@ public function withinRadians($key, $point, $maxDistance) /** * Add a proximity based constraint for finding objects with key point * values near the point given and within the maximum distance given. - * Radius of earth used is 3958.5 miles. + * Radius of earth used is 3958.8 miles. * * @param string $key The key of the ParseGeoPoint * @param ParseGeoPoint $point The ParseGeoPoint that is used. * @param int $maxDistance Maximum distance (in miles) + * @param bool $sort Return objects sorted by distance * * @return ParseQuery Returns this query, so you can chain this call. */ - public function withinMiles($key, $point, $maxDistance) + public function withinMiles($key, $point, $maxDistance, $sort = true) { - $this->near($key, $point); - $this->addCondition($key, '$maxDistance', $maxDistance / 3958.8); - - return $this; + return $this->withinRadians($key, $point, $maxDistance / 3958.8, $sort); } /** @@ -808,15 +820,13 @@ public function withinMiles($key, $point, $maxDistance) * @param string $key The key of the ParseGeoPoint * @param ParseGeoPoint $point The ParseGeoPoint that is used. * @param int $maxDistance Maximum distance (in kilometers) + * @param bool $sort Return objects sorted by distance * * @return ParseQuery Returns this query, so you can chain this call. */ - public function withinKilometers($key, $point, $maxDistance) + public function withinKilometers($key, $point, $maxDistance, $sort = true) { - $this->near($key, $point); - $this->addCondition($key, '$maxDistance', $maxDistance / 6371.0); - - return $this; + return $this->withinRadians($key, $point, $maxDistance / 6371.0, $sort); } /** diff --git a/tests/Parse/ParseGeoPointTest.php b/tests/Parse/ParseGeoPointTest.php index fb623ffa..4b336dd2 100644 --- a/tests/Parse/ParseGeoPointTest.php +++ b/tests/Parse/ParseGeoPointTest.php @@ -202,6 +202,119 @@ public function testGeoMaxDistanceWithUnits() $this->assertEquals(0, count($results)); } + public function testGeoMaxDistanceWithUnitsUnsorted() + { + Helper::clearClass('PlaceObject'); + // [SAC] 38.52 -121.50 Sacramento,CA + $sacramento = new ParseGeoPoint(38.52, -121.50); + $obj = ParseObject::create('PlaceObject'); + $obj->set('location', $sacramento); + $obj->set('name', 'Sacramento'); + $obj->save(); + + // [HNL] 21.35 -157.93 Honolulu Int,HI + $honolulu = new ParseGeoPoint(21.35, -157.93); + $obj = ParseObject::create('PlaceObject'); + $obj->set('location', $honolulu); + $obj->set('name', 'Honolulu'); + $obj->save(); + + // [51Q] 37.75 -122.68 San Francisco,CA + $sanfran = new ParseGeoPoint(37.75, -122.68); + $obj = ParseObject::create('PlaceObject'); + $obj->set('location', $sanfran); + $obj->set('name', 'San Francisco'); + $obj->save(); + + // test point SFO + $point = new ParseGeoPoint(37.6189722, -122.3748889); + + // Kilometers + // baseline all + $query = new ParseQuery('PlaceObject'); + $query->near('location', $point); + $results = $query->find(); + $this->assertEquals(3, count($results)); + + // max with all + $query = new ParseQuery('PlaceObject'); + $query->withinKilometers('location', $point, 4000.0, false); + $results = $query->find(); + $this->assertEquals(3, count($results)); + + // drop hawaii + $query = new ParseQuery('PlaceObject'); + $query->withinKilometers('location', $point, 3700.0, false); + $results = $query->find(); + $this->assertEquals(2, count($results)); + + // drop sacramento + $query = new ParseQuery('PlaceObject'); + $query->withinKilometers('location', $point, 100.0, false); + $results = $query->find(); + $this->assertEquals(1, count($results)); + $this->assertEquals('San Francisco', $results[0]->get('name')); + + // drop SF + $query = new ParseQuery('PlaceObject'); + $query->withinKilometers('location', $point, 10.0, false); + $results = $query->find(); + $this->assertEquals(0, count($results)); + + // Miles + // max with all + $query = new ParseQuery('PlaceObject'); + $query->withinMiles('location', $point, 2500.0, false); + $results = $query->find(); + $this->assertEquals(3, count($results)); + + // drop hawaii + $query = new ParseQuery('PlaceObject'); + $query->withinMiles('location', $point, 2200.0, false); + $results = $query->find(); + $this->assertEquals(2, count($results)); + + // drop sacramento + $query = new ParseQuery('PlaceObject'); + $query->withinMiles('location', $point, 75.0, false); + $results = $query->find(); + $this->assertEquals(1, count($results)); + $this->assertEquals('San Francisco', $results[0]->get('name')); + + // drop SF + $query = new ParseQuery('PlaceObject'); + $query->withinMiles('location', $point, 10.0, false); + $results = $query->find(); + $this->assertEquals(0, count($results)); + } + + public function testGeoQueriesUnsorted() + { + Helper::clearClass('PlaceObject'); + $sacramento = new ParseGeoPoint(38.52, -121.50); + $obj = ParseObject::create('PlaceObject'); + $obj->set('location', $sacramento); + $obj->set('name', 'Sacramento'); + $obj->save(); + + $point = new ParseGeoPoint(37.6189722, -122.3748889); + + $query = new ParseQuery('PlaceObject'); + $query->withinRadians('location', $point, 3.14 * 2, false); + $this->assertEquals($query->_getOptions(), [ + 'where' => [ + 'location' => [ + '$geoWithin' => [ + '$centerSphere' => [ + [-122.3748889, 37.6189722], + 3.14 * 2 + ] + ] + ] + ] + ]); + } + public function testBadLatitude() { $this->setExpectedException(