route.cpp 6.35 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
bool flight_plan::route(const BoostPolygon &area,
                        const flight_plan::Transects &transects,
                        Transects &transectsRouted, flight_plan::Route &route,
                        string &errorString) {
  //=======================================
  // Route Transects using Google or-tools.
  //=======================================

  // Create vertex list;
  BoostLineString vertices;
  size_t n0 = 0;
  for (const auto &t : transects) {
    n0 += std::min<std::size_t>(t.size(), 2);
  }
  vertices.reserve(n0);

  struct TransectInfo {
    TransectInfo(size_t n, bool f) : index(n), front(f) {}
    size_t index;
    bool front;
  };
  std::vector<TransectInfo> transectInfoList;
  for (size_t i = 0; i < transects.size(); ++i) {
    const auto &t = transects[i];
    vertices.push_back(t.front());
    transectInfoList.push_back(TransectInfo{i, true});
    if (t.size() >= 2) {
      vertices.push_back(t.back());
      transectInfoList.push_back(TransectInfo{i, false});
    }
  }

  for (long i = 0; i < long(area.outer().size()) - 1; ++i) {
    vertices.push_back(area.outer()[i]);
  }
  for (auto &ring : area.inners()) {
    for (auto &vertex : ring)
      vertices.push_back(vertex);
  }
  size_t n1 = vertices.size();
  // Generate routing model.
  RoutingDataModel dataModel;
  Matrix<double> connectionGraph(n1, n1);
  // Offset joined area.
  BoostPolygon areaOffset;
  offsetPolygon(area, areaOffset, detail::offsetConstant);
#ifdef SNAKE_SHOW_TIME
  auto start = std::chrono::high_resolution_clock::now();
#endif
  generateRoutingModel(vertices, areaOffset, n0, dataModel, connectionGraph);
#ifdef SNAKE_SHOW_TIME
  auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(
      std::chrono::high_resolution_clock::now() - start);
  cout << "Execution time _generateRoutingModel(): " << delta.count() << " ms"
       << endl;
#endif

  // Create Routing Index Manager.
  RoutingIndexManager manager(dataModel.distanceMatrix.getN(),
                              dataModel.numVehicles, dataModel.depot);
  // Create Routing Model.
  RoutingModel routing(manager);

  // Create and register a transit callback.
  const int transitCallbackIndex = routing.RegisterTransitCallback(
      [&dataModel, &manager](int64 from_index, int64 to_index) -> int64 {
        // Convert from routing variable Index to distance matrix NodeIndex.
        auto from_node = manager.IndexToNode(from_index).value();
        auto to_node = manager.IndexToNode(to_index).value();
        return dataModel.distanceMatrix.get(from_node, to_node);
      });

  // Define cost of each arc.
  routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);

  // Define Constraints (this constraints have a huge impact on the
  // solving time, improvments could be done, e.g. SearchFilter).
#ifdef SNAKE_DEBUG
  std::cout << "snake::route(): Constraints:" << std::endl;
#endif
  Solver *solver = routing.solver();
  size_t index = 0;
  for (size_t i = 0; i < transects.size(); ++i) {
    const auto &t = transects[i];
#ifdef SNAKE_DEBUG
    std::cout << "i = " << i << ": t.size() = " << t.size() << std::endl;
#endif
    if (t.size() >= 2) {
      auto idx0 = manager.NodeToIndex(RoutingIndexManager::NodeIndex(index));
      auto idx1 =
          manager.NodeToIndex(RoutingIndexManager::NodeIndex(index + 1));
      auto cond0 = routing.NextVar(idx0)->IsEqual(idx1);
      auto cond1 = routing.NextVar(idx1)->IsEqual(idx0);
      auto c = solver->MakeNonEquality(cond0, cond1);
      solver->AddConstraint(c);
#ifdef SNAKE_DEBUG
      std::cout << "( next(" << index << ") == " << index + 1 << " ) != ( next("
                << index + 1 << ") == " << index << " )" << std::endl;
#endif
      index += 2;
    } else {
      index += 1;
    }
  }

  // Setting first solution heuristic.
  RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters();
  searchParameters.set_first_solution_strategy(
      FirstSolutionStrategy::PATH_CHEAPEST_ARC);
  google::protobuf::Duration *tMax =
      new google::protobuf::Duration(); // seconds
  tMax->set_seconds(10);
  searchParameters.set_allocated_time_limit(tMax);

  // Solve the problem.
#ifdef SNAKE_SHOW_TIME
  start = std::chrono::high_resolution_clock::now();
#endif
  const Assignment *solution = routing.SolveWithParameters(searchParameters);
#ifdef SNAKE_SHOW_TIME
  delta = std::chrono::duration_cast<std::chrono::milliseconds>(
      std::chrono::high_resolution_clock::now() - start);
  cout << "Execution time routing.SolveWithParameters(): " << delta.count()
       << " ms" << endl;
#endif

  if (!solution || solution->Size() <= 1) {
    errorString = "Not able to solve the routing problem.";
    return false;
  }

  // Extract index list from solution.
  index = routing.Start(0);
  std::vector<size_t> route_idx;
  route_idx.push_back(manager.IndexToNode(index).value());
  while (!routing.IsEnd(index)) {
    index = solution->Value(routing.NextVar(index));
    route_idx.push_back(manager.IndexToNode(index).value());
  }

  // Helper Lambda.
  auto idx2Vertex = [&vertices](const std::vector<size_t> &idxArray,
                                std::vector<BoostPoint> &path) {
    for (auto idx : idxArray)
      path.push_back(vertices[idx]);
  };

  // Construct route.
  for (size_t i = 0; i < route_idx.size() - 1; ++i) {
    size_t idx0 = route_idx[i];
    size_t idx1 = route_idx[i + 1];
    const auto &info1 = transectInfoList[idx0];
    const auto &info2 = transectInfoList[idx1];
    if (info1.index == info2.index) { // same transect?
      if (!info1.front) {             // transect reversal needed?
        BoostLineString reversedTransect;
        const auto &t = transects[info1.index];
        for (auto it = t.end() - 1; it >= t.begin(); --it) {
          reversedTransect.push_back(*it);
        }
        transectsRouted.push_back(reversedTransect);
        for (auto it = reversedTransect.begin();
             it < reversedTransect.end() - 1; ++it) {
          route.push_back(*it);
        }
      } else {
        const auto &t = transects[info1.index];
        for (auto it = t.begin(); it < t.end() - 1; ++it) {
          route.push_back(*it);
        }
        transectsRouted.push_back(t);
      }
    } else {
      std::vector<size_t> idxList;
      shortestPathFromGraph(connectionGraph, idx0, idx1, idxList);
      if (i != route_idx.size() - 2) {
        idxList.pop_back();
      }
      idx2Vertex(idxList, route);
    }
  }
  return true;
}