Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 1123 | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php
/**
 * @api {get} /nasa-srtm/altitude?lat=:lat&lon=:lon Donne l'altitude.
 * @apiExample Exemple d'usage :
 * curl -i "http://api.tela-botanica.org/service:eflore:0.1/nasa-srtm/altitude?lon=3.86589&lat=44.73702"
 * @apiName GetNasaSrtm
 * @apiGroup nasa-srtm
 * @apiGroupDescription  Le web service nasa-srtm/altitude détermine l'altitude d'un point à partir de ses coordonnees.
 * Les coordonnees sont definies dans des fichiers au format HGT dans un dossier specifique
 * (dont le chemin est defini dans le fichier de configuration propre au web service).
 * Les ressources utilisees sont les donnees issues du programme SRTM-3 de la NASA qui couvre
 * l'ensemble terrestre du monde. La precision des points dont on renvoie l'altitude est de 90 metres.
 * Chaque fichier couvre un secteur de 1 degre sur 1 degre et contient un tableau de 1201 lignes
 * (axe des latitudes) sur 1201 colonnes (axe des longitudes) contenant l'altitude en metres
 * correspondant a des point precis. L'ecart entre chaque entree des tableaux est constant, ce qui
 * permet de calculer la latitude et la longitude de chaque point. L'altitude du point le plus proche
 * de celui passe en parametres sera renvoyee au client.
 *
 * @apiParam {Number} lat Latitude au format décimal (séparateur ".").
 * @apiParam {Number} lon Longitude au format décimal (séparateur ".").
 *
 * @apiSuccess {Number} altitude Altitude en mètre du point correspondant aux coordonées passées en paramètre.
 * @apiSuccess {Number} latitude Latitude au format décimal (séparateur ".").
 * @apiSuccess {Number} longitude Longitude au format décimal (séparateur ".").
 *
 * @category   eFlore
 * @package    Services
 * @subpackage Nasa-srtm
 * @version    0.1
 * @author     Mathias CHOUET <mathias@tela-botanica.org>
 * @author     Jean-Pascal MILCENT <jpm@tela-botanica.org>
 * @author     Aurelien PERONNET <aurelien@tela-botanica.org>
 * @license    GPL v3 <http://www.gnu.org/licenses/gpl.txt>
 * @license    CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
 * @copyright  1999-2014 Tela Botanica <accueil@tela-botanica.org>
 */
class Altitude {

        private $parametres = array();
        private $ressources = array();
        private $coordonnees = null;
        private $fichierSrtm = '';

        const LONGUEUR_COTE = 1201;
        const OFFSET = 2;


        public function consulter($ressources, $parametres) {
                $this->ressources = $ressources;
                $this->parametres = $parametres;
                $retour = null;
                try {
                        $this->traiterCoordonnees();
                        $this->rechercherFichierSrtm();
                        $this->recupererAltitude();
                        $retour = $this->coordonnees;
                } catch (Exception $erreur) {
                        $retour = $erreur->getMessage();
                }
                return $retour;
        }

        private function traiterCoordonnees() {
                if ($this->estParametreExistant('lat') && $this->estParametreExistant('lon')) {
                        $longitude = $this->parametres['lon'];
                        $latitude  = $this->parametres['lat'];
                        if ($this->estUnFloat($longitude) && $this->estUnFloat($latitude)) {
                                $this->verifierValiditeCoordonnees($longitude, $latitude);
                        } else {
                                $message = "La valeur des coordonnées longitude ou latitude n'est pas correcte. ".
                                " Elle doit être pour les deux paramètres une valeur décimale.";
                                throw new Exception($message, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
                        }
                } else {
                        $message = "Tous les paramètres passés dans l'URL ne correspondent pas à ceux attendus. ".
                        "Le web service nécessite de lui fournir une longitude et une latitude pour son bon fonctionnement.";
                        throw new Exception($message, RestServeur::HTTP_CODE_CONTENU_REQUIS);
                }
        }

        private function estParametreExistant($nomParametre) {
                return in_array($nomParametre, array_keys($this->parametres));
        }

        private function estUnFloat($variable) {
                return (preg_match("/^(-)?\d+(\.\d+)?$/", $variable) == 1);
        }

        private function verifierValiditeCoordonnees($longitude, $latitude) {
                $longitude = floatval($longitude);
                $latitude  = floatval($latitude);
                $longitudeMax = Config::get("limite_longitude");
                $latitudeMax  = Config::get("limite_latitude");
                if (abs($longitude) > $longitudeMax || abs($latitude) > $latitudeMax) {
                        $message = "Les coordonnées passées en paramètres désignent un point qui se trouve ".
                        "en dehors des limites du monde. Elles doivent être comprises entre -{$longitudeMax} ".
                        "et $longitudeMax sur l'axe des longitudes, et entre -{$latitudeMax} et {$latitudeMax} ".
                        "sur l'axe des latitudes.";
                        throw new Exception($message, RestServeur::HTTP_CODE_MAUVAISE_REQUETE);
                } else {
                        $this->coordonnees = new StdClass();
                        $this->coordonnees->longitude = $longitude;
                        $this->coordonnees->latitude  = $latitude;
                }
        }

        private function rechercherFichierSrtm() {
                $nomFichierSrtm = $this->construireNomFichierSrtm();
                if (!file_exists($nomFichierSrtm)) {
                        $message = "Erreur interne : la ressource « $nomFichierSrtm » demandée n'a pas pu être trouvée sur le serveur.";
                        throw new Exception($message, restServeur::HTTP_CODE_ERREUR);
                } else {
                        $this->fichierSrtm = $nomFichierSrtm;
                }
        }

        private function construireNomFichierSrtm() {
                $latitudeEntier = abs(floor($this->coordonnees->latitude));
                if ($latitudeEntier < 10) {
                        $latitudeEntier = "0".$latitudeEntier;
                }
                $suffixeLatitude = $this->coordonnees->latitude < 0 ? "S" : "N";
                $longitudeEntier = abs(floor($this->coordonnees->longitude));
                if ($longitudeEntier < 10) {
                        $longitudeEntier = "00".$longitudeEntier;
                } elseif ($longitudeEntier < 100) {
                        $longitudeEntier = "0".$longitudeEntier;
                }
                $suffixeLongitude = $this->coordonnees->longitude < 0 ? "W" : "E";
                $dossierSrtm = Config::get('dossier_srtm').DS;
                $nomFichierSrtm = $dossierSrtm.$suffixeLatitude.$latitudeEntier.$suffixeLongitude.$longitudeEntier.".hgt.zip";
                return $nomFichierSrtm;
        }

        private function recupererAltitude() {
                $zip = zip_open($this->fichierSrtm);
                $fichier = zip_read($zip);
                $donnees = zip_entry_read($fichier, zip_entry_filesize($fichier));
                zip_close($zip);

                $xDepart = floor($this->coordonnees->longitude);
                $yDepart = floor($this->coordonnees->latitude);
                $longitude = $this->coordonnees->longitude;
                $latitude = $this->coordonnees->latitude;
                $positionX = (self::LONGUEUR_COTE-1) * ($longitude - $xDepart);
                $positionY = (self::LONGUEUR_COTE-1) * (1 - $latitude + $yDepart);
                $positionX = ($positionX + 0.5 > ceil($positionX)) ? ceil($positionX) : floor($positionX);
                $positionY = ($positionY + 0.5 > ceil($positionY)) ? ceil($positionY) : floor($positionY);

                $binaire = substr($donnees, ($positionY * self::LONGUEUR_COTE + $positionX) * self::OFFSET, self::OFFSET);
                $this->coordonnees->altitude = current(unpack("n*", $binaire));
                if (!$this->coordonnees->altitude) {
                        $this->coordonnees->altitude = 0;
                }
        }
}