My current answer makes the following assumptions about your TileSet:
- The Tile Shape is "Isometric".
- The Tile Layout is "Diamond Right" or "Diamond Down".
- The Tile Offset Axis is "Horizontal Offset" (default).
- You aren't going to move any layer using
move_layer().
Different settings may require changing some formulas below to consider different layout orientations or topologies.
Find the TileMap corners
Before computing the centre of mass of the TileMap, we need to know its current extent:
var used_rect: Rect2 = get_used_rect()
This describes where the top-leftmost cell is (via used_rect.position) and the size of the current map (via used_rect.size), in your case, 8x4. Now, we can work out the position of all corners in map space and convert them into TileMap local space:
var map_corners: Array = [ rect.position, rect.position + rect.size * Vector2.RIGHT, rect.position + rect.size * Vector2.DOWN, rect.position + rect.size ] var local_corners: Array = map_corners.map( func(v): return map_to_local(v))
However, we are counting an extra cell along each layout axis (I'm using a custom script for drawing. The Godot icon is at the (0,0) position for reference, and the blue dot is the top-leftmost corner of the layout):

We can fix this by subtracting one from each direction. After we get the used rectangle, and before computing the map_corners array, we do:
used_rect.size -= Vector2.ONE
Now, everything is fine:

Find the TileMap centroid
The centre of the TileMap is the average of the corner positions in local space. The following line uses reduce() to accumulate the sum of all corner coordinates, and finally divides it by their number to compute the mean position:
var local_centroid: Vector2 = local_corners.reduce( func(accum, corner): return accum + corner, Vector2.ZERO) / len(local_corners)
This is indeed the centroid of the TileMap in local coordinates (red dot):

Move the TileMap
This turned out to be very easy. At first, I started thinking about inverse transforms and stuff... This line works perfectly and is independent of any Node hierarchy involved:
global_position = -centroid

Put it all together
Here's the final class and the function to centre the TileMap:
extends TileMap func _ready() -> void: recentre() func recentre() -> void: var used_rect: Rect2 = get_used_rect() used_rect.size -= Vector2.ONE var map_corners: Array = [ rect.position, rect.position + rect.size * Vector2.RIGHT, rect.position + rect.size * Vector2.DOWN, rect.position + rect.size ] var local_corners: Array = map_corners.map( func(v): return map_to_local(v)) var local_centroid: Vector2 = local_corners.reduce( func(accum, corner): return accum + corner, Vector2.ZERO) / len(local_corners) global_position = -centroid