diff --git a/GeoLib/PointVec.cpp b/GeoLib/PointVec.cpp
index a65c76af4101b5b7732157bd43cf2f9ed1034117..d9f322feffebb9a19d4a82c161a75b3e948d7118 100644
--- a/GeoLib/PointVec.cpp
+++ b/GeoLib/PointVec.cpp
@@ -33,23 +33,44 @@ PointVec::PointVec (const std::string& name, std::vector<Point*>* points,
 	TemplateVec<Point> (name, points, name_id_map),
 	_type(type),
 	_aabb(points->begin(), points->end()),
-	_rel_eps(rel_eps)
+	_rel_eps(rel_eps*sqrt(MathLib::sqrDist(_aabb.getMinPoint(), _aabb.getMaxPoint()))),
+	_oct_tree(OctTree<GeoLib::Point, 16>::createOctTree(
+		_aabb.getMinPoint(), _aabb.getMaxPoint(), _rel_eps))
 {
 	assert (_data_vec);
-	std::size_t const number_of_all_input_pnts (_data_vec->size());
+	std::size_t const number_of_all_input_pnts(_data_vec->size());
+
+	// add all points in the oct tree in order to make them unique
+	_pnt_id_map.resize(number_of_all_input_pnts);
+	std::iota(_pnt_id_map.begin(), _pnt_id_map.end(), 0);
+	GeoLib::Point * ret_pnt(nullptr);
+	std::size_t cnt(0);
+	for (std::size_t k(0); k<_data_vec->size(); ++k) {
+		GeoLib::Point *const pnt((*_data_vec)[k]);
+		if (! _oct_tree->addPoint(pnt, ret_pnt)) {
+			_pnt_id_map[pnt->getID()] = ret_pnt->getID();
+			delete (*_data_vec)[k];
+			(*_data_vec)[k] = nullptr;
+		} else {
+			_pnt_id_map[k] = cnt;
+			cnt++;
+		}
+	}
 
-	_rel_eps *= sqrt(MathLib::sqrDist (_aabb.getMinPoint(),_aabb.getMaxPoint()));
-	makePntsUnique (_data_vec, _pnt_id_map, _rel_eps);
+	auto const data_vec_end = std::remove(_data_vec->begin(), _data_vec->end(), nullptr);
+	_data_vec->erase(data_vec_end, _data_vec->end());
 
 	if (number_of_all_input_pnts > _data_vec->size())
 		WARN("PointVec::PointVec(): there are %d double points.",
-		     number_of_all_input_pnts - _data_vec->size());
+			number_of_all_input_pnts - _data_vec->size());
 
 	correctNameIDMapping();
 	// create the inverse mapping
 	_id_to_name_map.resize(_data_vec->size());
 	// fetch the names from the name id map
 	for (auto p : *_name_id_map) {
+		if (p.second >= _id_to_name_map.size())
+			continue;
 		_id_to_name_map[p.second] = p.first;
 	}
 }
@@ -57,14 +78,14 @@ PointVec::PointVec (const std::string& name, std::vector<Point*>* points,
 PointVec::~PointVec ()
 {}
 
-std::size_t PointVec::push_back (Point* pnt)
+std::size_t PointVec::push_back(Point* pnt)
 {
 	_pnt_id_map.push_back(uniqueInsert(pnt));
 	_id_to_name_map.push_back("");
 	return _pnt_id_map[_pnt_id_map.size() - 1];
 }
 
-void PointVec::push_back (Point* pnt, std::string const*const name)
+void PointVec::push_back(Point* pnt, std::string const*const name)
 {
 	if (name == nullptr) {
 		_pnt_id_map.push_back (uniqueInsert(pnt));
@@ -85,102 +106,36 @@ void PointVec::push_back (Point* pnt, std::string const*const name)
 	_id_to_name_map.push_back(*name);
 }
 
-std::size_t PointVec::uniqueInsert (Point* pnt)
-{
-	auto const it = std::find_if(_data_vec->begin(), _data_vec->end(),
-		[this, &pnt](Point* const p)
-		{
-			return MathLib::maxNormDist(p, pnt) <= _rel_eps;
-		});
-
-	if (it != _data_vec->end())
-	{
-		delete pnt;
-		pnt = nullptr;
-		return static_cast<std::size_t>(std::distance(_data_vec->begin(), it));
-	}
-
-	_data_vec->push_back(pnt);
-
-	// update bounding box
-	_aabb.update (*(_data_vec->back()));
-
-	return _data_vec->size()-1;
-}
-
-void PointVec::makePntsUnique (std::vector<GeoLib::Point*>* pnt_vec,
-                               std::vector<std::size_t> &pnt_id_map, double eps)
+std::size_t PointVec::uniqueInsert(Point* pnt)
 {
-	std::size_t const n_pnts_in_file(pnt_vec->size());
-	std::vector<std::size_t> perm(n_pnts_in_file);
-	pnt_id_map.resize(n_pnts_in_file);
-	std::iota(perm.begin(), perm.end(), 0);
-	std::iota(pnt_id_map.begin(), pnt_id_map.end(), 0);
-
-	// sort the points
-	BaseLib::Quicksort<GeoLib::Point*>(*pnt_vec, 0, n_pnts_in_file, perm);
-
-	// unfortunately quicksort is not stable -
-	// sort identical points by id - to make sorting stable
-	// determine intervals with identical points to resort for stability of sorting
-	std::vector<std::size_t> identical_pnts_interval;
-	bool identical(false);
-	for (std::size_t k = 0; k < n_pnts_in_file - 1; k++)
-	{
-		if (MathLib::maxNormDist((*pnt_vec)[k + 1], (*pnt_vec)[k]) <= eps)
-		{
-			// points are identical, sort by id
-			if (!identical)
-				identical_pnts_interval.push_back(k);
-			identical = true;
-		} else {
-			if (identical)
-				identical_pnts_interval.push_back(k + 1);
-			identical = false;
-		}
+	GeoLib::Point * ret_pnt(nullptr);
+	if (_oct_tree->addPoint(pnt, ret_pnt)) {
+		_data_vec->push_back(pnt);
+		return _data_vec->size()-1;
 	}
-	if (identical)
-		identical_pnts_interval.push_back(n_pnts_in_file);
-
-	for (std::size_t i(0); i < identical_pnts_interval.size() / 2; i++) {
-		// bubble sort by id
-		std::size_t beg(identical_pnts_interval[2 * i]);
-		std::size_t end(identical_pnts_interval[2 * i + 1]);
-		for (std::size_t j(beg); j < end; j++)
-			for (std::size_t k(beg); k < end - 1; k++)
-				if (perm[k] > perm[k + 1])
-					std::swap(perm[k], perm[k + 1]);
-	}
-
-	// check if there are identical points
-	for (std::size_t k = 0; k < n_pnts_in_file - 1; k++)
-		if (MathLib::maxNormDist((*pnt_vec)[k + 1], (*pnt_vec)[k]) <= eps)
-			pnt_id_map[perm[k + 1]] = pnt_id_map[perm[k]];
-
-	// reverse permutation
-	BaseLib::Quicksort<std::size_t, GeoLib::Point*>(perm, 0, n_pnts_in_file, *pnt_vec);
 
-	// remove the second, third, ... occurrence from vector
-	for (std::size_t k(0); k < n_pnts_in_file; k++) {
-		if (pnt_id_map[k] < k) {
-			delete (*pnt_vec)[k];
-			(*pnt_vec)[k] = nullptr;
+	if (ret_pnt == nullptr) { // pnt is outside of OctTree object
+		// update the axis aligned bounding box
+		_aabb.update(*pnt);
+		// recreate the (enlarged) OctTree
+		_oct_tree.reset(GeoLib::OctTree<GeoLib::Point, 16>::createOctTree(
+			_aabb.getMinPoint(), _aabb.getMaxPoint(), _rel_eps));
+		// add all points that are already in the _data_vec
+		for (std::size_t k(0); k<_data_vec->size(); ++k) {
+			GeoLib::Point *const p((*_data_vec)[k]);
+			_oct_tree->addPoint(p, ret_pnt);
 		}
+		// add the new point
+		ret_pnt = nullptr;
+		_oct_tree->addPoint(pnt, ret_pnt);
+		pnt->setID(_data_vec->size());
+		_data_vec->push_back(pnt);
+		return _data_vec->size()-1;
+	} else {
+		delete pnt;
+		return ret_pnt->getID();
 	}
 
-	auto const pnt_vec_end = std::remove(pnt_vec->begin(), pnt_vec->end(), nullptr);
-	pnt_vec->erase(pnt_vec_end, pnt_vec->end());
-
-	// renumber id-mapping
-	std::size_t id(0);
-	for (std::size_t k(0); k < n_pnts_in_file; k++) {
-		if (pnt_id_map[k] == k) { // point not removed, if necessary: id change
-			pnt_id_map[k] = id;
-			id++;
-		} else {
-			pnt_id_map[k] = pnt_id_map[pnt_id_map[k]];
-		}
-	}
 }
 
 void PointVec::correctNameIDMapping()
diff --git a/GeoLib/PointVec.h b/GeoLib/PointVec.h
index 758ba49694eac8c7b0bf288c40ee80d5c4fa361d..095a17552577024e71f0107a08212b6887ff8a22 100644
--- a/GeoLib/PointVec.h
+++ b/GeoLib/PointVec.h
@@ -18,6 +18,7 @@
 #include "Station.h"
 
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -25,6 +26,7 @@
 #define POINTVEC_H_
 
 #include "TemplateVec.h"
+#include "OctTree.h"
 
 namespace GeoLib
 {
@@ -103,16 +105,6 @@ public:
 	std::string const& getItemNameByID(std::size_t id) const;
 
 private:
-	/**
-	 * Removes points out of the given point set that have the (nearly) same coordinates, i.e.
-	 * the distance of two points is smaller than eps measured in the maximum norm.
-	 * @param pnt_vec the given set of points stored in a vector
-	 * @param pnt_id_map the id mapping
-	 * @param eps if the distance (measured in maximum norm) between points \f$p_i\f$ and \f$p_j\f$
-	 * is smaller than eps the points \f$p_i\f$ and \f$p_j\f$ are considered as equal.
-	 */
-	void makePntsUnique (std::vector<GeoLib::Point*>* pnt_vec, std::vector<std::size_t> &pnt_id_map, double eps = std::numeric_limits<double>::epsilon());
-
 	/**
 	 * After the point set is modified (for example by makePntsUnique()) the mapping has to be corrected.
 	 */
@@ -146,6 +138,7 @@ private:
 
 	AABB<GeoLib::Point> _aabb;
 	double _rel_eps;
+	std::unique_ptr<GeoLib::OctTree<GeoLib::Point, 16>> _oct_tree;
 };
 } // end namespace