[RESOLU] Calcul à la volée d'une liste MySQL par mémoire tampon

Eléphant du PHP | 103 Messages

25 oct. 2013, 12:07

Bonjour!

J'ai une base structurée comme suit avec des coordonnées GPS sur moins de 1000 lignes :
description | name | coordinates
01033002 | BELLEGARDE | 5.814167,46.0865
01089001 | AMBERIEU | 5.329333,45.9765
01185004 | HAUTEVILLE | 5.598167,45.979667

Je part d'une coordonnée GPS de type $gps_plant = "43.558379,4.068991";

Avec un explode on parviens à séparer latitude et longitude pour chacun et la fonction suivante permet de calculer la distance entre deux point GPS :
function distance($lat1, $lon1, $lat2, $lon2, $alt1, $alt2) 
	{
		//rayon de la terre
		$r = 6366;
		$lat1 = deg2rad($lat1);
		$lat2 = deg2rad($lat2);
		$lon1 = deg2rad($lon1);
		$lon2 = deg2rad($lon2);
 
		//recuperation altitude en km
		$alt1 = $alt1/1000;
		$alt2 = $alt2/1000;
 
		//calcul précis
		$dp= 2 * asin(sqrt(pow (sin(($lat1-$lat2)/2) , 2) + cos($lat1)*cos($lat2)* pow( sin(($lon1-$lon2)/2) , 2)));
 
		//sortie en km
		$d = $dp * $r;
 
		//Pythagore a dit que :
		 $h = round(sqrt(pow($d,2)+pow($alt2-$alt1,2)),4);
 
		return $h;
	}
Est il possible de calculer à la volée (en mémoire tampon donc) la distance entre $gps_plant et l'intégralité de la liste MySQL et en resortir les 3 lignes les moins distantes?

C'est à dire une probable mise en mémoire des 1000 distances calculées avant extraction des plus petites, car me concernant, je ne sais pas trop comment m'y prendre sans lourdeurs de type création d'une table SQL à chaque fois avec des centaines de lignes en description | distance + select en order by.

Merci!

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

25 oct. 2013, 12:35

Salut,

Implémenter la chose en SQL + order by la distance (asc / desc suivant le cas) + limite.

Le mieux serait de créer une fonction ou procédure SQL.
Par contre je ne crois que mysql puisse retourner des tuples :/
Mais tu as moyen de faire quelque chose quand même ;)


@+
Il en faut peu pour être heureux ......

Eléphant du PHP | 103 Messages

25 oct. 2013, 13:02

Slu!

Ben en fait, un éclair à traversé mon esprit et j'ai pondu ceci :
    $alt0_1 = 1;
    $alt0_2 = 1;
    $r0 = 6366;
    $gps_plant = "43.558379,4.068991";
    $gps_plant2 = explode(",", $gps_plant);
    $lat0_1 = $gps_plant2[0];
    $lon0_1 = $gps_plant2[1];

$connexion = new PDO('mysql:host=localhost;dbname=xmlmeteo', 'xmlmeteo', 'PASSWORD');
$connexion->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$connexion->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
$connexion->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
$connexion->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER);
$resultat=$connexion->query('SELECT description, name, coordinates FROM `xmlmeteo`');

while( $distance_gps = $resultat->fetch() )
  {
    $gps_plant = explode(",", $distance_gps->coordinates);
    $lat2 = $gps_plant[1];
    $lon2 = $gps_plant[0];
    
    $alt1 = $alt0_1;
    $alt2 = $alt0_2;
    $r = $r0;
    $lat1 = $lat0_1;
    $lon1 = $lon0_1;
    
    $lat1 = deg2rad($lat1);
		$lat2 = deg2rad($lat2);
		$lon1 = deg2rad($lon1);
		$lon2 = deg2rad($lon2);
 
		//recuperation altitude en km
		$alt1 = $alt1/1000;
		$alt2 = $alt2/1000;
 
		//calcul précis
		$dp= 2 * asin(sqrt(pow (sin(($lat1-$lat2)/2) , 2) + cos($lat1)*cos($lat2)* pow( sin(($lon1-$lon2)/2) , 2)));
 
		//sortie en km
		$d = $dp * $r;
 
		//Pythagore a dit que :
		 $h = round(sqrt(pow($d,2)+pow($alt2-$alt1,2)),4);
 
		$tableau[] = array($distance_gps->description, $distance_gps->name, $h);
  }

print_r($tableau);
ce qui me liste bien :
Array (
[0] => Array ( [0] => 01033002 [1] => BELLEGARDE [2] => 312.7362 )
[1] => Array ( [0] => 01089001 [1] => AMBERIEU [2] => 286.4661 )
[2] => Array ( [0] => 01185004 [1] => HAUTEVILLE [2] => 294.8129 )
)
Reste à en ressortir de $tableau[] les 3 plus petites valeurs!! Un MIN pourrait faire l'affaire mais ne concerne que l'unique valeur la plus petite?!

Merci!

ViPHP
ViPHP | 2577 Messages

25 oct. 2013, 13:29

Si tu as 1000 lieux, tu peux pré-calculer les 500000 distances une fois pour toutes et exploiter ca ensuite. Ca me semble le plus simple.

Le fait que les coordonnées soit dans une seule zone complique les calculs inutilement, je trouve ca dommage. Dans le cas contraire, il y a toutes les fonctions pour faire les calculs avec mysql.

Eléphant du PHP | 103 Messages

25 oct. 2013, 13:34

Si tu as 1000 lieux, tu peux pré-calculer les 500000 distances une fois pour toutes et exploiter ca ensuite. Ca me semble le plus simple.

Le fait que les coordonnées soit dans une seule zone complique les calculs inutilement, je trouve ca dommage. Dans le cas contraire, il y a toutes les fonctions pour faire les calculs avec mysql.
Salut!
Je ne comprend pas ton post, j'ai bien 1000 lieux mais pourquoi 500 000 distances?

Dans mon dernier post, le code fourni calcule deja bien toutes les distances et les insère dans un array :
Array (
[0] => Array ( [0] => 01033002 [1] => BELLEGARDE [2] => 312.7362 )
[1] => Array ( [0] => 01089001 [1] => AMBERIEU [2] => 286.4661 )
[2] => Array ( [0] => 01185004 [1] => HAUTEVILLE [2] => 294.8129 )
)

Reste pour moi (éventuellement à optimiser ce code) surtout à extraire les 3 plus petites distances du array!

ViPHP
ViPHP | 2577 Messages

25 oct. 2013, 13:58

(1000*1000)/2 doit être le nombre de distance à calculer

Eléphant du PHP | 103 Messages

25 oct. 2013, 14:32

J'ai un souci pour faire ressortir les 3 plus petites valeurs, j'ai déjà fait cela :
$nblignestableau = count ( $tableau );
$u = 0;
$v = 10000;
$w = 10000;

while($u < $nblignestableau){
     if($tableau[$u][2] < $v){
        $k = $w;
        $w = $v;
        $v = $tableau[$u][2];
        $x1 = $tableau[$u][0];
        $y1 = $tableau[$u][1];
        $z1 = $tableau[$u][2];                
     }elseif($tableau[$u][2] < $w){
        $k = $w;     
        $w = $tableau[$u][2];
        $x2 = $tableau[$u][0];
        $y2 = $tableau[$u][1];
        $z2 = $tableau[$u][2];     
     }elseif($tableau[$u][2] < $k){
        $k = $tableau[$u][2];      
        $x3 = $tableau[$u][0];
        $y3 = $tableau[$u][1];
        $z3 = $tableau[$u][2];     
    };
    $u = $u + 1;
  };
echo "$x1 - $y1 - $z1 <br><br>";
echo "$x2 - $y2 - $z2 <br><br>";
echo "$x3 - $y3 - $z3 <br><br>";
Au lieu de me sortir MONTPELLIER puis MARSILLARGUES (11.5652Km) puis VILLEUNEUVE LES MAGUELONE, il me fait :
34154001 - MONTPELLIER - 8.7669
30132004 - LA GRAND COMBE - 76.2123
34337001 - VILLENEUVE-LES-MAG - 16.5258
Merci!!

Eléphant du PHP | 103 Messages

25 oct. 2013, 17:50

Rien à faire, je n'arrive pas à trier les résultats correctement, j'ai toujours un souci!

Eléphant du PHP | 103 Messages

26 oct. 2013, 12:37

Bonjour.
Pour les prochains, il suffisait de faire cette fonction exemple qui trie en ordre décroissant (il suffit de changer par >) :

<?php
$tab = array(array(5,6,2),array(3,7,1),array(1,5,9),array(3,8,8),array(1,2,3),array(7,4,4),array(4,2,10));

usort($tab, "cmp2");

$tab = array_slice($tab, 0, 3);

print_r($tab);

function cmp2($a,$b) {
if ($a[2] == $b[2])
return 0;
return ($a[2] < $b[2]) ? -1 : 1;
}
?>

Merci!
Bye