/// For algorithm details, see http://wiki.openstreetmap.org/wiki/Relation:multipolygon/Algorithm
void MultipolygonProcessor::process() {
  bool allClosed = true;
  // NOTE do not count multipolygon tag itself
  bool hasNoTags = relation_.tags.size() < 2;
  Ints outerIndecies;
  Ints innerIndecies;
  CoordinateSequences sequences;
  for (const auto &member : members_) {

    if (member.type!="w")
      continue;

    Coordinates coordinates;
    auto wayPair = context_.wayMap.find(member.refId);
    if (wayPair!=context_.wayMap.end()) {
      coordinates = wayPair->second->coordinates;
    } else {
      auto areaPair = context_.areaMap.find(member.refId);
      if (areaPair!=context_.areaMap.end()) {
        coordinates = areaPair->second->coordinates;

        // NOTE make coordinates to be closed ring
        coordinates.push_back(coordinates[0]);

        // NOTE merge tags to relation
        // hasNoTags prevents the case where relation has members with their own tags
        // which should be processed independently (geometry reusage)
        if (member.role=="outer" && hasNoTags)
          mergeTags(areaPair->second->tags);
      } else {
        auto relationPair = context_.relationMap.find(member.refId);
        if (relationPair==context_.relationMap.end())
          return; //  NOTE cannot fill relation: incomplete data

        resolve_(*relationPair->second);
      }
    }

    if (coordinates.empty())
      continue;

    if (member.role=="outer")
      outerIndecies.push_back(static_cast<int>(sequences.size()));
    else if (member.role=="inner")
      innerIndecies.push_back(static_cast<int>(sequences.size()));
    else
      continue;

    auto sequence = std::make_shared<CoordinateSequence>(member.refId, coordinates);
    if (!sequence->isClosed())
      allClosed = false;

    sequences.push_back(sequence);
  }

  if (outerIndecies.size()==1 && allClosed)
    simpleCase(sequences, outerIndecies, innerIndecies);
  else
    complexCase(sequences);
}
// see http://wiki.openstreetmap.org/wiki/Relation:multipolygon/Algorithm
void MultipolygonProcessor::process()
{
    bool allClosed = true;

    Ints outerIndecies;
    Ints innerIndecies;
    CoordinateSequences sequences;
    for (const auto& member : members_) {

        if (member.type != "way") 
            continue;

        Coordinates coordinates;
        auto wayPair = context_.wayMap.find(member.refId);
        if (wayPair != context_.wayMap.end()) {
            coordinates = wayPair->second->coordinates;
        }
        else {
            auto areaPair = context_.areaMap.find(member.refId);
            if (areaPair != context_.areaMap.end()) {
                coordinates = areaPair->second->coordinates;
                // NOTE merge tags to relation
                if (member.role == "outer")
                    mergeTags(areaPair->second->tags);
            }
            else {
                auto relationPair = context_.relationMap.find(member.refId);
                if (relationPair == context_.relationMap.end())
                    return; //  NOTE cannot fill relation: incomplete data

                resolve_(*relationPair->second);
            }
        }

        if (coordinates.empty()) 
            continue;

        if (member.role == "outer")
            outerIndecies.push_back(static_cast<int>(sequences.size()));
        else if (member.role == "inner")
            innerIndecies.push_back(static_cast<int>(sequences.size()));
        else
            continue;

        auto sequence = std::make_shared<CoordinateSequence>(member.refId, coordinates);
        if (!sequence->isClosed()) 
            allClosed = false;

        sequences.push_back(sequence);
    }

    if (outerIndecies.size() == 1 && allClosed)
        simpleCase(sequences, outerIndecies, innerIndecies);
    else
        complexCase(sequences);
}
std::vector<std::shared_ptr<MultipolygonProcessor::CoordinateSequence>> MultipolygonProcessor::createRings(
    CoordinateSequences &sequences) const {
  CoordinateSequences closedRings;
  std::shared_ptr<MultipolygonProcessor::CoordinateSequence> currentRing = nullptr;
  while (!sequences.empty()) {
    if (currentRing==nullptr) {
      // start a new ring with any remaining node sequence
      auto lastIndex = sequences.size() - 1;
      currentRing = sequences[lastIndex];
      sequences.erase(sequences.begin() + lastIndex);
    } else {
      // try to continue the ring by appending a node sequence
      bool isFound = false;
      for (auto it = sequences.begin(); it!=sequences.end(); ++it) {
        if (!currentRing->tryAdd(**it)) continue;
        isFound = true;
        sequences.erase(it);
        break;
      }

      if (!isFound)
        return CoordinateSequences();
    }

    // check whether the ring under construction is closed
    if (currentRing!=nullptr && currentRing->isClosed()) {
      // TODO check that it isn't self-intersecting!
      closedRings.push_back(currentRing);
      currentRing = nullptr;
    }
  }

  return std::move(closedRings);
}