diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py
index e8bf30ba11..a74d955f5d 100644
--- a/package/MDAnalysis/core/groups.py
+++ b/package/MDAnalysis/core/groups.py
@@ -3254,6 +3254,16 @@ def select_atoms(self, sel, *othersel, periodic=True, rtol=1e-05,
                 For example, ``prop z >= 5.0`` selects all atoms with z
                 coordinate greater than 5.0; ``prop abs z <= 5.0`` selects all
                 atoms within -5.0 <= z <= 5.0.
+            relprop [abs] *property*  *operator*  *value*  *selection*
+                selects atoms based on position relative to the center of
+                geometry (COG) of a given selection, using *property*
+                **x**, **y**, or **z** coordinate. Supports the **abs**
+                keyword (for absolute value) and the following
+                *operators*: **<, >, <=, >=, ==, !=**.
+                For example, ``relprop z >= 5.0 protein`` selects all atoms
+                with z coordinate greater than 5.0 relative to the COG
+                of protein; ``relprop abs z <= 5.0 protein`` selects all
+                atoms within -5.0 <= z <= 5.0 relative to the COG of protein.
             sphzone *radius* *selection*
                 Selects all atoms that are within *radius* of the center of
                 geometry of *selection*
diff --git a/package/MDAnalysis/core/selection.py b/package/MDAnalysis/core/selection.py
index 6c2ea3c817..82a03832e4 100644
--- a/package/MDAnalysis/core/selection.py
+++ b/package/MDAnalysis/core/selection.py
@@ -1384,6 +1384,55 @@ def _apply(self, group):
         return group[mask]
 
 
+class RelPropertySelection(PropertySelection):
+    """Some of the possible properties:
+    x, y, z,
+
+    .. versionadded:: 2.9.0
+    """
+
+    token = "relprop"
+    precedence = 1
+
+    def __init__(self, parser, tokens):
+        super().__init__(parser, tokens)
+        self.sel = parser.parse_expression(self.precedence)
+        self.periodic = parser.periodic
+
+    def _apply(self, group):
+        try:
+            values = getattr(group, self.props[self.prop])
+        except KeyError:
+            errmsg = f"Expected one of {list(self.props.keys())}"
+            raise SelectionError(errmsg) from None
+        except NoDataError:
+            attr = self.props[self.prop]
+            errmsg = f"This Universe does not contain {attr} information"
+            raise SelectionError(errmsg) from None
+
+        try:
+            col = {"x": 0, "y": 1, "z": 2}[self.prop]
+        except KeyError:
+            errmsg = f"Expected one of x y z for property, got {self.prop}"
+            raise SelectionError(errmsg) from None
+        else:
+            sel = self.sel.apply(group)
+            ref_value = sel.center_of_geometry().reshape(3).astype(np.float32)
+            box = group.dimensions if self.periodic else None
+            if box is not None:
+                values = distances.minimize_vectors(
+                    values - ref_value[None, :], box=box
+                )[:, col]
+            else:
+                values = values[:, col] - ref_value[col]
+
+        if self.absolute:
+            values = np.abs(values)
+        mask = self.operator(values, self.value)
+
+        return group[mask]
+
+
 class SameSelection(Selection):
     """
     Selects all atoms that have the same subkeyword value as any atom in selection
diff --git a/testsuite/MDAnalysisTests/core/test_atomselections.py b/testsuite/MDAnalysisTests/core/test_atomselections.py
index 51b395f94c..06331d0be3 100644
--- a/testsuite/MDAnalysisTests/core/test_atomselections.py
+++ b/testsuite/MDAnalysisTests/core/test_atomselections.py
@@ -255,6 +255,17 @@ def test_prop(self, universe):
         assert_equal(len(sel), 3194)
         assert_equal(len(sel2), 2001)
 
+    def test_relprop(self, universe):
+        sel1 = universe.select_atoms("relprop z <= 1 index 0")
+        sel2 = universe.select_atoms("relprop abs z <= 1 index 0")
+
+        positions = universe.trajectory[0].positions
+        ref = positions[0, 2]
+        mask_1 = (positions[:, 2] - ref) <= 1
+        assert_equal(len(sel1), np.count_nonzero(mask_1))
+        mask_2 = np.abs(positions[:, 2] - ref) <= 1
+        assert_equal(len(sel2), np.count_nonzero(mask_2))
+
     def test_bynum(self, universe):
         "Tests the bynum selection, also from AtomGroup instances (Issue 275)"
         sel = universe.select_atoms('bynum 5')
@@ -1110,6 +1121,18 @@ def test_invalid_prop_selection(self, universe):
         with pytest.raises(SelectionError, match="Expected one of"):
             universe.select_atoms("prop parsnip < 2")
 
+    def test_invalid_relprop_selection(self, universe):
+        with pytest.raises(SelectionError, match="Expected one of"):
+            universe.select_atoms("relprop parsnip < 2 index 0")
+        with pytest.raises(SelectionError, match="Unknown selection token"):
+            universe.select_atoms("relprop z < 2")
+        with pytest.raises(SelectionError, match="Expected one of"):
+            universe.select_atoms("relprop resid < 2 index 0")
+        with pytest.raises(
+            SelectionError, match="This Universe does not contain"
+        ):
+            universe.select_atoms("relprop z <= 1 index 0")
+
 
 def test_segid_and_resid():
     u = make_Universe(('segids', 'resids'))