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 size 
  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          * The RouterInterface instance 
 114         protected RouterInterface 
$router; 
 117          * The SluggerUtil instance 
 119         protected SluggerUtil 
$slugger; 
 129         public int $fontSize; 
 132          * The high fill color 
 134         public string $highFill; 
 139         public int $highFontSize; 
 144         public int $highRadius; 
 147          * The high stroke color 
 149         public string $highStroke; 
 154         public int $highStrokeWidth; 
 159         public string $stroke; 
 164         public int $strokeWidth; 
 172          * Creates a new map util 
 174          * @param RouterInterface $router The RouterInterface instance 
 175          * @param SluggerUtil $slugger The SluggerUtil instance 
 177         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
) { 
 179                 $this->router 
= $router; 
 182                 $this->slugger 
= $slugger; 
 188                 $this->fontSize 
= $fontSize; 
 191                 $this->highFill 
= $highFill; 
 194                 $this->highFontSize 
= $highFontSize; 
 196                 //Set high radius size 
 197                 $this->highRadius 
= $highRadius; 
 200                 $this->highStroke 
= $highStroke; 
 202                 //Set high stroke size 
 203                 $this->highStrokeWidth 
= $highStrokeWidth; 
 206                 $this->radius 
= $radius; 
 209                 $this->stroke 
= $stroke; 
 212                 $this->strokeWidth 
= $strokeWidth; 
 218          * @param string $caption The caption 
 219          * @param int $updated The updated timestamp 
 220          * @param float $latitude The latitude 
 221          * @param float $longitude The longitude 
 222          * @param int $zoom The zoom 
 223          * @param int $width The width 
 224          * @param int $height The height 
 225          * @return array The map data 
 227         public function getMap(string $caption, int $updated, float $latitude, float $longitude, int $zoom = self
::zoom
, int $width = self
::width
, int $height = self
::height
): array { 
 229                 $link = $this->slugger
->hash([$updated, $latitude, $longitude, $zoom + 
1, $width * 2, $height * 2]); 
 232                 $src = $this->slugger
->hash([$updated, $latitude, $longitude, $zoom, $width, $height]); 
 236                         'caption' => $caption, 
 237                         'link' => $this->router
->generate('rapsys_pack_map', ['hash' => $link, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'zoom' => $zoom + 
1, 'width' => $width * 2, 'height' => $height * 2]), 
 238                         'src' => $this->router
->generate('rapsys_pack_map', ['hash' => $src, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'zoom' => $zoom, 'width' => $width, 'height' => $height]), 
 247          * @param string $caption The caption 
 248          * @param int $updated The updated timestamp 
 249          * @param array $coordinates The coordinates array 
 250          * @param int $width The width 
 251          * @param int $height The height 
 252          * @return array The multi map data 
 254         public function getMultiMap(string $caption, int $updated, array $coordinates, int $width = self
::width
, int $height = self
::height
): array { 
 256                 $latitudes = array_map(function ($v) { return $v
['latitude']; }, $coordinates); 
 259                 $longitudes = array_map(function ($v) { return $v
['longitude']; }, $coordinates); 
 262                 $latitude = round((min($latitudes)+
max($latitudes))/2, 6); 
 265                 $longitude = round((min($longitudes)+
max($longitudes))/2, 6); 
 268                 $zoom = $this->getMultiZoom($latitude, $longitude, $coordinates, $width, $height); 
 271                 $coordinate = implode('-', array_map(function ($v) { return $v
['latitude'].','.$v
['longitude']; }, $coordinates)); 
 273                 //Set coordinate hash 
 274                 $hash = $this->slugger
->hash($coordinate); 
 277                 $link = $this->slugger
->hash([$updated, $latitude, $longitude, $hash, $zoom + 
1, $width * 2, $height * 2]); 
 280                 $src = $this->slugger
->hash([$updated, $latitude, $longitude, $hash, $zoom, $width, $height]); 
 284                         'caption' => $caption, 
 285                         '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]), 
 286                         'src' => $this->router
->generate('rapsys_pack_multimap', ['hash' => $src, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'coordinates' => $coordinate, 'zoom' => $zoom, 'width' => $width, 'height' => $height]), 
 295          * Compute a zoom to have all coordinates on multi map 
 296          * Multi map visible only from -($width / 2) until ($width / 2) and from -($height / 2) until ($height / 2) 
 298          * @see Wether we need to take in consideration circle radius in coordinates comparisons, likely +/-(radius / self::tz) 
 300          * @param float $latitude The latitude 
 301          * @param float $longitude The longitude 
 302          * @param array $coordinates The coordinates array 
 303          * @param int $width The width 
 304          * @param int $height The height 
 305          * @param int $zoom The zoom 
 306          * @return int The zoom 
 308         public function getMultiZoom(float $latitude, float $longitude, array $coordinates, int $width, int $height, int $zoom = self
::zoom
): int { 
 309                 //Iterate on each zoom 
 310                 for ($i = $zoom; $i >= 1; $i--) { 
 312                         $centerX = self
::longitudeToX($longitude, $i); 
 313                         $centerY = self
::latitudeToY($latitude, $i); 
 316                         $startX = floor($centerX - $width / 2 / self
::tz
); 
 317                         $startY = floor($centerY - $height / 2 / self
::tz
); 
 320                         $endX = ceil($centerX + 
$width / 2 / self
::tz
); 
 321                         $endY = ceil($centerY + 
$height / 2 / self
::tz
); 
 323                         //Iterate on each coordinates 
 324                         foreach($coordinates as $k => $coordinate) { 
 326                                 $destX = self
::longitudeToX($coordinate['longitude'], $i); 
 329                                 if ($startX >= $destX || $endX <= $destX) { 
 335                                 $destY = self
::latitudeToY($coordinate['latitude'], $i); 
 338                                 if ($startY >= $destY || $endY <= $destY) { 
 353          * Convert longitude to tile x number 
 355          * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_5 
 357          * @param float $longitude The longitude 
 358          * @param int $zoom The zoom 
 360          * @return float The tile x 
 362         public static function longitudeToX(float $longitude, int $zoom): float { 
 363                 return (($longitude + 
180) / 360) * pow(2, $zoom); 
 367          * Convert latitude to tile y number 
 369          * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_5 
 371          * @param $latitude The latitude 
 372          * @param $zoom The zoom 
 374          * @return float The tile y 
 376         public static function latitudeToY(float $latitude, int $zoom): float { 
 377                 return (1 - log(tan(deg2rad($latitude)) + 
1 / cos(deg2rad($latitude))) / pi()) / 2 * pow(2, $zoom); 
 381          * Convert tile x to longitude 
 383          * @param float $x The tile x 
 384          * @param int $zoom The zoom 
 386          * @return float The longitude 
 388         public static function xToLongitude(float $x, int $zoom): float { 
 389                 return $x / pow(2, $zoom) * 360.0 - 180.0; 
 393          * Convert tile y to latitude 
 395          * @param float $y The tile y 
 396          * @param int $zoom The zoom 
 398          * @return float The latitude 
 400         public static function yToLatitude(float $y, int $zoom): float { 
 401                 return rad2deg(atan(sinh(pi() * (1 - 2 * $y / pow(2, $zoom))))); 
 405          * Convert decimal latitude to sexagesimal 
 407          * @param float $latitude The decimal latitude 
 409          * @return string The sexagesimal longitude 
 411         public static function latitudeToSexagesimal(float $latitude): string { 
 413                 //TODO: see if round or intval is better suited to fix the Deprecated: Implicit conversion from float to int loses precision 
 414                 $degree = round($latitude) % 
60; 
 417                 $minute = round(($latitude - $degree) * 60) % 
60; 
 420                 $second = round(($latitude - $degree - $minute / 60) * 3600) % 
3600; 
 422                 //Return sexagesimal longitude 
 423                 return $degree.'°'.$minute.'\''.$second.'"'.($latitude >= 0 ? 'N' : 'S'); 
 427          * Convert decimal longitude to sexagesimal 
 429          * @param float $longitude The decimal longitude 
 431          * @return string The sexagesimal longitude 
 433         public static function longitudeToSexagesimal(float $longitude): string { 
 435                 //TODO: see if round or intval is better suited to fix the Deprecated: Implicit conversion from float to int loses precision 
 436                 $degree = round($longitude) % 
60; 
 439                 $minute = round(($longitude - $degree) * 60) % 
60; 
 442                 $second = round(($longitude - $degree - $minute / 60) * 3600) % 
3600; 
 444                 //Return sexagesimal longitude 
 445                 return $degree.'°'.$minute.'\''.$second.'"'.($longitude >= 0 ? 'E' : 'W');