Old Question, but duckduckgo brings you here for "pyqgis transform single geometry"
Just like Riccardo Pointed out, the accepted answer does not work for scripts in QGIS 3, the situation is explained in the api reference:
Python scripts should utilize the QgsProject.instance() project instance when creating QgsCoordinateTransform. This will ensure that any datum transforms defined in the project will be correctly respected during coordinate transforms. E.g.
transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:3111"),
QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance())
Using QgsCoordinateTransform in QGIS 3 will transform the geometry instance inplace, and return 0:
geom = QgsGeometry(QgsPoint(5,20))
sourceCrs = QgsCoordinateReferenceSystem(4326)
destCrs = QgsCoordinateReferenceSystem(2154)
tr = QgsCoordinateTransform(sourceCrs, destCrs, QgsProject.instance())
geom.transform(tr)
#[out]: 0
geom
#[out]: <QgsGeometry: Point (930236.95432910311501473 3567516.9810206750407815)>
back_tr = QgsCoordinateTransform(destCrs, sourceCrs, QgsProject.instance())
geom.transform(back_tr)
#[out]: 0
geom
#[out]: <QgsGeometry: Point (5 19.99999999999911182)>
If you want to copy an already existing QgsGeometry Instance, the QgsGeometry class constructor will make the trick. From the docs:
Class: QgsGeometry
class qgis.core.QgsGeometry
Bases: sip.wrapper
Constructor
QgsGeometry(QgsGeometry) Copy constructor will prompt a deep copy of the object
example:
geom2 = QgsGeometry(geom)
geom2
#[out]: <QgsGeometry: Point (5 19.99999999999911182)>
geom
#[out]: <QgsGeometry: Point (5 19.99999999999911182)>
geom2.transform(tr)
#[out]: 0
geom2
#[out]: <QgsGeometry: Point (930236.95432910579256713 3567516.98102056747302413)>
geom
#[out]: <QgsGeometry: Point (5 19.99999999999911182)>
transformPolygon()
that, then just take the outer ring again. Its pretty much just QVector operations all the way down...