You can add images on top of your map as a Tile Layer. Tile Layers are placed overtop of a map tile at a specific zoom level. With enough tiles, you can supplement Google's map data for the entire map, at multiple zoom levels.
Introduction
Tile layers (sometimes referred to as Tile Overlays) allow you to superimpose
images on top of Google's base map tiles. This is an excellent way to add data
- such as points of interest or traffic information - and local imagery to
your app. When combined with the kGMSTypeNone
map type,
tile layers effectively let you replace Google's base map data with your own.
Tile layers are useful when you wish to add extensive imagery, typically covering large geographical areas, to the map. By contrast, ground overlays are useful when you wish to fix a single image at one point on the map.
Tile coordinates
The Maps API breaks up imagery at each zoom level into a set of square map tiles, which are arranged in an ordered grid. When a map scrolls to a new location, or to a new zoom level, the Maps API determines which tiles are needed, and translates that into a set of tiles to retrieve.
For Google's implementation of the Mercator projection, the tile with
coordinate (0,0) is always at the northwest corner of the map, with x
values
increasing from west to east and y
values increasing from north to south.
Tiles are indexed using x,y
coordinates from that origin. For example, at
zoom level 2, when the earth is divided up into 16 tiles, each tile can be
referenced by a unique x,y
pair:
Each map tile is a 256x256 point square. At zoom level 0, the entire world is rendered in a single tile. Each zoom level increases the magnification by a factor of two. So, at zoom level 1 the map will be rendered as a 2x2 grid of tiles, or a 4x4 grid at zoom level 2, a 8x8 grid at zoom level 3, and so on. If you are creating images for a tile layer, you will need to create a new 256x256 point image for each tile at each zoom level that you wish to support.
Adding a Tile Layer
- Instantiate a
GMSURLTileLayer
object, or a custom subclass ofGMSTileLayer
/GMSSyncTileLayer
. - Optionally modify the
zIndex
property to adjust its position in relation to other tile layers. - Assign the
GMSTileLayer
object to the map by setting itsmap
property.
The Maps SDK for iOS provides three classes that can be used to
implement a tile layer. With each class, you will need to define how to fetch
the correct map tile for a given set of {x,y,zoom}
coordinates. The
available options are:
- Subclass
GMSSyncTileLayer
, providing the implementation oftileForX:y:zoom
that returnsUIImage
instances. - Subclass
GMSTileLayer
, providing the implementation of the asynchronous methodrequestTileForX:y:zoom
that later calls back with a tile image. - Use the existing class,
GMSURLTileLayer
, to fetch tiles automatically from URLs, providing theGMSTileURLConstructor
block.GMSURLTileLayer
is a concrete class that cannot be subclassed.
In the case of subclassing GMSSyncTileLayer
or GMSTileLayer
, providing a
nil
tile result will tell the Maps SDK for iOS that data is currently
unavailable but that it may be available in the future. Alternatively,
return kGMSTileLayerNoTile
to indicate that there is no tile at this
location.
For GMSURLTileLayer
, returning nil
from the GMSTileURLConstructor
will
indicate that there is no tile at this location.
Using `GMSURLTileLayer` to fetch tiles from URLs
The GMSURLTileLayer
does not require subclassing, but you will have to
implement the GMSTileURLConstructor
block. The below code shows how to
use GMSURLTileLayer
to display the floor plan of a multistory building.
Swift
let floor = 1 // Implement GMSTileURLConstructor // Returns a Tile based on the x,y,zoom coordinates, and the requested floor let urls: GMSTileURLConstructor = { (x, y, zoom) in let url = "https://www.example.com/floorplans/L\(floor)_\(zoom)_\(x)_\(y).png" return URL(string: url) } // Create the GMSTileLayer let layer = GMSURLTileLayer(urlConstructor: urls) // Display on the map at a specific zIndex layer.zIndex = 100 layer.map = mapView
Objective-C
NSInteger floor = 1; // Create the GMSTileLayer GMSURLTileLayer *layer = [GMSURLTileLayer tileLayerWithURLConstructor:^NSURL * _Nullable(NSUInteger x, NSUInteger y, NSUInteger zoom) { NSString *url = [NSString stringWithFormat:@"https://www.example.com/floorplans/L%ld_%lu_%lu_%lu.png", (long)floor, (unsigned long)zoom, (unsigned long)x, (unsigned long)y]; return [NSURL URLWithString:url]; }]; // Display on the map at a specific zIndex layer.zIndex = 100; layer.map = mapView;
Subclass GMSSyncTileLayer
to serve tiles as a UIImage
The GMSSyncTileLayer
and GMSTileLayer
are abstract classes designed to be
subclassed. You can use these classes to serve tiles as UIImage
's. The below
example shows how to render a custom image over some of the tiles on the map
by subclassing GMSSyncTileLayer
.
Swift
class TestTileLayer: GMSSyncTileLayer { override func tileFor(x: UInt, y: UInt, zoom: UInt) -> UIImage? { // On every odd tile, render an image. if (x % 2 == 1) { return UIImage(named: "australia") } else { return kGMSTileLayerNoTile } } }
Objective-C
@interface TestTileLayer : GMSSyncTileLayer @end @implementation TestTileLayer - (UIImage *)tileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom { // On every odd tile, render an image. if (x % 2 == 1) { return [UIImage imageNamed:@"australia"]; } else { return kGMSTileLayerNoTile; } } @end
To add the layer to your map, instantiate the object and set its map property.
Swift
let layer = TestTileLayer() layer.map = mapView
Objective-C
GMSTileLayer *layer = [[TestTileLayer alloc] init]; layer.map = mapView;
High DPI Tiles for Retina devices
You can use high DPI images with either GMSSyncTileLayer
or GMSURLTileLayer
by setting the tileSize
to 512.
The tileSize
property indicates the number of pixels that the returned tile
images will prefer to display as; this defaults to 256 — the dimension
of a Google Maps tile on a non-Retina device.
If you are displaying normal DPI tiles on a high DPI device you can scale the
images up by setting tileSize
to 512. Note that upscaling images may reduce
image quality, especially for fine lines or text. For best results, match the
tileSize
and image DPI to the display. Maps shown on a Retina device will
look their best when displaying high DPI images with a tileSize
of 512;
while maps shown on a non-Retina device will look great with normal images
and the default tileSize
of 256.
Clearing stale tiles
If the tiles provided by the layer become 'stale', then the method
clearTileCache
should be called on the layer to force a refresh. This will
cause all of the tiles on this layer to be reloaded.
Swift
layer.clearTileCache()
Objective-C
[layer clearTileCache];