Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 1143 | Rev 1151 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1143 Rev 1149
1
<?php
1
<?php
2
// declare(encoding='UTF-8');
2
// declare(encoding='UTF-8');
3
/**
3
/**
4
 * Classe permettant d'obtenir le nom et les codes d'une zone administrative à partir de ces coordonnées (latitude et longitude).
4
 * Classe permettant d'obtenir le nom et les codes d'une zone administrative à 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 zones administratives dont les contours ont été tracés dans OpenStreetMap.
6
 * Ce service fonctionne uniquement pour les zones administratives 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>
8
 * Tag:boundary=administrative : <http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative>
8
 * Tag:boundary=administrative : <http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative>
9
 * Paramètres du service :
9
 * Paramètres du service :
10
 *  - lat : latitude
10
 *  - lat : latitude
11
 *  - lon : longitude
11
 *  - lon : longitude
12
 * Exemple :
12
 * Exemple :
13
 * http://localhost/service-test:eflore:0.1/osm/nom-commune?lon=3.83619&lat=44.74231
13
 * http://localhost/service-test:eflore:0.1/osm/nom-commune?lon=3.83619&lat=44.74231
14
 *
14
 *
15
 * @category	php 5.2
15
 * @category	php 5.2
16
 * @package		eFlore
16
 * @package		eFlore
17
 * @subpackage	Services
17
 * @subpackage	Services
18
 * @author		Jean-Pascal MILCENT <jpm@tela-botanica.org>
18
 * @author		Jean-Pascal MILCENT <jpm@tela-botanica.org>
19
 * @author		Mohcen BENMOUNAH <mohcen@tela-botanica.org>
19
 * @author		Mohcen BENMOUNAH <mohcen@tela-botanica.org>
20
 * @copyright	Copyright (c) 2010, Tela Botanica (accueil@tela-botanica.org)
20
 * @copyright	Copyright (c) 2010, Tela Botanica (accueil@tela-botanica.org)
21
 * @license		CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt>
21
 * @license		CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt>
22
 * @license		GNU-GPL <http://www.gnu.org/licenses/gpl.html>
22
 * @license		GNU-GPL <http://www.gnu.org/licenses/gpl.html>
23
 */
23
 */
24
class ZoneADmin {
24
class ZoneADmin {
25
 
25
 
26
	const PATTERN_LAT = '/^[-]?[0-9]+(?:[.][0-9]+|)$/';
26
	const PATTERN_LAT = '/^[-]?[0-9]+(?:[.][0-9]+|)$/';
27
	const PATTERN_LON = '/^[-]?[0-9]+(?:[.][0-9]+|)$/';
27
	const PATTERN_LON = '/^[-]?[0-9]+(?:[.][0-9]+|)$/';
28
	const MIME_JSON = 'application/json';
28
	const MIME_JSON = 'application/json';
29
 
29
 
30
	private $parametres = array();
30
	private $parametres = array();
31
	private $bdd = null;
31
	private $bdd = null;
32
 
32
 
33
	private $lat = null;
33
	private $lat = null;
34
	private $lon = null;
34
	private $lon = null;
35
	private $zone = null;
35
	private $zone = null;
36
	private $niveaux = null;
36
	private $niveaux = null;
37
	
37
	
38
	private $masque = null;
38
	private $masque = null;
-
 
39
	private $limite = 1;
39
 
40
 
40
	public function __construct(Bdd $bdd) {
41
	public function __construct(Bdd $bdd) {
41
		$this->bdd = $bdd;
42
		$this->bdd = $bdd;
42
	}
43
	}
43
 
44
 
44
	public function consulter($ressources, $parametres) {
45
	public function consulter($ressources, $parametres) {
45
		$this->parametres = $parametres;
46
		$this->parametres = $parametres;
46
		$this->verifierParametres();
47
		$this->verifierParametres();
47
		$this->masque = isset($this->parametres['masque']) ? $this->parametres['masque'] : null;
48
		$this->masque = isset($this->parametres['masque']) ? $this->parametres['masque'] : null;
-
 
49
		$this->limite = isset($this->parametres['limite']) ? intval($this->parametres['limite']) : $this->limite;
48
		$this->lat = isset($this->parametres['lat']) ? $this->parametres['lat'] : null;
50
		$this->lat = isset($this->parametres['lat']) ? $this->parametres['lat'] : null;
49
		$this->lon = isset($this->parametres['lon']) ? $this->parametres['lon'] : null;
51
		$this->lon = isset($this->parametres['lon']) ? $this->parametres['lon'] : null;
50
		$this->zone = isset($this->parametres['zone']) ? $this->parametres['zone'] : null;
52
		$this->zone = isset($this->parametres['zone']) ? $this->parametres['zone'] : null;
51
		$this->niveaux = isset($this->parametres['niveau']) ? explode(',', $this->parametres['niveau']) : null;
53
		$this->niveaux = isset($this->parametres['niveau']) ? explode(',', $this->parametres['niveau']) : null;
52
		
54
		
53
		if($this->masque != null) {
55
		if($this->masque != null) {
54
			$corps = $this->rechercherZoneGeoParNom($this->masque);
56
			$corps = $this->rechercherZoneGeoParNom($this->masque, $this->niveaux, $this->limite);
55
		} else {
57
		} else {
56
			$zoneTrouveeInfos = $this->localiserPointLatLon();
58
			$zoneTrouveeInfos = $this->localiserPointLatLon();
57
			$corps = $this->formaterResultats($zoneTrouveeInfos);
59
			$corps = $this->formaterResultats($zoneTrouveeInfos);
58
		}
60
		}
59
 
61
 
60
		$resultat = new ResultatService();
62
		$resultat = new ResultatService();
61
		$resultat->mime = self::MIME_JSON;
63
		$resultat->mime = self::MIME_JSON;
62
		$resultat->corps = $corps;
64
		$resultat->corps = $corps;
63
		return $resultat;
65
		return $resultat;
64
	}
66
	}
65
 
67
 
66
	private function verifierParametres() {
68
	private function verifierParametres() {
67
		extract($this->parametres);
69
		extract($this->parametres);
68
		$messages = array();
70
		$messages = array();
69
		
71
		
70
		if(array_key_exists('masque', $this->parametres)) {
72
		if(array_key_exists('masque', $this->parametres)) {
71
			if (empty($masque)) {
73
			if (empty($masque)) {
72
				$messages[] = "S'il est présent le paramètre recherche ne peut pas être vide";
74
				$messages[] = "S'il est présent le paramètre recherche ne peut pas être vide";
73
			}
75
			}
74
		} else {	
76
		} else {	
75
			if (! array_key_exists('lat', $this->parametres)) {
77
			if (! array_key_exists('lat', $this->parametres)) {
76
				$messages[] = "Vous devez indiquer une latitude en degré décimal à l'aide du paramètres d'url : lat";
78
				$messages[] = "Vous devez indiquer une latitude en degré décimal à l'aide du paramètres d'url : lat";
77
			} else if (!preg_match(self::PATTERN_LAT, $lat)) {
79
			} else if (!preg_match(self::PATTERN_LAT, $lat)) {
78
				$messages[] = "La valeur de latitude doit être un nombre décimal dont le séparateur décimal est un point. Ex. : 44 ou 43.03";
80
				$messages[] = "La valeur de latitude doit être un nombre décimal dont le séparateur décimal est un point. Ex. : 44 ou 43.03";
79
			}
81
			}
80
			if (! array_key_exists('lon', $this->parametres)) {
82
			if (! array_key_exists('lon', $this->parametres)) {
81
				$messages[] = "Vous devez indiquer une longitude en degré décimal à l'aide du paramètres d'url : lon";
83
				$messages[] = "Vous devez indiquer une longitude en degré décimal à l'aide du paramètres d'url : lon";
82
			} else if (!preg_match(self::PATTERN_LON, $lon)) {
84
			} else if (!preg_match(self::PATTERN_LON, $lon)) {
83
				$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";
85
				$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";
84
			}
86
			}
85
		}
87
		}
86
		if (count($messages) != 0) {
88
		if (count($messages) != 0) {
87
			$message = implode('<br />', $messages);
89
			$message = implode('<br />', $messages);
88
			$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
90
			$code = RestServeur::HTTP_CODE_MAUVAISE_REQUETE;
89
			throw new Exception($message, $code);
91
			throw new Exception($message, $code);
90
		}
92
		}
91
	}
93
	}
92
 
94
 
93
	private function formaterResultats($zonesTrouveesInfos) {
95
	private function formaterResultats($zonesTrouveesInfos) {
94
		$retour = array();
96
		$retour = array();
95
		foreach ($zonesTrouveesInfos as $infos) {
97
		foreach ($zonesTrouveesInfos as $infos) {
96
			$donnees = array();
98
			$donnees = array();
97
			foreach ($infos as $champ => $valeur) {
99
			foreach ($infos as $champ => $valeur) {
98
				if ($valeur != '' && $champ != 'niveau') {
100
				if ($valeur != '' && $champ != 'niveau') {
99
					if ($champ == 'wikipedia') {
101
					if ($champ == 'wikipedia') {
100
						$valeur = $this->formaterUrlWikipedia($valeur);
102
						$valeur = $this->formaterUrlWikipedia($valeur);
101
					}
103
					}
102
					$donnees[$champ] = $valeur;
104
					$donnees[$champ] = $valeur;
103
				}
105
				}
104
			}
106
			}
105
			$retour[$infos['niveau']] = $donnees;
107
			$retour[$infos['niveau']] = $donnees;
106
		}
108
		}
107
		ksort($retour);
109
		ksort($retour);
108
		return $retour;
110
		return $retour;
109
	}
111
	}
110
 
112
 
111
	private function localiserPointLatLon() {
113
	private function localiserPointLatLon() {
112
		$osmIdsInClause = $this->getOsmIdsDesCentresAProximites();
114
		$osmIdsInClause = $this->getOsmIdsDesCentresAProximites();
113
		$zone = isset($this->zone) ? $this->bdd->proteger($this->zone) : null;
115
		$zone = isset($this->zone) ? $this->bdd->proteger($this->zone) : null;
114
 
116
 
115
		$requete = 'SELECT id_zone_geo AS codeZoneGeo, intitule, code_iso_3166_1 AS codeIso31661, '.
117
		$requete = 'SELECT id_zone_geo AS codeZoneGeo, intitule, code_iso_3166_1 AS codeIso31661, '.
116
			'code_iso_3166_2 AS codeIso31662, code_insee AS codeInsee, code_nuts as codeNuts, '.
118
			'code_iso_3166_2 AS codeIso31662, code_insee AS codeInsee, code_nuts as codeNuts, '.
117
			'nom_en AS nomEn, nom_es AS nomEs, wikipedia, niveau '.
119
			'nom_en AS nomEn, nom_es AS nomEs, wikipedia, niveau '.
118
			'FROM osm_zones_admin '.
120
			'FROM osm_zones_admin '.
119
			"WHERE st_within(GEOMFROMTEXT('POINT($this->lon $this->lat)'), polygone) = 1 ".
121
			"WHERE st_within(GEOMFROMTEXT('POINT($this->lon $this->lat)'), polygone) = 1 ".
120
			(isset($zone) ? "AND zone = $zone " : '').
122
			(isset($zone) ? "AND zone = $zone " : '').
121
			"AND osm_id IN ($osmIdsInClause) ".
123
			"AND osm_id IN ($osmIdsInClause) ".
122
			' -- '.__FILE__.' : '.__LINE__;
124
			' -- '.__FILE__.' : '.__LINE__;
123
		$resultat = $this->bdd->recupererTous($requete);
125
		$resultat = $this->bdd->recupererTous($requete);
124
		if ($resultat === false) {
126
		if ($resultat === false) {
125
			$msgTpl = "Service '%s' : aucune zone correspondant aux coordonnées : %s, %s.";
127
			$msgTpl = "Service '%s' : aucune zone correspondant aux coordonnées : %s, %s.";
126
			$msg = sprintf($msgTpl, get_class($this), $this->lat, $this->lon);
128
			$msg = sprintf($msgTpl, get_class($this), $this->lat, $this->lon);
127
			throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
129
			throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
128
		}
130
		}
129
		return $resultat;
131
		return $resultat;
130
	}
132
	}
131
 
133
 
132
	private function getOsmIdsDesCentresAProximites() {
134
	private function getOsmIdsDesCentresAProximites() {
133
		$requete = $this->construireRequeteUnion();
135
		$requete = $this->construireRequeteUnion();
134
		$resultats = $this->bdd->recupererTous($requete);
136
		$resultats = $this->bdd->recupererTous($requete);
135
		if ($resultats === false) {
137
		if ($resultats === false) {
136
			$msgTpl = "Service '%s' : aucun centroïde correspondant aux coordonnées : %s, %s.";
138
			$msgTpl = "Service '%s' : aucun centroïde correspondant aux coordonnées : %s, %s.";
137
			$msg = sprintf($msgTpl, get_class($this), $this->lat, $this->lon);
139
			$msg = sprintf($msgTpl, get_class($this), $this->lat, $this->lon);
138
			throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
140
			throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
139
		}
141
		}
140
		$osmIds = array();
142
		$osmIds = array();
141
		foreach ($resultats as $info) {
143
		foreach ($resultats as $info) {
142
			$osmIds[] = $info['osm_id'];
144
			$osmIds[] = $info['osm_id'];
143
		}
145
		}
144
		$osmIdsInClause = implode(',', $this->bdd->proteger($osmIds));
146
		$osmIdsInClause = implode(',', $this->bdd->proteger($osmIds));
145
		return $osmIdsInClause;
147
		return $osmIdsInClause;
146
	}
148
	}
147
 
149
 
148
	private function construireRequeteUnion() {
150
	private function construireRequeteUnion() {
149
		$zone = isset($this->zone) ? $this->bdd->proteger($this->zone) : null;
151
		$zone = isset($this->zone) ? $this->bdd->proteger($this->zone) : null;
150
		$niveaux = isset($this->niveaux) ? $this->niveaux : null;
152
		$niveaux = isset($this->niveaux) ? $this->niveaux : null;
151
		
153
		
152
		$requeteTpl = 'SELECT osm_id, '.
154
		$requeteTpl = 'SELECT osm_id, '.
153
			"(($this->lon - centre_lng) * ($this->lon -centre_lng) + ($this->lat - centre_lat) * ($this->lat - centre_lat)) AS distance ".
155
			"(($this->lon - centre_lng) * ($this->lon -centre_lng) + ($this->lat - centre_lat) * ($this->lat - centre_lat)) AS distance ".
154
			'FROM osm_zones_admin '.
156
			'FROM osm_zones_admin '.
155
			'WHERE niveau = %s '.
157
			'WHERE niveau = %s '.
156
			(isset($zone) ? "AND zone = $zone " : '').
158
			(isset($zone) ? "AND zone = $zone " : '').
157
			'ORDER BY distance ASC '.
159
			'ORDER BY distance ASC '.
158
			"LIMIT %s ";
160
			"LIMIT %s ";
159
		$niveaux = isset($niveaux) ? $niveaux : array(2,3,4,5,6,7,8);
161
		$niveaux = isset($niveaux) ? $niveaux : array(2,3,4,5,6,7,8);
160
		$requetesAUnir = array();
162
		$requetesAUnir = array();
161
		foreach ($niveaux as $niveau) {
163
		foreach ($niveaux as $niveau) {
162
			$requetesAUnir[] = sprintf($requeteTpl, $this->bdd->proteger($niveau), ($niveau * 2));
164
			$requetesAUnir[] = sprintf($requeteTpl, $this->bdd->proteger($niveau), ($niveau * 2));
163
		}
165
		}
164
		$requete = '('.implode(') UNION (', $requetesAUnir).') -- '.__FILE__.' : '.__LINE__;
166
		$requete = '('.implode(') UNION (', $requetesAUnir).') -- '.__FILE__.' : '.__LINE__;
165
		return $requete;
167
		return $requete;
166
	}
168
	}
167
 
169
 
168
	private function formaterUrlWikipedia($infoWikipedia) {
170
	private function formaterUrlWikipedia($infoWikipedia) {
169
		list($lang, $page) = explode(':', $infoWikipedia);
171
		list($lang, $page) = explode(':', $infoWikipedia);
170
		$pageEncode = rawurlencode($page);
172
		$pageEncode = rawurlencode($page);
171
		$urlTpl = 'https://%s.wikipedia.org/wiki/%s';
173
		$urlTpl = 'https://%s.wikipedia.org/wiki/%s';
172
		return sprintf($urlTpl, $lang, $pageEncode);
174
		return sprintf($urlTpl, $lang, $pageEncode);
173
	}
175
	}
174
	
176
	
175
	public function rechercherZoneGeoParNom($masque) {
177
	public function rechercherZoneGeoParNom($masque, $niveaux = null, $limite = 1) {
-
 
178
		$masque_fmt = str_replace(array(' ', '-'), '_', $masque);
-
 
179
		// Cas d'une recherche pour autocompletion
176
		$masque_fmt = str_replace(array(' ', '-'), '_', $masque);
180
		$masque_fmt = ($limite > 1) ? $masque_fmt.'%' : $masque_fmt;
177
		
181
		
178
		$champs = 'id_zone_geo, osm_id, intitule, centre_lat, centre_lng, '.
182
		$champs = 'id_zone_geo, osm_id, intitule, centre_lat, centre_lng, '.
179
					'zone, niveau, code_iso_3166_1, code_iso_3166_2, code_insee, '.
183
					'zone, niveau, code_iso_3166_1, code_iso_3166_2, code_insee, '.
180
					'nom, nom_fr, nom_en, nom_es, wikipedia';
184
					'nom, nom_fr, nom_en, nom_es, wikipedia';
-
 
185
		
-
 
186
		$champs_tri = 'IF(ISNULL(nom_fr), 0, 1) as nom_fr_present, '.
-
 
187
				'IF(ISNULL(nom_en), 0, 1) as nom_en_present, '.
-
 
188
				'IF(ISNULL(nom_es), 0, 1) as nom_es_present, '.
-
 
189
				'IF(ISNULL(intitule), 0, 1) as intitule_present, '.
-
 
190
				'IF(ISNULL(nom), 0, 1) as nom_present ';
-
 
191
		
-
 
192
		$ordre = 'nom_present DESC, nom_fr_present DESC, nom_fr_present DESC, '.
-
 
193
				 'nom_es_present DESC, intitule_present DESC, niveau DESC';
-
 
194
		
-
 
195
		$niveau_str = array();
-
 
196
		foreach ($niveaux as $niveau) {
-
 
197
			$niveau_str[] = $this->bdd->proteger($niveau);
-
 
198
		}
-
 
199
		
-
 
200
		$requete = "SELECT $champs, $champs_tri FROM osm_zones_admin ".
-
 
201
					"WHERE ".
-
 
202
						(!empty($niveau_str) ? ("niveau IN (".implode(',', $niveau_str).") AND ") : "").
-
 
203
						"(nom LIKE ".$this->bdd->proteger($masque_fmt).' OR '.
-
 
204
						"intitule LIKE ".$this->bdd->proteger($masque_fmt).' OR '.
-
 
205
						"nom_fr LIKE ".$this->bdd->proteger($masque_fmt).' OR '.
181
		
206
						"nom_en LIKE ".$this->bdd->proteger($masque_fmt).' OR '.
182
		$requete = "SELECT $champs FROM osm_zones_admin WHERE nom_fr LIKE ".$this->bdd->proteger($masque_fmt).' '.
207
						"nom_es LIKE ".$this->bdd->proteger($masque_fmt).') '.
183
				"ORDER BY NIVEAU DESC LIMIT 1";
208
				"ORDER BY ".$ordre." LIMIT ".$limite;
184
 
209
 
185
		$resultat = $this->bdd->recupererTous($requete);
210
		$resultat = $this->bdd->recupererTous($requete);
186
		if (empty($resultat)) {
211
		if (empty($resultat)) {
187
			$msgTpl = "Service '%s' : aucune zone correspondant au nom : %s .";
212
			$msgTpl = "Service '%s' : aucune zone correspondant au nom : %s .";
188
			$msg = sprintf($msgTpl, get_class($this), $masque);
213
			$msg = sprintf($msgTpl, get_class($this), $masque);
189
			throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
214
			throw new Exception($msg, RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE);
-
 
215
		} else {
-
 
216
			$resultat = ($limite == 1) ? $resultat[0] : $resultat;
190
		}
217
		}
191
		
218
		
192
		return $resultat[0];
219
		return $resultat;
193
	}
220
	}
194
}
221
}