Bursa-Wolf vs Molodensky-Badekas Transformations

Both the Bursa-Wolf and Molodensky-Badekas models carry the same seven quantities — three translations, three rotations, one scale — that drive Helmert 7-parameter transformations in Python, the parent topic under Core Transformation Fundamentals & Standards; the difference is where the rotations act. Bursa-Wolf rotates the position vector about the geocentre, while Molodensky-Badekas rotates it about a local centroid, and because the two publish different translation numbers for the very same physical transformation, using one set through the other’s formula is a silent, systematic error.

Rotation origin: Bursa-Wolf versus Molodensky-Badekas Two panels. On the left, Bursa-Wolf: the rotation axes pass through the Earth's geocentre, far from the survey region, so the rotation is applied about the origin of the geocentric frame. On the right, Molodensky-Badekas: the rotation axes pass through the centroid of the control points inside the survey region, so the rotation is applied about that local evaluation point. Bursa-Wolf geocentre O survey far from O Molodensky-Badekas survey centroid Xₘ rotate about local point

Figure — Bursa-Wolf applies the rotation about the distant geocentre; Molodensky-Badekas applies it about the local control centroid.

Why the Two Models Are Not Interchangeable

Bursa-Wolf writes the transformation as a scaled rotation of the raw geocentric vector plus a translation:

Bursa-Wolf and Molodensky-Badekas differ only in where the rotation pivots Two parallel routes from the same Cartesian input. The Bursa-Wolf route rotates about the geocentre, so its translation terms absorb the whole offset between the two frames and are strongly correlated with the rotations. The Molodensky-Badekas route first subtracts the centroid of the control network, rotates about that centroid, and adds the centroid back, which decorrelates the parameters over a regional network. Both routes produce the same transformed coordinate when their parameter sets are converted consistently. Cartesian input X, Y, Z on source frame Bursa-Wolf rotate about geocentre Molodensky-Badekas rotate about network centroid Translations absorb the full frame offset Translations stay small parameters decorrelated Identical transformed coordinate when parameters are converted consistently global fit regional fit

Figure — same seven numbers, different pivot: the rotation origin is the whole difference.

Xt=TBW+(1+ds)RXs \mathbf{X}_t = \mathbf{T}_{BW} + (1 + d_s)\,\mathbf{R}\,\mathbf{X}_s

Molodensky-Badekas first subtracts the centroid Xm\mathbf{X}_m of the control points, rotates and scales about that local point, then adds the centroid back with its own translation:

Xt=Xm+TMB+(1+ds)R(XsXm) \mathbf{X}_t = \mathbf{X}_m + \mathbf{T}_{MB} + (1 + d_s)\,\mathbf{R}\,(\mathbf{X}_s - \mathbf{X}_m)

The rotation matrix R\mathbf{R} and scale dsd_s are identical between the two forms; only the translation differs. Expanding the Molodensky-Badekas expression and matching it to Bursa-Wolf gives the exact conversion:

TBW=TMB+(I(1+ds)R)Xm \mathbf{T}_{BW} = \mathbf{T}_{MB} + \left(\mathbf{I} - (1 + d_s)\,\mathbf{R}\right)\mathbf{X}_m

The correction term (I(1+ds)R)Xm(\mathbf{I} - (1+d_s)\mathbf{R})\mathbf{X}_m is not small: with Xm\mathbf{X}_m on the order of six million metres and rotations of an arc-second, it reaches tens of metres, which is why swapping the translations between models produces a gross datum error. The reason both forms exist is conditioning: rotating about the geocentre makes the Bursa-Wolf rotation and translation parameters strongly correlated when the control sits far from the origin, whereas centring on Xm\mathbf{X}_m decorrelates them — the same numerical motivation behind centring in deriving 7-parameter Helmert from control points. Many national agencies therefore estimate and publish in Molodensky-Badekas form, quoting the centroid alongside the seven parameters.

Comparison Table

Property Bursa-Wolf Molodensky-Badekas
Rotation origin geocentre O\mathbf{O} control centroid Xm\mathbf{X}_m
Extra published quantity none centroid coordinates Xm\mathbf{X}_m
Translation meaning offset at the geocentre offset at the centroid
Rotation R\mathbf{R}, scale dsd_s shared, identical shared, identical
Parameter correlation high when survey far from O\mathbf{O} low (decorrelated by centring)
Typical use global/continental sets, EPSG 1033/1032 regional/national fits, EPSG 1034/9636
Interchangeable numbers? no — translations differ by tens of metres no — requires the conversion above

Complete Runnable Implementation

The module applies both forms and converts between their translations. Feeding a Bursa-Wolf and its equivalent Molodensky-Badekas set through their respective functions must transform any point identically.

from __future__ import annotations
from dataclasses import dataclass
import math

import numpy as np

ARCSEC_TO_RAD: float = math.pi / (180.0 * 3600.0)


@dataclass(frozen=True)
class SevenParameters:
    """Shared rotation/scale, plus a translation whose meaning depends on model."""
    tx: float           # metres
    ty: float           # metres
    tz: float           # metres
    rx: float           # arc-seconds
    ry: float           # arc-seconds
    rz: float           # arc-seconds
    ds: float           # ppm


def _rot_and_scale(p: SevenParameters) -> tuple[np.ndarray, float]:
    rx, ry, rz = (p.rx * ARCSEC_TO_RAD, p.ry * ARCSEC_TO_RAD, p.rz * ARCSEC_TO_RAD)
    rot = np.array([[1.0, -rz, ry],
                    [rz, 1.0, -rx],
                    [-ry, rx, 1.0]], dtype=np.float64)   # position-vector sense
    return rot, 1.0 + p.ds * 1.0e-6


def bursa_wolf(xyz: np.ndarray, p: SevenParameters) -> np.ndarray:
    """Rotate/scale about the geocentre, then translate (EPSG method 1033)."""
    pts = np.atleast_2d(np.asarray(xyz, dtype=np.float64))
    rot, scale = _rot_and_scale(p)
    out = scale * (pts @ rot.T) + np.array([p.tx, p.ty, p.tz])
    return out[0] if np.ndim(xyz) == 1 else out


def molodensky_badekas(xyz: np.ndarray, p: SevenParameters,
                       centroid: np.ndarray) -> np.ndarray:
    """Rotate/scale about the control centroid, then translate (EPSG method 1034)."""
    pts = np.atleast_2d(np.asarray(xyz, dtype=np.float64))
    xm = np.asarray(centroid, dtype=np.float64)
    rot, scale = _rot_and_scale(p)
    out = xm + np.array([p.tx, p.ty, p.tz]) + scale * ((pts - xm) @ rot.T)
    return out[0] if np.ndim(xyz) == 1 else out


def mb_translation_to_bw(p: SevenParameters, centroid: np.ndarray) -> np.ndarray:
    """Convert a Molodensky-Badekas translation to the Bursa-Wolf translation:
    T_BW = T_MB + (I - (1+ds)R) Xm."""
    rot, scale = _rot_and_scale(p)
    xm = np.asarray(centroid, dtype=np.float64)
    return np.array([p.tx, p.ty, p.tz]) + (np.eye(3) - scale * rot) @ xm

Parameter Reference

Name Type Units Valid range Notes
tx, ty, tz float metres published translation; meaning differs by model
rx, ry, rz float arc-seconds typ. ±5″ shared rotations, position-vector convention
ds float ppm typ. ±50 ppm shared scale
centroid np.ndarray metres (3,) ECEF control centroid; mandatory for Molodensky-Badekas
xyz np.ndarray metres (3,) or (n,3) geocentric ECEF to transform
return np.ndarray metres finite target-datum ECEF

Worked Example

Define a Bursa-Wolf set, choose a centroid, derive the matching Molodensky-Badekas translation, and confirm both transform a point to the same place.

bw = SevenParameters(tx=-446.448, ty=125.157, tz=-542.060,
                     rx=-0.1502, ry=-0.2470, rz=-0.8421, ds=20.4894)
xm = np.array([3874938.0, -114020.0, 5047165.0])       # a British control centroid

rot, scale = _rot_and_scale(bw)
t_mb = np.array([bw.tx, bw.ty, bw.tz]) - (np.eye(3) - scale * rot) @ xm
mb = SevenParameters(*t_mb, bw.rx, bw.ry, bw.rz, bw.ds)

point = np.array([3888891.410, -129007.815, 5036943.920])
print(np.round(bursa_wolf(point, bw), 4))
print(np.round(molodensky_badekas(point, mb, xm), 4))
# both: [ 3888518.0845 -128897.5105  5036509.8149 ]

Both models land the point on the identical target coordinate, yet the Molodensky-Badekas translation t_mb differs from the Bursa-Wolf [-446.448, 125.157, -542.060] by tens of metres — the visible proof that the two translation vectors are not interchangeable.

Validation Check

out_bw = bursa_wolf(point, bw)
out_mb = molodensky_badekas(point, mb, xm)
assert np.allclose(out_bw, out_mb, atol=1e-3), "models disagree — check centroid"
recovered = mb_translation_to_bw(mb, xm)
assert np.allclose(recovered, [bw.tx, bw.ty, bw.tz], atol=1e-6), "round-trip failed"
Translation-rotation correlation against distance from the centroid Two traces over distance from the network centroid, from zero to three hundred kilometres. The Bursa-Wolf correlation between the X translation and the Y rotation starts near 0.97 and stays above 0.9 across the whole range, because the pivot is thousands of kilometres away. The Molodensky-Badekas correlation starts near zero at the centroid and climbs only to about 0.35 at three hundred kilometres. 0.00 0.25 0.50 0.75 0 50 100 150 200 250 300 distance from control-network centroid (km) |corr(tx, ry)| Bursa-Wolf Molodensky-Badekas

Figure — parameter correlation with distance from the network centroid (illustrative regional fit).

Common Mistakes

Applying Bursa-Wolf translations through the Molodensky-Badekas formula
The translation numbers are not the same quantity. A Bursa-Wolf translation is the offset at the geocentre; a Molodensky-Badekas translation is the offset at the centroid. Substituting one into the other's equation shifts every result by the tens-of-metres correction term (I - (1+ds)R)·Xm. Convert first with the explicit formula.
Using the wrong centroid, or omitting it entirely
Molodensky-Badekas is undefined without the exact centroid the parameters were estimated against; it is a published quantity, not something you may choose freely. Using a nearby-but-different centroid, or defaulting it to the geocentre, reintroduces the very error the model was designed to avoid.
Flipping the sign of the rotations during conversion
The rotations and scale are shared and unchanged between the two models — only the translation is recomputed. If a conversion also appears to require a rotation sign change, the two sets were published in different rotation conventions (position-vector versus coordinate-frame), a separate issue handled in the parent Helmert page. Do not fold a convention flip into the Bursa-Wolf to Molodensky-Badekas conversion.

Frequently Asked Questions

Can I convert a Bursa-Wolf parameter set into a Molodensky-Badekas one?

Yes, exactly, provided you know the rotation origin the Molodensky-Badekas set uses. The rotation and scale terms are identical between the two forms; only the translations differ, and they differ by the amount the rotation and scale move the chosen origin. Convert by applying the rotation and scale to the origin point and adjusting the translations accordingly. What you cannot do is take the seven numbers from one form, feed them to the other, and expect anything sensible — the translations will be wrong by the size of the origin offset, which for a geocentric pivot is thousands of kilometres scaled by the rotation, typically several metres on the ground.

Which form should I publish for a regional transformation?

Molodensky-Badekas, with the pivot stated explicitly. Over a regional network the Bursa-Wolf translations and rotations are so strongly correlated that the individual parameter values are close to meaningless, and any uncertainty quoted on them is misleading even though the transformation as a whole is fine. The Molodensky-Badekas form decorrelates them, which makes the parameter uncertainties readable and makes it obvious to a reviewer that the set applies to a specific region.

Do the two forms give different coordinates?

No. Applied correctly with their own parameter sets, both produce the same transformed coordinate to within floating-point noise. The difference is entirely in the conditioning of the estimation and the interpretability of the parameters, not in the result. If your two implementations disagree on the output, the cause is a parameter set used in the wrong form or a sign convention mismatch in the rotations, not the choice of model.