Skip to content

v0.2.54..v0.2.55 changeset ElementGeometryUtils.cpp

Garret Voltz edited this page Aug 14, 2020 · 1 revision
diff --git a/hoot-core/src/main/cpp/hoot/core/elements/ElementGeometryUtils.cpp b/hoot-core/src/main/cpp/hoot/core/elements/ElementGeometryUtils.cpp
new file mode 100644
index 0000000..6a57fce
--- /dev/null
+++ b/hoot-core/src/main/cpp/hoot/core/elements/ElementGeometryUtils.cpp
@@ -0,0 +1,248 @@
+/*
+ * This file is part of Hootenanny.
+ *
+ * Hootenanny is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --------------------------------------------------------------------
+ *
+ * The following copyright notices are generated automatically. If you
+ * have a new notice to add, please use the format:
+ * " * @copyright Copyright ..."
+ * This will properly maintain the copyright information. DigitalGlobe
+ * copyrights will be updated automatically.
+ *
+ * @copyright Copyright (C) 2020 DigitalGlobe (http://www.digitalglobe.com/)
+ */
+
+#include "ElementGeometryUtils.h"
+
+// Hoot
+#include <hoot/core/util/HootException.h>
+#include <hoot/core/util/Log.h>
+#include <hoot/core/elements/ElementConverter.h>
+#include <hoot/core/util/GeometryUtils.h>
+#include <hoot/core/criterion/PolygonCriterion.h>
+#include <hoot/core/criterion/RelationWithLinearMembersCriterion.h>
+#include <hoot/core/criterion/RelationWithPointMembersCriterion.h>
+#include <hoot/core/criterion/RelationWithPolygonMembersCriterion.h>
+
+// GEOS
+#include <geos/util/TopologyException.h>
+
+namespace hoot
+{
+
+int ElementGeometryUtils::_badGeomCount = 0;
+
+bool ElementGeometryUtils::haveGeometricRelationship(
+  const ConstElementPtr& element1, const ConstElementPtr& element2,
+  const GeometricRelationship& relationship, ConstOsmMapPtr map)
+{
+  if (!element1 || !element2)
+  {
+    throw IllegalArgumentException("One of the input elements is null.");
+  }
+
+  std::shared_ptr<geos::geom::Geometry> geom1 = _getGeometry(element1, map);
+  std::shared_ptr<geos::geom::Geometry> geom2 = _getGeometry(element2, map);
+  bool haveRelationship = false;
+  if (geom1 && geom2)
+  {
+    switch (relationship.getEnum())
+    {
+      case GeometricRelationship::Contains:
+        haveRelationship = geom1->contains(geom2.get());
+        break;
+      case GeometricRelationship::Covers:
+        haveRelationship = geom1->covers(geom2.get());
+        break;
+      case GeometricRelationship::Crosses:
+        haveRelationship = geom1->crosses(geom2.get());
+        break;
+      case GeometricRelationship::DisjointWith:
+        haveRelationship = geom1->disjoint(geom2.get());
+        break;
+      case GeometricRelationship::Intersects:
+        haveRelationship = geom1->intersects(geom2.get());
+        break;
+      case GeometricRelationship::IsWithin:
+        haveRelationship = geom1->within(geom2.get());
+        break;
+      case GeometricRelationship::Overlaps:
+        haveRelationship = geom1->overlaps(geom2.get());
+        break;
+      case GeometricRelationship::Touches:
+        haveRelationship = geom1->touches(geom2.get());
+        break;
+      default:
+        throw IllegalArgumentException("Unsupported geometry relationship type.");
+    }
+  }
+  else
+  {
+    LOG_TRACE(
+      "Unable to calculate geometric relationship: " << relationship.toString() << " for: " <<
+      element1->getElementId() << " and: " << element2->getElementId() << ".");
+  }
+  return haveRelationship;
+}
+
+double ElementGeometryUtils::getDistance(const ConstElementPtr& element1,
+                                         const ConstElementPtr& element2, ConstOsmMapPtr map)
+{
+  if (!element1 || !element2)
+  {
+    throw IllegalArgumentException("One of the input elements is null.");
+  }
+  LOG_VART(element1->getElementId());
+  LOG_VART(element2->getElementId());
+
+  double distance = -1.0;
+
+  std::shared_ptr<geos::geom::Geometry> element1Geom = _getGeometry(element1, map);
+  std::shared_ptr<geos::geom::Geometry> element2Geom = _getGeometry(element2, map);
+  if (element1Geom && element2Geom)
+  {
+    distance = element1Geom->distance(element2Geom.get());
+    LOG_TRACE(
+      "Calculated distance: " << distance << " for: " << element1->getElementId() <<
+      " and: " << element2->getElementId() << ".");
+  }
+  else
+  {
+    LOG_TRACE(
+      "Unable to calculate distance for: " << element1->getElementId() <<
+      " and: " << element2->getElementId() << ".");
+  }
+
+  return distance;
+}
+
+double ElementGeometryUtils::getArea(const ConstElementPtr& element, ConstOsmMapPtr map)
+{
+  if (!element)
+  {
+    throw IllegalArgumentException("The input element is null.");
+  }
+
+  std::shared_ptr<geos::geom::Geometry> geom = _getGeometry(element, map);
+  double area = -1.0;
+  if (geom)
+  {
+    area = geom->getArea();
+  }
+  else
+  {
+    LOG_TRACE("Unable to calculate area for: " << element->getElementId() << ".");
+  }
+  return area;
+}
+
+std::shared_ptr<geos::geom::Geometry> ElementGeometryUtils::_getGeometry(
+  const ConstElementPtr& element, ConstOsmMapPtr map)
+{
+  if (!element)
+  {
+    throw IllegalArgumentException("The input element is null.");
+  }
+
+  std::shared_ptr<geos::geom::Geometry> newGeom;
+  QString errorMsg =
+    "Feature passed to OsmUtils caused topology exception on conversion to a geometry: ";
+  try
+  {
+    newGeom = ElementConverter(map).convertToGeometry(element);
+  }
+  catch (const geos::util::TopologyException& e)
+  {
+    // try to clean it
+    newGeom.reset(GeometryUtils::validateGeometry(newGeom.get()));
+    if (_badGeomCount <= Log::getWarnMessageLimit())
+    {
+      LOG_TRACE(errorMsg << element->toString() << "\n" << e.what());
+      _badGeomCount++;
+    }
+  }
+  catch (const HootException& e)
+  {
+    if (_badGeomCount <= Log::getWarnMessageLimit())
+    {
+      LOG_TRACE(errorMsg << element->toString() << "\n" << e.what());
+      _badGeomCount++;
+    }
+  }
+  if (newGeom.get() &&
+      QString::fromStdString(newGeom->toString()).toUpper().contains("EMPTY"))
+  {
+    if (_badGeomCount <= Log::getWarnMessageLimit())
+    {
+      LOG_TRACE("Invalid element passed: " << newGeom->toString());
+      _badGeomCount++;
+    }
+    newGeom.reset();
+  }
+  return newGeom;
+}
+
+GeometryTypeCriterion::GeometryType geometryTypeForElement(
+  const ConstElementPtr& element, ConstOsmMapPtr map)
+{
+  const ElementType type = element->getElementType();
+  if (type == ElementType::Node)
+  {
+    return GeometryTypeCriterion::GeometryType::Point;
+  }
+  else if (type == ElementType::Way)
+  {
+    if (PolygonCriterion().isSatisfied(element))
+    {
+      return GeometryTypeCriterion::GeometryType::Polygon;
+    }
+    else
+    {
+      return GeometryTypeCriterion::GeometryType::Line;
+    }
+  }
+  else if (type == ElementType::Relation)
+  {
+    if (!map)
+    {
+      throw IllegalArgumentException(
+        "A map must be set when determining the geometry type of relations.");
+    }
+    // using the strict definition only here
+    if (RelationWithLinearMembersCriterion(map).isSatisfied(element))
+    {
+      return GeometryTypeCriterion::GeometryType::Point;
+    }
+    else if (RelationWithPolygonMembersCriterion(map).isSatisfied(element))
+    {
+      return GeometryTypeCriterion::GeometryType::Polygon;
+    }
+    else if (RelationWithLinearMembersCriterion(map).isSatisfied(element))
+    {
+      return GeometryTypeCriterion::GeometryType::Line;
+    }
+    else
+    {
+      return GeometryTypeCriterion::GeometryType::Unknown;
+    }
+  }
+  else
+  {
+    throw IllegalArgumentException("Invalid element type.");
+  }
+}
+
+}
Clone this wiki locally