Rev 2747 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
<?phpclass CelStreets extends Cel {/*** Méthode appelée avec une requête de type GET.*/public function getRessource() {$this->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 pointsif($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 finif($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 rueif($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];}// Insertion des points utilisateurs en début et fin de tableauarray_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);}$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 '<pre>'.print_r($infos['elements'][$index], true).'</pre>';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 pointsif($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 pointsif ($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_debut et $index_debut + 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;} else {// il doit être gardéreturn $index_max + 1;}}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 ABfunction 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).'<br />';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;}}?>