*/
        public function map(Request $request, string $hash, int $updated, float $latitude, float $longitude, int $zoom, int $width, int $height): Response {
                //Without matching hash
         */
        public function map(Request $request, string $hash, int $updated, float $latitude, float $longitude, int $zoom, int $width, int $height): Response {
                //Without matching hash
-               //With map up to date file
-               if (is_file($map) && ($mtime = stat($map)['mtime']) && $mtime >= $updated) {
-                       //Read map from cache
-                       //TODO: handle modified, etag, cache, etc ???
-                       return new BinaryFileResponse($map);
-               }
-
-               //Without existing map
-               if (!is_dir($dir = dirname($map))) {
-                       //Create filesystem object
-                       $filesystem = new Filesystem();
-
-                       try {
-                               //Create path
-                               //XXX: set as 0775, symfony umask (0022) will reduce rights (0755)
-                               //XXX: on CoW filesystems execute a chattr +C before filling
-                               $filesystem->mkdir($dir, 0775);
-                       } catch (IOExceptionInterface $e) {
-                               //Throw error
-                               throw new \Exception(sprintf('Output path "%s" do not exists and unable to create it', $dir), 0, $e);
+               //Without multi up to date file
+               if (!is_file($map) || !($mtime = stat($map)['mtime']) || $mtime < $updated) {
+                       //Without existing map path
+                       if (!is_dir($dir = dirname($map))) {
+                               //Create filesystem object
+                               $filesystem = new Filesystem();
+
+                               try {
+                                       //Create path
+                                       //XXX: set as 0775, symfony umask (0022) will reduce rights (0755)
+                                       //XXX: on CoW filesystems execute a chattr +C before filling
+                                       $filesystem->mkdir($dir, 0775);
+                               } catch (IOExceptionInterface $e) {
+                                       //Throw error
+                                       throw new \Exception(sprintf('Output path "%s" do not exists and unable to create it', $dir), 0, $e);
+                               }
-               //Create image instance
-               $image = new \Imagick();
-
-               //Add new image
-               $image->newImage($width, $height, new \ImagickPixel('transparent'), 'jpeg');
-
-               //Create tile instance
-               $tile = new \Imagick();
-
-               //Get tile xy
-               $centerX = $this->map->longitudeToX($longitude, $zoom);
-               $centerY = $this->map->latitudeToY($latitude, $zoom);
-
-               //Calculate start xy
-               $startX = floor(floor($centerX) - $width / MapUtil::tz);
-               $startY = floor(floor($centerY) - $height / MapUtil::tz);
-
-               //Calculate end xy
-               $endX = ceil(ceil($centerX) + $width / MapUtil::tz);
-               $endY = ceil(ceil($centerY) + $height / MapUtil::tz);
-
-               for($x = $startX; $x <= $endX; $x++) {
-                       for($y = $startY; $y <= $endY; $y++) {
-                               //Set cache path
-                               $cache = $this->cache.'/'.$zoom.'/'.$x.'/'.$y.'.png';
-
-                               //Without cache image
-                               if (!is_file($cache)) {
-                                       //Set tile url
-                                       $tileUri = str_replace(['{Z}', '{X}', '{Y}'], [$zoom, $x, $y], $this->url);
-
-                                       //Without cache path
-                                       if (!is_dir($dir = dirname($cache))) {
-                                               //Create filesystem object
-                                               $filesystem = new Filesystem();
-
-                                               try {
-                                                       //Create path
-                                                       //XXX: set as 0775, symfony umask (0022) will reduce rights (0755)
-                                                       $filesystem->mkdir($dir, 0775);
-                                               } catch (IOExceptionInterface $e) {
-                                                       //Throw error
-                                                       throw new \Exception(sprintf('Output directory "%s" do not exists and unable to create it', $dir), 0, $e);
+                       //Create image instance
+                       $image = new \Imagick();
+
+                       //Add new image
+                       $image->newImage($width, $height, new \ImagickPixel('transparent'), 'jpeg');
+
+                       //Create tile instance
+                       $tile = new \Imagick();
+
+                       //Get tile xy
+                       $centerX = $this->map->longitudeToX($longitude, $zoom);
+                       $centerY = $this->map->latitudeToY($latitude, $zoom);
+
+                       //Calculate start xy
+                       $startX = floor(floor($centerX) - $width / MapUtil::tz);
+                       $startY = floor(floor($centerY) - $height / MapUtil::tz);
+
+                       //Calculate end xy
+                       $endX = ceil(ceil($centerX) + $width / MapUtil::tz);
+                       $endY = ceil(ceil($centerY) + $height / MapUtil::tz);
+
+                       for($x = $startX; $x <= $endX; $x++) {
+                               for($y = $startY; $y <= $endY; $y++) {
+                                       //Set cache path
+                                       $cache = $this->cache.'/'.$zoom.'/'.$x.'/'.$y.'.png';
+
+                                       //Without cache image
+                                       if (!is_file($cache)) {
+                                               //Set tile url
+                                               $tileUri = str_replace(['{Z}', '{X}', '{Y}'], [$zoom, $x, $y], $this->url);
+
+                                               //Without cache path
+                                               if (!is_dir($dir = dirname($cache))) {
+                                                       //Create filesystem object
+                                                       $filesystem = new Filesystem();
+
+                                                       try {
+                                                               //Create path
+                                                               //XXX: set as 0775, symfony umask (0022) will reduce rights (0755)
+                                                               $filesystem->mkdir($dir, 0775);
+                                                       } catch (IOExceptionInterface $e) {
+                                                               //Throw error
+                                                               throw new \Exception(sprintf('Output directory "%s" do not exists and unable to create it', $dir), 0, $e);
+                                                       }
+               //Read map from cache
+               $response = new BinaryFileResponse($map);
+
+               //Set file name
+               $response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, 'map-'.$latitude.','.$longitude.'-'.$zoom.'-'.$width.'x'.$height.'.jpeg');
+
+               //Set etag
+               $response->setEtag(md5(serialize([$updated, $latitude, $longitude, $zoom, $width, $height])));
+
+               //Set last modified
+               $response->setLastModified(\DateTime::createFromFormat('U', strval($mtime)));
+
+               //Set as public
+               $response->setPublic();
+
+               //Return 304 response if not modified
+               $response->isNotModified($request);
+
         */
        public function multiMap(Request $request, string $hash, int $updated, float $latitude, float $longitude, string $coordinates, int $zoom, int $width, int $height): Response {
                //Without matching hash
         */
        public function multiMap(Request $request, string $hash, int $updated, float $latitude, float $longitude, string $coordinates, int $zoom, int $width, int $height): Response {
                //Without matching hash
-               //With multi up to date file
-               if (is_file($map) && ($mtime = stat($map)['mtime']) && $mtime >= $updated) {
-                       //Read multi from cache
-                       //TODO: handle modified, etag, cache, etc ???
-                       return new BinaryFileResponse($map);
-               }
-
-               //Without existing multi
-               if (!is_dir($dir = dirname($map))) {
-                       //Create filesystem object
-                       $filesystem = new Filesystem();
-
-                       try {
-                               //Create path
-                               //XXX: set as 0775, symfony umask (0022) will reduce rights (0755)
-                               //XXX: on CoW filesystems execute a chattr +C before filling
-                               $filesystem->mkdir($dir, 0775);
-                       } catch (IOExceptionInterface $e) {
-                               //Throw error
-                               throw new \Exception(sprintf('Output path "%s" do not exists and unable to create it', $dir), 0, $e);
+               //Without multi up to date file
+               if (!is_file($map) || !($mtime = stat($map)['mtime']) || $mtime < $updated) {
+                       //Without existing multi path
+                       if (!is_dir($dir = dirname($map))) {
+                               //Create filesystem object
+                               $filesystem = new Filesystem();
+
+                               try {
+                                       //Create path
+                                       //XXX: set as 0775, symfony umask (0022) will reduce rights (0755)
+                                       //XXX: on CoW filesystems execute a chattr +C before filling
+                                       $filesystem->mkdir($dir, 0775);
+                               } catch (IOExceptionInterface $e) {
+                                       //Throw error
+                                       throw new \Exception(sprintf('Output path "%s" do not exists and unable to create it', $dir), 0, $e);
+                               }
-               //Create image instance
-               $image = new \Imagick();
-
-               //Add new image
-               $image->newImage($width, $height, new \ImagickPixel('transparent'), 'jpeg');
-
-               //Create tile instance
-               $tile = new \Imagick();
-
-               //Get tile xy
-               $centerX = $this->map->longitudeToX($longitude, $zoom);
-               $centerY = $this->map->latitudeToY($latitude, $zoom);
-
-               //Calculate start xy
-               $startX = floor(floor($centerX) - $width / MapUtil::tz);
-               $startY = floor(floor($centerY) - $height / MapUtil::tz);
-
-               //Calculate end xy
-               $endX = ceil(ceil($centerX) + $width / MapUtil::tz);
-               $endY = ceil(ceil($centerY) + $height / MapUtil::tz);
-
-               for($x = $startX; $x <= $endX; $x++) {
-                       for($y = $startY; $y <= $endY; $y++) {
-                               //Set cache path
-                               $cache = $this->cache.'/'.$zoom.'/'.$x.'/'.$y.'.png';
-
-                               //Without cache image
-                               if (!is_file($cache)) {
-                                       //Set tile url
-                                       $tileUri = str_replace(['{Z}', '{X}', '{Y}'], [$zoom, $x, $y], $this->url);
-
-                                       //Without cache path
-                                       if (!is_dir($dir = dirname($cache))) {
-                                               //Create filesystem object
-                                               $filesystem = new Filesystem();
-
-                                               try {
-                                                       //Create path
-                                                       //XXX: set as 0775, symfony umask (0022) will reduce rights (0755)
-                                                       $filesystem->mkdir($dir, 0775);
-                                               } catch (IOExceptionInterface $e) {
-                                                       //Throw error
-                                                       throw new \Exception(sprintf('Output directory "%s" do not exists and unable to create it', $dir), 0, $e);
+                       //Create image instance
+                       $image = new \Imagick();
+
+                       //Add new image
+                       $image->newImage($width, $height, new \ImagickPixel('transparent'), 'jpeg');
+
+                       //Create tile instance
+                       $tile = new \Imagick();
+
+                       //Get tile xy
+                       $centerX = $this->map->longitudeToX($longitude, $zoom);
+                       $centerY = $this->map->latitudeToY($latitude, $zoom);
+
+                       //Calculate start xy
+                       $startX = floor(floor($centerX) - $width / MapUtil::tz);
+                       $startY = floor(floor($centerY) - $height / MapUtil::tz);
+
+                       //Calculate end xy
+                       $endX = ceil(ceil($centerX) + $width / MapUtil::tz);
+                       $endY = ceil(ceil($centerY) + $height / MapUtil::tz);
+
+                       for($x = $startX; $x <= $endX; $x++) {
+                               for($y = $startY; $y <= $endY; $y++) {
+                                       //Set cache path
+                                       $cache = $this->cache.'/'.$zoom.'/'.$x.'/'.$y.'.png';
+
+                                       //Without cache image
+                                       if (!is_file($cache)) {
+                                               //Set tile url
+                                               $tileUri = str_replace(['{Z}', '{X}', '{Y}'], [$zoom, $x, $y], $this->url);
+
+                                               //Without cache path
+                                               if (!is_dir($dir = dirname($cache))) {
+                                                       //Create filesystem object
+                                                       $filesystem = new Filesystem();
+
+                                                       try {
+                                                               //Create path
+                                                               //XXX: set as 0775, symfony umask (0022) will reduce rights (0755)
+                                                               $filesystem->mkdir($dir, 0775);
+                                                       } catch (IOExceptionInterface $e) {
+                                                               //Throw error
+                                                               throw new \Exception(sprintf('Output directory "%s" do not exists and unable to create it', $dir), 0, $e);
+                                                       }
-               //Convert to array      
-               $coordinates = array_reverse(array_map(function ($v) { $p = strpos($v, ','); return ['latitude' => floatval(substr($v, 0, $p)), 'longitude' => floatval(substr($v, $p + 1))]; }, explode('-', $coordinates)), true);
+                       //Convert to array
+                       $coordinates = array_reverse(array_map(function ($v) { $p = strpos($v, ','); return ['latitude' => floatval(substr($v, 0, $p)), 'longitude' => floatval(substr($v, $p + 1))]; }, explode('-', $coordinates)), true);
+               //Read map from cache
+               $response = new BinaryFileResponse($map);
+
+               //Set file name
+               $response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, 'multimap-'.$latitude.','.$longitude.'-'.$zoom.'-'.$width.'x'.$height.'.jpeg');
+
+               //Set etag
+               $response->setEtag(md5(serialize([$updated, $latitude, $longitude, $zoom, $width, $height])));
+
+               //Set last modified
+               $response->setLastModified(\DateTime::createFromFormat('U', strval($mtime)));
+
+               //Set as public
+               $response->setPublic();
+
+               //Return 304 response if not modified
+               $response->isNotModified($request);
+