The map contains global tile IDs, and a list of tilesets along with information about which tiles come from which tilesets (that’s what firstGID is).
To find which tile in the map belongs to which tileset, just look at the firstGIDs compared to the tile’d GID. It’ll belong to the tileset where firstGID <= tileGID, and where the next tileset’s firstGID > tileGID. Subtract the firstGID from the tileGID to get that tile’s 0-index into the tileset. From the 0-index you can get the x and y coordinates from the tileset’s tileWidth and image width.
A detailed example in case you need it.
Let’s say Garden.json is a map that uses two tilesets,
Castle.json with firstGID = 1
Forest.json with firstGID = 101
And let’s say the first few tiles in the Garden are 4, 130, 105, 34
Tile 4 must be from Castle.json because its GID is 4, which is >= 1 and < 101.
Tile 130 must be from Forest.json because its GID is > Forest’s firstGID and there are no further tilesets.
Ditto for tile 105.
Tile 34 must be from Castle.json because 34 >= 1 but < 101.
To get the pixel coordinates of a given tile in the tileset image, you need four pieces of information:
The tile’s 0-based index into the tileset, which is just tileGID - the tileset’s firstGID.
The width of the image, provided in the tileset file.
The tileWidth and tileHeight, provided in the tileset file.
The indexing is just linear from the top left corner of the image, left to right, top to bottom. You can calculate how many tiles are in each row:
widthInTiles = floor(imageWidth / tileWidth)
You can calculate which row the tile is in:
row = floor(tileIndex / widthInTiles)
You can calculate which column the tile is in:
column = tileIndex % widthInTiles
Multiply row by tileWidth and column by tileHeight to get the pixel coordinate of the top left corner of the tile. The tile will be tileWidth x tileHeight in size.
Note that a map can have its own tileWidth and tileHeight parameters as well. If these differ from those given in the tilesets, that means the tiles should be scaled to the size requested by the map. This is rarely useful, but I wanted to mention it since it can be a source of confusion.