bool FlightPlan::generate(double lineDistance, double minTransectLength) { _waypointsENU.clear(); _waypoints.clear(); _arrivalPathIdx.clear(); _returnPathIdx.clear(); #ifndef NDEBUG _PathVertices.clear(); #endif #ifdef SHOW_TIME auto start = std::chrono::high_resolution_clock::now(); #endif if (!_generateTransects(lineDistance, minTransectLength)) return false; #ifdef SHOW_TIME auto delta = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); cout << endl; cout << "Execution time _generateTransects(): " << delta.count() << " ms" << endl; #endif //======================================= // Route Transects using Google or-tools. //======================================= // Offset joined area. const BoostPolygon &jArea = _scenario.getJoineAreaENU(); BoostPolygon jAreaOffset; offsetPolygon(jArea, jAreaOffset, detail::offsetConstant); // Create vertex list; BoostLineString vertices; size_t n_t = _transects.size()*2; size_t n0 = n_t+1; vertices.reserve(n0); for (auto lstring : _transects){ for (auto vertex : lstring){ vertices.push_back(vertex); } } vertices.push_back(_scenario.getHomePositonENU()); for (long i=0; i connectionGraph(n1, n1); #ifdef SHOW_TIME start = std::chrono::high_resolution_clock::now(); #endif _generateRoutingModel(vertices, jAreaOffset, n0, dataModel, connectionGraph); #ifdef SHOW_TIME delta = std::chrono::duration_cast(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 transit_callback_index = 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(transit_callback_index); // Define Constraints. size_t n = _transects.size()*2; Solver *solver = routing.solver(); for (size_t i=0; iIsEqual(idx1); // auto cond1 = routing.NextVar(idx1)->IsEqual(idx0); // auto c = solver->MakeNonEquality(cond0, cond1); // solver->AddConstraint(c); // alternative auto idx0 = manager.NodeToIndex(RoutingIndexManager::NodeIndex(i)); auto idx1 = manager.NodeToIndex(RoutingIndexManager::NodeIndex(i+1)); auto cond0 = routing.NextVar(idx0)->IsEqual(idx1); auto cond1 = routing.NextVar(idx1)->IsEqual(idx0); vector conds{cond0, cond1}; auto c = solver->MakeAllDifferent(conds); solver->MakeRejectFilter(); solver->AddConstraint(c); } // 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 SHOW_TIME start = std::chrono::high_resolution_clock::now(); #endif const Assignment* solution = routing.SolveWithParameters(searchParameters); #ifdef SHOW_TIME delta = std::chrono::duration_cast(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 waypoints from solution. long index = routing.Start(0); std::vector route; route.push_back(manager.IndexToNode(index).value()); while (!routing.IsEnd(index)){ index = solution->Value(routing.NextVar(index)); route.push_back(manager.IndexToNode(index).value()); } // Connect transects #ifndef NDEBUG _PathVertices = vertices; #endif { _waypointsENU.push_back(vertices[route[0]]); vector pathIdx; long arrivalPathLength = 0; for (long i=0; i 0; --i) _returnPathIdx.push_back(_waypointsENU.size()-i); for (long i=0; i < arrivalPathLength; ++i) _arrivalPathIdx.push_back(i); } // Back transform waypoints. GeoPoint3D origin{_scenario.getOrigin()}; for (auto vertex : _waypointsENU) { GeoPoint3D geoVertex; fromENU(origin, Point3D{vertex.get<0>(), vertex.get<1>(), 0}, geoVertex); _waypoints.push_back(GeoPoint2D{geoVertex[0], geoVertex[1]}); } return true; } bool FlightPlan::_generateTransects(double lineDistance, double minTransectLength) { _transects.clear(); if (_scenario.getTilesENU().size() != _progress.size()){ ostringstream strstream; strstream << "Number of tiles (" << _scenario.getTilesENU().size() << ") is not equal to progress array length (" << _progress.size() << ")"; errorString = strstream.str(); return false; } // Calculate processed tiles (_progress[i] == 100) and subtract them from measurement area. size_t num_tiles = _progress.size(); vector processedTiles; { const auto &tiles = _scenario.getTilesENU(); for (size_t i=0; i(vertex.get<0>()*CLIPPER_SCALE), static_cast(vertex.get<1>()*CLIPPER_SCALE)}); } vector processedTilesClipper; for (auto t : processedTiles){ ClipperLib::Path path; for (auto vertex : t.outer()){ path.push_back(ClipperLib::IntPoint{static_cast(vertex.get<0>()*CLIPPER_SCALE), static_cast(vertex.get<1>()*CLIPPER_SCALE)}); } processedTilesClipper.push_back(path); } const min_bbox_rt &bbox = _scenario.getMeasurementAreaBBoxENU(); double alpha = bbox.angle; double x0 = bbox.corners.outer()[0].get<0>(); double y0 = bbox.corners.outer()[0].get<1>(); double bboxWidth = bbox.width; double bboxHeight = bbox.height; double delta = detail::offsetConstant; size_t num_t = int(ceil((bboxHeight + 2*delta)/lineDistance)); // number of transects vector yCoords; yCoords.reserve(num_t); double y = -delta; for (size_t i=0; i < num_t; ++i) { yCoords.push_back(y); y += lineDistance; } // Generate transects and convert them to clipper path. trans::rotate_transformer rotate_back(-alpha*180/M_PI); trans::translate_transformer translate_back(x0, y0); vector transectsClipper; transectsClipper.reserve(num_t); for (size_t i=0; i < num_t; ++i) { // calculate transect BoostPoint v1{-delta, yCoords[i]}; BoostPoint v2{bboxWidth+delta, yCoords[i]}; BoostLineString transect; transect.push_back(v1); transect.push_back(v2); // transform back BoostLineString temp_transect; bg::transform(transect, temp_transect, rotate_back); transect.clear(); bg::transform(temp_transect, transect, translate_back); ClipperLib::IntPoint c1{static_cast(transect[0].get<0>()*CLIPPER_SCALE), static_cast(transect[0].get<1>()*CLIPPER_SCALE)}; ClipperLib::IntPoint c2{static_cast(transect[1].get<0>()*CLIPPER_SCALE), static_cast(transect[1].get<1>()*CLIPPER_SCALE)}; ClipperLib::Path path{c1, c2}; transectsClipper.push_back(path); } // Perform clipping. // Clip transects to measurement area. ClipperLib::Clipper clipper; clipper.AddPath(mAreaClipper, ClipperLib::ptClip, true); clipper.AddPaths(transectsClipper, ClipperLib::ptSubject, false); ClipperLib::PolyTree clippedTransecsPolyTree1; clipper.Execute(ClipperLib::ctIntersection, clippedTransecsPolyTree1, ClipperLib::pftNonZero, ClipperLib::pftNonZero); // Subtract holes (tiles with measurement_progress == 100) from transects. clipper.Clear(); for (auto child : clippedTransecsPolyTree1.Childs) clipper.AddPath(child->Contour, ClipperLib::ptSubject, false); clipper.AddPaths(processedTilesClipper, ClipperLib::ptClip, true); ClipperLib::PolyTree clippedTransecsPolyTree2; clipper.Execute(ClipperLib::ctDifference, clippedTransecsPolyTree2, ClipperLib::pftNonZero, ClipperLib::pftNonZero); // Extract transects from PolyTree and convert them to BoostLineString for (auto child : clippedTransecsPolyTree2.Childs){ ClipperLib::Path clipperTransect = child->Contour; BoostPoint v1{static_cast(clipperTransect[0].X)/CLIPPER_SCALE, static_cast(clipperTransect[0].Y)/CLIPPER_SCALE}; BoostPoint v2{static_cast(clipperTransect[1].X)/CLIPPER_SCALE, static_cast(clipperTransect[1].Y)/CLIPPER_SCALE}; BoostLineString transect{v1, v2}; if (bg::length(transect) >= minTransectLength) _transects.push_back(transect); } if (_transects.size() < 1) return false; return true; } void FlightPlan::_generateRoutingModel(const BoostLineString &vertices, const BoostPolygon &polygonOffset, size_t n0, FlightPlan::RoutingDataModel_t &dataModel, Matrix &graph) { #ifdef SHOW_TIME auto start = std::chrono::high_resolution_clock::now(); #endif graphFromPolygon(polygonOffset, vertices, graph); #ifdef SHOW_TIME auto delta = std::chrono::duration_cast(std::chrono::high_resolution_clock::now()-start); cout << "Execution time graphFromPolygon(): " << delta.count() << " ms" << endl; #endif // cout << endl; // for (size_t i=0; i &row = graph[i]; // for (size_t j=0; j distanceMatrix(graph); #ifdef SHOW_TIME start = std::chrono::high_resolution_clock::now(); #endif toDistanceMatrix(distanceMatrix); #ifdef SHOW_TIME delta = std::chrono::duration_cast(std::chrono::high_resolution_clock::now()-start); cout << "Execution time toDistanceMatrix(): " << delta.count() << " ms" << endl; #endif dataModel.distanceMatrix.setDimension(n0, n0); for (size_t i=0; i