Skip to content

v0.2.52..v0.2.53 changeset PoiPolygonTypeScoreExtractor.cpp

Garret Voltz edited this page Feb 12, 2020 · 1 revision
diff --git a/hoot-core/src/main/cpp/hoot/core/algorithms/extractors/poi-polygon/PoiPolygonTypeScoreExtractor.cpp b/hoot-core/src/main/cpp/hoot/core/algorithms/extractors/poi-polygon/PoiPolygonTypeScoreExtractor.cpp
index bdd3ce7..3b6c442 100644
--- a/hoot-core/src/main/cpp/hoot/core/algorithms/extractors/poi-polygon/PoiPolygonTypeScoreExtractor.cpp
+++ b/hoot-core/src/main/cpp/hoot/core/algorithms/extractors/poi-polygon/PoiPolygonTypeScoreExtractor.cpp
@@ -22,22 +22,20 @@
  * This will properly maintain the copyright information. DigitalGlobe
  * copyrights will be updated automatically.
  *
- * @copyright Copyright (C) 2016, 2017, 2018, 2019 DigitalGlobe (http://www.digitalglobe.com/)
+ * @copyright Copyright (C) 2016, 2017, 2018, 2019, 2020 DigitalGlobe (http://www.digitalglobe.com/)
  */
 #include "PoiPolygonTypeScoreExtractor.h"
 
 // hoot
-#include <hoot/core/algorithms/extractors/AddressScoreExtractor.h>
-#include <hoot/core/algorithms/extractors/poi-polygon/PoiPolygonNameScoreExtractor.h>
 #include <hoot/core/conflate/poi-polygon/PoiPolygonDistanceTruthRecorder.h>
-#include <hoot/core/criterion/BuildingCriterion.h>
-#include <hoot/core/schema/MetadataTags.h>
 #include <hoot/core/schema/OsmSchema.h>
 #include <hoot/core/util/ConfigOptions.h>
 #include <hoot/core/util/Factory.h>
-#include <hoot/core/util/FileUtils.h>
+#include <hoot/core/conflate/poi-polygon/PoiPolygonSchemaType.h>
+
 // Qt
 #include <QSet>
+#include <QStringBuilder>
 
 using namespace std;
 
@@ -47,18 +45,16 @@ namespace hoot
 HOOT_FACTORY_REGISTER(FeatureExtractor, PoiPolygonTypeScoreExtractor)
 
 std::shared_ptr<ToEnglishTranslator> PoiPolygonTypeScoreExtractor::_translator;
-QSet<QString> PoiPolygonTypeScoreExtractor::_allTagKeys;
 QMap<QString, QSet<QString>> PoiPolygonTypeScoreExtractor::_categoriesToSchemaTagValues;
-QMultiHash<QString, QString> PoiPolygonTypeScoreExtractor::_typeToNames;
 
-PoiPolygonTypeScoreExtractor::PoiPolygonTypeScoreExtractor() :
+PoiPolygonTypeScoreExtractor::PoiPolygonTypeScoreExtractor(PoiPolygonInfoCachePtr infoCache) :
 _typeScoreThreshold(-1.0),
 _featureDistance(-1.0),
 _printMatchDistanceTruth(false),
 _translateTagValuesToEnglish(false),
-_noTypeFound(false)
-{
-  _readTypeToNames();
+_noTypeFound(false),
+_infoCache(infoCache)
+{ 
 }
 
 void PoiPolygonTypeScoreExtractor::setConfiguration(const Settings& conf)
@@ -80,52 +76,29 @@ void PoiPolygonTypeScoreExtractor::setConfiguration(const Settings& conf)
   }
 }
 
-void PoiPolygonTypeScoreExtractor::_readTypeToNames()
-{
-  // see related note in ImplicitTagUtils::_modifyUndesirableTokens
-  if (_typeToNames.isEmpty())
-  {
-    const QStringList typeToNamesRaw =
-      FileUtils::readFileToList(ConfigOptions().getPoiPolygonTypeToNamesFile());
-    for (int i = 0; i < typeToNamesRaw.size(); i++)
-    {
-      const QString typeToNamesRawEntry = typeToNamesRaw.at(i);
-      const QStringList typeToNamesRawEntryParts = typeToNamesRawEntry.split(";");
-      if (typeToNamesRawEntryParts.size() != 2)
-      {
-        throw HootException("Invalid POI/Polygon type to names entry: " + typeToNamesRawEntry);
-      }
-      const QString kvp = typeToNamesRawEntryParts.at(0);
-      const QStringList names = typeToNamesRawEntryParts.at(1).split(",");
-      for (int j = 0; j < names.size(); j++)
-      {
-        _typeToNames.insert(kvp, names.at(j));
-      }
-    }
-  }
-}
-
 double PoiPolygonTypeScoreExtractor::extract(const OsmMap& /*map*/,
                                              const ConstElementPtr& poi,
                                              const ConstElementPtr& poly) const
 {
-  LOG_VART(_translateTagValuesToEnglish);
+  if (!_infoCache)
+  {
+    throw HootException("No cache passed to extractor.");
+  }
 
-  const Tags& t1 = poi->getTags();
-  const Tags& t2 = poly->getTags();
+  LOG_VART(_translateTagValuesToEnglish);
 
   //be a little more restrictive with each of these
-  if (_failsCuisineMatch(t1, t2))
+  if (_failsCuisineMatch(poi, poly))
   {
     _failedMatchRequirements.append("cusine");
     return 0.0;
   }
-  else if (_failsSportMatch(t1, t2))
+  else if (_failsSportMatch(poi, poly))
   {
     _failedMatchRequirements.append("sport");
     return 0.0;
   }
-  else if (_failsReligionMatch(t1, t2))
+  else if (_failsReligionMatch(poi, poly))
   {
     _failedMatchRequirements.append("religion");
     return 0.0;
@@ -218,8 +191,10 @@ double PoiPolygonTypeScoreExtractor::_getTagScore(ConstElementPtr poi,
 {
   double result = 0.0;
 
-  QStringList poiTagList = _getRelatedTags(poi->getTags());
-  QStringList polyTagList = _getRelatedTags(poly->getTags());
+  QStringList poiTagList, polyTagList;
+  poiTagList = _getRelatedTags(poi->getTags());
+  polyTagList = _getRelatedTags(poly->getTags());
+
   LOG_VART(poiTagList);
   LOG_VART(polyTagList);
   if (poiTagList.size() == 0 || polyTagList.size() == 0)
@@ -257,6 +232,7 @@ double PoiPolygonTypeScoreExtractor::_getTagScore(ConstElementPtr poi,
     polyTagList.removeAll(excludeKvp);
   }
 
+  // Can this be replaced with OsmSchema::score(const Tags& tags1, const Tags& tags2)?
   for (int i = 0; i < poiTagList.size(); i++)
   {
     const QString poiKvp = poiTagList.at(i).toLower();
@@ -329,223 +305,13 @@ QStringList PoiPolygonTypeScoreExtractor::_getRelatedTags(const Tags& tags) cons
            (OsmSchemaCategory::building() | OsmSchemaCategory::use() | OsmSchemaCategory::poi()))
              != OsmSchemaCategory::Empty)
       {
-        tagsList.append(key + "=" + value);
+        tagsList.append(key % "=" % value);
       }
     }
   }
   return tagsList;
 }
 
-bool PoiPolygonTypeScoreExtractor::_typeHasName(const QString& kvp, const QString& name)
-{
-  const QStringList typeNames =_typeToNames.values(kvp);
-  for (int i = 0; i < typeNames.size(); i++)
-  {
-    if (name.contains(typeNames.at(i)))
-    {
-      return true;
-    }
-  }
-  return false;
-}
-
-QString PoiPolygonTypeScoreExtractor::_getMatchingTypeName(const QString& kvp, const QString& name)
-{
-  const QStringList typeNames =_typeToNames.values(kvp);
-  for (int i = 0; i < typeNames.size(); i++)
-  {
-    const QString typeName = typeNames.at(i);
-    if (name.contains(typeName))
-    {
-      return typeName;
-    }
-  }
-  return "";
-}
-
-bool PoiPolygonTypeScoreExtractor::_haveMatchingTypeNames(const QString& kvp, const QString& name1,
-                                                          const QString& name2)
-{
-  const QString typeName1 = _getMatchingTypeName(kvp, name1);
-  const QString typeName2 = _getMatchingTypeName(kvp, name2);
-  return typeName1 == typeName2 && !typeName1.isEmpty();
-}
-
-// As part of #2633, attempted to re-implement some of this hardcoded type code as categories in
-// the hoot schema.  In doing that, several strange bugs started occurring and many poi/poly unit
-// tests started to break.  Using the categories in that manner may not be the best approach and
-// possibly a different on is needed.  The branch "2633-new-categories" is an example of the failed
-// changes.
-
-bool PoiPolygonTypeScoreExtractor::isSchool(ConstElementPtr element)
-{
-  const QString amenityStr = element->getTags().get("amenity").toLower();
-  return amenityStr == "school" || amenityStr == "university";
-}
-
-// Schools are the only example of the concept of trying to reduce reviews between features of the
-// same type when their names indicate they are actually different types.  If this concept proves
-// useful with other types, the code could be abstracted to handle them.
-
-bool PoiPolygonTypeScoreExtractor::isSpecificSchool(ConstElementPtr element)
-{
-  if (!isSchool(element))
-  {
-    return false;
-  }
-  return _typeHasName("amenity=school", element->getTags().getName().toLower());
-}
-
-bool PoiPolygonTypeScoreExtractor::specificSchoolMatch(ConstElementPtr element1,
-                                                       ConstElementPtr element2)
-{
-  if (isSpecificSchool(element1) && isSpecificSchool(element2))
-  {
-    const QString name1 = element1->getTags().getName().toLower();
-    const QString name2 = element2->getTags().getName().toLower();
-    if (_haveMatchingTypeNames("amenity=school", name1, name2))
-    {
-      return true;
-    }
-  }
-  return false;
-}
-
-bool PoiPolygonTypeScoreExtractor::isPark(ConstElementPtr element)
-{
-  return !BuildingCriterion().isSatisfied(element) &&
-         (element->getTags().get("leisure") == "park");
-}
-
-bool PoiPolygonTypeScoreExtractor::isParkish(ConstElementPtr element)
-{
-  if (BuildingCriterion().isSatisfied(element))
-  {
-    return false;
-  }
-  const QString leisureVal = element->getTags().get("leisure").toLower();
-  return leisureVal == "garden" || leisureVal == "dog_park";
-}
-
-bool PoiPolygonTypeScoreExtractor::isPlayground(ConstElementPtr element)
-{
-  return element->getTags().get("leisure") == "playground";
-}
-
-bool PoiPolygonTypeScoreExtractor::isSport(const Tags& tags)
-{
-  const QString leisureVal = tags.get("leisure").toLower();
-  return tags.contains("sport") || leisureVal.contains("sport") || leisureVal == "pitch";
-}
-
-bool PoiPolygonTypeScoreExtractor::isSport(ConstElementPtr element)
-{
-  return isSport(element->getTags());
-}
-
-bool PoiPolygonTypeScoreExtractor::isRestroom(ConstElementPtr element)
-{
-  return element->getTags().get("amenity").toLower() == "toilets";
-}
-
-bool PoiPolygonTypeScoreExtractor::isParking(ConstElementPtr element)
-{
-  const Tags& tags = element->getTags();
-  return
-    tags.get("amenity") == "parking" || tags.contains("parking") ||
-    tags.get("amenity") == "bicycle_parking";
-}
-
-bool PoiPolygonTypeScoreExtractor::isReligion(ConstElementPtr element)
-{
-  return isReligion(element->getTags());
-}
-
-bool PoiPolygonTypeScoreExtractor::isReligion(const Tags& tags)
-{
-  return tags.get("amenity").toLower() == "place_of_worship" ||
-         tags.get("building").toLower() == "church" ||
-         tags.get("building").toLower() == "mosque" ||
-         // TODO: this one is an alias of building=mosque, so we should be getting it from there
-         //instead
-         tags.get("amenity").toLower() == "mosque" ||
-         tags.get("building").toLower() == "synagogue";
-}
-
-bool PoiPolygonTypeScoreExtractor::hasMoreThanOneType(ConstElementPtr element)
-{
-  int typeCount = 0;
-  QStringList typesParsed;
-  if (_allTagKeys.size() == 0)
-  {
-    QSet<QString> allTagKeysTemp = OsmSchema::getInstance().getAllTagKeys();
-    allTagKeysTemp.remove(MetadataTags::Ref1());
-    allTagKeysTemp.remove(MetadataTags::Ref2());
-    allTagKeysTemp.remove("uuid");
-    allTagKeysTemp.remove("name");
-    allTagKeysTemp.remove("ele");
-    for (QSet<QString>::const_iterator it = allTagKeysTemp.begin(); it != allTagKeysTemp.end(); ++it)
-    {
-      const QString tagKey = *it;
-      //address tags aren't really type tags
-      if (!tagKey.startsWith("addr:"))
-      {
-        _allTagKeys.insert(tagKey);
-      }
-    }
-  }
-
-  const Tags elementTags = element->getTags();
-  for (Tags::const_iterator it = elementTags.begin(); it != elementTags.end(); ++it)
-  {
-    const QString elementTagKey = it.key();
-    //there may be duplicate keys in allTags
-    if (_allTagKeys.contains(elementTagKey) && !typesParsed.contains(elementTagKey))
-    {
-      LOG_TRACE("Has key: " << elementTagKey);
-      typeCount++;
-      if (typeCount > 1)
-      {
-        return true;
-      }
-    }
-
-    typesParsed.append(elementTagKey);
-  }
-  return false;
-}
-
-bool PoiPolygonTypeScoreExtractor::hasType(ConstElementPtr element)
-{
-  return
-    OsmSchema::getInstance().getCategories(element->getTags()).intersects(
-      OsmSchemaCategory::building() | OsmSchemaCategory::poi());
-}
-
-bool PoiPolygonTypeScoreExtractor::hasSpecificType(ConstElementPtr element)
-{
-  return
-    hasType(element) && !element->getTags().contains("poi") &&
-          element->getTags().get("building") != QLatin1String("yes") &&
-          element->getTags().get("office") != QLatin1String("yes") &&
-          element->getTags().get("area") != QLatin1String("yes");
-}
-
-bool PoiPolygonTypeScoreExtractor::isRestaurant(ConstElementPtr element)
-{
-  return isRestaurant(element->getTags());
-}
-
-bool PoiPolygonTypeScoreExtractor::isRestaurant(const Tags& tags)
-{
-  return tags.get("amenity") == "restaurant" || tags.get("amenity") == "fast_food";
-}
-
-bool PoiPolygonTypeScoreExtractor::isNatural(ConstElementPtr element)
-{
-  return element->getTags().contains("natural");
-}
-
 bool PoiPolygonTypeScoreExtractor::_haveConflictingTags(const QString& tagKey, const Tags& t1,
                                                         const Tags& t2, QString& tag1Val,
                                                         QString& tag2Val) const
@@ -557,18 +323,23 @@ bool PoiPolygonTypeScoreExtractor::_haveConflictingTags(const QString& tagKey, c
   tag1Val = t1Val;
   tag2Val = t2Val;
   if (t1HasVal && t2HasVal &&
-      OsmSchema::getInstance().score(tagKey + "=" + t1Val, tagKey + "=" + t2Val) != 1.0)
+      OsmSchema::getInstance().score(tagKey % "=" % t1Val, tagKey % "=" % t2Val) != 1.0)
   {
     return true;
   }
   return false;
 }
 
-bool PoiPolygonTypeScoreExtractor::_failsCuisineMatch(const Tags& t1, const Tags& t2) const
+bool PoiPolygonTypeScoreExtractor::_failsCuisineMatch(const ConstElementPtr& e1,
+                                                      const ConstElementPtr& e2) const
 {
+  const Tags& t1 = e1->getTags();
+  const Tags& t2 = e2->getTags();
   QString t1Val;
   QString t2Val;
-  if (isRestaurant(t1) && isRestaurant(t2) && _haveConflictingTags("cuisine", t1, t2, t1Val, t2Val))
+  if (_infoCache->isType(e1, PoiPolygonSchemaType::Restaurant) &&
+      _infoCache->isType(e2, PoiPolygonSchemaType::Restaurant) &&
+      _haveConflictingTags("cuisine", t1, t2, t1Val, t2Val))
   {
     if (//Don't return false on regional, since its location dependent, and we don't take the
         //location into account for this.
@@ -583,9 +354,13 @@ bool PoiPolygonTypeScoreExtractor::_failsCuisineMatch(const Tags& t1, const Tags
   return false;
 }
 
-bool PoiPolygonTypeScoreExtractor::_failsSportMatch(const Tags& t1, const Tags& t2) const
+bool PoiPolygonTypeScoreExtractor::_failsSportMatch(const ConstElementPtr& e1,
+                                                    const ConstElementPtr& e2) const
 {
-  if (isSport(t1) && isSport(t2))
+  const Tags& t1 = e1->getTags();
+  const Tags& t2 = e2->getTags();
+  if (_infoCache->isType(e1, PoiPolygonSchemaType::Sport) &&
+      _infoCache->isType(e2, PoiPolygonSchemaType::Sport))
   {
     QString t1Val;
     QString t2Val;
@@ -599,9 +374,13 @@ bool PoiPolygonTypeScoreExtractor::_failsSportMatch(const Tags& t1, const Tags&
   return false;
 }
 
-bool PoiPolygonTypeScoreExtractor::_failsReligionMatch(const Tags& t1, const Tags& t2) const
+bool PoiPolygonTypeScoreExtractor::_failsReligionMatch(const ConstElementPtr& e1,
+                                                       const ConstElementPtr& e2) const
 {
-  if (isReligion(t1) && isReligion(t2))
+  const Tags& t1 = e1->getTags();
+  const Tags& t2 = e2->getTags();
+  if (_infoCache->isType(e1, PoiPolygonSchemaType::Religion) &&
+       _infoCache->isType(e2, PoiPolygonSchemaType::Religion))
   {
     QString t1Val;
     QString t2Val;
Clone this wiki locally