Last active
August 29, 2015 14:01
-
-
Save dherges/b17f94ab4daada3c1358 to your computer and use it in GitHub Desktop.
GeoQuery
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2014 David Herges <david@spektrakel.de> | |
* Licensed under MIT (http://opensource.org/licenses/MIT) | |
*/ | |
define('M_NEGATIVE_PI_2', ((double) 0.0 - M_PI_2)); | |
define('M_NEGATIVE_PI', ((double) 0.0 - M_PI)); | |
define('M_2PI', ((double) 2.0 * M_PI)); | |
/** | |
* A Query to perform geolocation searches. Should be easy to adopt to other Frameworks than TYPO3 | |
* | |
* <p> | |
* All you need is (latitude, longitude, perimeter) -> (southEast, northWest) boundary? | |
* Have a look at {@link #toRectangularBounds($latitude, $longitude, $perimeter)}! | |
* | |
* <p> | |
* Lots of credits go to Jan Matuschek. | |
* | |
* @link http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates | |
*/ | |
class GeoQuery extends \TYPO3\CMS\Extbase\Persistence\Generic\Query { | |
const PROPERTY_NAME_LATITUDE = "latitude"; | |
const PROPERTY_NAME_LONGITUDE = "longitude"; | |
/** | |
* Radius of the Earth's surface in meters | |
*/ | |
const EARTH_RADIUS_METERS = 6371010; | |
const MIN_LAT = M_NEGATIVE_PI_2; // Math.toRadians(-90d); // -PI/2 | |
const MAX_LAT = M_PI_2; // Math.toRadians(90d); // PI/2 | |
const MIN_LON = M_NEGATIVE_PI; // Math.toRadians(-180d); // -PI | |
const MAX_LON = M_PI; // Math.toRadians(180d); // PI | |
/** | |
* Returns a constraint that filters entries whose geolocation is within the rectangular bounds of a plane surface | |
* area around the center point (latitude, longitude) and within radius (perimeter) in meters. | |
* | |
* @param $latitude Latitude in degrees; floating-point value | |
* @param $longitude Longitude in degrees; floating-point value | |
* @param $perimeter Perimeter; integer value in meters | |
* | |
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface | |
*/ | |
public function around($latitude, $longitude, $perimeter) { | |
// (lat, long, perimeter) to rectangular bounds | |
$bounds = $this->toRectangularBounds($latitude, $longitude, $perimeter); | |
// where condition: (lat >= minLat) && (lat <= maxLat) && (lon >= minLon) && (lon <= maxLon) | |
return $this->logicalAnd( | |
$this->greaterThanOrEqual(self::PROPERTY_NAME_LATITUDE, $bounds->minLat), | |
$this->lessThanOrEqual(self::PROPERTY_NAME_LATITUDE, $bounds->maxLat), | |
$this->greaterThanOrEqual(self::PROPERTY_NAME_LONGITUDE, $bounds->minLon), | |
$this->lessThanOrEqual(self::PROPERTY_NAME_LONGITUDE, $bounds->maxLon) | |
); | |
} | |
/** | |
* Courtesy of http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates | |
* | |
* @param $latitude | |
* @param $longitude | |
* @param $perimeter | |
* | |
* @return \stdClass With properties 'minLat', 'maxLat', 'minLon', 'maxLon' | |
*/ | |
private function toRectangularBounds($latitude, $longitude, $perimeter) { | |
// degrees to radians | |
$radLat = deg2rad($latitude); | |
$radLon = deg2rad($longitude); | |
$radDist = $perimeter / self::EARTH_RADIUS_METERS; | |
// initial values for minLat, maxLat, minLon, maxLon | |
$minLat = $radLat - $radDist; | |
$maxLat = $radLat + $radDist; | |
$minLon = (double) 0.0; | |
$maxLon = (double) 0.0; | |
if (($minLat > self::MIN_LAT) && ($maxLat < self::MAX_LAT)) { | |
$deltaLon = asin(sin($radDist) / cos($radLat)); | |
$minLon = $radLon - $deltaLon; | |
if ($minLon < self::MIN_LON) { | |
$minLon += M_2PI; | |
} | |
$maxLon = $radLon + $deltaLon; | |
if ($maxLon > self::MAX_LON) { | |
$maxLon -= M_2PI; | |
} | |
} else { | |
// a pole is within the distance | |
$minLat = max($minLat, self::MIN_LAT); | |
$maxLat = min($maxLat, self::MAX_LAT); | |
$minLon = self::MIN_LON; | |
$maxLon = self::MAX_LON; | |
} | |
$bounds = new \stdClass(); | |
$bounds->minLat = rad2deg($minLat); | |
$bounds->maxLat = rad2deg($maxLat); | |
$bounds->minLon = rad2deg($minLon); | |
$bounds->maxLon = rad2deg($maxLon); | |
return $bounds; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment