Map service GetMapTile method

Gets the image tile from a fused map cache for a specific location.

GetMapTile(string MapName, int Level, int Row, int Column, string Format)

Parameter

Description

MapName

The name of the map (data frame) that contains a fused map cache.

Level

The level of detail (scale) of the fused map cache from which a tile will be retrieved.

Row

The row in which a tile is located. Row values start at 0 from top of each cache level.

Column

The column in which a tile is located. Column values start at 0 from the left of each cache level.

Format

The image format to retrieve from a cache. This defines the file extension to be used when retrieving the cached tile. A cache can be generated in multiple image formats.

Return Value

A byte array containing the raw image content (byte[ ]). The image format for the fused map cache can be discovered using the TileImageInfo object returned from a call to GetTileImageInfo on the map service proxy. If the image tile is not available, a SOAP exception will be thrown. A tile may not be generated in situations where no map data was present in the tile extent.

Remarks

In general, this method is used to retrieve image tiles that have been pre-generated (cached) for a map service. All layers in the map service have been combined (or fused) into a cache of map images generated at different scales. The cache properties such as the number and scale of levels and the physical size of image tiles in pixels can be defined by the map service administrator, thus the values can be arbitrary.

Keep in mind that this method does not provide the optimum solution from retrieving cached map images. When calling this method, the map service is responsible for retrieving the cached image, writing the byte stream and returning it to the client. If you are able to access the cached tiles directly, you can skip the call to this method and remove unneeded requests to the map service. In general, there two options provide the fastest way to retrieve map tiles:

  1. Using a simple HTTP Get request to the virtual cache directory of the web server
  2. Requesting a tile from the ArcGIS Server map service tile handler

The URL for each option is deconstructed and discussed below:

Virtual cache directory

If the a cache's virtual directory is exposed, you can request a tile from the web server via the public cache directory. Use the GetVirtualCacheDirectory method to determine the virtual directory. Here's an example of a URL for a tile retrieved from the virtual cache directory:

http://serverx.esri.com:6080/arcgiscache/dgaerials/Layers/_alllayers/L00/R0000029f/C00000280.jpg

The URL contains the following parameters:

Parameter

Description

http://serverx.esri.com:6080/arcgiscache

Root URL of a virtual cache directory.

dgaerials

Map service name.

Layers

The name of the data frame of interest in the map service.

_alllayers

All layers in the map. This is always the case for single fused caches, if you have a multi-layer cache it will correspond to the specific group layer that was cached in the multi-layer cache.

L00

Level of detail ID.

R0000029f

Cache tile row in padded hexadecimal format.

C00000280.jpg

Cache tile column in padded hexadecimal format.

Map service tile handler

If the virtual directory is not exposed, you can still request a tile from the web server, but in this case you need to use the map service tile handler. Here's an example of a URL for a tile retrieved by the map service tile handler:

http://serverx.esri.com:6080/arcgis/services/dgaerials/MapServer?mapName=Layers&format=JPEG&level=0&row=671&column=640

The URL contains the following parameters:

Parameter

Description

http://serverx.esri.com:6080/arcgis/services/dgaerials/MapServer

URL to the map service of the cache

mapName=Layers

The name of the data frame of interest in the map service

format=JPEG

Image type of the cache

level=0

Level of detail ID

row=671

Cache tile row in decimal format

column=640

Cache tile column in decimal format

Replacing the call to GetMapTile

In order to call this method you need to provide a value for all of the input parameters. These parameters are determined by working with the TileCacheInfo, LODInfo, and TileImageInfo objects which can be returned from a single call to the GetCacheDescriptionInfo method.

  • TileCacheInfo: Contains spatial reference information, as well as the width and height of the tile in pixels.
  • LODInfo: Contains information about the resolution and scale for a level of detail in the cache. You can access an array of these objects from TileCacheInfo.
  • TileImageInfo: Contains information about the cache image format.

To find the specific tile that covers a point of interest requires a few calculations. Here are a few important items to consider:

  • Tile origin: The tiling origin is the upper-left point of the tiling scheme grid. You can get it using TileCacheInfo.TileOrigin.
  • Width and height of a tile in map units: Calculate this value by multiplying the tile width (TileCacheInfo.TileCols) or height (TileCacheInfo.TileRows) by the resolution (LODInfo.Resolution).
  • Row and column of the point of interest on the tiling grid: Columns are zero-based and increase as you move to the right from the tiling origin. Rows are also zero-based and increase as you move down from the tiling origin. For example, in the map below, if you were traveling from the tiling origin to Salt Lake City, you would have to move five tiles to the right and four tiles down. This puts Salt Lake City in Column 4, Row 3.

    tiling grid

With these values, use the following calculations to find the tile in which a point of interest lies:

  • Column = Floor((Point of interest X –; Tile origin X) / width of a tile in map units)
  • Row = Floor((Tile origin Y –; Point of interest Y) / height of a tile in map units)

The example code referenced via the links below illustrate how to query cache properties and retrieve a tile via GetMapTile or the virtual cache directory.

Examples

C#

MapService_MapServer mapservice = new MapService_MapServer();

mapservice.Url = "http://localhost:6080/arcgis/services/MapFusedCache/MapServer";

 

string mapname = mapservice.GetDefaultMapName();

MapServerInfo mapinfo = mapservice.GetServerInfo(mapname);

MapDescription mapdesc = mapinfo.DefaultMapDescription;

// Pixel height and width of map display on the client. In this case, a Windows Form

// PictureBox control.

int picturewidth = pictureBox1.Width;

int pictureheight = pictureBox1.Height;

EnvelopeN mapextent = (EnvelopeN)mapdesc.MapArea.Extent;

// Use map scale resolution (map units per pixel) to determine tile level

double mapresolution = Math.Abs(mapextent.XMax - mapextent.XMin) / picturewidth;

System.Drawing.Bitmap imgbitmap = new System.Drawing.Bitmap(picturewidth, pictureheight);

System.Drawing.Graphics imggraphics = System.Drawing.Graphics.FromImage(imgbitmap);

imggraphics.FillRectangle(new System.Drawing.SolidBrush(System.Drawing.Color.LightGray), 0, 0, picturewidth, pictureheight);

if (mapservice.HasSingleFusedMapCache(mapname))

{

      TileCacheInfo tci = mapservice.GetTileCacheInfo(mapname);

      LODInfo[] tcis = tci.LODInfos;

      // Map units per pixel

      double tileresolution = 0;

      // Scale level

      int tilelevel = 0;

      foreach (LODInfo ldi in tcis)

      {

            double ldi_resolution = ldi.Resolution;

            tileresolution = ldi_resolution;

            tilelevel = ldi.LevelID;

            if (mapresolution >= ldi_resolution)

            {

                  break;

            }

      }

      // Measured from the origin

      double minx = mapextent.XMin;

      double miny = mapextent.YMin;

      double maxx = mapextent.XMax;

      double maxy = mapextent.YMax;

      // Origin of the cache (upper left corner)

      double xorigin = ((PointN)tci.TileOrigin).X;

      double yorigin = ((PointN)tci.TileOrigin).Y;

      // Get minimum tile column

      double minxtile = (minx - xorigin) / (tci.TileCols * tileresolution);

      // Get minimum tile row

      // From the origin, maxy is minimum y

      double minytile = (yorigin - maxy) / (tci.TileRows * tileresolution);

 

      // Get maximum tile column

      double maxxtile = (maxx - xorigin) / (tci.TileCols * tileresolution);

      // Get maximum tile row

      // From the origin, miny is maximum y

      double maxytile = (yorigin - miny) / (tci.TileRows * tileresolution);

      // Return integer value for min and max, row and column

      int mintilecolumn = (int)Math.Floor(minxtile);

      int mintilerow = (int)Math.Floor(minytile);

      int maxtilecolumn = (int)Math.Floor(maxxtile);

      int maxtilerow = (int)Math.Floor(maxytile);

      // Origin of the min tile

      double xmintileorigin = xorigin + (mintilecolumn * (tci.TileCols * tileresolution));

      double ymintileorigin = yorigin - (mintilerow * (tci.TileRows * tileresolution));

      // Since the origin of the extent and origin of the min tile are different

      // get the difference and use to place consolidated image graphic in correct location

      double xadjust = Math.Abs(minx - xmintileorigin);

      double yadjust = Math.Abs(maxy - ymintileorigin);

      int xpixadjust = (int)(xadjust / tileresolution);

      int ypixadjust = (int)(yadjust / tileresolution);

      TileImageInfo tii = mapservice.GetTileImageInfo(mapservice.GetDefaultMapName());

      int rowindex = 0;

      // for each row in the map extent

      for (int row = mintilerow; row <= maxtilerow; row++)

      {

            int colindex = 0;

            // for each column in the row, in the map extent

            for (int col = mintilecolumn; col <= maxtilecolumn; col++)

            {

                  byte[] myByteArray = null;

                  string cacheUrl = null;

                  try

                  {

                        // Return the byte array of the tile image

                        myByteArray = mapservice.GetMapTile(mapname, tilelevel, row, col, tii.CacheTileFormat);

                        // -or-

                        // Construct url manually

                        cacheUrl = virtualCacheDirectory + "/L" + tilelevel.ToString().PadLeft(2, '0')

                        + "/R" + row.ToString("x").PadLeft(8, '0') + "/C"

                        + col.ToString("x").PadLeft(8, '0') + imgType;

                        HttpWebRequest webreq = (HttpWebRequest)WebRequest.Create(cacheUrl);
      
                        HttpWebResponse webresp = (HttpWebResponse)webreq.GetResponse();

                        // Can also use: System.Drawing.Image.FromStream(webresp.GetResponseStream()) to

                        // read http response with image data

                        System.IO.Stream theStream = webresp.GetResponseStream();

                        int byte1;

                        System.IO.MemoryStream tempStream = new System.IO.MemoryStream();

                        while ((byte1 = theStream.ReadByte()) != -1)

                        {

                              tempStream.WriteByte(((byte)byte1));

                        }

                        myByteArray = tempStream.ToArray();

                  }

                  catch

                  {

                        // Tile may not be available because no data was present when creating the cache

                  }

                  // If Tile was found, add it to the consolidated image graphic

                  if (myByteArray != null)

                  {

                        System.Drawing.Image newImage;

                        using (System.IO.MemoryStream ms = new System.IO.MemoryStream(myByteArray, 0, myByteArray.Length))

                        {

                              ms.Write(myByteArray, 0, myByteArray.Length);

                              newImage = Image.FromStream(ms, true);

                              imggraphics.DrawImage

                              (newImage, (tci.TileCols * colindex) - xpixadjust, (tci.TileRows * rowindex) - ypixadjust, tci.TileCols, tci.TileRows);

                        }

                  }

                  colindex++;

            }

            rowindex++;

      }

}

// Post-processing, if necessary... otherwise just use imgbitmap with PictureBox

System.Drawing.Bitmap picturebitmap = new System.Drawing.Bitmap(picturewidth, pictureheight);

System.Drawing.Graphics graphicsimage = System.Drawing.Graphics.FromImage(picturebitmap);

graphicsimage.FillRectangle(new System.Drawing.SolidBrush(System.Drawing.Color.LightGray), 0, 0, picturewidth, pictureheight);

graphicsimage.DrawImage(imgbitmap, 0, 0, picturewidth, pictureheight);

pictureBox1.Image = picturebitmap;

VB.NET

Dim mapservice As MapService_MapServer = New MapService_MapServer()

mapservice.Url = "http://localhost:6080/arcgis/services/MapFusedCache/MapServer"

 

Dim mapname As String = mapservice.GetDefaultMapName()

Dim mapinfo As MapServerInfo = mapservice.GetServerInfo(mapname)

Dim mapdesc As MapDescription = mapinfo.DefaultMapDescription

 

' Pixel height and width of map display on the client. In this case, a Windows Form

' PictureBox control.

Dim picturewidth As Integer = pictureBox1.Width

Dim pictureheight As Integer = pictureBox1.Height

 

Dim mapextent As EnvelopeN = CType(mapdesc.MapArea.Extent, EnvelopeN)

 

' Use map scale resolution (map units per pixel) to determine tile level

Dim mapresolution As Double = Math.Abs(mapextent.XMax - mapextent.XMin) / picturewidth

Dim imgbitmap As System.Drawing.Bitmap = New System.Drawing.Bitmap(picturewidth, pictureheight)

Dim imggraphics As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(imgbitmap)

imggraphics.FillRectangle(New System.Drawing.SolidBrush(System.Drawing.Color.LightGray), _

0, 0, picturewidth, pictureheight)

 

If mapservice.HasSingleFusedMapCache(mapname) Then

      Dim tci As TileCacheInfo = mapservice.GetTileCacheInfo(mapname)

      Dim tcis() As LODInfo = tci.LODInfos

 

      ' Map units per pixel

      Dim tileresolution As Double = 0

 

      ' Scale level

      Dim tilelevel As Integer = 0

      Dim ldi As LODInfo

 

      For Each ldi In tcis

            Dim ldi_resolution As Double = ldi.Resolution

            tileresolution = ldi_resolution

            tilelevel = ldi.LevelID

 

            If mapresolution >= ldi_resolution Then

                  Exit For

            End If

      Next

 

      ' Measured from the origin

      Dim minx As Double = mapextent.XMin

      Dim miny As Double = mapextent.YMin

      Dim maxx As Double = mapextent.XMax

      Dim maxy As Double = mapextent.YMax

 

      ' Origin of the cache (upper left corner)

      Dim xorigin As Double = (CType(tci.TileOrigin, PointN)).X

      Dim yorigin As Double = (CType(tci.TileOrigin, PointN)).Y

 

      ' Get minimum tile column

      Dim minxtile As Double = (minx - xorigin) / (tci.TileCols * tileresolution)

 

      ' Get minimum tile row

      ' From the origin, maxy is minimum y

      Dim minytile As Double = (yorigin - maxy) / (tci.TileRows * tileresolution)

 

      ' Get maximum tile column

      Dim maxxtile As Double = (maxx - xorigin) / (tci.TileCols * tileresolution)

 

      ' Get maximum tile row

      ' From the origin, miny is maximum y

      Dim maxytile As Double = (yorigin - miny) / (tci.TileRows * tileresolution)

 

      ' Return integer value for min and max, row and column

      Dim mintilecolumn As Integer = CType(Math.Floor(minxtile), Integer)

      Dim mintilerow As Integer = CType(Math.Floor(minytile), Integer)

      Dim maxtilecolumn As Integer = CType(Math.Floor(maxxtile), Integer)

      Dim maxtilerow As Integer = CType(Math.Floor(maxytile), Integer)

 

      ' Origin of the min tile

      Dim xmintileorigin As Double = xorigin + (mintilecolumn * (tci.TileCols * tileresolution))

      Dim ymintileorigin As Double = yorigin - (mintilerow * (tci.TileRows * tileresolution))

 

      ' Since the origin of the extent and origin of the min tile are different

      ' get the difference and use to place consolidated image graphic in correct location

      Dim xadjust As Double = Math.Abs(minx - xmintileorigin)

      Dim yadjust As Double = Math.Abs(maxy - ymintileorigin)

      Dim xpixadjust As Integer = CType((xadjust / tileresolution), Integer)

      Dim ypixadjust As Integer = CType((yadjust / tileresolution), Integer)

      Dim tii As TileImageInfo = mapservice.GetTileImageInfo(mapservice.GetDefaultMapName())

 

      Dim rowindex As Integer = 0

      ' for each row in the map extent

      Dim row As Integer

      For row = mintilerow To maxtilerow Step row + 1

            Dim colindex As Integer = 0

            ' for each column in the row, in the map extent

            Dim col As Integer

            For col = mintilecolumn To maxtilecolumn Step col + 1

                  Dim myByteArray() As Byte = Nothing

                  Dim cacheUrl As String = Nothing

 

                  Try

                        ' Return the byte array of the tile image

                        myByteArray = mapservice.GetMapTile(mapname, tilelevel, row, col, tii.CacheTileFormat)

 

                        ' -or-

                        ' Construct url manually

                        cacheUrl = virtualCacheDirectory + "/L" + tilelevel.ToString().PadLeft(2, "0"c) + "/R" + row.ToString("x").PadLeft(8, "0"c) + "/C"+ col.ToString("x").PadLeft(8, "0"c) + imgType

                        Dim webreq As HttpWebRequest = CType(WebRequest.Create(cacheUrl), HttpWebRequest)

                        Dim webresp As HttpWebResponse = CType(webreq.GetResponse(), HttpWebResponse)

 

                        ' Can also use: System.Drawing.Image.FromStream(webresp.GetResponseStream()) to

                        ' read http response with image data

                        Dim theStream As System.IO.Stream = webresp.GetResponseStream()

                        Dim byte1 As Integer

                        Dim tempStream As System.IO.MemoryStream = New System.IO.MemoryStream()

 

                        While (byte1 = theStream.ReadByte()) <> -1

                              tempStream.WriteByte((CType(byte1, Byte)))

                        End While

 

                        myByteArray = tempStream.ToArray()

                  Catch

                        ' Tile may not be available because no data was present when creating the cache

                  End Try

 

                  ' If Tile was found, add it to the consolidated image graphic

                  If Not myByteArray Is Nothing Then

                        Dim NewImage As System.Drawing.Image

                        Dim ms As System.IO.MemoryStream = New System.IO.MemoryStream(myByteArray, 0, myByteArray.Length)

 

                        Try

                              ms.Write(myByteArray, 0, myByteArray.Length)

                              NewImage = Image.FromStream(ms, True)

                              imggraphics.DrawImage(NewImage, (tci.TileCols * colindex) - xpixadjust, _

                                    (tci.TileRows * rowindex) - ypixadjust, tci.TileCols, tci.TileRows)

                        Finally

                              ms.Dispose()

                        End Try

 

                  End If

                  colindex = colindex + 1

            Next

            rowindex = rowindex + 1

      Next

End If

 

' Post-processing, if necessary... otherwise just use imgbitmap with PictureBox

Dim picturebitmap As System.Drawing.Bitmap = New System.Drawing.Bitmap(picturewidth, pictureheight)

Dim graphicsimage As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(picturebitmap)

graphicsimage.FillRectangle(New System.Drawing.SolidBrush(System.Drawing.Color.LightGray), _

0, 0, picturewidth, pictureheight)

 

graphicsimage.DrawImage(imgbitmap, 0, 0, picturewidth, pictureheight)

pictureBox1.Image = picturebitmap

Java

String serviceURL = "http://localhost:6080/arcgis/services/MapService/MapServer";

MapServerBindingStub mapService = new MapServerBindingStub(serviceURL);

  

String mapName = mapService.getDefaultMapName();

MapServerInfo mapInfo = mapService.getServerInfo(mapName);

MapDescription mapDesc = mapInfo.getDefaultMapDescription();

EnvelopeN mapExtent = (EnvelopeN)mapDesc.getMapArea().getExtent();

 

//Use map scale resolution (map units per pixel) to determine tile level

double mapResolution = Math.abs(mapExtent.getXMax() - mapExtent.getXMin()) / width;

if (mapService.hasSingleFusedMapCache(mapName))

{

      TileCacheInfo tci = mapService.getTileCacheInfo(mapName);

      LODInfo[] tcis = tci.getLODInfos();

 

      //Map units per pixel

      double tileResolution = 0;

 

      //Scale level

      int tileLevel = 0;

      for (LODInfo ldi : tcis)

      {

            double ldiResolution = ldi.getResolution();

            tileResolution = ldiResolution;

            tileLevel = ldi.getLevelID();

            if (mapResolution >= ldiResolution)
            {

                  break;

            }

      }

 

      //Measured from the origin

      double minX = mapExtent.getXMin();

      double minY = mapExtent.getYMin();

      double maxX = mapExtent.getXMax();

      double maxY = mapExtent.getYMax();

 

      //Origin of the cache (upper left corner)

      double xOrigin = ((PointN)tci.getTileOrigin()).getX();

      double yOrigin = ((PointN)tci.getTileOrigin()).getY();

 

      //Get minimum tile column

      double minXTile = (minX - xOrigin) / (tci.getTileCols() * tileResolution);

 

      //Get minimum tile row

      //From the origin, maxY is minimum y

      double minYTile = (yOrigin - maxY) / (tci.getTileRows() * tileResolution);

 

      //Get maximum tile column

      double maxXTile = (maxX - xOrigin) / (tci.getTileCols() * tileResolution);

 

      //Get maximum tile row

      //From the origin, minY is maximum y

      double maxYTile = (yOrigin - minY) / (tci.getTileRows() * tileResolution);

 

      //Return integer value for min and max, row and column

      int minTileColumn = (int)Math.floor(minXTile);

      int minTileRow = (int)Math.floor(minYTile);

      int maxTileColumn = (int)Math.floor(maxXTile);

      int maxTileRow = (int)Math.floor(maxYTile);

 

      TileImageInfo tii = mapService.getTileImageInfo(mapName);

 

      //for each row in the map extent

      for (int row = minTileRow; row <= maxTileRow; row++)

      {

            //for each column in the row, in the map extent

            for (int col = minTileColumn; col <= maxTileColumn; col++)

            {

                  //Return the byte array of the tile image

                  byte[] tileByteArray = mapService.getMapTile(mapName, tileLevel, row, col, tii.getCacheTileFormat());
 

                  //If Tile was found, add it to the consolidated image graphic

                  if (tileByteArray != null)

                  {

                        String currentDir = System.getProperty("user.dir");

                        FileOutputStream newFile;

                        try {

                              newFile = new FileOutputStream(currentDir + "\\" + row + col + ".jpg");

                              newFile.write(tileByteArray);

                              newFile.close();

                              System.out.println("Tile Location: " + currentDir + "\\" + row + col + ".jpg");

                        } catch (FileNotFoundException e) {

                              e.printStackTrace();

                        }

                  }

            }

      }

}

11/8/2016