getStreetGeom(); } private function getStreetGeom() { $erreurs = $this->verifierParametres(); if(!empty($erreurs)) { $this->envoyerMessageErreur(400, implode("\n", $erreurs)); exit; } $latitude_debut = $_GET['latitude_debut']; $longitude_debut = $_GET['longitude_debut']; $latitude_fin = $_GET['latitude_fin']; $longitude_fin = $_GET['longitude_fin']; $dist_max = round(self::haversineGreatCircleDistance($latitude_debut, $longitude_debut, $latitude_fin, $longitude_fin)/2); $url_tpl = $this->getUrlRequeteOverPass().urlencode($this->getRequetePasLoin($latitude_debut, $longitude_debut, $latitude_fin, $longitude_fin, $dist_max)); $json = file_get_contents($url_tpl); $infos = json_decode($json, true); $this->filtrerNoeuds($infos, $latitude_debut, $longitude_debut, $latitude_fin, $longitude_fin); header('Content-type: application/json'); echo json_encode($infos); exit; } private function verifierParametres() { $parametres_obligatoires = array('latitude_debut', 'longitude_debut', 'latitude_fin', 'longitude_fin'); $erreurs = array(); foreach($parametres_obligatoires as $param) { if(empty($_GET[$param]) || !is_numeric($_GET[$param])) { $erreurs[] = "Le paramètre $param est obligatoire et doit être un flottant"; } } return $erreurs; } function getUrlRequeteOverPass() { return $this->config['cel']['url_service_rue']; } function getRequetePasLoin($latitude_debut, $longitude_debut, $latitude_fin, $longitude_fin, $dist = 50) { $req = 'way["highway"] (around:'.$dist.','.$latitude_debut.','.$longitude_debut.') (around:'.$dist.','.$latitude_fin.','.$longitude_fin.') ; ( ._; >; ); out geom;'; return $req; } function getRequete($nord, $sud, $est, $ouest) { return '( way["highway"] ('.$sud.','.$ouest.','.$nord.','.$est.'); >; );out geom;'; } function filtrerNoeuds(&$infos, $latitude_debut, $longitude_debut, $latitude_fin, $longitude_fin) { $distances_debut = array(); $distances_fin = array(); foreach($infos['elements'] as $index_e => &$element) { if(empty($element['geometry'])) { unset($infos['elements'][$index_e]); } else { $index_fin_tableau = count($element['geometry']) - 1; $distances_debut[$index_e] = -1; $distances_fin[$index_e] = -1; $index_point_plus_proche_debut = 0; $dist_point_plus_proche_debut = 999999999; $index_point_plus_proche_fin = $index_fin_tableau; $dist_point_plus_proche_fin = 9999999999; $index_debut = array(0, 1); $index_fin = array($index_fin_tableau -1, $index_fin_tableau); $element['geometryOrig'] = $element['geometry']; foreach($element['geometry'] as $index_g => &$geometry) { // Calcul de la plus petite distance de la droite de rue au point // pour sélectionner la rue la plus proche des deux points if($index_g + 1 < count($element['geometry'])) { $geom_next = $element['geometry'][$index_g + 1]; $this->stockerDistancePlusCourte($distances_debut, $index_debut, $index_e, $index_g, $geometry, $geom_next, $latitude_debut, $longitude_debut); $this->stockerDistancePlusCourte($distances_fin, $index_fin, $index_e, $index_g, $geometry, $geom_next, $latitude_fin, $longitude_fin); } $dist_debut= self::haversineGreatCircleDistance($latitude_debut, $longitude_debut, $geometry['lat'], $geometry['lon']); $dist_fin = self::haversineGreatCircleDistance($latitude_fin, $longitude_fin, $geometry['lat'], $geometry['lon']); if($dist_point_plus_proche_debut > $dist_debut) { $dist_point_plus_proche_debut = $dist_debut; $index_point_plus_proche_debut = $index_g; } if($dist_point_plus_proche_fin > $dist_fin) { $dist_point_plus_proche_fin = $dist_fin; $index_point_plus_proche_fin = $index_g; } } $index_min = min($index_point_plus_proche_debut, $index_point_plus_proche_fin); $index_max = max($index_point_plus_proche_debut, $index_point_plus_proche_fin); $infos_debut = array('lat' => (float)$latitude_debut, 'lon' => (float)$longitude_debut); $infos_fin = array('lat' => (float)$latitude_fin, 'lon' => (float)$longitude_fin); // Inversion des points de début et de fin si le début donné par l'utilisateur est plus // proche de la fin if($index_min != $index_point_plus_proche_debut) { $tmp = $infos_debut; $infos_debut = $infos_fin; $infos_fin = $tmp; } // Projection des points de début et d'arrivée sur le segment de rue if($index_min < $index_fin_tableau) { $proj_debut = $this->getOrthoPoint($element['geometry'][$index_min], $element['geometry'][$index_min+1], $infos_debut); $index_insertion_point_debut = $this->getIndexInsertionDebut($element, $index_min, $infos_debut); } else { $proj_debut = $infos_debut; $index_insertion_point_debut = 0; } if($index_max > 0) { $proj_fin = $this->getOrthoPoint($element['geometry'][$index_max], $element['geometry'][$index_max-1], $infos_fin); $index_insertion_point_fin = $this->getIndexInsertionFin($element, $index_max, $infos_fin); } else { $proj_fin = $infos_fin; $index_insertion_point_fin = $index_fin_tableau; } $index_insertion_point_debut = $index_insertion_point_debut < 0 ? 0 : $index_insertion_point_debut; $index_insertion_point_fin = $index_insertion_point_fin > $index_fin_tableau ? $index_fin_tableau : $index_insertion_point_fin; $rd = array(); for($ii = $index_insertion_point_debut+1; $ii <= $index_insertion_point_fin; $ii++) { $rd[] = $element['geometry'][$ii]; } $proj_debut = $infos_debut; $proj_fin = $infos_fin; // Insertion des points utilisateurs en début et fin de tableau array_unshift($rd, $proj_debut); array_push($rd, $proj_fin); $element['geometry'] = $rd; } } if(!empty($distances_debut)) { asort($distances_debut); reset($distances_debut); $cle_plus_courte_debut = key($distances_debut); } if(!empty($distances_fin)) { asort($distances_fin); reset($distances_fin); $cle_plus_courte_fin = key($distances_fin); } if(!empty($distances_fin)) { $cle_choisie = $this->choisirIndexRuePlusProchePourDebutEtFin($cle_plus_courte_debut, $cle_plus_courte_fin, $distances_debut, $distances_fin); $rue_plus_proche = $distances_fin[$cle_choisie]; unset($distances_fin[$cle_choisie]); $distances_fin = array($cle_choisie => $rue_plus_proche) + $distances_fin; } $elements_fmt = array(); foreach($distances_fin as $index => $distance) { //echo '
'.print_r($infos['elements'][$index], true).'
';exit; $rue_proche = $infos['elements'][$index]; $rue_proche['dist_min'] = $distance; $elements_fmt[] = $rue_proche; } $infos['elements'] = $elements_fmt; } function choisirIndexRuePlusProchePourDebutEtFin($cle_plus_courte_debut, $cle_plus_courte_fin, $distances_debut, $distances_fin) { $cle_choisie = $cle_plus_courte_fin; // Si la rue la plus proche n'est pas la même pour les deux points if($cle_plus_courte_debut != $cle_plus_courte_fin) { // on calcule celle qui est la mieux positionnée en moyenne $index_debut_tab_debut = array_search($cle_plus_courte_debut, array_keys($distances_debut)); $index_debut_tab_fin = array_search($cle_plus_courte_debut, array_keys($distances_fin)); $index_fin_tab_debut = array_search($cle_plus_courte_fin, array_keys($distances_debut)); $index_fin_tab_fin = array_search($cle_plus_courte_fin, array_keys($distances_fin)); $moy_index_debut = ($index_debut_tab_debut + $index_debut_tab_fin)/2; $moy_index_fin = ($index_fin_tab_debut + $index_fin_tab_fin)/2; $cle_choisie = ($moy_index_debut < $moy_index_fin) ? $cle_plus_courte_debut : $cle_plus_courte_fin; // Si ça ne suffit pas on prend celle qui a la distance moyenne la plus courte aux deux points if ($moy_index_debut == $moy_index_fin) { $moyenne_dist_cle_debut = ($distances_debut[$cle_plus_courte_debut] + $distances_fin[$cle_plus_courte_debut])/2; $moyenne_dist_cle_fin = ($distances_debut[$cle_plus_courte_fin] + $distances_fin[$cle_plus_courte_fin])/2; $cle_choisie = ($moyenne_dist_cle_debut < $moyenne_dist_cle_fin) ? $cle_plus_courte_debut : $cle_plus_courte_fin; } } return $cle_choisie; } function getIndexInsertionDebut($element, $index_min, $debut) { // le point de début est situé entre $index_min et $index_min + 1 $proj_debut = $this->getOrthoPoint($element['geometry'][$index_min], $element['geometry'][$index_min+1], $debut); $point_deb = $element['geometry'][$index_min]; $point_deb_plus_un = $element['geometry'][$index_min+1]; $distance_point_deb_plus_un_proj = self::haversineGreatCircleDistance($point_deb_plus_un['lat'], $point_deb_plus_un['lon'], $proj_debut['lat'], $proj_debut['lon']); $distance_point_deb_deb_plus_un = self::haversineGreatCircleDistance($point_deb['lat'], $point_deb['lon'], $point_deb_plus_un['lat'], $point_deb_plus_un['lon']); if($distance_point_deb_plus_un_proj < $distance_point_deb_deb_plus_un) { // le point le plus proche doit être éliminé return $index_min + 1; } else { // il doit être gardé return $index_min; } } function getIndexInsertionFin($element, $index_max, $fin) { $proj_fin = $this->getOrthoPoint($element['geometry'][$index_max], $element['geometry'][$index_max-1], $fin); $point_fin = $element['geometry'][$index_max]; $point_fin_moins_un = $element['geometry'][$index_max-1]; $distance_point_fin_moins_un_proj = self::haversineGreatCircleDistance($point_fin_moins_un['lat'], $point_fin_moins_un['lon'], $proj_fin['lat'], $proj_fin['lon']); $distance_point_fin_fin_moins_un = self::haversineGreatCircleDistance($point_fin['lat'], $point_fin['lon'], $point_fin_moins_un['lat'], $point_fin_moins_un['lon']); if($distance_point_fin_moins_un_proj < $distance_point_fin_fin_moins_un) { // le point le plus proche doit être éliminé return $index_max - 1; } else { // il doit être gardé return $index_max; } } function stockerDistancePlusCourte(&$distances, &$infos_index_position_point, $index_e, $index_g, $geometry, $geom_next, $latitude_point, $longitude_point) { $a = $geometry['lat']; $b = $geometry['lon']; $c = $geom_next['lat']; $d = $geom_next['lon']; $x = $latitude_point ; $y = $longitude_point ; $dist = $this->getShortestDistance($a, $b, $c, $d, $x, $y); if($distances[$index_e] == -1 || $dist < $distances[$index_e]) { $distances[$index_e] = $dist; $infos_index_position_point = array($index_g, $index_g+1); } } // projette le point C sur le segment AB function getOrthoPoint($a, $b, $c) { $x1 = $a['lat']; $y1 = $a['lon']; $x2 = $b['lat']; $y2 = $b['lon']; $x3 = $c['lat']; $y3 = $c['lon']; $px = $x2-$x1; $py = $y2-$y1; $dAB = $px*$px + $py*$py; $u = (($x3 - $x1) * $px + ($y3 - $y1) * $py) / $dAB; $x = $x1 + $u * $px; $y = $y1 + $u * $py; return array('lat' => $x, 'lon' => $y); } function getShortestDistance($a, $b, $c, $d, $x, $y) { //Coordinates are (a,b) and (c,d) //the point (x,y) is the required point. //echo 'nagi '; //echo $c.' - '.$a." = ".($c-$a).'
'; if(($c-$a) == 0) { return 56465465456485; } $m=($d-$b)/($c-$a); //echo $m."\n"; //echo $y-($m*$x)-$b+($m*$a)."\n"; $distance=abs($y-($m*$x)-$b+($m*$a))/sqrt(1+($m*$m)); return $distance; } function haversineGreatCircleDistance( $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000) { // convert from degrees to radians $latFrom = deg2rad($latitudeFrom); $lonFrom = deg2rad($longitudeFrom); $latTo = deg2rad($latitudeTo); $lonTo = deg2rad($longitudeTo); $latDelta = $latTo - $latFrom; $lonDelta = $lonTo - $lonFrom; $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) + cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2))); return $angle * $earthRadius; } public static function vincentyGreatCircleDistance($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000) { // convert from degrees to radians $latFrom = deg2rad($latitudeFrom); $lonFrom = deg2rad($longitudeFrom); $latTo = deg2rad($latitudeTo); $lonTo = deg2rad($longitudeTo); $lonDelta = $lonTo - $lonFrom; $a = pow(cos($latTo) * sin($lonDelta), 2) + pow(cos($latFrom) * sin($latTo) - sin($latFrom) * cos($latTo) * cos($lonDelta), 2); $b = sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lonDelta); $angle = atan2(sqrt($a), $b); return $angle * $earthRadius; } } ?>