Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 169 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 169 Rev 1056
Line 2... Line 2...
2
// declare(encoding='UTF-8');
2
// declare(encoding='UTF-8');
3
/**
3
/**
4
 * Classe permettant d'obtenir le nom et le code INSEE d'une commune à partir de ces coordonnées (latitude et longitude).
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.
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.
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
7
 * Source des données : OpenStreetMap http://wiki.openstreetmap.org (Projet France limites administratives : http://wiki.openstreetmap.org/wiki/WikiProject_France/Limites_administratives)
8
 * Projet France limites administratives : http://wiki.openstreetmap.org/wiki/WikiProject_France/Limites_administratives
8
 * Paramètres du service :
9
 * Paramètres du service :
9
 *  - lat : latitude
10
 *  - lat : latitude
10
 *  - lon : longitude
11
 *  - lon : longitude
11
 * Exemple :
12
 * Exemple :
12
 * http://localhost/cartoOSM/services/0.1/nom-commune?lat=44.71546&lon=3.84216
13
 * http://localhost/service-test:eflore:0.1/osm/nom-commune?lon=3.83619&lat=44.74231
13
 *
14
 *
14
 * @category	php 5.2
15
 * @category	php 5.2
15
 * @package		carto-osm
16
 * @package		carto-osm
16
 * @author		Mohcen BENMOUNAH <mohcen@tela-botanica.org>
17
 * @author		Mohcen BENMOUNAH <mohcen@tela-botanica.org>
17
 * @author		Jean-Pascal MILCENT <jpm@tela-botanica.org>
18
 * @author		Jean-Pascal MILCENT <jpm@tela-botanica.org>
Line 26... Line 27...
26
	const PATTERN_LON = '/^[-]?[0-9]+(?:[.][0-9]+|)$/';
27
	const PATTERN_LON = '/^[-]?[0-9]+(?:[.][0-9]+|)$/';
27
	const LAT_MAX = 51.071667;
28
	const LAT_MAX = 51.071667;
28
	const LAT_MIN = 41.316667;
29
	const LAT_MIN = 41.316667;
29
	const LON_MAX = 9.513333;
30
	const LON_MAX = 9.513333;
30
	const LON_MIN = -5.140278;
31
	const LON_MIN = -5.140278;
31
	const NBRE_COMMUNE_PAR_DEFAUT = 10;
-
 
32
	const NBRE_COMMUNE_MAX = 100;
-
 
33
	const MIME_JSON = 'application/json';
32
	const MIME_JSON = 'application/json';
Line 34... Line 33...
34
 
33
 
35
	private $parametres = array();
34
	private $parametres = array();
Line -... Line 35...
-
 
35
	private $bdd = null;
-
 
36
 
-
 
37
	private $lat = null;
36
	private $bdd = null;
38
	private $lon = null;
37
 
39
 
38
	public function __construct(Bdd $bdd) {
40
	public function __construct(Bdd $bdd) {
Line 39... Line 41...
39
		$this->bdd = $bdd;
41
		$this->bdd = $bdd;
40
	}
42
	}
41
 
43
 
-
 
44
	public function consulter($ressources, $parametres) {
-
 
45
		$this->parametres = $parametres;
Line 42... Line 46...
42
	public function consulter($ressources, $parametres) {
46
		$this->verifierParametres();
-
 
47
		$this->lat = $this->parametres['lat'];
43
		$this->parametres = $parametres;
48
		$this->lon = $this->parametres['lon'];
Line 44... Line 49...
44
		$this->verifierParametres();
49
 
45
 
50
		$communesProches = $this->trouverCommunesProches();
46
		$nomINSEEs = $this->trouverCommunesProches();
51
		$communeTrouveeInfos = $this->localiserPointLatLon($communesProches);
47
		$corps = $this->formaterResultats($nomINSEEs);
52
		$corps = $this->formaterResultats($communeTrouveeInfos);
Line 58... Line 63...
58
		if (! array_key_exists('lat', $this->parametres)) {
63
		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";
64
			$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)) {
65
		} 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";
66
			$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) {
67
		} 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.";
68
			$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) {
69
		} 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.";
70
			$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
		}
71
		}
67
		if (! array_key_exists('lon', $this->parametres)) {
72
		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";
73
			$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)) {
74
		} 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";
75
			$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) {
76
		} 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.";
77
			$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) {
78
		} 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.";
79
			$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
		}
80
		}
76
		if (count($messages) != 0) {
81
		if (count($messages) != 0) {
77
			$message = implode('<br />', $messages);
82
			$message = implode('<br />', $messages);
78
			$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
83
			$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
79
			throw new Exception($message, $code);
84
			throw new Exception($message, $code);
Line 81... Line 86...
81
	}
86
	}
Line 82... Line 87...
82
 
87
 
83
	/**
88
	/**
84
	 * requête qui récupère les 20 communes les plus proches du point recherché
89
	 * 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
90
	 * La distance(AB = \sqrt{(x_B-x_A)^2 + (y_B-y_A)^2}) est calculée sans la racine
-
 
91
	 * (calcul en plus qui change pas le résultat). La distance n'est donc pas juste mais permet de récupérer
86
	 * (calcul en plus qui change pas le résultat).
92
	 * le bon ordre.
87
	*/
93
	*/
88
	private function trouverCommunesProches() {
-
 
89
		$lat = $this->parametres['lat'];
-
 
90
		$lon = $this->parametres['lon'];
94
	private function trouverCommunesProches() {
91
		$requete =	'SELECT  '.
95
		$requete =	'SELECT id_osm, '.
92
			"	(({$lat} - X(centre)) * ({$lat} -X(centre)) + ({$lon} - Y(centre)) * ({$lon} - Y(centre))) AS distance, ".
-
 
93
			"	code_insee, nom, ASTEXT(polygon) AS poly ".
96
			"(({$this->lon} - X(centre)) * ({$this->lon} -X(centre)) + ({$this->lat} - Y(centre)) * ({$this->lat} - Y(centre))) AS distance ".
94
			'FROM osm_communes '.
-
 
95
			'WHERE note = "Polygone complet" '.
97
			'FROM osm_communes '.
96
			'ORDER BY distance '.
98
			'ORDER BY distance ASC '.
-
 
99
			'LIMIT 20 '.
97
			'LIMIT 20';
100
			' -- '.__FILE__.' : '.__LINE__;
Line 98... Line 101...
98
		$resultat = $this->bdd->recupererTous($requete);
101
		$resultat = $this->bdd->recupererTous($requete);
99
 
102
 
100
		if ($resultat == false) {
103
		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']}.";
104
			$msgTpl = "Service '%s' : aucune commune dont le centroïde correspond aux coordonnées : %s, %s.";
102
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
105
			$msg = sprintf($msgTpl, get_class($this), $this->lat, $this->lon);
103
			throw new Exception($message, $code);
106
			throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
104
		}
107
		}
Line 105... Line 108...
105
		return $resultat;
108
		return $resultat;
106
	}
109
	}
107
 
110
 
108
	private function formaterResultats($nomINSEEs) {
111
	private function formaterResultats($communeTrouveeInfos) {
109
		$communeTrouvee = null;
-
 
110
		foreach ($nomINSEEs as $nomINSEE) {
112
		return array(
111
			$resultat = $this->localiserPointLatLon($nomINSEE['poly']);
113
			'nom' => $communeTrouveeInfos['nom'],
112
			if ($resultat == 1) {
114
			'codeINSEE' => $communeTrouveeInfos['code_insee'],
-
 
115
			'wikipedia' => $this->formaterUrlWikipedia($communeTrouveeInfos['wikipedia'])
-
 
116
		);
-
 
117
	}
-
 
118
 
-
 
119
	private function localiserPointLatLon($communesProches) {
113
				$communeTrouvee = array('nom' => $nomINSEE['nom'], 'codeINSEE' => $nomINSEE['code_insee']);
120
		$zonesIds = array();
-
 
121
		foreach ($communesProches as $commune) {
-
 
122
			$zonesIds[] = $commune['id_osm'];
-
 
123
		}
-
 
124
		$idsOsmClauseIn = implode(',', $this->bdd->proteger($zonesIds));
-
 
125
		$requete = "SELECT id_osm, code_insee, nom, wikipedia ".
-
 
126
			'FROM osm_communes '.
-
 
127
			"WHERE st_within(GEOMFROMTEXT('POINT($this->lon $this->lat)'), polygone) = 1 ".
114
				break;
128
			"AND id_osm IN ($idsOsmClauseIn) ".
115
			}
129
			' -- '.__FILE__.' : '.__LINE__;
116
		}
130
		$resultat = $this->bdd->recuperer($requete);
117
		if (is_null($communeTrouvee)) {
131
		if ($resultat === false) {
118
			$message = "Le service '".get_class($this)."' n'a trouvé aucune commune correspondant aux coordonnées : {$this->parametres['lat']}, {$this->parametres['lon']}.";
132
			$msgTpl = "Service '%s' : aucune commune correspondant aux coordonnées : %s, %s.";
119
			$code = RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE;
133
			$msg = sprintf($msgTpl, get_class($this), $this->lat, $this->lon);
120
			throw new Exception($message, $code);
134
			throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
Line 121... Line 135...
121
		}
135
		}
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) {
136
		return $resultat;
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
				}
137
	}
184
				$p1x = $p2x;
-
 
185
				$p1y = $p2y;
-
 
186
				$pb = $pe + 1;
-
 
187
				$pe = strpos($str, ',', $pb);
-
 
188
			}
-
 
189
			return $counter % 2;
-
 
190
		}
138
 
191
	}
139
	private function formaterUrlWikipedia($infoWikipedia) {
192
 
-
 
193
	private function nettoyerChaineMultipolygone($chaine) {
-
 
194
		$chaine = str_replace('MULTIPOLYGON(((', '', $chaine);
140
		list($lang, $page) = explode(':', $infoWikipedia);
195
		$chaine = str_replace(')))', '', $chaine);
-
 
196
		$chaine .= ',';
-
 
197
		return $chaine;
141
		$pageEncode = rawurlencode($page);
-
 
142
		$urlTpl = 'https://%s.wikipedia.org/wiki/%s';
198
	}
143
		return sprintf($urlTpl, $lang, $pageEncode);