|
|
@@ -3,7 +3,7 @@
|
|
|
namespace App\Doctrine\ORM\AST;
|
|
|
|
|
|
use Doctrine\ORM\Query\AST\ASTException;
|
|
|
-use Doctrine\ORM\Query\AST\ComparisonExpression;
|
|
|
+use Doctrine\ORM\Query\AST\Functions\FunctionNode;
|
|
|
use Doctrine\ORM\Query\AST\Node;
|
|
|
use Doctrine\ORM\Query\Lexer;
|
|
|
use Doctrine\ORM\Query\Parser;
|
|
|
@@ -11,7 +11,9 @@ use Doctrine\ORM\Query\QueryException;
|
|
|
use Doctrine\ORM\Query\SqlWalker;
|
|
|
|
|
|
/**
|
|
|
- * Calcules la distance en km à vol d'oiseau entre les coordonnées géographiques données (latitude + longitude) de deux points.
|
|
|
+ * SphericalDistanceFunction ::= "SPHERICAL_DISTANCE" "(" ArithmeticPrimary "," ArithmeticPrimary "," ArithmeticPrimary "," ArithmeticPrimary ")"
|
|
|
+ *
|
|
|
+ * Calcule la distance en km à vol d'oiseau entre les coordonnées géographiques données (latitude, longitude) de deux points.
|
|
|
*
|
|
|
* Implémentation de la formule de Haversine, dont la précision est de l'ordre de la dizaine de mètres dans les cas les plus courants.
|
|
|
*
|
|
|
@@ -19,10 +21,12 @@ use Doctrine\ORM\Query\SqlWalker;
|
|
|
*
|
|
|
* SPHERICAL_DISTANCE(latitude1, longitude1, latitude2, longitude2)
|
|
|
*
|
|
|
+ * WARNING: passing latitude2 and longitude2 as parameter, even named, is not properly interpreted, pass them directly.
|
|
|
+ *
|
|
|
* @see https://fr.wikipedia.org/wiki/Coordonn%C3%A9es_sph%C3%A9riques
|
|
|
* @see https://fr.wikipedia.org/wiki/Formule_de_haversine
|
|
|
*/
|
|
|
-class SphericDistance
|
|
|
+class SphericalDistance extends FunctionNode
|
|
|
{
|
|
|
private Node | string $latitude1;
|
|
|
private Node | string $longitude1;
|
|
|
@@ -60,24 +64,22 @@ class SphericDistance
|
|
|
{
|
|
|
$R = 6371; // Rayon terrestre, en km
|
|
|
|
|
|
- $rLat1 = '({lat1} * PI() / 180)'; // Latitude 1, en radians
|
|
|
- $rLon1 = '({lon1} * PI() / 180)'; // Longitude 1, en radians
|
|
|
- $rLat2 = '({lat2} * PI() / 180)'; // Latitude 2, en radians
|
|
|
- $rLon2 = '({lon2} * PI() / 180)'; // Longitude 2, en radians
|
|
|
+ $lat2 = $this->latitude2->dispatch($sqlWalker);
|
|
|
+ $lat1 = $this->latitude1->dispatch($sqlWalker);
|
|
|
+
|
|
|
+ // Call two additional dispatch so doctrine complete the parameters stack (careful: the order is important)
|
|
|
+ $this->latitude1->dispatch($sqlWalker);
|
|
|
+ $this->latitude2->dispatch($sqlWalker);
|
|
|
|
|
|
- $sql = sprintf(
|
|
|
- '2 * %1$s * ASIN(SQRT(POW(SIN((%4$s - %2$s) / 2), 2) + COS(%2$s) * COS(%4$s) * POW(SIN((%5$s - %3$s) / 2), 2)))',
|
|
|
- $R, $rLat1, $rLon1, $rLat2, $rLon2
|
|
|
- );
|
|
|
+ $lon1 = $this->longitude1->dispatch($sqlWalker);
|
|
|
+ $lon2 = $this->longitude2->dispatch($sqlWalker);
|
|
|
|
|
|
- $sql = str_replace(array('{lat1}', '{lon1}', '{lat2}', '{lon2}'), array('(%1$s)', '(%2$s)', '(%3$s)', '(%4$s)'), $sql);
|
|
|
+ // Latitudes et longitudes en radians
|
|
|
+ $rLat1 = "($lat1 * PI() / 180)";
|
|
|
+ $rLon1 = "($lon1 * PI() / 180)";
|
|
|
+ $rLat2 = "($lat2 * PI() / 180)";
|
|
|
+ $rLon2 = "($lon2 * PI() / 180)";
|
|
|
|
|
|
- return sprintf(
|
|
|
- $sql,
|
|
|
- $this->latitude1->dispatch($sqlWalker),
|
|
|
- $this->longitude1->dispatch($sqlWalker),
|
|
|
- $this->latitude2->dispatch($sqlWalker),
|
|
|
- $this->longitude2->dispatch($sqlWalker)
|
|
|
- );
|
|
|
+ return "2 * $R * ASIN(SQRT(POW(SIN(($rLat2 - $rLat1) / 2), 2) + COS($rLat1) * COS($rLat2) * POW(SIN(($rLon2 - $rLon1) / 2), 2)))";
|
|
|
}
|
|
|
}
|