|
|
@@ -0,0 +1,83 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace App\Doctrine\ORM\AST;
|
|
|
+
|
|
|
+use Doctrine\ORM\Query\AST\ASTException;
|
|
|
+use Doctrine\ORM\Query\AST\ComparisonExpression;
|
|
|
+use Doctrine\ORM\Query\AST\Node;
|
|
|
+use Doctrine\ORM\Query\Lexer;
|
|
|
+use Doctrine\ORM\Query\Parser;
|
|
|
+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.
|
|
|
+ *
|
|
|
+ * 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.
|
|
|
+ *
|
|
|
+ * Pour utiliser la fonction :
|
|
|
+ *
|
|
|
+ * SPHERICAL_DISTANCE(latitude1, longitude1, latitude2, longitude2)
|
|
|
+ *
|
|
|
+ * @see https://fr.wikipedia.org/wiki/Coordonn%C3%A9es_sph%C3%A9riques
|
|
|
+ * @see https://fr.wikipedia.org/wiki/Formule_de_haversine
|
|
|
+ */
|
|
|
+class SphericDistance
|
|
|
+{
|
|
|
+ private Node | string $latitude1;
|
|
|
+ private Node | string $longitude1;
|
|
|
+ private Node | string $latitude2;
|
|
|
+ private Node | string $longitude2;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Parse DQL Function
|
|
|
+ *
|
|
|
+ * @param Parser $parser
|
|
|
+ * @throws QueryException
|
|
|
+ */
|
|
|
+ public function parse(Parser $parser): void
|
|
|
+ {
|
|
|
+ $parser->match(Lexer::T_IDENTIFIER);
|
|
|
+ $parser->match(Lexer::T_OPEN_PARENTHESIS);
|
|
|
+ $this->latitude1 = $parser->ArithmeticPrimary();
|
|
|
+ $parser->match(Lexer::T_COMMA);
|
|
|
+ $this->longitude1 = $parser->ArithmeticPrimary();
|
|
|
+ $parser->match(Lexer::T_COMMA);
|
|
|
+ $this->latitude2 = $parser->ArithmeticPrimary();
|
|
|
+ $parser->match(Lexer::T_COMMA);
|
|
|
+ $this->longitude2 = $parser->ArithmeticPrimary();
|
|
|
+ $parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get SQL
|
|
|
+ *
|
|
|
+ * @param SqlWalker $sqlWalker
|
|
|
+ * @return string
|
|
|
+ * @throws ASTException
|
|
|
+ */
|
|
|
+ public function getSql(SqlWalker $sqlWalker): string
|
|
|
+ {
|
|
|
+ $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
|
|
|
+
|
|
|
+ $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
|
|
|
+ );
|
|
|
+
|
|
|
+ $sql = str_replace(array('{lat1}', '{lon1}', '{lat2}', '{lon2}'), array('(%1$s)', '(%2$s)', '(%3$s)', '(%4$s)'), $sql);
|
|
|
+
|
|
|
+ return sprintf(
|
|
|
+ $sql,
|
|
|
+ $this->latitude1->dispatch($sqlWalker),
|
|
|
+ $this->longitude1->dispatch($sqlWalker),
|
|
|
+ $this->latitude2->dispatch($sqlWalker),
|
|
|
+ $this->longitude2->dispatch($sqlWalker)
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|