diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8e7d93a2b99530226cdacaa478464f8ff6389a34..447618a490642e374e5745e5164470456f626bf8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -379,6 +379,7 @@ SET(HDRS_FEATURE
 SET(HDRS_LANDMARK
   include/base/landmark/landmark_base.h
   include/base/landmark/landmark_match.h
+  include/base/landmark/landmark_match_polyline_2D.h
   include/base/landmark/landmark_corner_2D.h
   include/base/landmark/landmark_container.h
   include/base/landmark/landmark_line_2D.h
diff --git a/include/base/feature/feature_match_polyline_2D.h b/include/base/feature/feature_match_polyline_2D.h
new file mode 100644
index 0000000000000000000000000000000000000000..38624b6f3c346ab581cce34662c20d51732dbcc6
--- /dev/null
+++ b/include/base/feature/feature_match_polyline_2D.h
@@ -0,0 +1,30 @@
+#ifndef FEATURE_MATCH_POLYLINE_2D_H_
+#define FEATURE_MATCH_POLYLINE_2D_H_
+
+// Wolf includes
+#include "base/wolf.h"
+#include "base/feature/feature_match.h"
+
+//wolf nampseace
+namespace wolf {
+    
+//forward declaration to typedef class pointers
+WOLF_STRUCT_PTR_TYPEDEFS(FeatureMatchPolyline2D);
+
+/** \brief Match between a feature_polyline_2D and a feature_polyline_2D
+ *
+ * Match between a feature_polyline_2D and a feature_polyline_2D (feature-feature correspondence)
+ *
+ */
+struct FeatureMatchPolyline2D : public FeatureMatch
+{
+    int feature_last_from_id_;
+    int feature_last_to_id_;
+    int feature_incoming_from_id_;
+    int feature_incoming_to_id_;
+};
+
+}//end namespace
+
+#endif
+
diff --git a/include/base/landmark/landmark_match_polyline_2D.h b/include/base/landmark/landmark_match_polyline_2D.h
new file mode 100644
index 0000000000000000000000000000000000000000..3298c99645a3d0d157795e49adb52c85f72a1f1e
--- /dev/null
+++ b/include/base/landmark/landmark_match_polyline_2D.h
@@ -0,0 +1,31 @@
+#ifndef LANDMARK_MATCH_POLYLINE_2D_H_
+#define LANDMARK_MATCH_POLYLINE_2D_H_
+
+// Wolf includes
+#include "base/wolf.h"
+#include "base/landmark/landmark_match.h"
+
+//wolf nampseace
+namespace wolf {
+
+// Map of Feature - Landmark matches
+WOLF_STRUCT_PTR_TYPEDEFS(LandmarkMatchPolyline2D);
+typedef std::map<FeatureBasePtr, LandmarkMatchPolyline2DPtr> LandmarkMatchPolyline2DMap;
+
+/** \brief Match between a feature_polyline_2D and a landmark_polyline_2D
+ *
+ * Match between a feature_polyline_2D and a landmark_polyline_2D
+ *
+ **/
+struct LandmarkMatchPolyline2D : public LandmarkMatch
+{
+    int landmark_from_id_;
+    int feature_from_id_;
+    int landmark_to_id_;
+    int feature_to_id_;
+    int landmark_version_;
+};
+
+}//end namespace
+
+#endif
diff --git a/include/base/landmark/landmark_polyline_2D.h b/include/base/landmark/landmark_polyline_2D.h
index 13db3f53acd6aaf8cd13f9293a21312bc1e9c7be..62624e9cac7fcaa474dd731452c38141e97690ce 100644
--- a/include/base/landmark/landmark_polyline_2D.h
+++ b/include/base/landmark/landmark_polyline_2D.h
@@ -10,6 +10,7 @@
 
 // Wolf
 #include "base/landmark/landmark_base.h"
+#include "base/processor/polyline_2D_utils.h"
 
 // STL
 #include <deque>
@@ -211,9 +212,7 @@ class LandmarkPolyline2D : public LandmarkBase
 
         YAML::Node saveToYaml() const;
 
-        /** \brief Computes the squared distance from a point to a segment defined by two points
-         **/
-        Scalar sqDistPoint2Segment(const Eigen::Vector2s& p, const Eigen::Vector2s& a, const Eigen::Vector2s& b) const;
+        static void tryMergeLandmarks(LandmarkPolyline2DList& _lmk_list, const Scalar& _dist_tol);
 };
 
 inline StateBlockPtr& LandmarkPolyline2D::firstStateBlock()
@@ -253,6 +252,9 @@ inline bool LandmarkPolyline2D::isClosed() const
 
 inline LandmarkPolyline2DPtr LandmarkPolyline2D::getMergedInLandmark() const
 {
+    // recursive call
+    if (merged_in_lmk_->getMergedInLandmark() != nullptr)
+        return merged_in_lmk_->getMergedInLandmark();
     return merged_in_lmk_;
 }
 
diff --git a/include/base/processor/polyline_2D_utils.h b/include/base/processor/polyline_2D_utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..947db7d7028739e0f9df6c73469d1b0f99204b31
--- /dev/null
+++ b/include/base/processor/polyline_2D_utils.h
@@ -0,0 +1,357 @@
+/*
+ * polyline_2D_utils.h
+ *
+ *  Created on: Mar 14, 2019
+ *      Author: jvallve
+ */
+
+#ifndef POLYLINE_2D_UTILS_H_
+#define POLYLINE_2D_UTILS_H_
+
+#include "base/wolf.h"
+#include "base/landmark/landmark_match_polyline_2D.h"
+#include "base/landmark/landmark_polyline_2D.h"
+#include "base/feature/feature_match_polyline_2D.h"
+#include "base/feature/feature_polyline_2D.h"
+
+namespace wolf {
+
+/** \brief Computes the squared distance from a point to a segment defined by two points
+ **/
+Scalar sqDistPoint2Segment(const Eigen::Vector2s& p, const Eigen::Vector2s& a, const Eigen::Vector2s& b)
+{
+    Scalar ap_sq = (p-a).squaredNorm();
+    Scalar bp_sq = (p-b).squaredNorm();
+    Scalar ab_sq = (b-a).squaredNorm();
+    Scalar sqDist;
+
+    // If the projection of p onto ab is inside the segment (acute triangle: all angles < 90º), return the sq distance point-line.
+    if (ap_sq <= bp_sq + ab_sq && bp_sq <= ap_sq + ab_sq) // acute triangle: applying pitagoras twice, angles ABP and PAB <= 90º
+        sqDist = ap_sq - std::pow((b-a).dot(p-a),2) / ab_sq;
+
+    // Otherwise, return the sq distance to the closest segment point.
+    else
+        sqDist = std::min(bp_sq,ap_sq);
+
+    return sqDist;
+}
+
+Scalar sqDistPointToLine(const Eigen::Vector3s& _A, const Eigen::Vector3s& _A_aux,
+                         const Eigen::Vector3s& _B, bool _A_defined, bool _B_defined, bool strict=false)
+{
+    /* Squared distance from B to the line A_aux-A (match evaluated is A-B)
+     *
+     * No matter if A_aux is defined or not.
+     *
+     * Case 1: B not defined & A not defined
+     *
+     *      If projection of B over AAaux is in [A_aux, inf), return sq.dist. B-Line(AAux)
+     *      Otherwise, return sqDist(Aaux-B) or 1e12 if strict=true.
+     *
+     *      Check: Angle BAauxA is <= 90º: (AB)² <= (AauxB)² + (AAaux)²
+     *
+     * Case 2: B not defined & A defined
+     *
+     *      If projection of B over AAaux is in [A_aux, A], return sq.dist. B-Line(AAux)
+     *      Otherwise, return min(sqDist(Aaux-B),sqDist(A-B)) or 1e12 if strict=true.
+     *
+     *      Check: Angles BAauxA & BAAaux are <= 90º: (AB)² <= (AauxB)² + (AAaux)² & (AauxB)² <= (AB)² + (AAaux)²
+     *
+     * Case 3: B defined & A not defined
+     *
+     *      If projection of B over AAaux is in [A, inf), return sq.dist. B-Line(AAux)
+     *      Otherwise, return sqDist(A-B)) or 1e12 if strict=true.
+     *
+     *      Check: Angle BAAaux is >= 90º: (AauxB)² >= (AB)² + (AAaux)²
+     *
+     * (both points defined is not point-to-line, is point-to-point)
+     *
+     */
+
+    assert((!_A_defined || !_B_defined) && "ProcessorPolyline::sqDistPointToLine: at least one point must not be defined.");
+
+    Scalar AB_sq = (_B-_A).head<2>().squaredNorm();
+    Scalar AauxB_sq = (_B-_A_aux).head<2>().squaredNorm();
+    Scalar AAaux_sq = (_A_aux-_A).head<2>().squaredNorm();
+
+    // squared distance to line
+    // Case 1
+    if (!_B_defined && !_A_defined)
+    {
+        if (AB_sq <= AauxB_sq + AAaux_sq)
+            return AB_sq - std::pow(((_A_aux-_A).head<2>()).dot((_B-_A).head<2>()),2) / AAaux_sq;
+        else
+            return (strict ? 1e12 : AauxB_sq);
+    }
+
+    // Case 2
+    if (!_B_defined && _A_defined)
+    {
+        if (AB_sq <= AauxB_sq + AAaux_sq && AauxB_sq <= AB_sq + AAaux_sq)
+            return AB_sq - std::pow(((_A_aux-_A).head<2>()).dot((_B-_A).head<2>()),2) / AAaux_sq;
+        else
+            return (strict ? 1e12 : std::min(AauxB_sq,AB_sq));
+    }
+
+    // Case 3
+    if (_B_defined && !_A_defined)
+    {
+        if (AauxB_sq >= AB_sq + AAaux_sq)
+            return AB_sq - std::pow(((_A_aux-_A).head<2>()).dot((_B-_A).head<2>()),2) / AAaux_sq;
+        else
+            return (strict ? 1e12 : AB_sq);
+    }
+
+    // Default return unused
+    return 1e12;
+}
+
+LandmarkMatchPolyline2DPtr computeBestSqDist(const Eigen::MatrixXs& _points_A, bool _first_defined_A, bool _last_defined_A, bool _closed_A,
+                                             const Eigen::MatrixXs& _points_B, bool _first_defined_B, bool _last_defined_B, bool _closed_B,
+                                             const Scalar& max_sq_error)
+{
+    //std::cout << "computeBestSqDist...\n";
+    //std::cout << "\tA is " << (_closed_A ? "CLOSED" : "OPEN") << " has "<< _points_A.cols() << " points\n";
+    //std::cout << "\tB is " << (_closed_B ? "CLOSED" : "OPEN") << " has "<< _points_B.cols() << " points\n";
+    //std::cout << "\tpoints_A:\n" << _points_A.topRows(2) << std::endl;
+    //std::cout << "\tpoints_B:\n" << _points_B.topRows(2) << std::endl;
+
+    // ASSERTIONS if closed -> all points defined
+    assert((_closed_A &&_first_defined_A && _last_defined_A) || !_closed_A);
+    assert((_closed_B &&_first_defined_B && _last_defined_B) || !_closed_B);
+
+    /* ASSUMPTIONS:
+     *
+     *      IF BOTH NOT CLOSED:
+     *          (a) - A has equal or more points than B         --> Otherwise: Call the function switching A<->B
+     *
+     *      IF ONE CLOSED:
+     *          (b) - Should be A closed (not B)                --> Otherwise: Call the function switching A<->B
+     *          (c) - B.defined_points <= A.(defined_)points    --> Otherwise: No matching
+     *
+     *      IF BOTH CLOSED:
+     *          (d) - B.(defined_)points == A.(defined_)points  --> Otherwise: No matching
+     */
+
+    LandmarkMatchPolyline2DPtr best_match = nullptr;
+
+    // ----------------- Call switching A<->B
+    if ( (!_closed_A && !_closed_B && _points_A.cols() < _points_B.cols()) || // (a)
+         (!_closed_A &&  _closed_B) )                                         // (b)
+    {
+        //std::cout << "Auto-calling switching A<->B...\n";
+
+        LandmarkMatchPolyline2DPtr best_match = computeBestSqDist(_points_B, _first_defined_B, _last_defined_B, _closed_B,
+                                                                  _points_A, _first_defined_A, _last_defined_A, _closed_A, max_sq_error);
+        // undo switching
+        if (best_match != nullptr)
+        {
+            std::swap(best_match->feature_from_id_, best_match->landmark_from_id_);
+            std::swap(best_match->feature_to_id_,   best_match->landmark_to_id_);
+        }
+        return best_match;
+    }
+
+    // ------------------ No matching (c) & (d)
+    int N_defined_B = _points_B.cols() - (_first_defined_B ? 0 : 1) - (_last_defined_B ? 0 : 1);
+
+    if ((_closed_A && !_closed_B && _points_A.cols() < N_defined_B) ||     // (c)
+        (_closed_A &&  _closed_B && _points_A.cols() != _points_B.cols())) // (d)
+        return nullptr;
+
+    /* ------------------ Normal function call:
+     * Search along all possible overlapping configurations between A&B points
+     *      We define offset as the correspondence (if exists) of the first point_A to B's.
+     *      Offset is within [min_offset, max_offset], we distinguish between 3 cases:
+     *          1. None closed:  [-last_A, last_B]
+     *          2. A closed:     [-last_A, 0]
+     */
+
+    int last_A, last_B, offset, max_offset, min_offset, from_A, to_A, from_B, to_B, N_overlapped;
+    last_A = _points_A.cols() - 1;
+    last_B = _points_B.cols() - 1;
+
+    if (!_closed_A)
+    {
+        min_offset = -last_A;
+        max_offset = last_B;
+    }
+    else
+    {
+        min_offset = -last_A;
+        max_offset = 0;
+    }
+
+    // Check all overlapping configurations
+    for (offset = min_offset; offset <= max_offset; offset++)
+    {
+        from_A = std::max(0,-offset);
+        from_B = std::max(0, offset);
+
+        if (!_closed_A && !_closed_B)
+            N_overlapped = std::min(last_B - from_B, last_A - from_A)+1;
+        else if(_closed_A)
+            N_overlapped = _points_B.cols();
+        else
+            std::runtime_error("Impossible state. Function should auto-called reversing A&B");
+
+        to_A = from_A+N_overlapped-1;
+        to_B = from_B+N_overlapped-1;
+
+        while (to_A > last_A && _closed_A)
+            to_A -= _points_A.cols();
+
+        //std::cout << "\toffset " << offset << std::endl;
+        //std::cout << "\t\tfrom_A " << from_A << std::endl;
+        //std::cout << "\t\tto_A   " << to_A << std::endl;
+        //std::cout << "\t\tfrom_B " << from_B << std::endl;
+        //std::cout << "\t\tto_B   " << to_B << std::endl;
+        //std::cout << "\t\tlast_A  " << last_A << std::endl;
+        //std::cout << "\t\tlast_B  " << last_B << std::endl;
+        //std::cout << "\t\tN_overlapped " << N_overlapped << std::endl;
+
+        bool from_A_defined = ((from_A != 0 && from_A != last_A) || _first_defined_A);
+        bool from_B_defined = ((from_B != 0 && from_B != last_B) || _first_defined_B);
+        bool to_A_defined   = ((to_A   != 0 && to_A   != last_A) || _last_defined_A);
+        bool to_B_defined   = ((to_B   != 0 && to_B   != last_B) || _last_defined_B);
+
+        //std::cout << "\t\tfrom_A_defined " << from_A_defined << (from_A == 0) << (from_A == last_A) << _first_defined_A << std::endl;
+        //std::cout << "\t\tfrom_B_defined " << from_B_defined << (from_B == 0) << (from_B == last_B) << _first_defined_B << std::endl;
+        //std::cout << "\t\tto_A_defined   " << to_A_defined   << (to_A == 0)   << (to_A == last_A)   << _last_defined_A << std::endl;
+        //std::cout << "\t\tto_B_defined   " << to_B_defined   << (to_B == 0)   << (to_B == last_B)   << _last_defined_B << std::endl;
+
+        // If only one overlapped point, both should be defined
+        if ( N_overlapped== 1 &&
+             (!from_A_defined || !from_B_defined || !to_A_defined || !to_B_defined ) )
+            continue;
+
+        assert(N_overlapped <= _points_B.cols());
+        assert(N_overlapped <= _points_A.cols() || (_closed_A && N_defined_B < _points_B.cols())); // N_overlapped can be > _points_A if all defined points of B match with A points and the not defined extremes should match too
+        assert(from_A >= 0);
+        assert(from_B >= 0);
+        assert(from_B == 0 || !_closed_A);
+        assert(to_B == _points_B.cols()-1 || !_closed_A);
+        assert(to_A <= last_A);
+        assert(to_B <= last_B);
+        assert(to_A < _points_A.cols());
+        assert(to_B < _points_B.cols());
+
+        // POINT-POINT DISTANCES for all overlapped points (in case of closed A in two parts)
+        // Fill with B points
+        Eigen::ArrayXXd d(_points_B.block(0,from_B, 2, N_overlapped).array());
+
+        // Substract A points
+        if (to_A >= from_A && N_overlapped <= _points_A.cols())
+            d -= _points_A.block(0,from_A, 2, N_overlapped).array();
+        else
+        {
+            d.leftCols(_points_A.cols()-from_A) -= _points_A.block(0,from_A, 2, _points_A.cols()-from_A).array();
+            d.rightCols(to_A+1)                 -= _points_A.block(0, 0, 2, to_A+1).array();
+        }
+        Eigen::ArrayXd dist2 = d.row(0).pow(2) + d.row(1).pow(2);
+
+        // CORRECT POINT-LINE DISTANCES for not defined extremes
+        // First
+        if (!from_B_defined || !from_A_defined)
+        {
+            // take care of closed A: next of from_A
+            int next_from_A = (from_A+1 > last_A ? 0 : from_A+1);
+            //std::cout << "\t\t\tFirst feature not defined distance to line" << std::endl;
+
+            // std::cout << "\t\t\tA   " << _points_A.col(from_A).transpose() << std::endl;
+            // std::cout << "\t\t\tAaux" << _points_A.col(next_from_A).transpose() << std::endl;
+            // std::cout << "\t\t\tB   " << _points_B.col(from_B).transpose() << std::endl;
+            dist2(0) = sqDistPointToLine(_points_A.col(from_A),
+                                         _points_A.col(next_from_A),
+                                         _points_B.col(from_B),
+                                         from_A_defined,
+                                         from_B_defined);
+            assert(N_overlapped > 1);
+        }
+        // Last
+        if (!to_B_defined || !to_A_defined)
+        {
+            // take care of closed A: next of to_A
+            int prev_to_A = (to_A == 0 ? last_A : to_A-1);
+            //std::cout << "\t\t\tLast feature not defined distance to line" << std::endl;
+
+            // std::cout << "\t\t\tA   " << _points_A.col(to_A).transpose() << std::endl;
+            // std::cout << "\t\t\tAaux" << _points_A.col(prev_to_A).transpose() << std::endl;
+            // std::cout << "\t\t\tB   " << _points_B.col(to_B).transpose() << std::endl;
+            dist2(N_overlapped-1) = sqDistPointToLine(_points_A.col(to_A),
+                                                      _points_A.col(prev_to_A),
+                                                      _points_B.col(to_B),
+                                                      to_A_defined,
+                                                      to_B_defined);
+            assert(N_overlapped > 1);
+        }
+        //std::cout << "\t\tsquared distances = " << dist2.transpose() << std::endl;
+
+        // All squared distances should be within a threshold
+        // Choose the most overlapped one
+        if ((dist2 < max_sq_error).all() &&
+            (best_match == nullptr ||
+             (N_overlapped >= best_match->feature_to_id_-best_match->feature_from_id_+1 &&
+              dist2.mean() < best_match->normalized_score_ )))
+        {
+            //std::cout << "\tBEST MATCH" << std::endl;
+            if (best_match == nullptr)
+                best_match = std::make_shared<LandmarkMatchPolyline2D>();
+
+            best_match->feature_from_id_= from_B;
+            best_match->feature_to_id_= to_B;
+            best_match->landmark_ptr_= nullptr;
+            best_match->normalized_score_ = dist2.mean();
+            best_match->landmark_from_id_= from_A;
+            best_match->landmark_to_id_= to_A;
+        }
+    }
+
+    return best_match;
+}
+
+bool isProjectedOverSegment(const Eigen::Vector3s& _A, const Eigen::Vector3s& _A_aux, const Eigen::Vector3s& _B)
+{
+    /* The orthogonal projection of B over the line A-Aaux is in the segment [A,Aaux].
+     *
+     *      Check: the angles BAAaux and BAauxA are <= 90º:
+     *          (BAaux)² <= (BA)² + (AAaux)² & (BA)² <= (BAaux)² + (AAaux)²
+     *
+     */
+
+    Scalar AB_sq = (_B-_A).head<2>().squaredNorm();
+    Scalar AauxB_sq = (_B-_A_aux).head<2>().squaredNorm();
+    Scalar AAaux_sq = (_A_aux-_A).head<2>().squaredNorm();
+
+    return (AauxB_sq <= AB_sq + AAaux_sq) && (AB_sq <= AauxB_sq + AAaux_sq);
+}
+
+Eigen::VectorXs computeSquaredMahalanobisDistances(const Eigen::Vector2s& _feature,
+                                                   const Eigen::Matrix2s& _feature_cov,
+                                                   const Eigen::Vector2s& _expected_feature,
+                                                   const Eigen::Matrix2s& _expected_feature_cov,
+                                                   const Eigen::MatrixXs& _mu)
+{
+    // ------------------------ d
+    Eigen::Vector2s d = _feature - _expected_feature;
+
+    // ------------------------ Sigma_d
+    Eigen::Matrix2s iSigma_d = (_feature_cov + _expected_feature_cov).inverse();
+    Eigen::VectorXs squared_mahalanobis_distances(_mu.cols());
+    for (unsigned int i = 0; i < _mu.cols(); i++)
+    {
+        squared_mahalanobis_distances(i) = (d - _mu.col(i)).transpose() * iSigma_d * (d - _mu.col(i));
+        //if ((d - _mu.col(i)).norm() < 1)
+        //{
+        //    std::cout << "distance:    " << (d - _mu.col(i)).norm() << std::endl;
+        //    std::cout << "iSigma_d:    " << std::endl << iSigma_d << std::endl;
+        //    std::cout << "mahalanobis: " << squared_mahalanobis_distances(i) << std::endl;
+        //}
+    }
+
+    return squared_mahalanobis_distances;
+}
+
+} // namespace wolf
+
+#endif /* POLYLINE_2D_UTILS_H_ */
diff --git a/include/base/processor/processor_polyline.h b/include/base/processor/processor_polyline.h
index ea13e37babf77294c3c8cc74fcf09cddee34002b..a9f05d94c223e84c8e5784c8a08b9b93fc9e5e08 100644
--- a/include/base/processor/processor_polyline.h
+++ b/include/base/processor/processor_polyline.h
@@ -27,47 +27,9 @@
 namespace wolf
 {
 
-//some consts.. TODO: this tuning params should be grouped in a struct and passed to the class from ros node, at constructor level
-const Scalar position_error_th_ = 1;
-const Scalar min_features_ratio_th_ = 0.5;
-
-WOLF_STRUCT_PTR_TYPEDEFS(LandmarkPolylineMatch);
 WOLF_STRUCT_PTR_TYPEDEFS(ProcessorParamsPolyline);
 WOLF_PTR_TYPEDEFS(ProcessorPolyline);
 
-// Match Feature - Landmark
-struct LandmarkPolylineMatch : public LandmarkMatch
-{
-        int landmark_match_from_id_;
-        int feature_match_from_id_;
-        int landmark_match_to_id_;
-        int feature_match_to_id_;
-        int landmark_version_;
-
-        LandmarkPolylineMatch() :
-            landmark_match_from_id_(0),
-            feature_match_from_id_(0),
-            landmark_match_to_id_(0),
-            feature_match_to_id_(0),
-            landmark_version_(0)
-        {
-            //
-        }
-        LandmarkPolylineMatch(int _landmark_match_from_id,
-                              int _feature_match_from_id,
-                              int _landmark_match_to_id,
-                              int _feature_match_to_id,
-                              int _landmark_version) :
-                                  landmark_match_from_id_(_landmark_match_from_id),
-                                  feature_match_from_id_(_feature_match_from_id),
-                                  landmark_match_to_id_(_landmark_match_to_id),
-                                  feature_match_to_id_(_landmark_match_to_id),
-                                  landmark_version_(_landmark_version)
-        {
-            //
-        }
-};
-
 struct ProcessorParamsPolyline : public ProcessorParamsBase
 {
         laserscanutils::LineFinderIterativeParams line_finder_params;
@@ -76,15 +38,10 @@ struct ProcessorParamsPolyline : public ProcessorParamsBase
         unsigned int new_features_th;
         Scalar loop_time_th;
         std::vector<PolylineRectangularClass> polyline_classes;
-
-        // These values below are constant and defined within the class -- provide a setter or accept them at construction time if you need to configure them
-        //        Scalar aperture_error_th_ = 20.0 * M_PI / 180.; //20 degrees
-        //        Scalar angular_error_th_ = 10.0 * M_PI / 180.; //10 degrees;
-        //        Scalar position_error_th_ = 1;
-        //        Scalar min_features_ratio_th_ = 0.5;
+        Scalar position_error_th_;
+        Scalar min_features_ratio_th_;
 };
 
-
 class ProcessorPolyline : public ProcessorBase
 {
     protected:
@@ -146,11 +103,8 @@ class ProcessorPolyline : public ProcessorBase
                                     const int& _lmk_point_id);
         bool rejectOutlier(ConstraintBasePtr& ctr_ptr);
         void classifyPolylineLandmarks(LandmarkBaseList& _lmk_list);
-        void tryMergeLandmarks(LandmarkPolyline2DList& _lmk_list) const;
-        LandmarkPolylineMatchPtr computeBestSqDist(const Eigen::MatrixXs& _points_A, bool _first_defined_A, bool _last_defined_A, bool _closed_A,
-                                                   const Eigen::MatrixXs& _points_B, bool _first_defined_B, bool _last_defined_B, bool _closed_B, bool print=false) const;
-        LandmarkPolylineMatchPtr tryMatch(const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr);
-        LandmarkPolylineMatchPtr tryMatch(const Eigen::MatrixXs& _lmk_expected_feature_points, const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr) const;
+        LandmarkMatchPolyline2DPtr tryMatch(const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr);
+        LandmarkMatchPolyline2DPtr tryMatch(const Eigen::MatrixXs& _lmk_expected_feature_points, const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr) const;
 
         // Gets/Sets
         const FeatureBaseList& getLastNewPolylines() const;
@@ -162,16 +116,6 @@ class ProcessorPolyline : public ProcessorBase
         void computeTransformations(const TimeStamp& _ts);
         void expectedFeature(LandmarkBasePtr _landmark_ptr, Eigen::MatrixXs& expected_feature_,
                              Eigen::MatrixXs& expected_feature_cov_);
-        Eigen::VectorXs computeSquaredMahalanobisDistances(const Eigen::Vector2s& _feature,
-                                                           const Eigen::Matrix2s& _feature_cov,
-                                                           const Eigen::Vector2s& _expected_feature,
-                                                           const Eigen::Matrix2s& _expected_feature_cov,
-                                                           const Eigen::MatrixXs& _mu);
-        Scalar sqDistPointToLine(const Eigen::Vector3s& _A, const Eigen::Vector3s& _A_aux, const Eigen::Vector3s& _B,
-                                 bool _A_extreme, bool _B_extreme, bool strict = false) const;
-
-        // The orthogonal projection of B over the line A-Aaux is in the segment [A,Aaux].
-        bool isProjectedOverSegment(const Eigen::Vector3s& _A, const Eigen::Vector3s& _A_aux, const Eigen::Vector3s& _B);
 
     // Factory method
     public:
diff --git a/include/base/processor/processor_tracker_feature_polyline.h b/include/base/processor/processor_tracker_feature_polyline.h
index a2823077ef8d883d0c37310dd4e0551a93641375..dbc701e3bef4257f9e05e537e64a0c0edc4e2500 100644
--- a/include/base/processor/processor_tracker_feature_polyline.h
+++ b/include/base/processor/processor_tracker_feature_polyline.h
@@ -8,12 +8,14 @@
 #ifndef PROCESSOR_TRACKER_FEATURE_POLYLINE_H_
 #define PROCESSOR_TRACKER_FEATURE_POLYLINE_H_
 
+#include "base/processor/polyline_2D_utils.h"
 #include "base/processor/processor_tracker_feature.h"
-#include "base/landmark/landmark_match.h"
 #include "base/landmark/landmark_polyline_2D.h"
+#include "base/landmark/landmark_match_polyline_2D.h"
 #include "base/sensor/sensor_laser_2D.h"
 #include "base/capture/capture_laser_2D.h"
 #include "base/feature/feature_polyline_2D.h"
+#include "base/feature/feature_match_polyline_2D.h"
 #include "base/constraint/constraint_point_2D.h"
 #include "base/constraint/constraint_point_to_line_2D.h"
 
@@ -26,7 +28,7 @@ namespace wolf
 {
 
 WOLF_STRUCT_PTR_TYPEDEFS(ProcessorParamsTrackerFeaturePolyline);
-WOLF_STRUCT_PTR_TYPEDEFS(FeaturePolylineMatch);
+WOLF_STRUCT_PTR_TYPEDEFS(FeatureMatchPolyline2D);
 WOLF_PTR_TYPEDEFS(ProcessorTrackerFeaturePolyline);
 
 struct ProcessorParamsTrackerFeaturePolyline : public ProcessorParamsTrackerFeature
@@ -45,27 +47,6 @@ struct ProcessorParamsTrackerFeaturePolyline : public ProcessorParamsTrackerFeat
         Scalar min_features_ratio_th_;// = 0.5;
 };
 
-// Match Feature - Feature
-struct FeaturePolylineMatch : public FeatureMatch
-{
-    int feature_last_from_id_;
-    int feature_last_to_id_;
-    int feature_incoming_from_id_;
-    int feature_incoming_to_id_;
-};
-
-// Match Feature - Landmark
-WOLF_STRUCT_PTR_TYPEDEFS(LandmarkPolylineMatch);
-typedef std::map<FeatureBasePtr, LandmarkPolylineMatchPtr> LandmarkPolylineMatchMap;
-struct LandmarkPolylineMatch : public LandmarkMatch
-{
-    int landmark_from_id_;
-    int feature_from_id_;
-    int landmark_to_id_;
-    int feature_to_id_;
-    int landmark_version_;
-};
-
 class ProcessorTrackerFeaturePolyline : public ProcessorTrackerFeature
 {
     protected:
@@ -73,7 +54,7 @@ class ProcessorTrackerFeaturePolyline : public ProcessorTrackerFeature
         laserscanutils::LineFinderIterative line_finder_;
 
         FeatureBaseList all_features_incoming_, all_features_last_;
-        LandmarkPolylineMatchMap landmark_match_map_;
+        LandmarkMatchPolyline2DMap landmark_match_map_;
 
         Eigen::Matrix2s R_sensor_world_last_, R_world_sensor_last_;
         Eigen::Vector2s t_sensor_world_last_, t_world_sensor_last_;
@@ -90,6 +71,8 @@ class ProcessorTrackerFeaturePolyline : public ProcessorTrackerFeature
 
         virtual ~ProcessorTrackerFeaturePolyline();
 
+        virtual void configure(SensorBasePtr _sensor){};
+
     protected:
         /** \brief Track provided features from \b last to \b incoming
          * \param _features_last_in input list of features in \b last to track
@@ -183,16 +166,19 @@ class ProcessorTrackerFeaturePolyline : public ProcessorTrackerFeature
          */
         virtual void postProcess() override;
 
-        FeaturePolylineMatchPtr computeBestMatch(const FeaturePolyline2DPtr& _ftr_last, const FeaturePolyline2DPtr& _ftr_incoming) const;
+        FeatureMatchPolyline2DPtr computeBestMatch(const FeaturePolyline2DPtr& _ftr_last, const FeaturePolyline2DPtr& _ftr_incoming) const;
 
-        FeaturePolylineMatchConstPtr bestMatch(const FeaturePolylineMatchConstPtr& _match_A,
-                                               const FeaturePolylineMatchConstPtr& _match_B) const;
+        FeatureMatchPolyline2DPtr bestMatch(const FeatureMatchPolyline2DPtr& _match_A,
+                                            const FeatureMatchPolyline2DPtr& _match_B) const;
 
-        Scalar sqDistPointToLine(const Eigen::Vector3s& _A, const Eigen::Vector3s& _A_aux,
-                                 const Eigen::Vector3s& _B, bool _A_defined, bool _B_defined, bool strict) const
+        LandmarkMatchPolyline2DPtr tryMatchWithLandmark(const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr) const;
+
+        LandmarkMatchPolyline2DPtr tryMatchWithLandmark(const Eigen::MatrixXs& _lmk_expected_feature_points, const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr) const;
 
         void computeIncomingTransformations(const TimeStamp& _ts);
 
+        void expectedFeatureLast(LandmarkBasePtr _landmark_ptr, Eigen::MatrixXs& expected_feature_, Eigen::MatrixXs& expected_feature_cov_) const;
+
     public:
 
         /// @brief Factory method
diff --git a/src/landmark/landmark_polyline_2D.cpp b/src/landmark/landmark_polyline_2D.cpp
index e64510750858b82bd6ed04fc6ece3dce6c13ff60..3694b28eb19b735285fe5a17a60eeabcb42b80a3 100644
--- a/src/landmark/landmark_polyline_2D.cpp
+++ b/src/landmark/landmark_polyline_2D.cpp
@@ -421,36 +421,6 @@ bool LandmarkPolyline2D::tryClose(const Scalar& _dist_tol)
     return false;
 }
 
-Scalar LandmarkPolyline2D::sqDistPoint2Segment(const Eigen::Vector2s& p, const Eigen::Vector2s& a, const Eigen::Vector2s& b) const
-{
-    Scalar ap_sq = (p-a).squaredNorm();
-    Scalar bp_sq = (p-b).squaredNorm();
-    Scalar ab_sq = (b-a).squaredNorm();
-    Scalar sqDist;
-    //WOLF_DEBUG("a = ", a);
-    //WOLF_DEBUG("b = ", b);
-    //WOLF_DEBUG("p = ", p);
-    //WOLF_DEBUG("ap_sq = ", ap_sq);
-    //WOLF_DEBUG("bp_sq = ", bp_sq);
-    //WOLF_DEBUG("ab_sq = ", ab_sq);
-
-    // If the projection of p onto ab is inside the segment (acute triangle: all angles < 90º), return the sq distance point-line.
-    // Otherwise, return the sq distance to the closest segment point.
-    if (ap_sq <= bp_sq + ab_sq && bp_sq <= ap_sq + ab_sq) // acute triangle: applying pitagoras twice, angles ABP and PAB <= 90º
-    {
-        //WOLF_DEBUG("projection of P onto AB -> returning point-line");
-        sqDist = ap_sq - std::pow((b-a).dot(p-a),2) / ab_sq;
-    }
-    else
-    {
-        //WOLF_DEBUG("projection of P out of AB -> returning min point-point");
-        sqDist = std::min(bp_sq,ap_sq);
-    }
-
-    //WOLF_DEBUG("sq_dist = ", sqDist);
-    return sqDist;
-}
-
 void LandmarkPolyline2D::setClosed()
 {
     assert(getNDefinedPoints()  >= 2 && "closing a polyline with less than 2 defined points");
@@ -997,6 +967,79 @@ YAML::Node LandmarkPolyline2D::saveToYaml() const
     return node;
 }
 
+void tryMergeLandmarks(LandmarkPolyline2DList& _lmk_list, const Scalar& _dist_tol)
+{
+    std::cout << "LandmarkPolyline2D::tryMergeLandmarks\n";
+    assert(_lmk_list.size() >= 2);
+
+    // 2 by 2
+    LandmarkPolyline2DPtr remaining_lmk = _lmk_list.front();
+    _lmk_list.pop_front();
+    LandmarkPolyline2DPtr A_lmk, B_lmk, merged_lmk;
+    int remaining_from, remaining_to, merged_from, merged_to;
+
+    while (!_lmk_list.empty())
+    {
+        A_lmk = remaining_lmk;
+        B_lmk = _lmk_list.front();
+        _lmk_list.pop_front();
+
+        if (B_lmk->isRemoving())
+            continue;
+
+        std::cout << "Candidates to be merged: " << A_lmk->id() << " " << B_lmk->id() << "\n";
+
+        // check best correspondence
+        Eigen::MatrixXs points_A = A_lmk->computePointsGlobal();
+        Eigen::MatrixXs points_B = B_lmk->computePointsGlobal();
+        LandmarkMatchPolyline2DPtr match = computeBestSqDist(points_A, A_lmk->isFirstDefined(), A_lmk->isLastDefined(), A_lmk->isClosed(),
+                                                             points_B, B_lmk->isFirstDefined(), B_lmk->isLastDefined(), B_lmk->isClosed(), _dist_tol*_dist_tol);
+
+        // match
+        if ( match )
+        {
+            std::vector<int> valid_id_A = A_lmk->getValidIds();
+            std::vector<int> valid_id_B = B_lmk->getValidIds();
+
+            // decide which is the remaining landmark:
+            // If only one closed, this one remains.
+            // Otherwise (both closed or both not closed) the one with most constraints remains
+            if ((A_lmk->isClosed() && !B_lmk->isClosed()) ||
+                (A_lmk->isClosed() == B_lmk->isClosed() && A_lmk->getHits() > B_lmk->getHits()))
+            {
+                remaining_lmk = A_lmk;
+                merged_lmk    = B_lmk;
+                remaining_from = valid_id_A.at(match->landmark_from_id_);
+                remaining_to   = valid_id_A.at(match->landmark_to_id_);
+                merged_from    = valid_id_B.at(match->feature_from_id_);
+                merged_to      = valid_id_B.at(match->feature_to_id_);
+            }
+            else
+            {
+                remaining_lmk = B_lmk;
+                merged_lmk    = A_lmk;
+                remaining_from = valid_id_B.at(match->feature_from_id_);
+                remaining_to   = valid_id_B.at(match->feature_to_id_);
+                merged_from    = valid_id_A.at(match->landmark_from_id_);
+                merged_to      = valid_id_A.at(match->landmark_to_id_);
+            }
+
+            // merge 2 landmarks
+            remaining_lmk->mergeLandmark(merged_lmk, remaining_from, remaining_to, merged_from, merged_to);
+
+            // reset pointers
+            A_lmk.reset();
+            B_lmk.reset();
+            assert(merged_lmk->getHits() == 0);
+            std::cout << "merged\n";
+        }
+        // no match (remaining: most constraints)
+        else
+            remaining_lmk = (A_lmk->getHits() > B_lmk->getHits() ? A_lmk : B_lmk);
+    }
+    std::cout << "tryMergeLandmarks::done\n";
+}
+
 // Register landmark creator
 namespace
 {
diff --git a/src/processor/processor_polyline.cpp b/src/processor/processor_polyline.cpp
index 77345600678116afc24d37e8088d6926e2bd915b..890f7eccb812e599ccc09746d962a09ac3bd0fb8 100644
--- a/src/processor/processor_polyline.cpp
+++ b/src/processor/processor_polyline.cpp
@@ -207,7 +207,7 @@ void ProcessorPolyline::process(CaptureBasePtr _incoming_ptr)
             continue;
 
         // Merge landmarks candidates and accumulate the correspondence of merged to the remaining ones
-        tryMergeLandmarks(merge_candidates);
+        LandmarkPolyline2D::tryMergeLandmarks(merge_candidates, params_->position_error_th_);
     }
     //WOLF_DEBUG( "-----End of post-process():");
 }
@@ -292,7 +292,7 @@ unsigned int ProcessorPolyline::findLandmarks(const LandmarkBaseList& _landmarks
         }
 
     // NAIVE NEAREST NEIGHBOR MATCHING
-    LandmarkPolylineMatchPtr best_match = nullptr;
+    LandmarkMatchPolyline2DPtr best_match = nullptr;
     FeaturePolyline2DPtr pl_ftr;
     LandmarkPolyline2DPtr pl_lmk;
     LandmarkPolyline2DList merging_candidates;
@@ -315,12 +315,12 @@ unsigned int ProcessorPolyline::findLandmarks(const LandmarkBaseList& _landmarks
             if (print)
                 std::cout << "\tcompute Best Sq Dist of landmark " << pl_lmk->id() << " (" << pl_lmk->getNPoints()
                           << ") and feature " << pl_ftr->id() << "(" << pl_ftr->getNPoints() << ")\n";
-            LandmarkPolylineMatchPtr best_correspondence = tryMatch(expected_features[pl_lmk],pl_lmk,pl_ftr);
+            LandmarkMatchPolyline2DPtr best_correspondence = tryMatch(expected_features[pl_lmk],pl_lmk,pl_ftr);
 
             // best match
             if (best_correspondence != nullptr &&
                 (best_match == nullptr ||
-                        (best_correspondence->feature_match_to_id_-best_correspondence->feature_match_from_id_ >= best_match->feature_match_to_id_-best_match->feature_match_from_id_ &&
+                        (best_correspondence->feature_to_id_-best_correspondence->feature_from_id_ >= best_match->feature_to_id_-best_match->feature_from_id_ &&
                          best_correspondence->normalized_score_ < best_match->normalized_score_ ) ) )
             {
                 if (print)
@@ -345,8 +345,8 @@ unsigned int ProcessorPolyline::findLandmarks(const LandmarkBaseList& _landmarks
             // make a copy of current iterator
             auto it_found = std::prev(feature_it);
 
-            assert(best_match->feature_match_from_id_ == 0 || !std::static_pointer_cast<LandmarkPolyline2D>(best_match->landmark_ptr_)->isClosed());
-            assert(best_match->feature_match_to_id_ == std::static_pointer_cast<FeaturePolyline2D>(*it_found)->getNPoints()-1 || !std::static_pointer_cast<LandmarkPolyline2D>(best_match->landmark_ptr_)->isClosed());
+            assert(best_match->feature_from_id_ == 0 || !std::static_pointer_cast<LandmarkPolyline2D>(best_match->landmark_ptr_)->isClosed());
+            assert(best_match->feature_to_id_ == std::static_pointer_cast<FeaturePolyline2D>(*it_found)->getNPoints()-1 || !std::static_pointer_cast<LandmarkPolyline2D>(best_match->landmark_ptr_)->isClosed());
 
             bool print = false;//best_match->landmark_ptr_->id() == 13;
             if (print)
@@ -354,10 +354,10 @@ unsigned int ProcessorPolyline::findLandmarks(const LandmarkBaseList& _landmarks
                 std::cout << "\tlandmark        : " << best_match->landmark_ptr_->id() << std::endl;
                 std::cout << "\tlandmark version: " << best_match->landmark_version_ << std::endl;
                 std::cout << "\tfeature         : " << (*it_found)->id() << std::endl;
-                std::cout << "\tlandmark from id: " << best_match->landmark_match_from_id_ << std::endl;
-                std::cout << "\tlandmark to id  : " << best_match->landmark_match_to_id_ << std::endl;
-                std::cout << "\tfeature from id : " << best_match->feature_match_from_id_ << std::endl;
-                std::cout << "\tfeature to id   : " << best_match->feature_match_to_id_ << std::endl;
+                std::cout << "\tlandmark from id: " << best_match->landmark_from_id_ << std::endl;
+                std::cout << "\tlandmark to id  : " << best_match->landmark_to_id_ << std::endl;
+                std::cout << "\tfeature from id : " << best_match->feature_from_id_ << std::endl;
+                std::cout << "\tfeature to id   : " << best_match->feature_to_id_ << std::endl;
             }
             // match
             _feature_landmark_correspondences[*it_found] = best_match;
@@ -393,11 +393,11 @@ void ProcessorPolyline::createNewLandmarks()
         // cast
         polyline_ft_ptr = std::static_pointer_cast<FeaturePolyline2D>(new_feature_ptr);
         // create new correspondence
-        LandmarkPolylineMatchPtr match = std::make_shared<LandmarkPolylineMatch>();
-        match->feature_match_from_id_= 0; // all points match
-        match->landmark_match_from_id_ = 0;
-        match->feature_match_to_id_= polyline_ft_ptr->getNPoints()-1; // all points match
-        match->landmark_match_to_id_ = polyline_ft_ptr->getNPoints()-1;
+        LandmarkMatchPolyline2DPtr match = std::make_shared<LandmarkMatchPolyline2D>();
+        match->feature_from_id_= 0; // all points match
+        match->landmark_from_id_ = 0;
+        match->feature_to_id_= polyline_ft_ptr->getNPoints()-1; // all points match
+        match->landmark_to_id_ = polyline_ft_ptr->getNPoints()-1;
         match->normalized_score_ = 1.0; // max score
         match->landmark_ptr_ = new_lmk_ptr;
         match->landmark_version_ = 0;
@@ -409,10 +409,10 @@ void ProcessorPolyline::createNewLandmarks()
         new_landmarks_.push_back(new_lmk_ptr);
 
         // std::cout << "new match: feature " << new_feature_ptr->id() << " with landmark " << new_lmk_ptr->id() << std::endl;
-        // std::cout << "\tfeature_match_from_id_ = " << match->feature_match_from_id_ << std::endl;
-        // std::cout << "\tlandmark_match_from_id_ = " << match->landmark_match_from_id_ << std::endl;
-        // std::cout << "\tfeature_match_to_id_ = " << match->feature_match_to_id_ << std::endl;
-        // std::cout << "\tlandmark_match_to_id_ = " << match->landmark_match_to_id_ << std::endl;
+        // std::cout << "\tfeature_from_id_ = " << match->feature_from_id_ << std::endl;
+        // std::cout << "\tlandmark_from_id_ = " << match->landmark_from_id_ << std::endl;
+        // std::cout << "\tfeature_to_id_ = " << match->feature_to_id_ << std::endl;
+        // std::cout << "\tlandmark_to_id_ = " << match->landmark_to_id_ << std::endl;
         // std::cout << "\tnormalized_score_ = " << match->normalized_score_ << std::endl;
     }
     //std::cout << "done" << std::endl;
@@ -446,7 +446,7 @@ void ProcessorPolyline::establishConstraints()
     //std::cout << "ProcessorPolyline::establishConstraints" << std::endl;
     //std::cout << "\tlast_ id: " << last_ptr_->id() << " linked to frame: " << last_ptr_->getFramePtr()->id() << " is linked to problem? " << (last_ptr_->getProblem() ? "YES" : "NO") << std::endl;
     unsigned int N_constraints = 0;
-    LandmarkPolylineMatchPtr pl_match;
+    LandmarkMatchPolyline2DPtr pl_match;
     FeaturePolyline2DPtr     pl_ftr;
     LandmarkPolyline2DPtr    pl_lmk;
     LandmarkPolyline2DList   modified_lmks;
@@ -454,10 +454,10 @@ void ProcessorPolyline::establishConstraints()
     for (auto last_ftr : last_ptr_->getFeatureList())
     {
         pl_ftr = std::static_pointer_cast<FeaturePolyline2D>(last_ftr);
-        pl_match = std::static_pointer_cast<LandmarkPolylineMatch>(matches_landmark_from_last_[last_ftr]);
+        pl_match = std::static_pointer_cast<LandmarkMatchPolyline2D>(matches_landmark_from_last_[last_ftr]);
         pl_lmk = std::static_pointer_cast<LandmarkPolyline2D>(pl_match->landmark_ptr_);
 
-        assert(pl_match->feature_match_from_id_ >= 0 && pl_match->feature_match_to_id_ >= 0);
+        assert(pl_match->feature_from_id_ >= 0 && pl_match->feature_to_id_ >= 0);
         assert(pl_lmk != nullptr && pl_match != nullptr);
 
         if (pl_lmk->isRemoving())
@@ -469,11 +469,10 @@ void ProcessorPolyline::establishConstraints()
         if (pl_match->landmark_version_ != pl_lmk->getVersion() || pl_lmk->getMergedInLandmark() != nullptr)
         {
             // get the not merged lmk
-            while (pl_lmk->getMergedInLandmark() != nullptr)
-                pl_lmk = pl_lmk->getMergedInLandmark();
+            pl_lmk = pl_lmk->getMergedInLandmark();
 
             // redo matching
-            LandmarkPolylineMatchPtr updated_match = tryMatch(pl_lmk, pl_ftr);
+            LandmarkMatchPolyline2DPtr updated_match = tryMatch(pl_lmk, pl_ftr);
 
             // if still matching, overwrite match
             if (updated_match)
@@ -486,7 +485,7 @@ void ProcessorPolyline::establishConstraints()
                 continue;
             }
 
-            assert(pl_match->feature_match_from_id_ >= 0 && pl_match->feature_match_to_id_ >= 0);
+            assert(pl_match->feature_from_id_ >= 0 && pl_match->feature_to_id_ >= 0);
         }
 
         // MODIFY LANDMARK (only for not closed and not recently created)
@@ -503,10 +502,10 @@ void ProcessorPolyline::establishConstraints()
                 std::cout << "\tpoints " << pl_lmk->getNPoints() << std::endl;
                 std::cout << "\tfirst defined " << pl_lmk->isFirstDefined() << std::endl;
                 std::cout << "\tlast defined " << pl_lmk->isLastDefined() << std::endl << std::endl;
-                std::cout << "\tmatch from feature point " << pl_match->feature_match_from_id_ << std::endl;
-                std::cout << "\tmatch to feature point " << pl_match->feature_match_to_id_ << std::endl;
-                std::cout << "\tmatch from landmark point " << pl_match->landmark_match_from_id_ << std::endl;
-                std::cout << "\tmatch to landmark point " << pl_match->landmark_match_to_id_ << std::endl;
+                std::cout << "\tmatch from feature point " << pl_match->feature_from_id_ << std::endl;
+                std::cout << "\tmatch to feature point " << pl_match->feature_to_id_ << std::endl;
+                std::cout << "\tmatch from landmark point " << pl_match->landmark_from_id_ << std::endl;
+                std::cout << "\tmatch to landmark point " << pl_match->landmark_to_id_ << std::endl;
             }
             Eigen::MatrixXs points_global = R_world_sensor_ * pl_ftr->getPoints().topRows<2>() +
                                             t_world_sensor_ * Eigen::VectorXs::Ones(pl_ftr->getNPoints()).transpose();
@@ -514,36 +513,36 @@ void ProcessorPolyline::establishConstraints()
             // MODIFY LANDMARK
             // -----------------GROW Front-----------------
             // Add new front points (if any feature point not matched)
-            if ( pl_match->feature_match_from_id_ > 0 ) // && !pl_lmk->isClosed()
+            if ( pl_match->feature_from_id_ > 0 ) // && !pl_lmk->isClosed()
             {
                 assert(!pl_lmk->isClosed() && "feature has not matched points in a closed landmark");
                 if (print)
                 {
                     std::cout << "Add new front points. Defined: " << pl_ftr->isFirstDefined() << std::endl;
-                    std::cout << "\tfeat from " << pl_match->feature_match_from_id_ << std::endl;
-                    std::cout << "\tfeat to " << pl_match->feature_match_to_id_ << std::endl;
-                    std::cout << "\tland from " << pl_match->landmark_match_from_id_ << std::endl;
-                    std::cout << "\tland to " << pl_match->landmark_match_to_id_ << std::endl;
+                    std::cout << "\tfeat from " << pl_match->feature_from_id_ << std::endl;
+                    std::cout << "\tfeat to " << pl_match->feature_to_id_ << std::endl;
+                    std::cout << "\tland from " << pl_match->landmark_from_id_ << std::endl;
+                    std::cout << "\tland to " << pl_match->landmark_to_id_ << std::endl;
                 }
                 pl_lmk->addPoints(points_global, // points matrix in global coordinates
-                                  pl_match->feature_match_from_id_-1, // last feature point index to be added
+                                  pl_match->feature_from_id_-1, // last feature point index to be added
                                   pl_ftr->isFirstDefined(), // is defined
                                   false); // front
 
                 modified_lmks.push_back(pl_lmk);
 
-                pl_match->landmark_match_from_id_ = pl_lmk->getFirstId();
-                pl_match->feature_match_from_id_ = 0;
-                //std::cout << "\tfeat from " << pl_match->feature_match_from_id_ << std::endl;
-                //std::cout << "\tfeat to " << pl_match->feature_match_to_id_ << std::endl;
-                //std::cout << "\tland from " << pl_match->landmark_match_from_id_ << std::endl;
-                //std::cout << "\tland to " << pl_match->landmark_match_to_id_ << std::endl;
+                pl_match->landmark_from_id_ = pl_lmk->getFirstId();
+                pl_match->feature_from_id_ = 0;
+                //std::cout << "\tfeat from " << pl_match->feature_from_id_ << std::endl;
+                //std::cout << "\tfeat to " << pl_match->feature_to_id_ << std::endl;
+                //std::cout << "\tland from " << pl_match->landmark_from_id_ << std::endl;
+                //std::cout << "\tland to " << pl_match->landmark_to_id_ << std::endl;
             }
             // -----------------CHANGE Front-----------------
             // Change first not defined point if landmark first not defined matched with first feature point that:
             // a. is defined
             // b. would extend the line (check if angle is bigger than 90º)
-            else if (pl_match->landmark_match_from_id_ == pl_lmk->getFirstId() && !pl_lmk->isFirstDefined()) // && pl_match->feature_match_from_id_ == 0
+            else if (pl_match->landmark_from_id_ == pl_lmk->getFirstId() && !pl_lmk->isFirstDefined()) // && pl_match->feature_from_id_ == 0
             {
                 if (print)
                 {
@@ -563,39 +562,39 @@ void ProcessorPolyline::establishConstraints()
                     modified_lmks.push_back(pl_lmk);
                 }
             }
-            assert(pl_match->feature_match_from_id_ == 0 && "Landmark didn't grow properly");
+            assert(pl_match->feature_from_id_ == 0 && "Landmark didn't grow properly");
             // -----------------GROW Back-----------------
             // Add new back points (if any feature point not matched)
-            if (pl_match->feature_match_to_id_ < pl_ftr->getNPoints()-1)
+            if (pl_match->feature_to_id_ < pl_ftr->getNPoints()-1)
             {
                 assert(!pl_lmk->isClosed() && "feature not matched points in a closed landmark");
                 if (print)
                 {
                     std::cout << "Add back points. Defined: " << pl_ftr->isLastDefined() << std::endl;
-                    std::cout << "\tfeat from " << pl_match->feature_match_from_id_ << std::endl;
-                    std::cout << "\tfeat to " << pl_match->feature_match_to_id_ << std::endl;
-                    std::cout << "\tland from " << pl_match->landmark_match_from_id_ << std::endl;
-                    std::cout << "\tland to " << pl_match->landmark_match_to_id_ << std::endl;
+                    std::cout << "\tfeat from " << pl_match->feature_from_id_ << std::endl;
+                    std::cout << "\tfeat to " << pl_match->feature_to_id_ << std::endl;
+                    std::cout << "\tland from " << pl_match->landmark_from_id_ << std::endl;
+                    std::cout << "\tland to " << pl_match->landmark_to_id_ << std::endl;
                 }
                 pl_lmk->addPoints(points_global, // points matrix in global coordinates
-                                  pl_match->feature_match_to_id_+1, // last feature point index to be added
+                                  pl_match->feature_to_id_+1, // last feature point index to be added
                                   pl_ftr->isLastDefined(), // is defined
                                   true); // back
 
                 modified_lmks.push_back(pl_lmk);
 
-                pl_match->landmark_match_to_id_ = pl_lmk->getLastId();
-                pl_match->feature_match_to_id_ = pl_ftr->getNPoints()-1;
-                //std::cout << "\tfeat from " << pl_match->feature_match_from_id_ << std::endl;
-                //std::cout << "\tfeat to " << pl_match->feature_match_to_id_ << std::endl;
-                //std::cout << "\tland from " << pl_match->landmark_match_from_id_ << std::endl;
-                //std::cout << "\tland to " << pl_match->landmark_match_to_id_ << std::endl;
+                pl_match->landmark_to_id_ = pl_lmk->getLastId();
+                pl_match->feature_to_id_ = pl_ftr->getNPoints()-1;
+                //std::cout << "\tfeat from " << pl_match->feature_from_id_ << std::endl;
+                //std::cout << "\tfeat to " << pl_match->feature_to_id_ << std::endl;
+                //std::cout << "\tland from " << pl_match->landmark_from_id_ << std::endl;
+                //std::cout << "\tland to " << pl_match->landmark_to_id_ << std::endl;
             }
             // -----------------CHANGE Back-----------------
             // Change last not defined point if landmark last not defined matched with last feature point that:
             // a. is defined
             // b. would extend the line (check if angle is bigger than 90º)
-            else if (pl_match->landmark_match_to_id_ == pl_lmk->getLastId() && !pl_lmk->isLastDefined()) //&& pl_match->feature_match_to_id_ == pl_ftr->getNPoints()-1
+            else if (pl_match->landmark_to_id_ == pl_lmk->getLastId() && !pl_lmk->isLastDefined()) //&& pl_match->feature_to_id_ == pl_ftr->getNPoints()-1
             {
                 if (print)
                 {
@@ -615,7 +614,7 @@ void ProcessorPolyline::establishConstraints()
                     modified_lmks.push_back(pl_lmk);
                 }
             }
-            assert(pl_match->feature_match_to_id_ == pl_ftr->getNPoints()-1 && "Landmark didn't grow properly");
+            assert(pl_match->feature_to_id_ == pl_ftr->getNPoints()-1 && "Landmark didn't grow properly");
 
             if (print)
             {
@@ -636,30 +635,30 @@ void ProcessorPolyline::establishConstraints()
             {
                 // add recently created landmarks to the list of modified landmark
                 modified_lmks.push_back(pl_lmk);
-                assert(pl_match->feature_match_from_id_ == 0 && "recently created landmark with not full feature match");
-                assert(pl_match->feature_match_to_id_ == pl_ftr->getNPoints()-1 && "recently created landmark with not full feature match");
+                assert(pl_match->feature_from_id_ == 0 && "recently created landmark with not full feature match");
+                assert(pl_match->feature_to_id_ == pl_ftr->getNPoints()-1 && "recently created landmark with not full feature match");
             }
             if (pl_lmk->isClosed())
             {
-                assert(pl_match->feature_match_from_id_ == 0 && "closed landmark with not full feature match");
-                assert(pl_match->feature_match_to_id_ == pl_ftr->getNPoints()-1 && "closed landmark with not full feature match");
+                assert(pl_match->feature_from_id_ == 0 && "closed landmark with not full feature match");
+                assert(pl_match->feature_to_id_ == pl_ftr->getNPoints()-1 && "closed landmark with not full feature match");
             }
         }
 
         // ESTABLISH CONSTRAINTS ------------------------------------------------------------------------
-        assert(pl_match->feature_match_from_id_ == 0 && "Landmark didn't grow properly");
-        assert(pl_match->feature_match_to_id_ == pl_ftr->getNPoints()-1 && "Landmark didn't grow properly");
-        assert((pl_lmk->getNPoints() >= pl_ftr->getNPoints() + pl_match->landmark_match_from_id_ || pl_lmk->isClosed()) && "Landmark didn't grow properly");
+        assert(pl_match->feature_from_id_ == 0 && "Landmark didn't grow properly");
+        assert(pl_match->feature_to_id_ == pl_ftr->getNPoints()-1 && "Landmark didn't grow properly");
+        assert((pl_lmk->getNPoints() >= pl_ftr->getNPoints() + pl_match->landmark_from_id_ || pl_lmk->isClosed()) && "Landmark didn't grow properly");
         //std::cout << "Establishing constraint between" << std::endl;
         //std::cout << "\tfeature " << pl_ftr->id() << " is liked to capture? " << (pl_ftr->getCapturePtr() ? pl_ftr->getCapturePtr()->id() : 9999999999999999) << " is liked to problem? " << (pl_ftr->getProblem() ? "YES" : "NO") << std::endl;
         //std::cout << "\tlandmark " << pl_lmk->id() << std::endl;
-        //std::cout << "\tmatch from feature point " << pl_match->feature_match_from_id_ << std::endl;
-        //std::cout << "\tmatch to feature point " << pl_match->feature_match_to_id_ << std::endl;
-        //std::cout << "\tmatch from landmark point " << pl_match->landmark_match_from_id_ << std::endl;
-        //std::cout << "\tmatch to landmark point " << pl_match->landmark_match_to_id_ << std::endl;
+        //std::cout << "\tmatch from feature point " << pl_match->feature_from_id_ << std::endl;
+        //std::cout << "\tmatch to feature point " << pl_match->feature_to_id_ << std::endl;
+        //std::cout << "\tmatch from landmark point " << pl_match->landmark_from_id_ << std::endl;
+        //std::cout << "\tmatch to landmark point " << pl_match->landmark_to_id_ << std::endl;
 
         // Constraints for all feature points
-        int lmk_point_id = pl_match->landmark_match_from_id_;
+        int lmk_point_id = pl_match->landmark_from_id_;
 
         for (int ftr_point_id = 0; ftr_point_id < pl_ftr->getNPoints(); ftr_point_id++)
         {
@@ -751,305 +750,9 @@ void ProcessorPolyline::classifyPolylineLandmarks(LandmarkBaseList& _lmk_list)
     //std::cout << "ProcessorPolyline::classifyPolylineLandmarks: done\n";
 }
 
-void ProcessorPolyline::tryMergeLandmarks(LandmarkPolyline2DList& _lmk_list) const
-{
-    std::cout << "ProcessorPolyline::tryMergeLandmarks\n";
-    assert(_lmk_list.size() >= 2);
-
-    // 2 by 2
-    LandmarkPolyline2DPtr remaining_lmk = _lmk_list.front();
-    _lmk_list.pop_front();
-    LandmarkPolyline2DPtr A_lmk, B_lmk, merged_lmk;
-    int remaining_from, remaining_to, merged_from, merged_to;
-
-    while (!_lmk_list.empty())
-    {
-        A_lmk = remaining_lmk;
-        B_lmk = _lmk_list.front();
-        _lmk_list.pop_front();
-
-        if (B_lmk->isRemoving())
-            continue;
-
-        std::cout << "Candidates to be merged: " << A_lmk->id() << " " << B_lmk->id() << "\n";
-
-        // if any closed, it should be A
-        if (!A_lmk->isClosed() && B_lmk->isClosed())
-        {
-            A_lmk = B_lmk;
-            B_lmk = remaining_lmk;
-        }
-
-        // check best correspondence
-        Eigen::MatrixXs points_A = A_lmk->computePointsGlobal();
-        Eigen::MatrixXs points_B = B_lmk->computePointsGlobal();
-        LandmarkPolylineMatchPtr match = computeBestSqDist(points_A, A_lmk->isFirstDefined(), A_lmk->isLastDefined(), A_lmk->isClosed(),
-                                                           points_B, B_lmk->isFirstDefined(), B_lmk->isLastDefined(), B_lmk->isClosed(), false);
-
-        // match
-        if ( match )
-        {
-            std::vector<int> valid_id_A = A_lmk->getValidIds();
-            std::vector<int> valid_id_B = B_lmk->getValidIds();
-
-            // decide which is the remaining landmark:
-            // If only one closed, this one remains.
-            // Otherwise (both closed or both not closed) the one with most constraints remains
-            if ((A_lmk->isClosed() && !B_lmk->isClosed()) ||
-                (A_lmk->isClosed() == B_lmk->isClosed() && A_lmk->getHits() > B_lmk->getHits()))
-            {
-                remaining_lmk = A_lmk;
-                merged_lmk    = B_lmk;
-                remaining_from = valid_id_A.at(match->landmark_match_from_id_);
-                remaining_to   = valid_id_A.at(match->landmark_match_to_id_);
-                merged_from    = valid_id_B.at(match->feature_match_from_id_);
-                merged_to      = valid_id_B.at(match->feature_match_to_id_);
-            }
-            else
-            {
-                remaining_lmk = B_lmk;
-                merged_lmk    = A_lmk;
-                remaining_from = valid_id_B.at(match->feature_match_from_id_);
-                remaining_to   = valid_id_B.at(match->feature_match_to_id_);
-                merged_from    = valid_id_A.at(match->landmark_match_from_id_);
-                merged_to      = valid_id_A.at(match->landmark_match_to_id_);
-            }
-
-            // merge 2 landmarks
-            remaining_lmk->mergeLandmark(merged_lmk, remaining_from, remaining_to, merged_from, merged_to);
 
-            // reset pointers
-            A_lmk.reset();
-            B_lmk.reset();
-            assert(merged_lmk->getHits() == 0);
-            assert(std::find(getProblem()->getMapPtr()->getLandmarkList().begin(), getProblem()->getMapPtr()->getLandmarkList().end(), merged_lmk) == getProblem()->getMapPtr()->getLandmarkList().end());
-            std::cout << "merged\n";
-        }
-        // no match (remaining: most constraints)
-        else
-            remaining_lmk = (A_lmk->getHits() > B_lmk->getHits() ? A_lmk : B_lmk);
-    }
-    std::cout << "tryMergeLandmarks::done\n";
-}
 
-LandmarkPolylineMatchPtr ProcessorPolyline::computeBestSqDist(const Eigen::MatrixXs& _points_A, bool _first_defined_A, bool _last_defined_A, bool _closed_A,
-                                                              const Eigen::MatrixXs& _points_B, bool _first_defined_B, bool _last_defined_B, bool _closed_B,
-                                                              bool print) const
-{
-    if (print)
-    {
-        std::cout << "ProcessorPolyline::computeBestSqDist...\n";
-        std::cout << "\tA is " << (_closed_A ? "CLOSED" : "OPEN") << " has "<< _points_A.cols() << " points\n";
-        std::cout << "\tB is " << (_closed_B ? "CLOSED" : "OPEN") << " has "<< _points_B.cols() << " points\n";
-        //std::cout << "\tpoints_A:\n" << _points_A.topRows(2) << std::endl;
-        //std::cout << "\tpoints_B:\n" << _points_B.topRows(2) << std::endl;
-    }
-
-    // ASSERTIONS if closed -> all points defined
-    assert((_closed_A &&_first_defined_A && _last_defined_A) || !_closed_A);
-    assert((_closed_B &&_first_defined_B && _last_defined_B) || !_closed_B);
-
-    /* ASSUMPTIONS:
-     *
-     *      IF BOTH NOT CLOSED:
-     *          (a) - A has equal or more points than B         --> Otherwise: Call the function switching A<->B
-     *
-     *      IF ONE CLOSED:
-     *          (b) - Should be A closed (not B)                --> Otherwise: Call the function switching A<->B
-     *          (c) - B.defined_points <= A.(defined_)points    --> Otherwise: No matching
-     *
-     *      IF BOTH CLOSED:
-     *          (d) - B.(defined_)points == A.(defined_)points  --> Otherwise: No matching
-     */
-
-    LandmarkPolylineMatchPtr best_match = nullptr;
-
-    // ----------------- Call switching A<->B
-    if ( (!_closed_A && !_closed_B && _points_A.cols() < _points_B.cols()) || // (a)
-         (!_closed_A &&  _closed_B) )                                         // (b)
-    {
-        if (print)
-            std::cout << "Auto-calling switching A<->B...\n";
-
-        LandmarkPolylineMatchPtr best_match = computeBestSqDist(_points_B, _first_defined_B, _last_defined_B, _closed_B,
-                                                                _points_A, _first_defined_A, _last_defined_A, _closed_A, print);
-        // undo switching
-        if (best_match != nullptr)
-        {
-            std::swap(best_match->feature_match_from_id_, best_match->landmark_match_from_id_);
-            std::swap(best_match->feature_match_to_id_,   best_match->landmark_match_to_id_);
-        }
-        return best_match;
-    }
-
-    // ------------------ No matching (c) & (d)
-    int N_defined_B = _points_B.cols() - (_first_defined_B ? 0 : 1) - (_last_defined_B ? 0 : 1);
-
-    if ((_closed_A && !_closed_B && _points_A.cols() < N_defined_B) ||     // (c)
-        (_closed_A &&  _closed_B && _points_A.cols() != _points_B.cols())) // (d)
-        return nullptr;
-
-    /* ------------------ Normal function call:
-     * Search along all possible overlapping configurations between A&B points
-     *      We define offset as the correspondence (if exists) of the first point_A to B's.
-     *      Offset is within [min_offset, max_offset], we distinguish between 3 cases:
-     *          1. None closed:  [-last_A, last_B]
-     *          2. A closed:     [-last_A, 0]
-     */
-
-    int last_A, last_B, offset, max_offset, min_offset, from_A, to_A, from_B, to_B, N_overlapped;
-    last_A = _points_A.cols() - 1;
-    last_B = _points_B.cols() - 1;
-
-    if (!_closed_A)
-    {
-        min_offset = -last_A;
-        max_offset = last_B;
-    }
-    else
-    {
-        min_offset = -last_A;
-        max_offset = 0;
-    }
-
-    // Check all overlapping configurations
-    for (offset = min_offset; offset <= max_offset; offset++)
-    {
-        from_A = std::max(0,-offset);
-        from_B = std::max(0, offset);
-
-        if (!_closed_A && !_closed_B)
-            N_overlapped = std::min(last_B - from_B, last_A - from_A)+1;
-        else if(_closed_A)
-            N_overlapped = _points_B.cols();
-        else
-            std::runtime_error("Impossible state. Function should auto-called reversing A&B");
-
-        to_A = from_A+N_overlapped-1;
-        to_B = from_B+N_overlapped-1;
-
-        while (to_A > last_A && _closed_A)
-            to_A -= _points_A.cols();
-
-        if (print)
-        {
-            std::cout << "\toffset " << offset << std::endl;
-            std::cout << "\t\tfrom_A " << from_A << std::endl;
-            std::cout << "\t\tto_A   " << to_A << std::endl;
-            std::cout << "\t\tfrom_B " << from_B << std::endl;
-            std::cout << "\t\tto_B   " << to_B << std::endl;
-            std::cout << "\t\tlast_A  " << last_A << std::endl;
-            std::cout << "\t\tlast_B  " << last_B << std::endl;
-            std::cout << "\t\tN_overlapped " << N_overlapped << std::endl;
-        }
-
-        bool from_A_defined = ((from_A != 0 && from_A != last_A) || _first_defined_A);
-        bool from_B_defined = ((from_B != 0 && from_B != last_B) || _first_defined_B);
-        bool to_A_defined   = ((to_A   != 0 && to_A   != last_A) || _last_defined_A);
-        bool to_B_defined   = ((to_B   != 0 && to_B   != last_B) || _last_defined_B);
-        if (print)
-        {
-            std::cout << "\t\tfrom_A_defined " << from_A_defined << (from_A == 0) << (from_A == last_A) << _first_defined_A << std::endl;
-            std::cout << "\t\tfrom_B_defined " << from_B_defined << (from_B == 0) << (from_B == last_B) << _first_defined_B << std::endl;
-            std::cout << "\t\tto_A_defined   " << to_A_defined   << (to_A == 0)   << (to_A == last_A)   << _last_defined_A << std::endl;
-            std::cout << "\t\tto_B_defined   " << to_B_defined   << (to_B == 0)   << (to_B == last_B)   << _last_defined_B << std::endl;
-        }
-
-        // If only one overlapped point, both should be defined
-        if ( N_overlapped== 1 &&
-             (!from_A_defined || !from_B_defined || !to_A_defined || !to_B_defined ) )
-            continue;
-
-        assert(N_overlapped <= _points_B.cols());
-        assert(N_overlapped <= _points_A.cols() || (_closed_A && N_defined_B < _points_B.cols())); // N_overlapped can be > _points_A if all defined points of B match with A points and the not defined extremes should match too
-        assert(from_A >= 0);
-        assert(from_B >= 0);
-        assert(from_B == 0 || !_closed_A);
-        assert(to_B == _points_B.cols()-1 || !_closed_A);
-        assert(to_A <= last_A);
-        assert(to_B <= last_B);
-        assert(to_A < _points_A.cols());
-        assert(to_B < _points_B.cols());
-
-        // POINT-POINT DISTANCES for all overlapped points (in case of closed A in two parts)
-        // Fill with B points
-        Eigen::ArrayXXd d(_points_B.block(0,from_B, 2, N_overlapped).array());
-
-        // Substract A points
-        if (to_A >= from_A && N_overlapped <= _points_A.cols())
-            d -= _points_A.block(0,from_A, 2, N_overlapped).array();
-        else
-        {
-            d.leftCols(_points_A.cols()-from_A) -= _points_A.block(0,from_A, 2, _points_A.cols()-from_A).array();
-            d.rightCols(to_A+1)                 -= _points_A.block(0, 0, 2, to_A+1).array();
-        }
-        Eigen::ArrayXd dist2 = d.row(0).pow(2) + d.row(1).pow(2);
-
-        // CORRECT POINT-LINE DISTANCES for not defined extremes
-        // First
-        if (!from_B_defined || !from_A_defined)
-        {
-            // take care of closed A: next of from_A
-            int next_from_A = (from_A+1 > last_A ? 0 : from_A+1);
-            if (print)
-                std::cout << "\t\t\tFirst feature not defined distance to line" << std::endl;
-
-            // std::cout << "\t\t\tA   " << _points_A.col(from_A).transpose() << std::endl;
-            // std::cout << "\t\t\tAaux" << _points_A.col(next_from_A).transpose() << std::endl;
-            // std::cout << "\t\t\tB   " << _points_B.col(from_B).transpose() << std::endl;
-            dist2(0) = sqDistPointToLine(_points_A.col(from_A),
-                                         _points_A.col(next_from_A),
-                                         _points_B.col(from_B),
-                                         from_A_defined,
-                                         from_B_defined);
-            assert(N_overlapped > 1);
-        }
-        // Last
-        if (!to_B_defined || !to_A_defined)
-        {
-            // take care of closed A: next of to_A
-            int prev_to_A = (to_A == 0 ? last_A : to_A-1);
-            if (print)
-                std::cout << "\t\t\tLast feature not defined distance to line" << std::endl;
-
-            // std::cout << "\t\t\tA   " << _points_A.col(to_A).transpose() << std::endl;
-            // std::cout << "\t\t\tAaux" << _points_A.col(prev_to_A).transpose() << std::endl;
-            // std::cout << "\t\t\tB   " << _points_B.col(to_B).transpose() << std::endl;
-            dist2(N_overlapped-1) = sqDistPointToLine(_points_A.col(to_A),
-                                                      _points_A.col(prev_to_A),
-                                                      _points_B.col(to_B),
-                                                      to_A_defined,
-                                                      to_B_defined);
-            assert(N_overlapped > 1);
-        }
-        if (print)
-            std::cout << "\t\tsquared distances = " << dist2.transpose() << std::endl;
-
-        // All squared distances should be within a threshold
-        // Choose the most overlapped one
-        if ((dist2 < params_->match_position_error_th*params_->match_position_error_th).all() &&
-            (best_match == nullptr ||
-             (N_overlapped >= best_match->feature_match_to_id_-best_match->feature_match_from_id_+1 &&
-              dist2.mean() < best_match->normalized_score_ )))
-        {
-            if (print)
-                std::cout << "\tBEST MATCH" << std::endl;
-            if (best_match == nullptr)
-                best_match = std::make_shared<LandmarkPolylineMatch>();
-
-            best_match->feature_match_from_id_= from_B;
-            best_match->feature_match_to_id_= to_B;
-            best_match->landmark_ptr_= nullptr;
-            best_match->normalized_score_ = dist2.mean();
-            best_match->landmark_match_from_id_= from_A;
-            best_match->landmark_match_to_id_= to_A;
-        }
-    }
-
-    return best_match;
-}
-
-LandmarkPolylineMatchPtr ProcessorPolyline::tryMatch(const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr)
+LandmarkMatchPolyline2DPtr ProcessorPolyline::tryMatch(const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr)
 {
     // updated expected feature
     Eigen::MatrixXs exp_feat, exp_cov;
@@ -1059,17 +762,18 @@ LandmarkPolylineMatchPtr ProcessorPolyline::tryMatch(const LandmarkPolyline2DPtr
     return tryMatch(exp_feat,_lmk_ptr, _feat_ptr);
 }
 
-LandmarkPolylineMatchPtr ProcessorPolyline::tryMatch(const Eigen::MatrixXs& _lmk_expected_feature_points, const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr) const
+LandmarkMatchPolyline2DPtr ProcessorPolyline::tryMatch(const Eigen::MatrixXs& _lmk_expected_feature_points, const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr) const
 {
     // compute best sq distance matching
-    LandmarkPolylineMatchPtr match_pl = computeBestSqDist(_lmk_expected_feature_points,   // <--landmark points in local coordinates
+    LandmarkMatchPolyline2DPtr match_pl = computeBestSqDist(_lmk_expected_feature_points,   // <--landmark points in local coordinates
                                                           _lmk_ptr->isFirstDefined(),
                                                           _lmk_ptr->isLastDefined(),
                                                           _lmk_ptr->isClosed(),
                                                           _feat_ptr->getPoints(),               // <--feature points
                                                           _feat_ptr->isFirstDefined(),
                                                           _feat_ptr->isLastDefined(),
-                                                          false);
+                                                          false,
+                                                          params_->position_error_th_*params_->position_error_th_);
     // still valid match
     if (match_pl)
     {
@@ -1077,11 +781,11 @@ LandmarkPolylineMatchPtr ProcessorPolyline::tryMatch(const Eigen::MatrixXs& _lmk
         match_pl->landmark_version_=_lmk_ptr->getVersion();
         // landmark point id's
         std::vector<int> lmk_valid_ids = _lmk_ptr->getValidIds();
-        match_pl->landmark_match_from_id_ = lmk_valid_ids[match_pl->landmark_match_from_id_];
-        match_pl->landmark_match_to_id_   = lmk_valid_ids[match_pl->landmark_match_to_id_];
+        match_pl->landmark_from_id_ = lmk_valid_ids[match_pl->landmark_from_id_];
+        match_pl->landmark_to_id_   = lmk_valid_ids[match_pl->landmark_to_id_];
 
-        assert(match_pl->feature_match_from_id_ == 0 || !_lmk_ptr->isClosed());
-        assert(match_pl->feature_match_to_id_ == _feat_ptr->getNPoints()-1 || !_lmk_ptr->isClosed());
+        assert(match_pl->feature_from_id_ == 0 || !_lmk_ptr->isClosed());
+        assert(match_pl->feature_to_id_ == _feat_ptr->getNPoints()-1 || !_lmk_ptr->isClosed());
     }
 
     return match_pl;
@@ -1197,170 +901,7 @@ void ProcessorPolyline::expectedFeature(LandmarkBasePtr _landmark_ptr, Eigen::Ma
     expected_ftr_ = pl_lmk->computePointsGlobal();
     expected_ftr_.topRows(2) = R_sensor_world_ * (expected_ftr_.topRows(2).colwise() - t_world_sensor_);
     expected_ftr_cov_ = pl_lmk->computePointsCovGlobal();
-
-    /*Eigen::MatrixXs expected_ftr2 = Eigen::MatrixXs::Zero(3,pl_lmk->getNPoints());
-    Eigen::MatrixXs expected_ftr_cov2 = Eigen::MatrixXs::Zero(2,2*pl_lmk->getNPoints());
-    Eigen::Vector3s col = Eigen::Vector3s::Ones();
-
-    std::vector<int> valid_ids = pl_lmk->getValidIds();
-
-    ////////// global coordinates points
-    if (pl_lmk->getClassification().type == UNCLASSIFIED)
-        for (int i = 0; i < pl_lmk->getNPoints(); i++)
-        {
-            //std::cout << "i " << i << " valid id: " << valid_ids[i] << std::endl;
-            //std::cout << "Landmark global position: " << pl_lmk->getPointVector(valid_ids[i]).transpose() << std::endl;
-
-            // ------------ expected feature point
-            col.head<2>() = R_sensor_world_ * (pl_lmk->getPointVector(valid_ids[i]) - t_world_sensor_);
-            expected_ftr2.col(i) = col;
-
-            // std::cout << "Expected point " << i << ": " << expected_ftr_.col(i).transpose() << std::endl;
-            // ------------ expected feature point covariance
-            // TODO
-            expected_ftr_cov2.middleCols(i*2, 2) = Eigen::MatrixXs::Identity(2,2);
-        }
-
-    ////////// landmark with origin
-    else
-    {
-        Eigen::Matrix2s R_world_points = Eigen::Rotation2Ds(pl_lmk->getOPtr()->getState()(0)).matrix();
-        const Eigen::VectorXs& t_world_points = pl_lmk->getPPtr()->getState();
-
-        for (int i = 0; i < pl_lmk->getNPoints(); i++)
-        {
-            //std::cout << "i " << i << " valid id: " << valid_ids[i] << std::endl;
-            //std::cout << "Landmark global position: " << pl_lmk->getPointVector(valid_ids[i]).transpose() << std::endl;
-
-            // ------------ expected feature point
-            col.head<2>() = R_sensor_world_ * (R_world_points * pl_lmk->getPointVector(valid_ids[i]) + t_world_points - t_world_sensor_);
-            expected_ftr2.col(i) = col;
-
-            // std::cout << "Expected point " << i << ": " << expected_ftr_.col(i).transpose() << std::endl;
-            // ------------ expected feature point covariance
-            // TODO
-            expected_ftr_cov2.middleCols(i*2, 2) = Eigen::MatrixXs::Identity(2,2);
-        }
-    }
-
-    if (!expected_ftr2.isApprox(expected_ftr_, 1e-6) || !expected_ftr_cov2.isApprox(expected_ftr_cov_, 1e-6))
-    {
-        WOLF_ERROR("ProcessorPolylines: expected features not properly computed...");
-        std::cout << expected_ftr_ << std::endl;
-        std::cout << expected_ftr2 << std::endl;
-    }*/
-}
-
-Eigen::VectorXs ProcessorPolyline::computeSquaredMahalanobisDistances(const Eigen::Vector2s& _feature,
-                                                                      const Eigen::Matrix2s& _feature_cov,
-                                                                      const Eigen::Vector2s& _expected_feature,
-                                                                      const Eigen::Matrix2s& _expected_feature_cov,
-                                                                      const Eigen::MatrixXs& _mu)
-{
-    // ------------------------ d
-    Eigen::Vector2s d = _feature - _expected_feature;
-
-    // ------------------------ Sigma_d
-    Eigen::Matrix2s iSigma_d = (_feature_cov + _expected_feature_cov).inverse();
-    Eigen::VectorXs squared_mahalanobis_distances(_mu.cols());
-    for (unsigned int i = 0; i < _mu.cols(); i++)
-    {
-        squared_mahalanobis_distances(i) = (d - _mu.col(i)).transpose() * iSigma_d * (d - _mu.col(i));
-        //if ((d - _mu.col(i)).norm() < 1)
-        //{
-        //    std::cout << "distance:    " << (d - _mu.col(i)).norm() << std::endl;
-        //    std::cout << "iSigma_d:    " << std::endl << iSigma_d << std::endl;
-        //    std::cout << "mahalanobis: " << squared_mahalanobis_distances(i) << std::endl;
-        //}
-    }
-
-    return squared_mahalanobis_distances;
-}
-
-Scalar ProcessorPolyline::sqDistPointToLine(const Eigen::Vector3s& _A, const Eigen::Vector3s& _A_aux,
-                                            const Eigen::Vector3s& _B, bool _A_defined, bool _B_defined, bool strict) const
-{
-    /* Squared distance from B to the line A_aux-A (match evaluated is A-B)
-     *
-     * No matter if A_aux is defined or not.
-     *
-     * Case 1: B not defined & A not defined
-     *
-     *      If projection of B over AAaux is in [A_aux, inf), return sq.dist. B-Line(AAux)
-     *      Otherwise, return sqDist(Aaux-B) or 1e12 if strict=true.
-     *
-     *      Check: Angle BAauxA is <= 90º: (AB)² <= (AauxB)² + (AAaux)²
-     *
-     * Case 2: B not defined & A defined
-     *
-     *      If projection of B over AAaux is in [A_aux, A], return sq.dist. B-Line(AAux)
-     *      Otherwise, return min(sqDist(Aaux-B),sqDist(A-B)) or 1e12 if strict=true.
-     *
-     *      Check: Angles BAauxA & BAAaux are <= 90º: (AB)² <= (AauxB)² + (AAaux)² & (AauxB)² <= (AB)² + (AAaux)²
-     *
-     * Case 3: B defined & A not defined
-     *
-     *      If projection of B over AAaux is in [A, inf), return sq.dist. B-Line(AAux)
-     *      Otherwise, return sqDist(A-B)) or 1e12 if strict=true.
-     *
-     *      Check: Angle BAAaux is >= 90º: (AauxB)² >= (AB)² + (AAaux)²
-     *
-     * (both points defined is not point-to-line, is point-to-point)
-     *
-     */
-
-    assert((!_A_defined || !_B_defined) && "ProcessorPolyline::sqDistPointToLine: at least one point must not be defined.");
-
-    Scalar AB_sq = (_B-_A).head<2>().squaredNorm();
-    Scalar AauxB_sq = (_B-_A_aux).head<2>().squaredNorm();
-    Scalar AAaux_sq = (_A_aux-_A).head<2>().squaredNorm();
-
-    // squared distance to line
-    // Case 1
-    if (!_B_defined && !_A_defined)
-    {
-        if (AB_sq <= AauxB_sq + AAaux_sq)
-            return AB_sq - std::pow(((_A_aux-_A).head<2>()).dot((_B-_A).head<2>()),2) / AAaux_sq;
-        else
-            return (strict ? 1e12 : AauxB_sq);
-    }
-
-    // Case 2
-    if (!_B_defined && _A_defined)
-    {
-        if (AB_sq <= AauxB_sq + AAaux_sq && AauxB_sq <= AB_sq + AAaux_sq)
-            return AB_sq - std::pow(((_A_aux-_A).head<2>()).dot((_B-_A).head<2>()),2) / AAaux_sq;
-        else
-            return (strict ? 1e12 : std::min(AauxB_sq,AB_sq));
-    }
-
-    // Case 3
-    if (_B_defined && !_A_defined)
-    {
-        if (AauxB_sq >= AB_sq + AAaux_sq)
-            return AB_sq - std::pow(((_A_aux-_A).head<2>()).dot((_B-_A).head<2>()),2) / AAaux_sq;
-        else
-            return (strict ? 1e12 : AB_sq);
-    }
-
-    // Default return unused
-    return 1e12;
-}
-
-bool ProcessorPolyline::isProjectedOverSegment(const Eigen::Vector3s& _A, const Eigen::Vector3s& _A_aux, const Eigen::Vector3s& _B)
-{
-    /* The orthogonal projection of B over the line A-Aaux is in the segment [A,Aaux].
-     *
-     *      Check: the angles BAAaux and BAauxA are <= 90º:
-     *          (BAaux)² <= (BA)² + (AAaux)² & (BA)² <= (BAaux)² + (AAaux)²
-     *
-     */
-
-    Scalar AB_sq = (_B-_A).head<2>().squaredNorm();
-    Scalar AauxB_sq = (_B-_A_aux).head<2>().squaredNorm();
-    Scalar AAaux_sq = (_A_aux-_A).head<2>().squaredNorm();
-
-    return (AauxB_sq <= AB_sq + AAaux_sq) && (AB_sq <= AauxB_sq + AAaux_sq);
+    // TODO Rotate covariances
 }
 
 ProcessorBasePtr ProcessorPolyline::create(const std::string& _unique_name, const ProcessorParamsBasePtr _params, const SensorBasePtr)
diff --git a/src/processor/processor_tracker_feature_polyline.cpp b/src/processor/processor_tracker_feature_polyline.cpp
index adaba8e6b8eb42d992fbb8b48aae41ebfd4a90d1..480ebc7a339dcbe291cf8eb602efbac42bf0ed85 100644
--- a/src/processor/processor_tracker_feature_polyline.cpp
+++ b/src/processor/processor_tracker_feature_polyline.cpp
@@ -35,7 +35,7 @@ unsigned int ProcessorTrackerFeaturePolyline::trackFeatures(const FeatureBaseLis
     // iterate over all polylines features
     while (ftr_it != all_features_incoming_.end())
     {
-        FeaturePolylineMatchPtr best_ftr_match = nullptr;
+        FeatureMatchPolyline2DPtr best_ftr_match = nullptr;
         auto pl_incoming = std::static_pointer_cast<FeaturePolyline2D>(*ftr_it);
 
         // Check matching with all features in last
@@ -44,7 +44,7 @@ unsigned int ProcessorTrackerFeaturePolyline::trackFeatures(const FeatureBaseLis
             auto pl_last = std::static_pointer_cast<FeaturePolyline2D>(ftr_last_);
             // TODO: move _features_last_in according to motion
 
-            FeaturePolylineMatchPtr best_pair_match = computeBestMatch(pl_last,pl_incoming);
+            FeatureMatchPolyline2DPtr best_pair_match = computeBestMatch(pl_last,pl_incoming);
 
             // best match
             best_ftr_match = bestMatch(best_ftr_match, best_pair_match);
@@ -74,7 +74,7 @@ unsigned int ProcessorTrackerFeaturePolyline::trackFeatures(const FeatureBaseLis
                 pl_lmk->getMergedInLandmark() == nullptr &&
                !pl_lmk->isRemoving())
             {
-                auto lmk_match_incoming = std::make_shared<LandmarkPolylineMatch>();
+                auto lmk_match_incoming = std::make_shared<LandmarkMatchPolyline2D>();
 
                 lmk_match_incoming->feature_from_id_  = best_ftr_match->feature_incoming_from_id_;
                 lmk_match_incoming->feature_to_id_    = best_ftr_match->feature_incoming_to_id_;
@@ -156,7 +156,7 @@ unsigned int ProcessorTrackerFeaturePolyline::processNew(const unsigned int& _ma
         auto pl_ftr = std::static_pointer_cast<FeaturePolyline2D>(*ftr_it);
 
         // Search a match with existing landmarks
-        LandmarkPolylineMatchPtr lmk_match = nullptr;
+        LandmarkMatchPolyline2DPtr lmk_match = nullptr;
         for (auto lmk : getProblem()->getMapPtr()->getLandmarkList())
         {
             if (lmk->getType() != "POLYLINE 2D")
@@ -171,7 +171,7 @@ unsigned int ProcessorTrackerFeaturePolyline::processNew(const unsigned int& _ma
             auto new_lmk_ptr = createLandmark(pl_ftr);
 
             // Add a new match to landmark_match_map_
-            lmk_match = std::make_shared<LandmarkPolylineMatch>();
+            lmk_match = std::make_shared<LandmarkMatchPolyline2D>();
             lmk_match->landmark_ptr_ = new_lmk_ptr;
             lmk_match->landmark_version_ = std::static_pointer_cast<LandmarkPolyline2D>(new_lmk_ptr)->getVersion();
             lmk_match->feature_from_id_= 0;                         // all points match
@@ -181,6 +181,9 @@ unsigned int ProcessorTrackerFeaturePolyline::processNew(const unsigned int& _ma
             lmk_match->normalized_score_ = 1.0;                     // max score
 
             landmark_match_map_[pl_ftr] = lmk_match;
+
+            // Add the new landmark to the map
+            getProblem()->addLandmark(new_lmk_ptr);
         }
         else
         {
@@ -218,7 +221,7 @@ ConstraintBasePtr ProcessorTrackerFeaturePolyline::createConstraint(FeatureBaseP
 {
     assert(landmark_match_map_.find(_feature_ptr) != landmark_match_map_.end() && "feature without landmark match");
 
-    auto match = landmark_match_map_[_feature_ptr];
+    auto match = landmark_match_map_.at(_feature_ptr);
 
 
     // TODO
@@ -231,11 +234,25 @@ void ProcessorTrackerFeaturePolyline::establishConstraints()
     for (auto ftr : last_ptr_->getFeatureList())
     {
         assert(landmark_match_map_.find(ftr) != landmark_match_map_.end() && "feature without landmark match in last features");
-        auto lmk = landmark_match_map_[ftr]->landmark_ptr_;
+        auto pl_lmk = std::static_pointer_cast<LandmarkPolyline2D>(landmark_match_map_[ftr]->landmark_ptr_);
+        auto pl_ftr = std::static_pointer_cast<FeaturePolyline2D>(ftr);
+        auto lmk_match = landmark_match_map_[ftr];
+
+        // CHECK IF LANDMARK CHANGED
+        if (lmk_match->landmark_version_ != pl_lmk->getVersion() || pl_lmk->getMergedInLandmark() != nullptr)
+        {
+            // TODO
+        }
+
+        // MODIFY LANDMARK (only for not closed and not recently created)
+        if (!pl_lmk->isClosed() && pl_lmk->getHits() > 0)
+        {
+            // TODO
+        }
 
-        auto ctr_ptr  = createConstraint(ftr, lmk);
+        auto ctr_ptr  = createConstraint(ftr, pl_lmk);
         ftr->addConstraint(ctr_ptr);
-        lmk->addConstrainedBy(ctr_ptr);
+        pl_lmk->addConstrainedBy(ctr_ptr);
     }
 }
 
@@ -245,7 +262,7 @@ LandmarkBasePtr ProcessorTrackerFeaturePolyline::createLandmark(FeatureBasePtr _
     //std::cout << "Robot global pose: " << t_world_robot_.transpose() << std::endl;
     //std::cout << "Sensor global pose: " << t_world_sensor_.transpose() << std::endl;
     assert(_feature_ptr->getType() == "POLYLINE 2D");
-    assert(std::find(_feature_ptr, new_features_last_.begin(),new_features_last_.end()) != new_features_last_.end() && "creating a landmark for a feature which is not in last capture");
+    assert(std::find(new_features_last_.begin(),new_features_last_.end(),_feature_ptr) != new_features_last_.end() && "creating a landmark for a feature which is not in last capture");
 
 
     FeaturePolyline2DPtr polyline_ptr = std::static_pointer_cast<FeaturePolyline2D>(_feature_ptr);
@@ -337,7 +354,7 @@ void ProcessorTrackerFeaturePolyline::postProcess()
 
 }
 
-FeaturePolylineMatchPtr ProcessorTrackerFeaturePolyline::computeBestMatch(const FeaturePolyline2DPtr& _ftr_L, const FeaturePolyline2DPtr& _ftr_I) const
+FeatureMatchPolyline2DPtr ProcessorTrackerFeaturePolyline::computeBestMatch(const FeaturePolyline2DPtr& _ftr_L, const FeaturePolyline2DPtr& _ftr_I) const
 {
     /* Search along all possible overlapping configurations between A&B points
      *      We define offset as the correspondence (if exists) of the first point_L to B's.
@@ -362,7 +379,7 @@ FeaturePolylineMatchPtr ProcessorTrackerFeaturePolyline::computeBestMatch(const
         std::cout << "\tdefined points_I:\n" << points_I.topRows(2) << std::endl;
     }
 
-    FeaturePolylineMatchPtr best_match = nullptr;
+    FeatureMatchPolyline2DPtr best_match = nullptr;
 
     // Check all overlapping configurations
     int min_offset = -last_L+first_I;
@@ -453,7 +470,7 @@ FeaturePolylineMatchPtr ProcessorTrackerFeaturePolyline::computeBestMatch(const
         // All squared distances should be within a threshold
         if ((dist2 < params_tracker_feature_polyline_->match_position_error_th*params_tracker_feature_polyline_->match_position_error_th).all())
         {
-            FeaturePolylineMatchPtr current_match;
+            FeatureMatchPolyline2DPtr current_match;
             current_match->feature_incoming_from_id_= from_I;
             current_match->feature_incoming_to_id_= to_I;
             current_match->feature_last_from_id_= from_L;
@@ -472,8 +489,7 @@ FeaturePolylineMatchPtr ProcessorTrackerFeaturePolyline::computeBestMatch(const
     return best_match;
 }
 
-wolf::FeaturePolylineMatchConstPtr ProcessorTrackerFeaturePolyline::bestMatch(
-        const FeaturePolylineMatchConstPtr& _match_A, const FeaturePolylineMatchConstPtr& _match_B) const
+wolf::FeatureMatchPolyline2DPtr ProcessorTrackerFeaturePolyline::bestMatch(const FeatureMatchPolyline2DPtr& _match_A, const FeatureMatchPolyline2DPtr& _match_B) const
 {
     assert(_match_A != nullptr || _match_B != nullptr);
     if (_match_A == nullptr)
@@ -490,74 +506,62 @@ wolf::FeaturePolylineMatchConstPtr ProcessorTrackerFeaturePolyline::bestMatch(
     return _match_B;
 }
 
-Scalar ProcessorTrackerFeaturePolyline::sqDistPointToLine(const Eigen::Vector3s& _A, const Eigen::Vector3s& _A_aux,
-                                                          const Eigen::Vector3s& _B, bool _A_defined, bool _B_defined, bool strict) const
-{
-    /* Squared distance from B to the line A_aux-A (match evaluated is A-B)
-     *
-     * No matter if A_aux is defined or not.
-     *
-     * Case 1: B not defined & A not defined
-     *
-     *      If projection of B over AAaux is in [A_aux, inf), return sq.dist. B-Line(AAux)
-     *      Otherwise, return sqDist(Aaux-B) or 1e12 if strict=true.
-     *
-     *      Check: Angle BAauxA is <= 90º: (AB)² <= (AauxB)² + (AAaux)²
-     *
-     * Case 2: B not defined & A defined
-     *
-     *      If projection of B over AAaux is in [A_aux, A], return sq.dist. B-Line(AAux)
-     *      Otherwise, return min(sqDist(Aaux-B),sqDist(A-B)) or 1e12 if strict=true.
-     *
-     *      Check: Angles BAauxA & BAAaux are <= 90º: (AB)² <= (AauxB)² + (AAaux)² & (AauxB)² <= (AB)² + (AAaux)²
-     *
-     * Case 3: B defined & A not defined
-     *
-     *      If projection of B over AAaux is in [A, inf), return sq.dist. B-Line(AAux)
-     *      Otherwise, return sqDist(A-B)) or 1e12 if strict=true.
-     *
-     *      Check: Angle BAAaux is >= 90º: (AauxB)² >= (AB)² + (AAaux)²
-     *
-     * (both points defined is not point-to-line, is point-to-point)
-     *
-     */
 
-    assert((!_A_defined || !_B_defined) && "ProcessorPolyline::sqDistPointToLine: at least one point must not be defined.");
+LandmarkMatchPolyline2DPtr ProcessorTrackerFeaturePolyline::tryMatchWithLandmark(const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr) const
+{
+    // updated expected feature
+    Eigen::MatrixXs exp_feat, exp_cov;
+    expectedFeatureLast(_lmk_ptr, exp_feat, exp_cov);
 
-    Scalar AB_sq = (_B-_A).head<2>().squaredNorm();
-    Scalar AauxB_sq = (_B-_A_aux).head<2>().squaredNorm();
-    Scalar AAaux_sq = (_A_aux-_A).head<2>().squaredNorm();
+    // try match
+    return tryMatchWithLandmark(exp_feat,_lmk_ptr, _feat_ptr);
+}
 
-    // squared distance to line
-    // Case 1
-    if (!_B_defined && !_A_defined)
+LandmarkMatchPolyline2DPtr ProcessorTrackerFeaturePolyline::tryMatchWithLandmark(const Eigen::MatrixXs& _lmk_expected_feature_points, const LandmarkPolyline2DPtr& _lmk_ptr, const FeaturePolyline2DPtr& _feat_ptr) const
+{
+    // compute best sq distance matching
+    LandmarkMatchPolyline2DPtr match_pl = computeBestSqDist(_lmk_expected_feature_points,   // <--landmark points in local coordinates
+                                                          _lmk_ptr->isFirstDefined(),
+                                                          _lmk_ptr->isLastDefined(),
+                                                          _lmk_ptr->isClosed(),
+                                                          _feat_ptr->getPoints(),               // <--feature points
+                                                          _feat_ptr->isFirstDefined(),
+                                                          _feat_ptr->isLastDefined(),
+                                                          false,
+                                                          params_tracker_feature_polyline_->match_position_error_th*params_tracker_feature_polyline_->match_position_error_th);
+    // still valid match
+    if (match_pl)
     {
-        if (AB_sq <= AauxB_sq + AAaux_sq)
-            return AB_sq - std::pow(((_A_aux-_A).head<2>()).dot((_B-_A).head<2>()),2) / AAaux_sq;
-        else
-            return (strict ? 1e12 : AauxB_sq);
+        match_pl->landmark_ptr_=_lmk_ptr;
+        match_pl->landmark_version_=_lmk_ptr->getVersion();
+        // landmark point id's
+        std::vector<int> lmk_valid_ids = _lmk_ptr->getValidIds();
+        match_pl->landmark_from_id_ = lmk_valid_ids[match_pl->landmark_from_id_];
+        match_pl->landmark_to_id_   = lmk_valid_ids[match_pl->landmark_to_id_];
+
+        assert(match_pl->feature_from_id_ == 0 || !_lmk_ptr->isClosed());
+        assert(match_pl->feature_to_id_ == _feat_ptr->getNPoints()-1 || !_lmk_ptr->isClosed());
     }
 
-    // Case 2
-    if (!_B_defined && _A_defined)
-    {
-        if (AB_sq <= AauxB_sq + AAaux_sq && AauxB_sq <= AB_sq + AAaux_sq)
-            return AB_sq - std::pow(((_A_aux-_A).head<2>()).dot((_B-_A).head<2>()),2) / AAaux_sq;
-        else
-            return (strict ? 1e12 : std::min(AauxB_sq,AB_sq));
-    }
+    return match_pl;
+}
 
-    // Case 3
-    if (_B_defined && !_A_defined)
-    {
-        if (AauxB_sq >= AB_sq + AAaux_sq)
-            return AB_sq - std::pow(((_A_aux-_A).head<2>()).dot((_B-_A).head<2>()),2) / AAaux_sq;
-        else
-            return (strict ? 1e12 : AB_sq);
-    }
+// MATH ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ProcessorTrackerFeaturePolyline::expectedFeatureLast(LandmarkBasePtr _landmark_ptr, Eigen::MatrixXs& expected_feature_, Eigen::MatrixXs& expected_feature_cov_) const
+{
+    assert(_landmark_ptr->getType() == "POLYLINE 2D" && "ProcessorTrackerFeaturePolyline::expectedFeature: Bad landmark type");
+    LandmarkPolyline2DPtr pl_lmk = std::static_pointer_cast<LandmarkPolyline2D>(_landmark_ptr);
+
+    //std::cout << "ProcessorPolyline::expectedFeature" << std::endl;
+    //std::cout << "t_world_sensor_last_: " << t_world_sensor_last_.transpose() << std::endl;
+    //std::cout << "R_sensor_world_last_: "  << std::endl << R_sensor_world_last_ << std::endl;
 
-    // Default return unused
-    return 1e12;
+    expected_feature_ = pl_lmk->computePointsGlobal();
+    expected_feature_.topRows(2) = R_sensor_world_last_ * (expected_feature_.topRows(2).colwise() - t_world_sensor_last_);
+    expected_feature_cov_ = pl_lmk->computePointsCovGlobal();
+    // TODO Rotate covariances
 }
 
 void ProcessorTrackerFeaturePolyline::computeIncomingTransformations(const TimeStamp& _ts)