Tiles are just a way of dividing & displaying high-resolution images very efficiently. At each zoom level, the image is divided on a set of squared areas or tiles. The key idea is that all tiles have the same resolution (independently of the zoom level), which guarantees a constant real-size / image-resolution ratio on each tile.
Take for example the following image: for that zoom level, the floorplan is divided in 6 x 5 tiles of constant size (e.g., 512×512 pixels).
As we zoom in, we focus on a particular area, which is itself divided into squared tiled as well. Since these smaller areas also have the same resolution, we can in fact represent greater levels of detail. And since the mobile or web viewer downloads and displays only the required tiles for each particular area/zoom level, the whole process is really efficient & fast! We have an entry on Tiles in our blog, in which we go into more detail on the benefits of using tiles to represent floorplans.
Displaying tiles on typical map providers #
Map providers such as Google Maps or Mapbox are able to retrieve & display (automatically) the tiles that correspond to the current camera view. To do so, you just need to pass them an URL in an standard format. Typically, this URL will be something like:
//Typical URL pattern to fetch tiles from a server https://tiles.server.com/{z}/{x}/{y}.png //Typical URI pattern to fetch tiles from a device's local folder /path/to/my/folder/{z}/{x}/{y}.png
The templated parameters will be modified at runtime by your map provider to retrieve the tiles needed:
- {z} – The tile’s zoom level, ranging from 0 to 23 (not all the venues will need to have generated all the zoom levels in the range). At zoom level 0, each tile covers the entire world map; at zoom level 1, it covers ¼ of the world; at zoom level 2, 1⁄16 of the world, and so on.
- {x} – The index of the tile along the map’s X axis according to Spherical Mercator projection. If the value is 0, the tile’s left edge corresponds to the 180th meridian west. If the value is 2z−1, the tile’s right edge corresponds to the 180th meridian east.
- {y} – The index of the tile along the map’s y axis according to Spherical Mercator projection. If the value is 0, the tile’s tile edge corresponds to arctan(sinh(π)), or approximately 85.0511 degrees north. If the value is 2z−1, the tile’s bottom edge corresponds to −arctan(sinh(π)), or approximately 85.0511 degrees south.
You will also need to know the resolution of each tile (typically, 512×512 pixels in our case).
Some useful links to know more about tiles in general and how to integrate them (the specifics will change depending on the provider you choose):
- Google Maps tiles documentation.
- React Native Maps tiles documentation.
- Deck.gl TileLayer (web only)
- …
Getting tiles from Situm APIs #
Situm provides a standard URL so you can plug-it in your map implementation with your favourite map provider. For each floor, Situm provides an URL like:
https://dashboard.situm.com/uploads/tiles/{floorId}/{z}/{x}/{y}.png
For example, if you want to display tiles from floor 1234, you should build an URL like this one:
https://dashboard.situm.com/uploads/tiles/1234/{z}/{x}/{y}.png
Whenever the floor changes (e.g. your user selects a different floor to display), you will need to pass a new URL to your map provider. Please refer to our docs to know the floor_id to display.
Getting tiles from a local source #
Using the previous approach, the map provider downloads the tiles as the user visualizes different areas of the map. But… what if the smartphone does not have connectivity?
In this case, you can pre-download all the tiles using Situm SDK (Android, iOS and ReactNative). Tiles will be stored in a local storage folder with an URI such as:
/path/to/my/local/folder/{building_id}/{floor_id}/{z}/{x}/{y}.png
Therefore, if you want to render tiles from building 9876 and floor 1234, you will need to pass this URI to your map provider:
/path/to/my/local/folder/9876/1234/{z}/{x}/{y}.png
Please refer to our docs to know the building_id and the floor_id to display.
Code examples #
In this section, we provide some code examples that will help to clarify the concepts explained. We will start with a (mostly) complete React Native example using React Native Maps. Of course, you can use other languages/platforms/libraries but we found this one to be very educational:
import {MapLocalTile, URLTile ...} from "react-native-maps" //Sample component export const TiledBuilding = () => { //State variable to store the local folder's URI const [offlineTilePath, setOfflineTilePath] = useState<String>(""); //In this example, floor id is fixed. However, if you have several floors you will need //to change this number accordingly (e.g. every time the user selects a new floor to display) const SITUM_FLOOR_ID = 1234; ... // First download tiles (e.g. call this from an useEffect) const getOfflineTiles = (building: any) => { SitumPlugin.fetchTilesFromBuilding(building, (result: any) => { //result.results contains the URI string with the local folder //where tiles are stored for this building setOfflineTilePath(result.results); }, (error: any) => { console.log("Fetch tiles from building error" + error); } ); } //The render method displays a map and the tiles over it return ( <View> <MapView style={{ width: "100%", height: "100%" }} initialRegion={mapRegion} maxZoomLevel={MAX_ZOOM_LEVEL} > ... <MapLocalTile //We pass the local URI to our map provider, which will fetch & render the appropriate //tiles as the user moves the view pathTemplate={offlineTilePath + '/' + SITUM_FLOOR_ID + '/{z}/{x}/{y}.png'} tileSize={512}> </MapLocalTile> ... </MapView> // Do additional stuff ... </View> ) };
The same approach can also be used in Android and iOS. You may download the tiles as shown in the following code snippets and then pass that URI to your favourite map provider:
SitumSdk.communicationManager().fetchTilesFromBuilding("BUILDING_IDENTIFIER", new HandlerImpl<String>() { @Override public void onSuccess(String result) { //result is an URI string with the local directory //where tiles are stored for this building ... } @Override public void onFailure(Error error) { // Some error happened ... } });
SITCommunicationManager.shared().fetchTiles(forBuilding: "BUILDING_IDENTIFIER", success: { (mapping: [AnyHashable: Any]?) in if let res = mapping { // Process results print(res["results"]) } }, failure: { (error: Error?) in // ... process error })