1 <?php
declare(strict_types
=1);
4 * This file is part of the Rapsys PackBundle package.
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Rapsys\PackBundle\Util
;
14 use Symfony\Component\Routing\RouterInterface
;
21 * The cycle tile server
23 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers
25 const cycle
= 'http://a.tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png';
40 const highFill
= '#c3c3f9';
45 const highFontSize
= 30;
48 * The high radius size
53 * The high stroke color
55 const highStroke
= '#3333c3';
58 * The high stroke width
60 const highStrokeWidth
= 4;
75 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers
77 const osm
= 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png';
87 const stroke
= '#00c3f9';
92 const strokeWidth
= 2;
95 * The transport tile server
97 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers
99 const transport
= 'http://a.tile.thunderforest.com/transport/{Z}/{X}/{Y}.png';
112 * Creates a new map util
114 * @param RouterInterface $router The RouterInterface instance
115 * @param SluggerUtil $slugger The SluggerUtil instance
117 function __construct(protected RouterInterface
$router, protected SluggerUtil
$slugger, protected string $fill = self
::fill
, protected int $fontSize = self
::fontSize
, protected string $highFill = self
::highFill
, protected int $highFontSize = self
::highFontSize
, protected int $highRadius = self
::highRadius
, protected string $highStroke = self
::highStroke
, protected int $highStrokeWidth = self
::highStrokeWidth
, protected int $radius = self
::radius
, protected string $stroke = self
::stroke
, protected int $strokeWidth = self
::strokeWidth
) {
130 function getFontSize() {
131 return $this->fontSize
;
135 * Get high fill color
137 function getHighFill() {
138 return $this->highFill
;
144 function getHighFontSize() {
145 return $this->highFontSize
;
149 * Get high radius size
151 function getHighRadius() {
152 return $this->highRadius
;
156 * Get high stroke color
158 function getHighStroke() {
159 return $this->highStroke
;
163 * Get high stroke width
165 function getHighStrokeWidth() {
166 return $this->highStrokeWidth
;
172 function getRadius() {
173 return $this->radius
;
179 function getStroke() {
180 return $this->stroke
;
186 function getStrokeWidth() {
187 return $this->strokeWidth
;
193 * @param string $caption The caption
194 * @param int $updated The updated timestamp
195 * @param float $latitude The latitude
196 * @param float $longitude The longitude
197 * @param int $zoom The zoom
198 * @param int $width The width
199 * @param int $height The height
200 * @return array The map data
202 public function getMap(string $caption, int $updated, float $latitude, float $longitude, int $zoom = self
::zoom
, int $width = self
::width
, int $height = self
::height
): array {
204 $link = $this->slugger
->hash([$updated, $latitude, $longitude, $zoom +
1, $width * 2, $height * 2]);
207 $src = $this->slugger
->hash([$updated, $latitude, $longitude, $zoom, $width, $height]);
211 'caption' => $caption,
212 'link' => $this->router
->generate('rapsyspack_map', ['hash' => $link, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'zoom' => $zoom +
1, 'width' => $width * 2, 'height' => $height * 2]),
213 'src' => $this->router
->generate('rapsyspack_map', ['hash' => $src, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'zoom' => $zoom, 'width' => $width, 'height' => $height]),
222 * @param string $caption The caption
223 * @param int $updated The updated timestamp
224 * @param array $coordinates The coordinates array
225 * @param int $width The width
226 * @param int $height The height
227 * @return array The multi map data
229 public function getMultiMap(string $caption, int $updated, array $coordinates, int $width = self
::width
, int $height = self
::height
): array {
230 //Without coordinates
231 if (empty($coordinates)) {
237 $latitudes = array_map(function ($v) { return $v
['latitude']; }, $coordinates);
240 $longitudes = array_map(function ($v) { return $v
['longitude']; }, $coordinates);
243 $latitude = round((min($latitudes)+
max($latitudes))/2, 6);
246 $longitude = round((min($longitudes)+
max($longitudes))/2, 6);
249 $zoom = $this->getMultiZoom($latitude, $longitude, $coordinates, $width, $height);
252 $coordinate = implode('-', array_map(function ($v) { return $v
['latitude'].','.$v
['longitude']; }, $coordinates));
254 //Set coordinate hash
255 $hash = $this->slugger
->hash($coordinate);
258 $link = $this->slugger
->hash([$updated, $latitude, $longitude, $hash, $zoom +
1, $width * 2, $height * 2]);
261 $src = $this->slugger
->hash([$updated, $latitude, $longitude, $hash, $zoom, $width, $height]);
265 'caption' => $caption,
266 'link' => $this->router
->generate('rapsyspack_multimap', ['hash' => $link, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'coordinates' => $coordinate, 'zoom' => $zoom +
1, 'width' => $width * 2, 'height' => $height * 2]),
267 'src' => $this->router
->generate('rapsyspack_multimap', ['hash' => $src, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'coordinates' => $coordinate, 'zoom' => $zoom, 'width' => $width, 'height' => $height]),
276 * Compute a zoom to have all coordinates on multi map
277 * Multi map visible only from -($width / 2) until ($width / 2) and from -($height / 2) until ($height / 2)
279 * @see Wether we need to take in consideration circle radius in coordinates comparisons, likely +/-(radius / self::tz)
281 * @param float $latitude The latitude
282 * @param float $longitude The longitude
283 * @param array $coordinates The coordinates array
284 * @param int $width The width
285 * @param int $height The height
286 * @param int $zoom The zoom
287 * @return int The zoom
289 public function getMultiZoom(float $latitude, float $longitude, array $coordinates, int $width, int $height, int $zoom = self
::zoom
): int {
290 //Iterate on each zoom
291 for ($i = $zoom; $i >= 1; $i--) {
293 $centerX = self
::longitudeToX($longitude, $i);
294 $centerY = self
::latitudeToY($latitude, $i);
297 $startX = floor($centerX - $width / 2 / self
::tz
);
298 $startY = floor($centerY - $height / 2 / self
::tz
);
301 $endX = ceil($centerX +
$width / 2 / self
::tz
);
302 $endY = ceil($centerY +
$height / 2 / self
::tz
);
304 //Iterate on each coordinates
305 foreach($coordinates as $k => $coordinate) {
307 $destX = self
::longitudeToX($coordinate['longitude'], $i);
310 if ($startX >= $destX || $endX <= $destX) {
316 $destY = self
::latitudeToY($coordinate['latitude'], $i);
319 if ($startY >= $destY || $endY <= $destY) {
334 * Convert longitude to tile x number
336 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_5
338 * @param float $longitude The longitude
339 * @param int $zoom The zoom
341 * @return float The tile x
343 public static function longitudeToX(float $longitude, int $zoom): float {
344 return (($longitude +
180) / 360) * pow(2, $zoom);
348 * Convert latitude to tile y number
350 * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_5
352 * @param $latitude The latitude
353 * @param $zoom The zoom
355 * @return float The tile y
357 public static function latitudeToY(float $latitude, int $zoom): float {
358 return (1 - log(tan(deg2rad($latitude)) +
1 / cos(deg2rad($latitude))) / pi()) / 2 * pow(2, $zoom);
362 * Convert tile x to longitude
364 * @param float $x The tile x
365 * @param int $zoom The zoom
367 * @return float The longitude
369 public static function xToLongitude(float $x, int $zoom): float {
370 return $x / pow(2, $zoom) * 360.0 - 180.0;
374 * Convert tile y to latitude
376 * @param float $y The tile y
377 * @param int $zoom The zoom
379 * @return float The latitude
381 public static function yToLatitude(float $y, int $zoom): float {
382 return rad2deg(atan(sinh(pi() * (1 - 2 * $y / pow(2, $zoom)))));
386 * Convert decimal latitude to sexagesimal
388 * @param float $latitude The decimal latitude
390 * @return string The sexagesimal longitude
392 public static function latitudeToSexagesimal(float $latitude): string {
394 //TODO: see if round or intval is better suited to fix the Deprecated: Implicit conversion from float to int loses precision
395 $degree = round($latitude) %
60;
398 $minute = round(($latitude - $degree) * 60) %
60;
401 $second = round(($latitude - $degree - $minute / 60) * 3600) %
3600;
403 //Return sexagesimal longitude
404 return $degree.'°'.$minute.'\''.$second.'"'.($latitude >= 0 ? 'N' : 'S');
408 * Convert decimal longitude to sexagesimal
410 * @param float $longitude The decimal longitude
412 * @return string The sexagesimal longitude
414 public static function longitudeToSexagesimal(float $longitude): string {
416 //TODO: see if round or intval is better suited to fix the Deprecated: Implicit conversion from float to int loses precision
417 $degree = round($longitude) %
60;
420 $minute = round(($longitude - $degree) * 60) %
60;
423 $second = round(($longitude - $degree - $minute / 60) * 3600) %
3600;
425 //Return sexagesimal longitude
426 return $degree.'°'.$minute.'\''.$second.'"'.($longitude >= 0 ? 'E' : 'W');