X-Git-Url: https://git.rapsys.eu/packbundle/blobdiff_plain/42a99324bb26ad7305e738882ac2de332ce5cc38..b31612d28cf2a5617667dd9dae62379c90dbaf1c:/Util/MapUtil.php diff --git a/Util/MapUtil.php b/Util/MapUtil.php new file mode 100644 index 0000000..5de2967 --- /dev/null +++ b/Util/MapUtil.php @@ -0,0 +1,428 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Rapsys\PackBundle\Util; + +use Symfony\Component\Routing\RouterInterface; + +/** + * Helps manage map + */ +class MapUtil { + /** + * The cycle tile server + * + * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers + */ + const cycle = 'http://a.tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png'; + + /** + * The fill color + */ + const fill = '#cff'; + + /** + * The font size + */ + const fontSize = 20; + + /** + * The high fill color + */ + const highFill = '#c3c3f9'; + + /** + * The high font size + */ + const highFontSize = 30; + + /** + * The high radius size + */ + const highRadius = 6; + + /** + * The high stroke color + */ + const highStroke = '#3333c3'; + + /** + * The high stroke size + */ + const highStrokeWidth = 4; + + /** + * The map length + */ + const length = 640; + + /** + * The osm tile server + * + * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers + */ + const osm = 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png'; + + /** + * The radius size + */ + const radius = 5; + + /** + * The stroke color + */ + const stroke = '#00c3f9'; + + /** + * The stroke size + */ + const strokeWidth = 2; + + /** + * The transport tile server + * + * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers + */ + const transport = 'http://a.tile.thunderforest.com/transport/{Z}/{X}/{Y}.png'; + + /** + * The tile size + */ + const tz = 256; + + /** + * The map zoom + */ + const zoom = 17; + + /** + * The RouterInterface instance + */ + protected RouterInterface $router; + + /** + * The SluggerUtil instance + */ + protected SluggerUtil $slugger; + + /** + * The fill color + */ + public string $fill; + + /** + * The font size + */ + public int $fontSize; + + /** + * The high fill color + */ + public string $highFill; + + /** + * The font size + */ + public int $highFontSize; + + /** + * The radius size + */ + public int $highRadius; + + /** + * The high stroke color + */ + public string $highStroke; + + /** + * The stroke size + */ + public int $highStrokeWidth; + + /** + * The stroke color + */ + public string $stroke; + + /** + * The stroke size + */ + public int $strokeWidth; + + /** + * The radius size + */ + public int $radius; + + /** + * Creates a new map util + * + * @param RouterInterface $router The RouterInterface instance + * @param SluggerUtil $slugger The SluggerUtil instance + */ + 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) { + //Set router + $this->router = $router; + + //Set slugger + $this->slugger = $slugger; + + //Set fill + $this->fill = $fill; + + //Set font size + $this->fontSize = $fontSize; + + //Set highFill + $this->highFill = $highFill; + + //Set high font size + $this->highFontSize = $highFontSize; + + //Set high radius size + $this->highRadius = $highRadius; + + //Set highStroke + $this->highStroke = $highStroke; + + //Set high stroke size + $this->highStrokeWidth = $highStrokeWidth; + + //Set radius size + $this->radius = $radius; + + //Set stroke + $this->stroke = $stroke; + + //Set stroke size + $this->strokeWidth = $strokeWidth; + } + + /** + * Return map url + * + * @param string $caption The caption + * @param int $updated The updated timestamp + * @param float $latitude The latitude + * @param float $longitude The longitude + * @param int $zoom The zoom + * @param int $width The width + * @param int $height The height + * @return int The zoom + */ + public function mapUrl(string $caption, int $updated, float $latitude, float $longitude, int $zoom = self::zoom, int $width = self::length, int $height = self::length): array { + //Set link hash + $link = $this->slugger->serialize([$updated, $latitude, $longitude, $zoom + 1, $width * 2, $height * 2]); + + //Set src hash + $src = $this->slugger->serialize([$updated, $latitude, $longitude, $zoom, $width, $height]); + + //Return array + return [ + 'caption' => $caption, + 'link' => $this->router->generate('rapsys_pack_map', ['hash' => $link, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'zoom' => $zoom + 1, 'width' => $width * 2, 'height' => $height * 2]), + 'src' => $this->router->generate('rapsys_pack_map', ['hash' => $src, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'zoom' => $zoom, 'width' => $width, 'height' => $height]), + 'width' => $width, + 'height' => $height + ]; + } + + /** + * Return multi map url + * + * @param string $caption The caption + * @param int $updated The updated timestamp + * @param float $latitude The latitude + * @param float $longitude The longitude + * @param array $coordinates The coordinates array + * @param int $zoom The zoom + * @param int $width The width + * @param int $height The height + * @return int The zoom + */ + 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 { + //Set coordinate + $coordinate = implode('-', array_map(function ($v) { return $v['latitude'].','.$v['longitude']; }, $coordinates)); + + //Set coordinate hash + $hash = $this->slugger->hash($coordinate); + + //Set link hash + $link = $this->slugger->serialize([$updated, $latitude, $longitude, $hash, $zoom + 1, $width * 2, $height * 2]); + + //Set src hash + $src = $this->slugger->serialize([$updated, $latitude, $longitude, $hash, $zoom, $width, $height]); + + //Return array + return [ + 'caption' => $caption, + '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]), + 'src' => $this->router->generate('rapsys_pack_multimap', ['hash' => $src, 'updated' => $updated, 'latitude' => $latitude, 'longitude' => $longitude, 'coordinates' => $coordinate, 'zoom' => $zoom, 'width' => $width, 'height' => $height]), + 'width' => $width, + 'height' => $height + ]; + } + + /** + * Return multi map zoom + * + * Compute a zoom to have all coordinates on multi map + * Multi map visible only from -($width / 2) until ($width / 2) and from -($height / 2) until ($height / 2) + * + * @see Wether we need to take in consideration circle radius in coordinates comparisons, likely +/-(radius / self::tz) + * + * @param float $latitude The latitude + * @param float $longitude The longitude + * @param array $coordinates The coordinates array + * @param int $zoom The zoom + * @param int $width The width + * @param int $height The height + * @return int The zoom + */ + public function multiMapZoom(float $latitude, float $longitude, array $coordinates = [], int $zoom = self::zoom, int $width = self::length, int $height = self::length): int { + //Iterate on each zoom + for ($i = $zoom; $i >= 1; $i--) { + //Get tile xy + $centerX = self::longitudeToX($longitude, $i); + $centerY = self::latitudeToY($latitude, $i); + + //Calculate start xy + $startX = floor($centerX - $width / 2 / self::tz); + $startY = floor($centerY - $height / 2 / self::tz); + + //Calculate end xy + $endX = ceil($centerX + $width / 2 / self::tz); + $endY = ceil($centerY + $height / 2 / self::tz); + + //Iterate on each coordinates + foreach($coordinates as $k => $coordinate) { + //Set dest x + $destX = self::longitudeToX($coordinate['longitude'], $i); + + //With outside point + if ($startX >= $destX || $endX <= $destX) { + //Skip zoom + continue(2); + } + + //Set dest y + $destY = self::latitudeToY($coordinate['latitude'], $i); + + //With outside point + if ($startY >= $destY || $endY <= $destY) { + //Skip zoom + continue(2); + } + } + + //Found zoom + break; + } + + //Return zoom + return $i; + } + + /** + * Convert longitude to tile x number + * + * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_5 + * + * @param float $longitude The longitude + * @param int $zoom The zoom + * + * @return float The tile x + */ + public static function longitudeToX(float $longitude, int $zoom): float { + return (($longitude + 180) / 360) * pow(2, $zoom); + } + + /** + * Convert latitude to tile y number + * + * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_5 + * + * @param $latitude The latitude + * @param $zoom The zoom + * + * @return float The tile y + */ + public static function latitudeToY(float $latitude, int $zoom): float { + return (1 - log(tan(deg2rad($latitude)) + 1 / cos(deg2rad($latitude))) / pi()) / 2 * pow(2, $zoom); + } + + /** + * Convert tile x to longitude + * + * @param float $x The tile x + * @param int $zoom The zoom + * + * @return float The longitude + */ + public static function xToLongitude(float $x, int $zoom): float { + return $x / pow(2, $zoom) * 360.0 - 180.0; + } + + /** + * Convert tile y to latitude + * + * @param float $y The tile y + * @param int $zoom The zoom + * + * @return float The latitude + */ + public static function yToLatitude(float $y, int $zoom): float { + return rad2deg(atan(sinh(pi() * (1 - 2 * $y / pow(2, $zoom))))); + } + + /** + * Convert decimal latitude to sexagesimal + * + * @param float $latitude The decimal latitude + * + * @return string The sexagesimal longitude + */ + public static function latitudeToSexagesimal(float $latitude): string { + //Set degree + $degree = $latitude % 60; + + //Set minute + $minute = ($latitude - $degree) * 60 % 60; + + //Set second + $second = ($latitude - $degree - $minute / 60) * 3600 % 3600; + + //Return sexagesimal longitude + return $degree.'°'.$minute.'\''.$second.'"'.($latitude >= 0 ? 'N' : 'S'); + } + + /** + * Convert decimal longitude to sexagesimal + * + * @param float $longitude The decimal longitude + * + * @return string The sexagesimal longitude + */ + public static function longitudeToSexagesimal(float $longitude): string { + //Set degree + $degree = $longitude % 60; + + //Set minute + $minute = ($longitude - $degree) * 60 % 60; + + //Set second + $second = ($longitude - $degree - $minute / 60) * 3600 % 3600; + + //Return sexagesimal longitude + return $degree.'°'.$minute.'\''.$second.'"'.($longitude >= 0 ? 'E' : 'W'); + } +}