void MaximalSublineStringMatcher::_validateElement(const ConstOsmMapPtr& map, ElementId eid) const
{
  ConstElementPtr e = map->getElement(eid);

  if (e->getElementType() == ElementType::Relation)
  {
    ConstRelationPtr r = dynamic_pointer_cast<const Relation>(e);

    if (OsmSchema::getInstance().isMultiLineString(*r) == false)
    {
      throw NeedsReviewException("Internal Error: When matching sublines expected a multilinestring "
        "relation not a " + r->getType() + ".  A non-multilinestring should never be found here.  "
        "Please report this to [email protected]");
    }

    const vector<RelationData::Entry>& entries = r->getMembers();
    for (size_t i = 0; i < entries.size(); i++)
    {
      if (entries[i].getElementId().getType() != ElementType::Way)
      {
        throw NeedsReviewException("MultiLineString relations can only contain ways when matching "
                                   "sublines.");
      }
    }
  }
  if (e->getElementType() == ElementType::Way)
  {
    ConstWayPtr w = dynamic_pointer_cast<const Way>(e);

    if (w->getNodeCount() <= 1)
    {
      throw NeedsReviewException("Internal Error: Attempting to match against a zero length way.");
    }
  }
}
void MaximalSublineStringMatcher::_validateElement(const ConstOsmMapPtr& map, ElementId eid) const
{
  ConstElementPtr e = map->getElement(eid);

  if (e->getElementType() == ElementType::Relation)
  {
    ConstRelationPtr r = dynamic_pointer_cast<const Relation>(e);

    if (OsmSchema::getInstance().isMultiLineString(*r) == false)
    {
      throw NeedsReviewException("When matching sublines expected a multilinestring relation not"
                                 " a " + r->getType());
    }

    const vector<RelationData::Entry>& entries = r->getMembers();
    for (size_t i = 0; i < entries.size(); i++)
    {
      if (entries[i].getElementId().getType() != ElementType::Way)
      {
        throw NeedsReviewException("MultiLineString relations can only contain ways when matching "
                                   "sublines.");
      }
    }
  }
}
WaySublineMatchString MaximalSublineStringMatcher::findMatch(const ConstOsmMapPtr& map,
  const ConstElementPtr& e1, const ConstElementPtr& e2, Meters maxRelevantDistance) const
{
  assert(_maxAngle >= 0);
  if (maxRelevantDistance == -1)
  {
    maxRelevantDistance = e1->getCircularError() + e2->getCircularError();
  }

  // make sure the inputs are legit. If either element isn't legit then throw a NeedsReviewException
  _validateElement(map, e1->getElementId());
  _validateElement(map, e2->getElementId());

  // extract the ways from the elements. In most cases it will return a vector of 1, but
  // multilinestrings may contain multiple ways
  vector<ConstWayPtr> ways1 = ExtractWaysVisitor::extractWays(map, e1);
  vector<ConstWayPtr> ways2 = ExtractWaysVisitor::extractWays(map, e2);

  if ((ways1.size() > 4 && ways2.size() > 4) || (ways1.size() + ways2.size() > 7))
  {
    throw NeedsReviewException("Elements contain too many ways and the computational complexity "
                               "is unreasonable.");
  }

  // Try with all combinations of forward and reversed ways. This is very expensive for
  // multilinestrings with lots of ways in them. Though those shouldn't be common.
  vector<bool> reversed1(ways1.size(), false), reversed2(ways2.size(), false);
  ScoredMatch scoredResult = _findBestMatch(map, maxRelevantDistance, ways1, ways2, reversed1,
    reversed2);

  // convert the best match into a WaySublineStringMatch and return.
  try
  {
    WaySublineMatchString result = scoredResult.matches;
    // this likely shouldn't be necessary. See #4593
    result.removeEmptyMatches();
    return result;
  }
  catch(OverlappingMatchesException &e)
  {
    throw NeedsReviewException("Internal Error: Multiple overlapping way matches were found within "
      "one set of ways.  Please report this to [email protected].");
  }
}