Program Listing for File map-manager-inl.h

Return to documentation for file (backend/map-manager/include/map-manager/map-manager-inl.h)

#ifndef MAP_MANAGER_MAP_MANAGER_INL_H_
#define MAP_MANAGER_MAP_MANAGER_INL_H_

#include <chrono>    // NOLINT
#include <iostream>  // NOLINT
#include <memory>
#include <sstream>
#include <string>
#include <thread>  // NOLINT
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#include <aslam/common/reader-writer-lock.h>
#include <glog/logging.h>
#include <maplab-common/file-system-tools.h>
#include <maplab-common/map-manager-config.h>
#include <maplab-common/map-traits.h>
#include <maplab-common/proto-serialization-helper.h>
#include <maplab-common/text-formatting.h>

#include "map-manager/map-manager.h"
#include "map-manager/map-storage.h"

namespace backend {

template <typename MapType>
MapManager<MapType>::MapManager()
    : map_storage_(CHECK_NOTNULL(MapStorage<MapType>::getInstance())) {}

template <typename MapType>
MapType* MapManager<MapType>::getMapMutable(const std::string& key) {
  aslam::ScopedReadLock lock(map_storage_->getContainerMutex());
  return map_storage_->getMapMutable(key);
}

template <typename MapType>
const MapType& MapManager<MapType>::getMap(const std::string& key) const {
  aslam::ScopedReadLock lock(map_storage_->getContainerMutex());
  return map_storage_->getMap(key);
}

template <typename MapType>
typename MapManager<MapType>::MapWriteAccess
MapManager<MapType>::getMapWriteAccess(const std::string& key) {
  aslam::ScopedReadLock lock(map_storage_->getContainerMutex());
  return map_storage_->getMapWriteAccess(key);
}

template <typename MapType>
typename MapManager<MapType>::MapReadAccess
MapManager<MapType>::getMapReadAccess(const std::string& key) const {
  aslam::ScopedReadLock lock(map_storage_->getContainerMutex());
  return map_storage_->getMapReadAccess(key);
}

template <typename MapType>
bool MapManager<MapType>::hasMap(const std::string& key) const {
  aslam::ScopedReadLock lock(map_storage_->getContainerMutex());
  return map_storage_->hasMap(key);
}

template <typename MapType>
bool MapManager<MapType>::isKeyValid(const std::string& key) const {
  return map_storage_->isKeyValid(key);
}

template <typename MapType>
std::string MapManager<MapType>::removeProhibitedCharactersFromKey(
    const std::string& key) const {
  return map_storage_->removeProhibitedCharactersFromKey(key);
}

template <typename MapType>
void MapManager<MapType>::getAllMapKeys(
    std::unordered_set<std::string>* all_map_keys_list) const {
  aslam::ScopedReadLock lock(map_storage_->getContainerMutex());
  map_storage_->getAllMapKeys(all_map_keys_list);
}

template <typename MapType>
void MapManager<MapType>::getAllMapKeys(
    std::vector<std::string>* all_map_keys_list) const {
  aslam::ScopedReadLock lock(map_storage_->getContainerMutex());
  map_storage_->getAllMapKeys(all_map_keys_list);
}

template <typename MapType>
size_t MapManager<MapType>::numberOfMaps() const {
  aslam::ScopedReadLock lock(map_storage_->getContainerMutex());
  return map_storage_->numberOfMaps();
}

template <typename MapType>
void MapManager<MapType>::addMap(
    const std::string& key, AlignedUniquePtr<MapType>& map) {  // NOLINT
  aslam::ScopedWriteLock lock(map_storage_->getContainerMutex());
  map_storage_->addMap(key, map);
}

template <typename MapType>
AlignedUniquePtr<MapType> MapManager<MapType>::releaseMap(
    const std::string& key) {
  std::unique_ptr<typename MapStorage<MapType>::MapAndMutex> map_and_mutex;
  {
    aslam::ScopedWriteLock lock(map_storage_->getContainerMutex());
    map_and_mutex = map_storage_->releaseMapAndMutex(key);
  }

  // Wait until map mutex is no longer in use anywhere.
  while (map_and_mutex->map_mutex.isInUse()) {
    constexpr size_t kSleepTimeMs = 50u;
    std::this_thread::sleep_for(std::chrono::milliseconds(kSleepTimeMs));
  }
  return std::move(map_and_mutex->map);
}

template <typename MapType>
void MapManager<MapType>::deleteMap(const std::string& key) {
  releaseMap(key);
}

template <typename MapType>
void MapManager<MapType>::renameMap(
    const std::string& old_key, const std::string& new_key) {
  aslam::ScopedWriteLock lock(map_storage_->getContainerMutex());
  map_storage_->renameMap(old_key, new_key);
}

template <typename MapType>
void MapManager<MapType>::copyMap(
    const std::string& source_key, const std::string& target_key) {
  map_storage_->getContainerMutex()->acquireReadLock();
  CHECK(map_storage_->hasMap(source_key))
      << "Source map key \"" << source_key << "\" doesn't exist.";
  MapReadAccess source_map = map_storage_->getMapReadAccess(source_key);
  map_storage_->getContainerMutex()->releaseReadLock();

  AlignedUniquePtr<MapType> target_map = aligned_unique<MapType>();
  traits<MapType>::deepCopy(*source_map, target_map.get());

  // Add copied map to storage.
  aslam::ScopedWriteLock lock(map_storage_->getContainerMutex());
  CHECK(!map_storage_->hasMap(target_key))
      << "Target map key \"" << target_key << "\" already exists.";
  map_storage_->addMap(target_key, target_map);
}

template <typename MapType>
bool MapManager<MapType>::mergeMaps(
    const std::string& source_key_merge_base,
    const std::string& source_key_merge_from) {
  CHECK(source_key_merge_base != source_key_merge_from)
      << "Cannot merge maps because the two map keys are identical (\""
      << source_key_merge_base << "\").";
  map_storage_->getContainerMutex()->acquireReadLock();
  for (const std::string& source_key :
       {std::ref(source_key_merge_base), std::ref(source_key_merge_from)}) {
    CHECK(map_storage_->hasMap(source_key))
        << "Source map key \"" << source_key << "\" doesn't exist.";
  }
  MapWriteAccess source_map_merge_base =
      map_storage_->getMapWriteAccess(source_key_merge_base);
  MapReadAccess source_map_merge_from =
      map_storage_->getMapReadAccess(source_key_merge_from);
  map_storage_->getContainerMutex()->releaseReadLock();

  return traits<MapType>::mergeTwoMaps(
      *source_map_merge_from, source_map_merge_base.get());
}

template <typename MapType>
bool MapManager<MapType>::mergeSubmapIntoBaseMap(
    const std::string& source_key_merge_base_map,
    const std::string& source_key_merge_submap) {
  CHECK(source_key_merge_base_map != source_key_merge_submap)
      << "Cannot merge submap into base map because the two map keys are "
         "identical (\""
      << source_key_merge_base_map << "\").";
  map_storage_->getContainerMutex()->acquireReadLock();
  for (const std::string& source_key : {std::ref(source_key_merge_base_map),
                                        std::ref(source_key_merge_submap)}) {
    CHECK(map_storage_->hasMap(source_key))
        << "Source base map key \"" << source_key << "\" doesn't exist.";
  }
  MapWriteAccess source_merge_base_map =
      map_storage_->getMapWriteAccess(source_key_merge_base_map);
  MapReadAccess source_merge_submap =
      map_storage_->getMapReadAccess(source_key_merge_submap);
  map_storage_->getContainerMutex()->releaseReadLock();

  return traits<MapType>::mergeSubmapIntoBaseMap(
      *source_merge_submap, source_merge_base_map.get());
}

template <typename MapType>
void MapManager<MapType>::getMapFolder(
    const std::string& map_key, std::string* map_folder) const {
  CHECK_NOTNULL(map_folder)->clear();
  traits<MapType>::getMapFolder(*getMapReadAccess(map_key), map_folder);
}

template <typename MapType>
void MapManager<MapType>::setMapFolder(
    const std::string& map_key, const std::string& map_folder) {
  traits<MapType>::setMapFolder(map_folder, getMapWriteAccess(map_key).get());
}

template <typename MapType>
bool MapManager<MapType>::loadMapFromFolder(
    const std::string& folder_path, const std::string& key_in) {
  CHECK(!folder_path.empty());
  CHECK(!key_in.empty());
  if (!map_storage_->isKeyValid(key_in)) {
    LOG(ERROR) << "The key \"" << key_in << "\" is not a valid key.";
    return false;
  }
  AlignedUniquePtr<MapType> map = aligned_unique<MapType>();
  if (!traits<MapType>::loadFromFolder(folder_path, map.get())) {
    LOG(ERROR) << "Loading the map failed.";
    return false;
  }

  aslam::ScopedWriteLock lock(map_storage_->getContainerMutex());
  if (map_storage_->hasMap(key_in)) {
    LOG(ERROR) << "Map with the key \"" << key_in
               << "\" already exists in the storage!";
    return false;
  }
  map_storage_->addMap(key_in, map);

  return true;
}

template <typename MapType>
bool MapManager<MapType>::loadMapFromFolder(
    const std::string& folder_path, std::string* key_out) {
  CHECK(!folder_path.empty());

  // Get filename to use as key.
  std::string path_without_trailing_slash(folder_path);
  if (path_without_trailing_slash.back() == '/') {
    // Remove trailing slash as that might cause problems with
    // splitPathAndFilename().
    path_without_trailing_slash.erase(
        path_without_trailing_slash.end() - 1,
        path_without_trailing_slash.end());
  }
  std::string folder_path_without_key, filename;
  common::splitPathAndFilename(
      path_without_trailing_slash, &folder_path_without_key, &filename);
  CHECK(!filename.empty());
  filename = map_storage_->removeProhibitedCharactersFromKey(filename);

  if (key_out != nullptr) {
    *key_out = filename;
  }

  return loadMapFromFolder(folder_path, filename);
}

template <typename MapType>
bool MapManager<MapType>::loadMapFromFolder(const std::string& folder_path) {
  constexpr std::nullptr_t kDontWantGeneratedKey = nullptr;
  return loadMapFromFolder(folder_path, kDontWantGeneratedKey);
}

template <typename MapType>
bool MapManager<MapType>::loadAllMapsFromFolder(
    const std::string& folder_path) {
  constexpr std::nullptr_t kDontWantListOfKeys = nullptr;
  return loadAllMapsFromFolder(folder_path, kDontWantListOfKeys);
}

template <typename MapType>
void MapManager<MapType>::listAllMapsInFolder(
    const std::string& folder_path, std::vector<std::string>* map_list) {
  CHECK_NOTNULL(map_list)->clear();

  CHECK(!folder_path.empty());
  if (!common::pathExists(folder_path)) {
    LOG(ERROR) << "Folder \"" << folder_path << "\" doesn't exist.";
    return;
  }
  const std::string real_path = common::getRealPath(folder_path);

  std::vector<std::string> folder_list;  // Stores all found folders in path.
  common::getAllFoldersInFolder(real_path, &folder_list);

  for (std::string& sub_folder_path : folder_list) {
    // Check if the sub-folder is a valid map.
    if (hasMapOnFileSystem(sub_folder_path)) {
      map_list->emplace_back(sub_folder_path);
    }
  }
}

template <typename MapType>
bool MapManager<MapType>::loadAllMapsFromFolder(
    const std::string& folder_path, std::unordered_set<std::string>* new_keys) {
  CHECK(!folder_path.empty());
  if (new_keys != nullptr) {
    new_keys->clear();
  }

  if (!common::pathExists(folder_path)) {
    LOG(ERROR) << "Folder \"" << folder_path << "\" doesn't exist.";
    return false;
  }

  std::vector<std::string> map_list;  // Stores path to all maps to load.
  listAllMapsInFolder(folder_path, &map_list);

  std::vector<std::string> key_list;  // Stores keys for the maps to load.
  getDefaultMapKeys(map_list, &key_list);

  if (map_list.empty()) {
    LOG(ERROR) << "No maps found in \"" << folder_path
               << "\" that can be loaded.";
    return false;
  }

  std::stringstream maps_to_load_ss;
  maps_to_load_ss << "Found the following maps to load:\n";
  for (size_t i = 0u; i < map_list.size(); ++i) {
    maps_to_load_ss << "- "
                    << common::formatText(
                           key_list[i], common::FormatOptions::kBold)
                    << " from " << map_list[i] << "\n";
  }
  VLOG(1) << maps_to_load_ss.str() << "\n";

  aslam::ScopedWriteLock lock(map_storage_->getContainerMutex());
  std::unordered_set<std::string> key_set;
  for (const std::string& map_key : key_list) {
    if (!key_set.emplace(map_key).second) {
      LOG(ERROR) << "Found duplicate map key: " << map_key << " in folder "
                 << folder_path << ". No maps will be loaded.";
      return false;
    }
    if (map_storage_->hasMap(map_key)) {
      LOG(ERROR) << "No maps will be loaded because a map with key \""
                 << map_key << "\" already exists in the storage.";
      return false;
    }
  }

  if (new_keys != nullptr) {
    new_keys->insert(key_list.cbegin(), key_list.cend());
  }

  // Load and insert all maps.
  CHECK_EQ(map_list.size(), key_list.size());
  for (size_t i = 0u; i < map_list.size(); ++i) {
    const std::string& map_folder = map_list[i];
    const std::string& key_name = key_list[i];
    CHECK(!map_folder.empty());
    CHECK(!key_name.empty());
    CHECK(isKeyValid(key_name));
    CHECK(!map_storage_->hasMap(key_name));

    AlignedUniquePtr<MapType> map = aligned_unique<MapType>();
    CHECK(traits<MapType>::loadFromFolder(map_folder, map.get()))
        << "Loading map " << map_folder << " failed.";
    map_storage_->addMap(key_name, map);
    VLOG(1) << "Loaded map " << key_name;
  }
  return true;
}

template <typename MapType>
bool MapManager<MapType>::saveMapToFolder(
    const std::string& key, const std::string& folder_path) const {
  const SaveConfig config;
  return saveMapToFolder(key, folder_path, config);
}

template <typename MapType>
bool MapManager<MapType>::saveMapToFolder(
    const std::string& key, const std::string& folder_path,
    const SaveConfig& config) const {
  CHECK(!key.empty());
  CHECK(!folder_path.empty());

  map_storage_->getContainerMutex()->acquireReadLock();
  if (!map_storage_->hasMap(key)) {
    map_storage_->getContainerMutex()->releaseReadLock();
    LOG(ERROR) << "Map with key \"" << key << "\" doesn't exist.";
    return false;
  }
  MapWriteAccess map = map_storage_->getMapWriteAccess(key);
  map_storage_->getContainerMutex()->releaseReadLock();
  return traits<MapType>::saveToFolder(folder_path, config, map.get());
}

template <typename MapType>
bool MapManager<MapType>::saveMapToMapFolder(const std::string& key) const {
  const SaveConfig config;
  return saveMapToMapFolder(key, config);
}

template <typename MapType>
bool MapManager<MapType>::saveMapToMapFolder(
    const std::string& key, const SaveConfig& config) const {
  std::string map_folder;
  getMapFolder(key, &map_folder);
  CHECK(!map_folder.empty());
  return saveMapToFolder(key, map_folder, config);
}

template <typename MapType>
bool MapManager<MapType>::saveAllMapsToFolder(
    const std::string& folder_path) const {
  const SaveConfig config;
  return saveAllMapsToFolder(folder_path, config);
}

template <typename MapType>
bool MapManager<MapType>::saveAllMapsToFolder(
    const std::string& folder_path, const SaveConfig& config) const {
  // Create path if it doesn't exist.
  if (!folder_path.empty() && !common::pathExists(folder_path)) {
    if (!common::createPath(folder_path)) {
      LOG(ERROR) << "Could not create path to file!";
      return false;
    }
  }

  // Get all map keys.
  aslam::ScopedReadLock lock(map_storage_->getContainerMutex());
  std::unordered_set<std::string> all_map_keys_list;
  map_storage_->getAllMapKeys(&all_map_keys_list);

  if (all_map_keys_list.empty()) {
    LOG(ERROR) << "No maps stored that could be saved.";
    return false;
  }

  std::unordered_map<std::string, std::string> key_to_folder_map;

  // Check if all maps can be saved.
  for (const std::string& key : all_map_keys_list) {
    std::string complete_folder_path;
    if (folder_path.empty()) {
      // If the map gets saved into the map folder, there is no need to append
      // the key.
      typename common::Monitor<MapType>::ReadAccess map =
          map_storage_->getMapReadAccess(key);
      if (!traits<MapType>::hasMapFolder(*map)) {
        LOG(ERROR) << "Can't save map \"" << key
                   << "\" to map folder because it doesn't have a map folder "
                      "associated with it.";
        return false;
      }
      traits<MapType>::getMapFolder(*map, &complete_folder_path);
    } else {
      common::concatenateFolderAndFileName(
          folder_path, key, &complete_folder_path);
    }

    key_to_folder_map.emplace(key, complete_folder_path);

    common::concatenateFolderAndFileName(
        complete_folder_path, traits<MapType>::getSubFolderName(),
        &complete_folder_path);
    if (!config.overwrite_existing_files &&
        (common::pathExists(complete_folder_path) ||
         common::fileExists(complete_folder_path))) {
      LOG(ERROR) << "No maps will be saved because this folder \""
                 << complete_folder_path << "\" already contains a map!";
      return false;
    }
  }

  // Save all maps.
  for (const std::string& key : all_map_keys_list) {
    MapWriteAccess map = map_storage_->getMapWriteAccess(key);
    CHECK(traits<MapType>::saveToFolder(
        key_to_folder_map[key], config, map.get()));
  }

  return true;
}

template <typename MapType>
bool MapManager<MapType>::saveAllMapsToMapFolder() const {
  const SaveConfig config;
  return saveAllMapsToMapFolder(config);
}

template <typename MapType>
bool MapManager<MapType>::saveAllMapsToMapFolder(
    const SaveConfig& config) const {
  const std::string kUseMapFolder = "";
  return saveAllMapsToFolder(kUseMapFolder, config);
}

template <typename MapType>
bool MapManager<MapType>::hasMapOnFileSystem(
    const std::string& folder_path) const {
  return traits<MapType>::hasMapOnFileSystem(folder_path);
}

template <typename MapType>
void MapManager<MapType>::getDefaultMapKeys(
    const std::vector<std::string>& map_list,
    std::vector<std::string>* key_list) {
  CHECK_NOTNULL(key_list)->clear();
  key_list->reserve(map_list.size());

  for (const std::string& map_folder : map_list) {
    CHECK(!map_folder.empty());
    std::string key_name, folder_without_key;
    common::splitPathAndFilename(map_folder, &folder_without_key, &key_name);
    key_name = map_storage_->removeProhibitedCharactersFromKey(key_name);
    key_list->emplace_back(key_name);
  }

  CHECK_EQ(map_list.size(), key_list->size());
}

}  // namespace backend

#endif  // MAP_MANAGER_MAP_MANAGER_INL_H_