Subversion Repositories eFlore/Projets.eflore-projets

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
169 jpm 1
<?php
2
// declare(encoding='UTF-8');
3
/**
4
 * Classe permettant d'obtenir le nom et le code INSEE d'une commune à partir de ces coordonnées (latitude et longitude).
5
 * La latitude et longitude doivent être exprimée par un nombre décimal.
6
 * Ce service fonctionne uniquement pour les communes de France métropolitaine (Corse comprise) dont les contours ont été tracés dans OpenStreetMap.
7
 * Source des données : OpenStreetMap http://wiki.openstreetmap.org (Projet France limites administratives : http://wiki.openstreetmap.org/wiki/WikiProject_France/Limites_administratives)
8
 * Paramètres du service :
9
 *  - lat : latitude
10
 *  - lon : longitude
11
 * Exemple :
12
 * http://localhost/cartoOSM/services/0.1/nom-commune?lat=44.71546&lon=3.84216
13
 *
14
 * @category	php 5.2
15
 * @package		carto-osm
16
 * @author		Mohcen BENMOUNAH <mohcen@tela-botanica.org>
17
 * @author		Jean-Pascal MILCENT <jpm@tela-botanica.org>
18
 * @copyright	Copyright (c) 2010, Tela Botanica (accueil@tela-botanica.org)
19
 * @license		http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
20
 * @license		http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
21
 * @version		$Id$
22
 */
23
class NomCommune {
24
 
25
	const PATTERN_LAT = '/^[0-9]+(?:[.][0-9]+|)$/';
26
	const PATTERN_LON = '/^[-]?[0-9]+(?:[.][0-9]+|)$/';
27
	const LAT_MAX = 51.071667;
28
	const LAT_MIN = 41.316667;
29
	const LON_MAX = 9.513333;
30
	const LON_MIN = -5.140278;
31
	const NBRE_COMMUNE_PAR_DEFAUT = 10;
32
	const NBRE_COMMUNE_MAX = 100;
33
	const MIME_JSON = 'application/json';
34
 
35
	private $parametres = array();
36
	private $bdd = null;
37
 
38
	public function __construct(Bdd $bdd) {
39
		$this->bdd = $bdd;
40
	}
41
 
42
	public function consulter($ressources, $parametres) {
43
		$this->parametres = $parametres;
44
		$this->verifierParametres();
45
 
46
		$nomINSEEs = $this->trouverCommunesProches();
47
		$corps = $this->formaterResultats($nomINSEEs);
48
 
49
		$resultat = new ResultatService();
50
		$resultat->mime = self::MIME_JSON;
51
		$resultat->corps = $corps;
52
		return $resultat;
53
	}
54
 
55
	private function verifierParametres() {
56
		extract($this->parametres);
57
		$messages = array();
58
		if (! array_key_exists('lat', $this->parametres)) {
59
			$messages[] = "Vous devez indiquer une latitude en degré décimal à l'aide du paramètres d'url : lat";
60
		} else if (!preg_match(self::PATTERN_LAT, $lat)) {
61
			$messages[] = "La valeur de latitude doit être un nombre décimal positif dont le séparateur décimal est un point. Ex. : 44 ou 43.03";
62
		} else if ($lat > self::LAT_MAX) {
63
			$messages[] = "La valeur de latitude indiquée est supérieure à {self::LAT_MAX} qui est le point le plus au Nord de la France métropolitaine.";
64
		} else if ($lat < self::LAT_MIN) {
65
			$messages[] = "La valeur de latitude indiquée est infèrieure à {self::LAT_MIN} qui est le point le plus au Sud de la France métropolitaine.";
66
		}
67
		if (! array_key_exists('lon', $this->parametres)) {
68
			$messages[] = "Vous devez indiquer une longitude en degré décimal à l'aide du paramètres d'url : lon";
69
		} else if (!preg_match(self::PATTERN_LON, $lon)) {
70
			$messages[] = "La valeur de longitude doit être un nombre décimal dont le séparateur décimal est un point. Ex. : -4.03 ou 3.256";
71
		} else if ($lon > self::LON_MAX) {
72
			$messages[] = "La valeur de longitude indiquée est supérieure à {self::LON_MAX} qui est le point le plus à l'Est de la France métropolitaine.";
73
		} else if ($lon < self::LON_MIN) {
74
			$messages[] = "La valeur de longitude indiquée est infèrieure à {self::LON_MIN} qui est le point le plus à l'Ouest de la France métropolitaine.";
75
		}
76
		if (count($messages) != 0) {
77
			$message = implode('<br />', $messages);
78
			$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
79
			throw new Exception($message, $code);
80
		}
81
	}
82
 
83
	/**
84
	 * requête qui récupère les 20 communes les plus proches du point recherché
85
	 * La distance(AB = \sqrt{(x_B-x_A)^2 + (y_B-y_A)^2}) est calculée sans la racine
86
	 * (calcul en plus qui change pas le résultat).
87
	*/
88
	private function trouverCommunesProches() {
89
		$lat = $this->parametres['lat'];
90
		$lon = $this->parametres['lon'];
91
		$requete =	'SELECT  '.
92
			"	(({$lat} - X(centre)) * ({$lat} -X(centre)) + ({$lon} - Y(centre)) * ({$lon} - Y(centre))) AS distance, ".
93
			"	code_insee, nom, ASTEXT(polygon) AS poly ".
94
			'FROM osm_communes '.
95
			'WHERE note = "Polygone complet" '.
96
			'ORDER BY distance '.
97
			'LIMIT 20';
98
		$resultat = $this->bdd->recupererTous($requete);
99
 
100
		if ($resultat == false) {
101
			$message = "Le service '".get_class($this)."' n'a trouvé aucune commune dont le centroïde correspond aux coordonnées : {$this->parametres['lat']}, {$this->parametres['lon']}.";
102
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
103
			throw new Exception($message, $code);
104
		}
105
		return $resultat;
106
	}
107
 
108
	private function formaterResultats($nomINSEEs) {
109
		$communeTrouvee = null;
110
		foreach ($nomINSEEs as $nomINSEE) {
111
			$resultat = $this->localiserPointLatLon($nomINSEE['poly']);
112
			if ($resultat == 1) {
113
				$communeTrouvee = array('nom' => $nomINSEE['nom'], 'codeINSEE' => $nomINSEE['code_insee']);
114
				break;
115
			}
116
		}
117
		if (is_null($communeTrouvee)) {
118
			$message = "Le service '".get_class($this)."' n'a trouvé aucune commune correspondant aux coordonnées : {$this->parametres['lat']}, {$this->parametres['lon']}.";
119
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
120
			throw new Exception($message, $code);
121
		}
122
		return $communeTrouvee;
123
	}
124
 
125
	private function localiserPointLatLon($multiPolygone) {
126
		$lat = $this->parametres['lat'];
127
		$lon = $this->parametres['lon'];
128
		$requeteMBR = "SELECT MBRWithin(GEOMFROMTEXT('POINT($lat $lon)'), GEOMFROMTEXT('$multiPolygone'))";
129
		$p = array_values($this->bdd->recuperer($requeteMBR));
130
 
131
		$counter = 0;
132
		if ($p[0] != 1) {
133
			 return 0;
134
		} else {
135
			$x = $lat;	  //Le point latitude recherché
136
			$y = $lon;		//Le point longitude recherché
137
			$str = $this->nettoyerChaineMultipolygone($multiPolygone); // Le multipolygone de la commune
138
			$pb = 0;
139
			$pe =  strpos($str, ','); //définition de la position de la première virgule
140
			$xy =  substr($str, $pb, $pe - $pb); //extraire X Y d'un point séparé par des virgules
141
			$p = strpos($xy, ' ');  //position espace dans un point
142
			$p1x = substr($xy, 0, $p ); //récupère X du point
143
			$p1y = substr($xy, $p + 1 ); //récupère Y du point
144
			$str = $str.$xy; // ajouter le premier point du polygone à la fin du polygone
145
			while ($pe > 0) {
146
				$xy = substr($str, $pb, $pe - $pb ); //extraire le point GPS
147
				$p	= strpos($xy, ' ');   // trouver la position de l'espace entre les deux points X et Y
148
				$p2x = substr($xy,0, $p );  // extraire le point X
149
				$p2y = substr($xy, $p + 1 );  // extraire le point Y
150
				if ($p1y < $p2y) {
151
					$m = $p1y;
152
				} else {
153
					$m = $p2y;
154
				}
155
				if ($y > $m) {
156
					if ($p1y > $p2y) {
157
						$m = $p1y;
158
					} else {
159
						$m = $p2y;
160
					}
161
					if ($y <= $m) {
162
						if ($p1x > $p2x) {
163
							$m = $p1x;
164
						} else {
165
							$m = $p2x;
166
						}
167
						if ($y <= $m) {
168
							if ($p1x > $p2x) {
169
								$m = $p1x;
170
							} else {
171
								$m = $p2x;
172
							}
173
							if ($x <= $m) {
174
								if ($p1y != $p2y) {
175
									$xinters = ($y - $p1y) * ($p2x - $p1x) / ($p2y - $p1y) + $p1x;
176
								}
177
								if (($p1x = $p2x) || ($x <= $xinters )) {
178
									$counter++;
179
								}
180
							}
181
						}
182
					}
183
				}
184
				$p1x = $p2x;
185
				$p1y = $p2y;
186
				$pb = $pe + 1;
187
				$pe = strpos($str, ',', $pb);
188
			}
189
			return $counter % 2;
190
		}
191
	}
192
 
193
	private function nettoyerChaineMultipolygone($chaine) {
194
		$chaine = str_replace('MULTIPOLYGON(((', '', $chaine);
195
		$chaine = str_replace(')))', '', $chaine);
196
		$chaine .= ',';
197
		return $chaine;
198
	}
199
}
200
?>