A bit more than 4 years ago, I wrote about moving centroids for polygons resulting in a good discussion at the time.
Some were puzzled that centroids could be moved at all. Others gave good reasons for moving the centroid for polygons.
What we didn't cover, however, was how to do this in an automated way. Some tools were mentioned but none would take a coordinate pair and update the polygon object's centroid to match these.
This article has come up based on a question I got from our internal data team. They want to move the centroid to a different location based on the gravity center.
Let's dive into this now. Happy #MapInfoMonday and #MapBasicMonday too!
Changing the Centroid through MapBasic
Let me start by saying that you can only change the centroid for polygon objects. For the other object types, the centroid is at a well-defined location.
You must use the Alter Object statement in MapBasic to change the centroid.
Alter Object obj
{ Info object_info_code, new_info_value
| Geography object_geo_code , new_geo_value
| Node
{ Add [ Position polygon_num, node_num ] ( x, y )
| Set Position polygon_num, node_num ( x , y )
| Remove Position polygon_num, node_num
}
}
As you can tell from the syntax, this statement allows you to change objects in various ways. You can use the attributes from ObjectInfo()
function, attributes from the ObjectGeography()
function, and change, delete, and add additional nodes to the object.
In our case, we will use the Geography
clause of the statement to modify the centroid. More specifically, we will be using the ObjectGeography()
attribute OBJ_GEO_CENTROID:
Alter Object oPolygon Geography OBJ_GEO_CENTROID, oPoint
The statement above will assign the point object oPoint
as the centroid to the polygon object oPolygon
.
But that's only part of the needed statements. Typically, objects are stored in a table. This means that we first need to get the object from the table into a variable, then change the centroid of this object variable, and finally write the object variable back to the record.
This could look like this including the variable definitions:
Dim oPolygon As Object
Dim nRowID As Integer
Dim fX, fY As Float
Set Coordsys Table Selection
Fetch First From Selection
oPolygon = Selection.OBJ
nRowID = Selection.ROWID
fX = Selection.X
fY = Selection.Y
Alter Object oPolygon Geography OBJ_GEO_CENTROID, CreatePoint(X, Y)
Update Selection Set OBJ = oPolygon
Where ROWID = nRowID
Note that I specify the coordinate system to use and that I read the new location of the centroid from columns X
and Y
.
Creating a Function for Changing the Centroid
There are good reasons to create a function instead of using the rather complex structure above. One reason is that it becomes easier to use and another is that it improves performance.
The basic element of the function is still the Alter Object
statement. A function also allows us to wrap some error handling around the process.
In my function below, you can see that I check the input object type and ensure that the new centroid point lies inside the input object.
The function below takes two parameters: the polygon to modify and the new centroid as a point.
Function OBJSetCentroid( ByVal oPolygon As Object
, ByVal oPoint As Object
) As Object
OnError GoTo ErrorOccured
OBJSetCentroid = oPolygon
If Not ObjectInfo(oPolygon, OBJ_INFO_TYPE) = OBJ_TYPE_REGION Then
'**Only works for Polygon/Region objects
Print "New Input Object not of type Polygon/Region!"
Exit Function
ElseIf Not oPoint Within oPolygon Then
Print "New Centroid not within Polygon! X: " & FormatNumber$(Round(CentroidX(oPoint), 0.01)) & " Y: " & FormatNumber$(Round(CentroidY(oPoint), 0.01))
Exit Function
End If
Alter Object oPolygon Geography OBJ_GEO_CENTROID, oPoint
OBJSetCentroid = oPolygon
Exit Function
'-------------------------
ErrorOccured:
Print Err() + " " Error$() + " OBJSetCentroid"
End Function
I can also create a function that takes 3 parameters: The polygon to modify and the coordinate pair of the new centroid.
As you can see, this function takes advantage of the first function by calling this with the polygon parameter and the CreatePoint
function to create a point from the coordinate pair.
Function OBJSetCentroidXY( ByVal oPolygon As Object
, ByVal fX As Float
, ByVal fY As Float
) As Object
OnError GoTo ErrorOccured
OBJSetCentroidXY = OBJSetCentroid(oPolygon, CreatePoint(fX, fY))
Exit Function
'-------------------------
ErrorOccured:
Print Err() + " " + Error$() + " OBJSetCentroidXY"
End Function
Having these two functions, you can now easily update the centroid of your polygons using the Update
statement:
Set CoordSys Table Boundaries
Update Boundaries Set OBJ = OBJSetCentroidXY(OBJ, X, Y)
I can also call the first function directly if I prefer:
Set CoordSys Table Boundaries
Update Boundaries Set OBJ = OBJSetCentroid(OBJ, CreatePoint(X, Y))
Notice how I in both cases set the projection to use with the Set CoordSys
statement.
MapBasic Common Libraries
I have added these two functions to the OBJLib module that is part of the Common MapBasic Libraries.
You can find the OBJLib.mb
file and the other MapBasic modules on GitHub.
I have earlier written about how to use these libraries via MapBasic projects. You can find the article here: MapBasic Monday: Introducing MapBasic Projects.
You can also copy and paste the functions into your MapBasic module. Remember to remove any references to functions or procedures from other modules, such as the procedures called under error handling.
------------------------------
Peter Horsbøll Møller
Principal Presales Consultant | Distinguished Engineer
Precisely | Trust in Data
------------------------------