]> Raphaël G. Git Repositories - packbundle/blob - Util/MapUtil.php
5de2967fbdcf5ed3e6d296ddf1492d8712e35203
[packbundle] / Util / MapUtil.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys PackBundle package.
5 *
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Rapsys\PackBundle\Util;
13
14 use Symfony\Component\Routing\RouterInterface;
15
16 /**
17 * Helps manage map
18 */
19 class MapUtil {
20 /**
21 * The cycle tile server
22 *
23 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers
24 */
25 const cycle = 'http://a.tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png';
26
27 /**
28 * The fill color
29 */
30 const fill = '#cff';
31
32 /**
33 * The font size
34 */
35 const fontSize = 20;
36
37 /**
38 * The high fill color
39 */
40 const highFill = '#c3c3f9';
41
42 /**
43 * The high font size
44 */
45 const highFontSize = 30;
46
47 /**
48 * The high radius size
49 */
50 const highRadius = 6;
51
52 /**
53 * The high stroke color
54 */
55 const highStroke = '#3333c3';
56
57 /**
58 * The high stroke size
59 */
60 const highStrokeWidth = 4;
61
62 /**
63 * The map length
64 */
65 const length = 640;
66
67 /**
68 * The osm tile server
69 *
70 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers
71 */
72 const osm = 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png';
73
74 /**
75 * The radius size
76 */
77 const radius = 5;
78
79 /**
80 * The stroke color
81 */
82 const stroke = '#00c3f9';
83
84 /**
85 * The stroke size
86 */
87 const strokeWidth = 2;
88
89 /**
90 * The transport tile server
91 *
92 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers
93 */
94 const transport = 'http://a.tile.thunderforest.com/transport/{Z}/{X}/{Y}.png';
95
96 /**
97 * The tile size
98 */
99 const tz = 256;
100
101 /**
102 * The map zoom
103 */
104 const zoom = 17;
105
106 /**
107 * The RouterInterface instance
108 */
109 protected RouterInterface $router;
110
111 /**
112 * The SluggerUtil instance
113 */
114 protected SluggerUtil $slugger;
115
116 /**
117 * The fill color
118 */
119 public string $fill;
120
121 /**
122 * The font size
123 */
124 public int $fontSize;
125
126 /**
127 * The high fill color
128 */
129 public string $highFill;
130
131 /**
132 * The font size
133 */
134 public int $highFontSize;
135
136 /**
137 * The radius size
138 */
139 public int $highRadius;
140
141 /**
142 * The high stroke color
143 */
144 public string $highStroke;
145
146 /**
147 * The stroke size
148 */
149 public int $highStrokeWidth;
150
151 /**
152 * The stroke color
153 */
154 public string $stroke;
155
156 /**
157 * The stroke size
158 */
159 public int $strokeWidth;
160
161 /**
162 * The radius size
163 */
164 public int $radius;
165
166 /**
167 * Creates a new map util
168 *
169 * @param RouterInterface $router The RouterInterface instance
170 * @param SluggerUtil $slugger The SluggerUtil instance
171 */
172 function __construct(RouterInterface $router, SluggerUtil $slugger, string $fill = self::fill, int $fontSize = self::fontSize, string $highFill = self::highFill, int $highFontSize = self::highFontSize, int $highRadius = self::highRadius, string $highStroke = self::highStroke, int $highStrokeWidth = self::highStrokeWidth, int $radius = self::radius, string $stroke = self::stroke, int $strokeWidth = self::strokeWidth) {
173 //Set router
174 $this->router = $router;
175
176 //Set slugger
177 $this->slugger = $slugger;
178
179 //Set fill
180 $this->fill = $fill;
181
182 //Set font size
183 $this->fontSize = $fontSize;
184
185 //Set highFill
186 $this->highFill = $highFill;
187
188 //Set high font size
189 $this->highFontSize = $highFontSize;
190
191 //Set high radius size
192 $this->highRadius = $highRadius;
193
194 //Set highStroke
195 $this->highStroke = $highStroke;
196
197 //Set high stroke size
198 $this->highStrokeWidth = $highStrokeWidth;
199
200 //Set radius size
201 $this->radius = $radius;
202
203 //Set stroke
204 $this->stroke = $stroke;
205
206 //Set stroke size
207 $this->strokeWidth = $strokeWidth;
208 }
209
210 /**
211 * Return map url
212 *
213 * @param string $caption The caption
214 * @param int $updated The updated timestamp
215 * @param float $latitude The latitude
216 * @param float $longitude The longitude
217 * @param int $zoom The zoom
218 * @param int $width The width
219 * @param int $height The height
220 * @return int The zoom
221 */
222 public function mapUrl(string $caption, int $updated, float $latitude, float $longitude, int $zoom = self::zoom, int $width = self::length, int $height = self::length): array {
223 //Set link hash
224 $link = $this->slugger->serialize([$updated, $latitude, $longitude, $zoom + 1, $width * 2, $height * 2]);
225
226 //Set src hash
227 $src = $this->slugger->serialize([$updated, $latitude, $longitude, $zoom, $width, $height]);
228
229 //Return array
230 return [
231 'caption' => $caption,
232 'link' => $this->router->generate('rapsys_pack_map', ['hash' => $link, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'zoom' => $zoom + 1, 'width' => $width * 2, 'height' => $height * 2]),
233 'src' => $this->router->generate('rapsys_pack_map', ['hash' => $src, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'zoom' => $zoom, 'width' => $width, 'height' => $height]),
234 'width' => $width,
235 'height' => $height
236 ];
237 }
238
239 /**
240 * Return multi map url
241 *
242 * @param string $caption The caption
243 * @param int $updated The updated timestamp
244 * @param float $latitude The latitude
245 * @param float $longitude The longitude
246 * @param array $coordinates The coordinates array
247 * @param int $zoom The zoom
248 * @param int $width The width
249 * @param int $height The height
250 * @return int The zoom
251 */
252 public function multiMapUrl(string $caption, int $updated, float $latitude, float $longitude, $coordinates = [], int $zoom = self::zoom, int $width = self::length, int $height = self::length): array {
253 //Set coordinate
254 $coordinate = implode('-', array_map(function ($v) { return $v['latitude'].','.$v['longitude']; }, $coordinates));
255
256 //Set coordinate hash
257 $hash = $this->slugger->hash($coordinate);
258
259 //Set link hash
260 $link = $this->slugger->serialize([$updated, $latitude, $longitude, $hash, $zoom + 1, $width * 2, $height * 2]);
261
262 //Set src hash
263 $src = $this->slugger->serialize([$updated, $latitude, $longitude, $hash, $zoom, $width, $height]);
264
265 //Return array
266 return [
267 'caption' => $caption,
268 'link' => $this->router->generate('rapsys_pack_multimap', ['hash' => $link, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'coordinates' => $coordinate, 'zoom' => $zoom + 1, 'width' => $width * 2, 'height' => $height * 2]),
269 'src' => $this->router->generate('rapsys_pack_multimap', ['hash' => $src, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'coordinates' => $coordinate, 'zoom' => $zoom, 'width' => $width, 'height' => $height]),
270 'width' => $width,
271 'height' => $height
272 ];
273 }
274
275 /**
276 * Return multi map zoom
277 *
278 * Compute a zoom to have all coordinates on multi map
279 * Multi map visible only from -($width / 2) until ($width / 2) and from -($height / 2) until ($height / 2)
280 *
281 * @see Wether we need to take in consideration circle radius in coordinates comparisons, likely +/-(radius / self::tz)
282 *
283 * @param float $latitude The latitude
284 * @param float $longitude The longitude
285 * @param array $coordinates The coordinates array
286 * @param int $zoom The zoom
287 * @param int $width The width
288 * @param int $height The height
289 * @return int The zoom
290 */
291 public function multiMapZoom(float $latitude, float $longitude, array $coordinates = [], int $zoom = self::zoom, int $width = self::length, int $height = self::length): int {
292 //Iterate on each zoom
293 for ($i = $zoom; $i >= 1; $i--) {
294 //Get tile xy
295 $centerX = self::longitudeToX($longitude, $i);
296 $centerY = self::latitudeToY($latitude, $i);
297
298 //Calculate start xy
299 $startX = floor($centerX - $width / 2 / self::tz);
300 $startY = floor($centerY - $height / 2 / self::tz);
301
302 //Calculate end xy
303 $endX = ceil($centerX + $width / 2 / self::tz);
304 $endY = ceil($centerY + $height / 2 / self::tz);
305
306 //Iterate on each coordinates
307 foreach($coordinates as $k => $coordinate) {
308 //Set dest x
309 $destX = self::longitudeToX($coordinate['longitude'], $i);
310
311 //With outside point
312 if ($startX >= $destX || $endX <= $destX) {
313 //Skip zoom
314 continue(2);
315 }
316
317 //Set dest y
318 $destY = self::latitudeToY($coordinate['latitude'], $i);
319
320 //With outside point
321 if ($startY >= $destY || $endY <= $destY) {
322 //Skip zoom
323 continue(2);
324 }
325 }
326
327 //Found zoom
328 break;
329 }
330
331 //Return zoom
332 return $i;
333 }
334
335 /**
336 * Convert longitude to tile x number
337 *
338 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_5
339 *
340 * @param float $longitude The longitude
341 * @param int $zoom The zoom
342 *
343 * @return float The tile x
344 */
345 public static function longitudeToX(float $longitude, int $zoom): float {
346 return (($longitude + 180) / 360) * pow(2, $zoom);
347 }
348
349 /**
350 * Convert latitude to tile y number
351 *
352 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_5
353 *
354 * @param $latitude The latitude
355 * @param $zoom The zoom
356 *
357 * @return float The tile y
358 */
359 public static function latitudeToY(float $latitude, int $zoom): float {
360 return (1 - log(tan(deg2rad($latitude)) + 1 / cos(deg2rad($latitude))) / pi()) / 2 * pow(2, $zoom);
361 }
362
363 /**
364 * Convert tile x to longitude
365 *
366 * @param float $x The tile x
367 * @param int $zoom The zoom
368 *
369 * @return float The longitude
370 */
371 public static function xToLongitude(float $x, int $zoom): float {
372 return $x / pow(2, $zoom) * 360.0 - 180.0;
373 }
374
375 /**
376 * Convert tile y to latitude
377 *
378 * @param float $y The tile y
379 * @param int $zoom The zoom
380 *
381 * @return float The latitude
382 */
383 public static function yToLatitude(float $y, int $zoom): float {
384 return rad2deg(atan(sinh(pi() * (1 - 2 * $y / pow(2, $zoom)))));
385 }
386
387 /**
388 * Convert decimal latitude to sexagesimal
389 *
390 * @param float $latitude The decimal latitude
391 *
392 * @return string The sexagesimal longitude
393 */
394 public static function latitudeToSexagesimal(float $latitude): string {
395 //Set degree
396 $degree = $latitude % 60;
397
398 //Set minute
399 $minute = ($latitude - $degree) * 60 % 60;
400
401 //Set second
402 $second = ($latitude - $degree - $minute / 60) * 3600 % 3600;
403
404 //Return sexagesimal longitude
405 return $degree.'°'.$minute.'\''.$second.'"'.($latitude >= 0 ? 'N' : 'S');
406 }
407
408 /**
409 * Convert decimal longitude to sexagesimal
410 *
411 * @param float $longitude The decimal longitude
412 *
413 * @return string The sexagesimal longitude
414 */
415 public static function longitudeToSexagesimal(float $longitude): string {
416 //Set degree
417 $degree = $longitude % 60;
418
419 //Set minute
420 $minute = ($longitude - $degree) * 60 % 60;
421
422 //Set second
423 $second = ($longitude - $degree - $minute / 60) * 3600 % 3600;
424
425 //Return sexagesimal longitude
426 return $degree.'°'.$minute.'\''.$second.'"'.($longitude >= 0 ? 'E' : 'W');
427 }
428 }