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;