diff --git a/core/src/main/java/org/orekit/rugged/api/Rugged.java b/core/src/main/java/org/orekit/rugged/api/Rugged.java
index f97e83459bdb2d8ebd80f138f6e3d5fcdc8031d6..94ba298ff6b7b2228036613654277c1102bba553 100644
--- a/core/src/main/java/org/orekit/rugged/api/Rugged.java
+++ b/core/src/main/java/org/orekit/rugged/api/Rugged.java
@@ -798,6 +798,80 @@ public class Rugged {
         }
     }
 
+    /** Direct localization of a single line-of-sight.
+     * @param date date of the localization
+     * @param pixel position in spacecraft frame
+     * @param los normalized line-of-sight in spacecraft frame
+     * @return ground position of all pixels of the specified sensor line
+     * @exception RuggedException if line cannot be localized, or sensor is unknown
+     */
+    public GeodeticPoint directLocalization(final AbsoluteDate date, final Vector3D position, final Vector3D los)
+        throws RuggedException {
+        try {
+
+            // compute the approximate transform between spacecraft and observed body
+            final Transform    scToInert   = scToBody.getScToInertial(date);
+            final Transform    inertToBody = scToBody.getInertialToBody(date);
+            final Transform    approximate = new Transform(date, scToInert, inertToBody);
+
+            final Vector3D spacecraftVelocity =
+                    scToInert.transformPVCoordinates(PVCoordinates.ZERO).getVelocity();
+
+            // compute localization of specified pixel
+            final Vector3D pInert    = scToInert.transformPosition(position);
+
+            final Vector3D obsLInert = scToInert.transformVector(los);
+            final Vector3D lInert;
+            if (aberrationOfLightCorrection) {
+                // apply aberration of light correction
+                // as the spacecraft velocity is small with respect to speed of light,
+                // we use classical velocity addition and not relativistic velocity addition
+                // we look for a positive k such that: c * lInert + vsat = k * obsLInert
+                // with lInert normalized
+                final double a = obsLInert.getNormSq();
+                final double b = -Vector3D.dotProduct(obsLInert, spacecraftVelocity);
+                final double c = spacecraftVelocity.getNormSq() - Constants.SPEED_OF_LIGHT * Constants.SPEED_OF_LIGHT;
+                final double s = FastMath.sqrt(b * b - a * c);
+                final double k = (b > 0) ? -c / (s + b) : (s - b) / a;
+                lInert = new Vector3D( k   / Constants.SPEED_OF_LIGHT, obsLInert,
+                                       -1.0 / Constants.SPEED_OF_LIGHT, spacecraftVelocity);
+            } else {
+                // don't apply aberration of light correction
+                lInert = obsLInert;
+            }
+
+            if (lightTimeCorrection) {
+                // compute DEM intersection with light time correction
+                final Vector3D  sP       = approximate.transformPosition(position);
+                final Vector3D  sL       = approximate.transformVector(los);
+                final Vector3D  eP1      = ellipsoid.transform(ellipsoid.pointOnGround(sP, sL));
+                final double    deltaT1  = eP1.distance(sP) / Constants.SPEED_OF_LIGHT;
+                final Transform shifted1 = inertToBody.shiftedBy(-deltaT1);
+                final GeodeticPoint gp1  = algorithm.intersection(ellipsoid,
+                                                                  shifted1.transformPosition(pInert),
+                                                                  shifted1.transformVector(lInert));
+
+                final Vector3D  eP2      = ellipsoid.transform(gp1);
+                final double    deltaT2  = eP2.distance(sP) / Constants.SPEED_OF_LIGHT;
+                final Transform shifted2 = inertToBody.shiftedBy(-deltaT2);
+                return algorithm.refineIntersection(ellipsoid,
+                                                    shifted2.transformPosition(pInert),
+                                                    shifted2.transformVector(lInert),
+                                                    gp1);
+
+            } else {
+                // compute DEM intersection without light time correction
+                final Vector3D pBody = inertToBody.transformPosition(pInert);
+                final Vector3D lBody = inertToBody.transformVector(lInert);
+                return algorithm.refineIntersection(ellipsoid, pBody, lBody,
+                                                    algorithm.intersection(ellipsoid, pBody, lBody));
+            }
+
+        } catch (OrekitException oe) {
+            throw new RuggedException(oe, oe.getSpecifier(), oe.getParts());
+        }
+    }
+
     /** Find the date at which sensor sees a ground point.
      * <p>
      * This method is a partial {@link #inverseLocalization(String,
diff --git a/core/src/test/java/org/orekit/rugged/api/RuggedTest.java b/core/src/test/java/org/orekit/rugged/api/RuggedTest.java
index 002a63c711cbce6febe121c15effede9d99856dd..85d1d48e82368dc96a8285707448c27ee33ed140 100644
--- a/core/src/test/java/org/orekit/rugged/api/RuggedTest.java
+++ b/core/src/test/java/org/orekit/rugged/api/RuggedTest.java
@@ -554,6 +554,59 @@ public class RuggedTest {
 
     }
 
+    @Test
+    public void testLocalizationsinglePoint()
+        throws RuggedException, OrekitException, URISyntaxException {
+
+        int dimension = 200;
+
+        String path = getClass().getClassLoader().getResource("orekit-data").toURI().getPath();
+        DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(path)));
+        final BodyShape  earth = createEarth();
+        final Orbit      orbit = createOrbit(Constants.EIGEN5C_EARTH_MU);
+
+        AbsoluteDate crossing = new AbsoluteDate("2012-01-01T12:30:00.000", TimeScalesFactory.getUTC());
+
+        // one line sensor
+        // position: 1.5m in front (+X) and 20 cm above (-Z) of the S/C center of mass
+        // los: swath in the (YZ) plane, looking at 50° roll, ±1° aperture
+        Vector3D position = new Vector3D(1.5, 0, -0.2);
+        List<Vector3D> los = createLOS(new Rotation(Vector3D.PLUS_I, FastMath.toRadians(50.0)).applyTo(Vector3D.PLUS_K),
+                                       Vector3D.PLUS_I,
+                                       FastMath.toRadians(1.0), dimension);
+
+        // linear datation model: at reference time we get line 100, and the rate is one line every 1.5ms
+        LineDatation lineDatation = new LinearLineDatation(crossing, dimension / 2, 1.0 / 1.5e-3);
+        int firstLine = 0;
+        int lastLine  = dimension;
+        LineSensor lineSensor = new LineSensor("line", lineDatation, position, los);
+        AbsoluteDate minDate = lineSensor.getDate(firstLine);
+        AbsoluteDate maxDate = lineSensor.getDate(lastLine);
+
+        TileUpdater updater =
+                new RandomLandscapeUpdater(0.0, 9000.0, 0.5, 0xf0a401650191f9f6l,
+                                           FastMath.toRadians(1.0), 257);
+
+        Rugged rugged = new Rugged(updater, 8, AlgorithmId.DUVENHAGE,
+                                   EllipsoidId.WGS84, InertialFrameId.EME2000, BodyRotatingFrameId.ITRF,
+                                   minDate, maxDate, 5.0,
+                                   orbitToPV(orbit, earth, minDate.shiftedBy(-1.0), maxDate.shiftedBy(+1.0), 0.25), 8,
+                                   CartesianDerivativesFilter.USE_PV,
+                                   orbitToQ(orbit, earth, minDate.shiftedBy(-1.0), maxDate.shiftedBy(+1.0), 0.25), 2,
+                                   AngularDerivativesFilter.USE_R, 0.001);
+        rugged.addLineSensor(lineSensor);
+        GeodeticPoint[] gpLine = rugged.directLocalization("line", 100);
+
+        for (int i = 0; i < gpLine.length; ++i) {
+            GeodeticPoint gpPixel =
+                    rugged.directLocalization(lineSensor.getDate(100), lineSensor.getPosition(), lineSensor.getLos(i));
+            Assert.assertEquals(gpLine[i].getLatitude(),  gpPixel.getLatitude(),  1.0e-10);
+            Assert.assertEquals(gpLine[i].getLongitude(), gpPixel.getLongitude(), 1.0e-10);
+            Assert.assertEquals(gpLine[i].getAltitude(),  gpPixel.getAltitude(),  1.0e-10);
+        }
+
+    }
+
     @Test
     public void testBasicScan()
         throws RuggedException, OrekitException, URISyntaxException {