Skip to content

v0.2.53..v0.2.54 changeset PoiPolygonInfoCache.cpp

Garret Voltz edited this page Mar 31, 2020 · 1 revision
diff --git a/hoot-core/src/main/cpp/hoot/core/conflate/poi-polygon/PoiPolygonInfoCache.cpp b/hoot-core/src/main/cpp/hoot/core/conflate/poi-polygon/PoiPolygonInfoCache.cpp
index 0e79174..0d92373 100644
--- a/hoot-core/src/main/cpp/hoot/core/conflate/poi-polygon/PoiPolygonInfoCache.cpp
+++ b/hoot-core/src/main/cpp/hoot/core/conflate/poi-polygon/PoiPolygonInfoCache.cpp
@@ -35,6 +35,7 @@
 #include <hoot/core/util/Log.h>
 #include <hoot/core/util/StringUtils.h>
 #include <hoot/core/util/Factory.h>
+#include <hoot/core/util/GeometryUtils.h>
 #include <hoot/core/algorithms/extractors/AddressScoreExtractor.h>
 
 // Std
@@ -47,53 +48,91 @@ namespace hoot
 {
 
 PoiPolygonInfoCache::PoiPolygonInfoCache(const ConstOsmMapPtr& map) :
-_map(map)
+_map(map),
+_cacheEnabled(true),
+_elementIntersectsCache(CACHE_SIZE_DEFAULT),
+_isTypeCache(CACHE_SIZE_DEFAULT),
+_hasCriterionCache(CACHE_SIZE_DEFAULT),
+_hasMoreThanOneTypeCache(CACHE_SIZE_DEFAULT),
+_numAddressesCache(CACHE_SIZE_DEFAULT),
+_reviewDistanceCache(CACHE_SIZE_DEFAULT)
 {
+  _geometryCache.reset(
+    new Tgs::LruCache<ElementId, std::shared_ptr<geos::geom::Geometry>>(CACHE_SIZE_DEFAULT));
 }
 
 void PoiPolygonInfoCache::setConfiguration(const Settings& conf)
 {
   _addressParser.setConfiguration(conf);
+
+  const int maxCacheSize = ConfigOptions(conf).getPoiPolygonMaxSizePerCache();
+  if (maxCacheSize > 0)
+  {
+    _elementIntersectsCache.setMaxCost(maxCacheSize);
+    _isTypeCache.setMaxCost(maxCacheSize);
+    _hasCriterionCache.setMaxCost(maxCacheSize);
+    _hasMoreThanOneTypeCache.setMaxCost(maxCacheSize);
+    _numAddressesCache.setMaxCost(maxCacheSize);
+    _reviewDistanceCache.setMaxCost(maxCacheSize);
+    _geometryCache.reset(
+        new Tgs::LruCache<ElementId, std::shared_ptr<geos::geom::Geometry>>(maxCacheSize));
+  }
+  else
+  {
+    _cacheEnabled = false;
+  }
 }
 
 void PoiPolygonInfoCache::clear()
 {
-  _numCacheHitsByCacheType.clear();
-  _numCacheEntriesByCacheType.clear();
-
-  _isTypeCache.clear();
-  _hasCriterionCache.clear();
-  _criterionCache.clear();
-  _geometryCache.clear();
-  _hasMoreThanOneTypeCache.clear();
-  _numAddressesCache.clear();
-  _elementIntersectsCache.clear();
-  _numAddressesCache.clear();
+  if (_cacheEnabled)
+  {
+    LOG_DEBUG("Clearing cache...");
+
+    _numCacheHitsByCacheType.clear();
+    _numCacheEntriesByCacheType.clear();
+
+    _isTypeCache.clear();
+    _hasCriterionCache.clear();
+    _criterionCache.clear();
+    _geometryCache->clear();
+    _hasMoreThanOneTypeCache.clear();
+    _numAddressesCache.clear();
+    _elementIntersectsCache.clear();
+    _numAddressesCache.clear();
+  }
 }
 
 void PoiPolygonInfoCache::printCacheInfo()
 {
-  // Add one for the address cache on AddressScoreExtractor.
-  LOG_VARD(_numCacheHitsByCacheType.size());
-  LOG_DEBUG("POI/Polygon caches used: " <<  (_numCacheHitsByCacheType.size() + 1));
-  for (QMap<QString, int>::const_iterator numCacheHitsByCacheTypeItr = _numCacheHitsByCacheType.begin();
-       numCacheHitsByCacheTypeItr != _numCacheHitsByCacheType.end(); ++numCacheHitsByCacheTypeItr)
+  if (_cacheEnabled)
   {
+    // Add one for the address cache on AddressScoreExtractor.
+    LOG_VARD(_numCacheHitsByCacheType.size());
+    LOG_DEBUG("POI/Polygon caches used: " <<  (_numCacheHitsByCacheType.size() + 1));
+    for (QMap<QString, int>::const_iterator numCacheHitsByCacheTypeItr = _numCacheHitsByCacheType.begin();
+         numCacheHitsByCacheTypeItr != _numCacheHitsByCacheType.end(); ++numCacheHitsByCacheTypeItr)
+    {
+      const QString line =
+        QString("%1:\t%2 hits     entries: %3")
+          .arg(numCacheHitsByCacheTypeItr.key())
+          .arg(StringUtils::formatLargeNumber(numCacheHitsByCacheTypeItr.value()))
+          .arg(
+            StringUtils::formatLargeNumber(
+              _numCacheEntriesByCacheType[numCacheHitsByCacheTypeItr.key()]));
+      LOG_DEBUG(line);
+    }
     const QString line =
       QString("%1:\t%2 hits     entries: %3")
-        .arg(numCacheHitsByCacheTypeItr.key())
-        .arg(StringUtils::formatLargeNumber(numCacheHitsByCacheTypeItr.value()))
-        .arg(
-          StringUtils::formatLargeNumber(
-            _numCacheEntriesByCacheType[numCacheHitsByCacheTypeItr.key()]));
+        .arg("address")
+        .arg(StringUtils::formatLargeNumber(AddressScoreExtractor::getNumAddressCacheHits()))
+        .arg(StringUtils::formatLargeNumber(AddressScoreExtractor::getAddressCacheSize()));
     LOG_DEBUG(line);
   }
-  const QString line =
-    QString("%1:\t%2 hits     entries: %3")
-      .arg("address")
-      .arg(StringUtils::formatLargeNumber(AddressScoreExtractor::getNumAddressCacheHits()))
-      .arg(StringUtils::formatLargeNumber(AddressScoreExtractor::getAddressCacheSize()));
-  LOG_DEBUG(line);
+  else
+  {
+    LOG_DEBUG("POI/Polygon caching disabled.")
+  }
 }
 
 void PoiPolygonInfoCache::_incrementCacheHitCount(const QString& cacheTypeKey)
@@ -130,12 +169,11 @@ std::shared_ptr<geos::geom::Geometry> PoiPolygonInfoCache::_getGeometry(
     throw IllegalArgumentException("The input element is null.");
   }
 
-  QHash<ElementId, std::shared_ptr<geos::geom::Geometry>>::const_iterator itr =
-    _geometryCache.find(element->getElementId());
-  if (itr != _geometryCache.end())
+  std::shared_ptr<geos::geom::Geometry> cachedVal;
+  if (_cacheEnabled && _geometryCache->get(element->getElementId(), cachedVal))
   {
     _incrementCacheHitCount("geometry");
-    return itr.value();
+    return cachedVal;
   }
 
   std::shared_ptr<geos::geom::Geometry> newGeom;
@@ -147,6 +185,8 @@ std::shared_ptr<geos::geom::Geometry> PoiPolygonInfoCache::_getGeometry(
   }
   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());
@@ -171,8 +211,13 @@ std::shared_ptr<geos::geom::Geometry> PoiPolygonInfoCache::_getGeometry(
     }
     newGeom.reset();
   }
-  _geometryCache[element->getElementId()] = newGeom;
-  _incrementCacheSizeCount("geometry");
+
+  if (_cacheEnabled)
+  {
+    _geometryCache->insert(element->getElementId(), newGeom);
+    _incrementCacheSizeCount("geometry");
+  }
+
   return newGeom;
 }
 
@@ -219,16 +264,24 @@ int PoiPolygonInfoCache::numAddresses(const ConstElementPtr& element)
     throw IllegalArgumentException("The input element is null.");
   }
 
-  QHash<ElementId, int>::const_iterator itr = _numAddressesCache.find(element->getElementId());
-  if (itr != _numAddressesCache.end())
+  if (_cacheEnabled)
   {
-    _incrementCacheHitCount("numAddresses");
-    return itr.value();
+    const int* cachedVal = _numAddressesCache[element->getElementId()];
+    if (cachedVal != 0)
+    {
+      _incrementCacheHitCount("numAddresses");
+      return *cachedVal;
+    }
   }
 
   const int numAddresses = _addressParser.numAddressesRecursive(element, *_map);
-  _numAddressesCache[element->getElementId()] = numAddresses;
-  _incrementCacheSizeCount("numAddresses");
+
+  if (_cacheEnabled)
+  {
+    _numAddressesCache.insert(element->getElementId(), new int(numAddresses));
+    _incrementCacheSizeCount("numAddresses");
+  }
+
   return numAddresses;
 }
 
@@ -272,25 +325,30 @@ bool PoiPolygonInfoCache::elementsIntersect(
     throw IllegalArgumentException("One of the input elements is null.");
   }
 
-  const QString key1 =
-    element1->getElementId().toString() % ";" % element2->getElementId().toString();
-  const QString key2 =
-    element2->getElementId().toString() % ";" % element1->getElementId().toString();
-  QHash<QString, bool>::const_iterator itr = _elementIntersectsCache.find(key1);
-  if (itr != _elementIntersectsCache.end())
-  {
-    _incrementCacheHitCount("intersects");
-    const bool intersects = itr.value();
-    LOG_TRACE("Found cached intersects: " << intersects << " for key: " << key1);
-    return intersects;
-  }
-  itr = _elementIntersectsCache.find(key2);
-  if (itr != _elementIntersectsCache.end())
+  QString key1;
+  QString key2;
+
+  if (_cacheEnabled)
   {
-    _incrementCacheHitCount("intersects");
-    const bool intersects = itr.value();
-    LOG_TRACE("Found cached intersects: " << intersects << " for key: " << key2);
-    return intersects;
+    key1 = element1->getElementId().toString() % ";" % element2->getElementId().toString();
+    key2 = element2->getElementId().toString() % ";" % element1->getElementId().toString();
+
+    bool* cachedVal = _elementIntersectsCache[key1];
+    if (cachedVal != 0)
+    {
+      _incrementCacheHitCount("intersects");
+      const bool intersects = *cachedVal;
+      LOG_TRACE("Found cached intersects: " << intersects << " for key: " << key1);
+      return intersects;
+    }
+    cachedVal = _elementIntersectsCache[key2];
+    if (cachedVal != 0)
+    {
+      _incrementCacheHitCount("intersects");
+      const bool intersects = *cachedVal;
+      LOG_TRACE("Found cached intersects: " << intersects << " for key: " << key2);
+      return intersects;
+    }
   }
 
   std::shared_ptr<geos::geom::Geometry> geom1 = _getGeometry(element1);
@@ -299,6 +357,10 @@ bool PoiPolygonInfoCache::elementsIntersect(
   if (geom1 && geom2)
   {
     intersects = geom1->intersects(geom2.get());
+
+    LOG_TRACE(
+      "Calculated intersects: " << intersects << " for elements: " <<
+      element1->getElementId() << " and " << element2->getElementId() << ".");
   }
   else
   {
@@ -306,8 +368,13 @@ bool PoiPolygonInfoCache::elementsIntersect(
       "Unable to calculate intersects for: " << element1->getElementId() <<
       " and: " << element2->getElementId() << ".");
   }
-  _elementIntersectsCache[key1] = intersects;
-  _incrementCacheSizeCount("intersects");
+
+  if (_cacheEnabled)
+  {
+    _elementIntersectsCache.insert(key1, new bool(intersects));
+    _incrementCacheSizeCount("intersects");
+  }
+
   return intersects;
 }
 
@@ -371,18 +438,28 @@ bool PoiPolygonInfoCache::hasCriterion(const ConstElementPtr& element,
       "The input element is null or the criterion class name is empty.");
   }
 
-  const QString key = element->getElementId().toString() % ";" % criterionClassName;
-  QHash<QString, bool>::const_iterator itr = _hasCriterionCache.find(key);
-  if (itr != _hasCriterionCache.end())
+  QString key;
+
+  if (_cacheEnabled)
   {
-    _incrementCacheHitCount("hasCrit");
-    return itr.value();
+    key = element->getElementId().toString() % ";" % criterionClassName;
+    const bool* cachedVal = _hasCriterionCache[key];
+    if (cachedVal != 0)
+    {
+      _incrementCacheHitCount("hasCrit");
+      return *cachedVal;
+    }
   }
 
   ElementCriterionPtr crit = _getCrit(criterionClassName);
   const bool hasCrit = crit->isSatisfied(element);
-  _hasCriterionCache[key] = hasCrit;
-  _incrementCacheSizeCount("hasCrit");
+
+  if (_cacheEnabled)
+  {
+    _hasCriterionCache.insert(key, new bool(hasCrit));
+    _incrementCacheSizeCount("hasCrit");
+  }
+
   return hasCrit;
 }
 
@@ -393,11 +470,14 @@ ElementCriterionPtr PoiPolygonInfoCache::_getCrit(const QString& criterionClassN
     throw IllegalArgumentException("The criterion class name is empty.");
   }
 
-  QHash<QString, ElementCriterionPtr>::const_iterator itr =
-    _criterionCache.find(criterionClassName);
-  if (itr != _criterionCache.end())
+  if (_cacheEnabled)
   {
-    return itr.value();
+    QHash<QString, ElementCriterionPtr>::const_iterator itr =
+      _criterionCache.find(criterionClassName);
+    if (itr != _criterionCache.end())
+    {
+      return itr.value();
+    }
   }
 
   ElementCriterionPtr crit =
@@ -408,7 +488,12 @@ ElementCriterionPtr PoiPolygonInfoCache::_getCrit(const QString& criterionClassN
     throw IllegalArgumentException(
       "Invalid criterion passed to PoiPolygonInfoCache::hasCriterion: " + criterionClassName);
   }
-  _criterionCache[criterionClassName] = crit;
+
+  if (_cacheEnabled)
+  {
+     _criterionCache[criterionClassName] = crit;
+  }
+
   return crit;
 }
 
@@ -421,14 +506,20 @@ bool PoiPolygonInfoCache::isType(const ConstElementPtr& element, const PoiPolygo
     throw IllegalArgumentException("The input element is null.");
   }
 
-  const QString key = element->getElementId().toString() % ";" % type.toString().toLower();
-  QHash<QString, bool>::const_iterator itr = _isTypeCache.find(key);
-  if (itr != _isTypeCache.end())
+  QString key;
+
+  if (_cacheEnabled)
   {
-    _incrementCacheHitCount("isType");
-    return itr.value();
+    key = element->getElementId().toString() % ";" % type.toString().toLower();
+    const bool* cachedVal = _isTypeCache[key];
+    if (cachedVal != 0)
+    {
+      _incrementCacheHitCount("isType");
+      return *cachedVal;
+    }
   }
 
+
   // A re-design is needed to make this a little less maintenance prone...possiby add custom
   // schema lookup files for poi/poly?
   bool isType = false;
@@ -471,8 +562,11 @@ bool PoiPolygonInfoCache::isType(const ConstElementPtr& element, const PoiPolygo
       throw IllegalArgumentException("Unsupported POI/Polygon schema type.");
   }
 
-  _isTypeCache[key] = isType;
-  _incrementCacheSizeCount("isType");
+  if (_cacheEnabled)
+  {
+    _isTypeCache.insert(key, new bool(isType));
+    _incrementCacheSizeCount("isType");
+  }
 
   return isType;
 }
@@ -484,17 +578,24 @@ bool PoiPolygonInfoCache::hasMoreThanOneType(const ConstElementPtr& element)
     throw IllegalArgumentException("The input element is null.");
   }
 
-  QHash<ElementId, bool>::const_iterator itr =
-    _hasMoreThanOneTypeCache.find(element->getElementId());
-  if (itr != _hasMoreThanOneTypeCache.end())
+  if (_cacheEnabled)
   {
-    _incrementCacheHitCount("hasMoreThanOneType");
-    return itr.value();
+    const bool* cachedVal = _hasMoreThanOneTypeCache[element->getElementId()];
+    if (cachedVal != 0)
+    {
+      _incrementCacheHitCount("hasMoreThanOneType");
+      return *cachedVal;
+    }
   }
 
   const bool hasMoreThanOneType = PoiPolygonSchema::hasMoreThanOneType(element);
-  _hasMoreThanOneTypeCache[element->getElementId()] = hasMoreThanOneType;
-  _incrementCacheSizeCount("hasMoreThanOneType");
+
+  if (_cacheEnabled)
+  {
+    _hasMoreThanOneTypeCache.insert(element->getElementId(), new bool(hasMoreThanOneType));
+    _incrementCacheSizeCount("hasMoreThanOneType");
+  }
+
   return hasMoreThanOneType;
 }
 
@@ -513,38 +614,44 @@ double PoiPolygonInfoCache::getReviewDistance(const ConstElementPtr& element,
                                           const Tags& polyTags,
                                           const double reviewDistanceThresholdDefault)
 {
-  QHash<ElementId, double>::const_iterator itr = _reviewDistanceCache.find(element->getElementId());
-  if (itr != _reviewDistanceCache.end())
+  if (_cacheEnabled)
+  {
+    const double* cachedVal = _reviewDistanceCache[element->getElementId()];
+    if (cachedVal != 0)
+    {
+      _incrementCacheHitCount("reviewDistance");
+      double distance = *cachedVal;
+      LOG_TRACE("Found review distance: " << distance << " for: " << element->getElementId());
+      return distance;
+    }
+  }
+
+  double distance;
+  const Tags& tags = element->getTags();
+  //these distances could be moved to a config
+  if (tags.get("leisure") == "park")
   {
-    _incrementCacheHitCount("reviewDistance");
-    double distance = itr.value();
-    LOG_TRACE("Found review distance: " << distance << " for: " << element->getElementId());
-    return distance;
+    distance = 25.0;
+  }
+  else if ((tags.get("station").toLower() == "light_rail" ||
+            tags.get("railway").toLower() == "platform") &&
+           (polyTags.get("subway").toLower() == "yes" ||
+            polyTags.get("tunnel").toLower() == "yes"))
+  {
+    distance = 150.0;
   }
   else
   {
-    double distance;
-    const Tags& tags = element->getTags();
-    //these distances could be moved to a config
-    if (tags.get("leisure") == "park")
-    {
-      distance = 25.0;
-    }
-    else if ((tags.get("station").toLower() == "light_rail" ||
-              tags.get("railway").toLower() == "platform") &&
-             (polyTags.get("subway").toLower() == "yes" ||
-              polyTags.get("tunnel").toLower() == "yes"))
-    {
-      distance = 150.0;
-    }
-    else
-    {
-      distance = reviewDistanceThresholdDefault;
-    }
-    _reviewDistanceCache[element->getElementId()] = distance;
+    distance = reviewDistanceThresholdDefault;
+  }
+
+  if (_cacheEnabled)
+  {
+    _reviewDistanceCache.insert(element->getElementId(), new double(distance));
     _incrementCacheSizeCount("reviewDistance");
-    return distance;
   }
+
+  return distance;
 }
 
 }
Clone this wiki locally