#ifndef ONF_STEPVECTORIZECONDUCTIVITYMAP_H #define ONF_STEPVECTORIZECONDUCTIVITYMAP_H #include "ct_step/abstract/ct_abstractstep.h" #include "ct_itemdrawable/ct_image2d.h" class ONF_StepVectorizeConductivityMap: public CT_AbstractStep { Q_OBJECT using SuperClass = CT_AbstractStep; public: ONF_StepVectorizeConductivityMap(); QString description() const override; QString detailledDescription() const override; QString inputDescription() const override; QString outputDescription() const override; QString detailsDescription() const override; QString URL() const override; CT_VirtualAbstractStep* createNewInstance() const final; protected: void declareInputModels(CT_StepInModelStructureManager& manager) final; virtual void fillPostInputConfigurationDialog(CT_StepConfigurableDialog* postInputConfigDialog) final; void declareOutputModels(CT_StepOutModelStructureManager& manager) final; void compute() final; private: CT_HandleInResultGroupCopy<> _inResult; CT_HandleInStdZeroOrMoreGroup _inZeroOrMoreRootGroup; CT_HandleInStdGroup<> _inGroup; CT_HandleInSingularItem > _inConductivityMap; CT_HandleOutSingularItem > _outRaster; // class DrivingAgent // { // public: // float sightline; // Sightline of the driving agent input by the user // float min_conductivity; // float current_sightline; // The sightline maybe reduced locally. We use current_sightline and sightline hold the original input // float resolution; // Resolution of the map on which we are suppose to drive // float rcost; // Relative cost compared to the maximal allowed value (which is not necessarily cost_max) // float cost_max; // Maximal cost allowed (user input). This value may be locally changed. // float fov; // Field of view (in degrees) of the driving agent // float angle; // The angle of the 0 degree for the field of view // CT_Image2D* map; // [SpatRaster] on which the driving agent drives on step // float trans; // [TransitionMatrix] the transition matrix associated with map // Eigen::Vector2d start; // [sfc] the position of the driving agent // Eigen::Vector2d ends; // [sfc] the potentially reachable points // float p1; // [sfc] The first point of the seed used to compute the angle // float p2; // [sfc] The second point of the seed used to compute the angle (same than start actually) // int k; // A counter of move // float cost_profile; // [vect] The angle-cost profile // float cost_peaks; // [data.frame] The location of the peaks in the cost profile // float lcp; // [sfc] line of the least cost path // float intersections; // [sfc] lines of the intersection seeds founds // float novercost; // Number of time we moved forward despite a cost higher than the maximum allowed cost // float dovercost; // Distance travelled despite a cost higher than the maximum allowed cost // float list_lines; // [list] list of sfg linestring of each step driven // float list_intersections; // [list] list of sfg linestring of each intersection seed // float sightline_min; // Miminum allowed sightline // float pahead; // When moving forward we do not move of 100% of the sightline but rather 80% (this prevent missing intersection) // float partial_gap_size; // DrivingAgent() // { // sightline = 100.0; // min_conductivity = 0.6; // current_sightline = sightline; // resolution = 2.0; // rcost = 0; // cost_max = sightline * 1.0/min_conductivity; // fov = 160.0; // angle = 0.0; // CT_Image2D* map = nullptr; // trans = 0; // start = Eigen::Vector2d(0,0); // ends = Eigen::Vector2d(0,0); // p1 = 0; // p2 = 0; // k = 2; // cost_profile = 0; // cost_peaks = 0; // lcp = 0; // intersections = 0; // novercost = 0; // dovercost = 0; // list_lines = 0; // list_intersections = 0; // sightline_min = 15; // pahead = 0.8; // partial_gap_size = 2.5; // } // void query_sightview(val) // { // bb = sf::st_bbox(c(start, sf::st_geometry(ends))); // bb = terra::ext(bb) + 10; // aoi = terra::crop(val, bb); // p = sf::st_polygon(list(rbind(sf::st_coordinates(start), sf::st_coordinates(ends), sf::st_coordinates(start)))); // p = sf::st_sfc(p); // p = sf::st_buffer(p, 2); // p = terra::vect(p); // map = terra::mask(aoi, p); // trans = transition(map, geocorrection = true); // } // float get_novercost() // { // return novercost; // } // void set_start(Eigen::Vector2d val) // { // start = val; // float angles_rad = generate_angles(resolution, current_sightline, fov); // ends = generate_ends(p2, angles_rad, current_sightline, angle); // ends$angle = angles_rad; // } // Eigen::Vector2d get_start() // { // return start; // } // void set_seed(line) // { // if (length(list_lines) == 0) // list_lines[[1]] = sf::st_geometry(line)[[1]]; // l = lwgeom::st_linesubstring(line, 0.5, 1); // p1 = lwgeom::st_startpoint(l); // p2 = lwgeom::st_endpoint(l); // p1 = p1; // p2 = p2; // from = sf::st_coordinates(p1); // to = sf::st_coordinates(p2); // M = rbind(from, to); // Ax = M[2,1]; // Ay = M[2,2]; // Bx = M[1,1]; // By = M[1,2]; // angle = atan2(Ay-By, Ax-Bx); // self$set_start(p2); // } // void get_seed() // { // n = length(list_lines); // return(list_lines[[n]]); // } // void set_fov(float val) // { // fov = val; // } // float set_sightline(float val) // { // current_sightline = val; // return val; // } // float get_intersections() // { // return intersections; // } // void get_road() // { // roads = list_lines; // roads = roads[1:(length(roads) - novercost)]; // first = list_lines[[1]]; // roads = lapply(roads, function(x) { x = x[] ; return(x[-1,])}); // roads[[1]] = first; // newline = do.call(rbind, roads) |>; // sf::st_linestring() |>; // sf::st_sfc() |>; // intersections = nullptr; // if (length(list_intersections) >= 1) // { // intersections = do.call(c, list_intersections); // p = lwgeom::st_startpoint(intersections); // d = as.numeric(sf::st_distance(p, newline)); // intersections = intersections[d < 1]; // } // return(list(road = newline, seeds = intersections)); // } // void get_trace() // { // u = self$get_road(); // road = u$road; // seed = u$seeds; // return(c(road, seed)); // } // void has_new_intersections() // { // return intersections != nullptr; // } // void has_path() // { // return self$least_cost_path() != nullptr;; // } // void network_connection(network) // { // // Create a prolongation line // angle = angle; // xstart = sf::st_coordinates(start)[1]; // ystart = sf::st_coordinates(start)[2]; // xend = xstart+50*cos(angle); // yend = ystart+50*sin(angle); // m = matrix(c(xstart, xend, ystart, yend), ncol = 2); // prolongation = sf::st_sfc(sf::st_linestring(m)); // // Intersection between the prolongation and the existing network // p = sf::st_intersection(prolongation, network); // // No intersection: double the length of the prolongation // if (length(p) == 0) // { // xend = xstart+100*cos(angle); // yend = ystart+100*sin(angle); // m = matrix(c(xstart, xend, ystart, yend), ncol = 2); // prolongation = sf::st_sfc(sf::st_linestring(m)); // // Intersection between the prolongation and the existing network // p = sf::st_intersection(prolongation, network); // } // if (length(p) > 1) // { // p = p[1]; // warning("Connection problem. More than one line intersection"); // } // // Still no intersection we may be in the wrong direction e.g. if we are trying to // // connect to a parallel line and we have 0 tolerance. Connect to the NN // if (length(p) == 0) // { // bbb = sf::st_buffer(start, dist = 50); // subnet = sf::st_intersection(network, bbb); // prolongation = sf::st_nearest_points(start, subnet); // // we have a connection but we must round the value to match a node of the line // } else // { // bbb = sf::st_buffer(p, dist = 5); // subnet = sf::st_intersection(network, bbb); // subnet = sf::st_cast(subnet, "POINT"); // i = sf::st_nearest_feature(p, subnet); // p = subnet[i]; // ///prolongation = sf::st_coordinates(prolongation)[,1:2]; // prolongation[2,] = sf::st_coordinates(p); // prolongation = sf::st_sfc(sf::st_linestring(prolongation)); // } // novercost = 0L; // dovercost = 0; // list_lines[[k]] = prolongation[[1]]; // } // float least_cost_path() // { // return lcp; // } // bool has_reached_a_road() // { // return any_infinite_cost(cost_profile); // } // void divide_sightline(div = 2) // { // current_sightline = current_sightline/2; // angles_rad = generate_angles(resolution, current_sightline, fov); // ends = generate_ends(p2, angles_rad, current_sightline, angle); // ends$angle = angles_rad; // self$query_sightview(map); // } // bool is_on_edge() // { // val = terra::extract(map, sf::st_coordinates(ends))[[1]]; // return(any(is.na(val))); // } // void move() // { // k = k+1; // current_sightline = sightline; // cost_profile = nullptr; // cost_peaks = nullptr; // intersections = nullptr; // self$set_seed(lcp); // lcp = nullptr; // } // bool reached_end() // { // if (is.infinite(cost_max)) // return true; // // @param partial_gap_size numeric. The algorithm can drive on low conductivity segments even if the // // conductivity is too low and should trigger a stop signal if it looks like a sharp line and an // // high conductivity road is reach after the gap (see references). The allowed distance is a multiple // // of the sightline. Default is 2.5 which roughly means that it can follow a too low conductivity // // track for two iterations and at the third one it must be back on a high conductivity track. // if (dovercost > partial_gap_size*sightline) // return true; // return false; // } // void compute_cost() // { // ans = find_reachable(start, ends, trans, cost_max); // if (is.nullptr(ans)) // return(nullptr); // // The only way to get infinite values is to reach an existing road // // We need to reduce the sightline to reduce down the approach speed and connect accurately // current_cost = cost_max; // while (any_infinite_cost(ans$cost) & current_sightline > sightline_min) // { // self$divide_sightline(); // current_cost = current_cost/2; // ans = find_reachable(start, ends, trans, current_cost); // } // cost_profile = ans$cost; // cost_peaks = ans$minima; // // We now have the cost to reach each point of the sightline and the peaks in the cost // // profile indicating which point we must reach // cost = cost_profile; // // The main direction // idx_main = cost_peaks$idx[1]; // depth_main = cost_peaks$depth[1]; // ///end = ends[idx_main,]; // // Compute the shortest path between the starting points and the valid end points // L = gdistance::shortestPath(trans, sf::st_coordinates(start), sf::st_coordinates(end), output = "SpatialLines"); // L = sf::st_geometry(sf::st_as_sf(L)); // C = sf::st_coordinates(L); // C = C[1:(nrow(C)*pahead),-3]; // L = sf::st_sfc(sf::st_linestring(C)); // lcp = L; // list_lines[[k]] = L[[1]]; // // The other direction // idx_other = cost_peaks$idx[-1]; // rcost = cost_peaks$rcost[1]; // ///ends_other = ends[idx_other,]; // cost_other = cost_peaks$rcost[-1]; // // Check the angle between the main road and the other road. If the angle is < 15 degrees there // // are false positive. // // (This is probably not useful anymore but was need to fix bugs when using very poor map) // delta_angle = abs(end$angle - ends_other$angle)*180/pi; // ends_other = ends_other[delta_angle > 15,]; // cost_other = cost_other[delta_angle > 15]; // // Now we are processing the intersection founds (if any) // I = nullptr; // if (length(sf::st_geometry(ends_other)) > 0) // { // I = gdistance::shortestPath(trans, sf::st_coordinates(start), sf::st_coordinates(ends_other), output = "SpatialLines") |> suppressWarnings(); // I = sf::st_geometry(sf::st_as_sf(I)); // I = sf::st_difference(I,L); // I = Filter(function(x){ // if (sf::st_geometry_type(x) != "LINESTRING") // return false; // if (lwgeom::st_endpoint(L) == lwgeom::st_startpoint(x)) // return false; // return true; // }, I); // intersections = I; // list_intersections = c(list_intersections, list(I)); // } // // If the current cost is > 1 it means that we drove on a track that is partially or totally // // not labelled as road. We record this state // if (rcost > 1) // { // novercost = novercost + 1L; // dovercost = dovercost + current_sightline; // } // else // { // novercost = 0L; // dovercost = 0; // } // } // void print_speed(t0, bool done = false) // { // if (isfalse(done)) // { // if (k %% 5 == 0) // { // dist = (k-1)*sightline*pahead; // cat(dist, " m (", get_speed(dist, t0), " km/h)\r", sep = ""); // utils::flush.console(); // } // } // else // { // h = km = nullptr; // tf = Sys.time(); // dt = tf-t0; // dt = round(units::as_units(dt),1); // dist = sf::st_length(self$get_road()$road); // hdt = dt; // units(hdt) = units::make_units(h); // units(dist) = units::make_units(km); // dist = round(dist, 2); // speed = round(dist/hdt); // cat("Processed ended in", dt, units::deparse_unit(dt), // ": road of", dist, units::deparse_unit(dist), // "driven at", speed, units::deparse_unit(speed), // "\n"); // } // } // void generate_angles(float resolution, float sightline, float fov, QVector &angles_rad) // { // angles_rad.resize(0); // float angular_resolution = std::floor(1.5 * (resolution / sightline) * (180.0 / M_PI)); // // TODO : use atan to compute angular-resolution ? // // float angular_resolution = std::atan(resolution / sightline) * (180.0 / M_PI); // if (angular_resolution < 1.0) {angular_resolution = 1.0;} // float fov2 = std::floor(fov / angular_resolution)*angular_resolution; // for (float angle = -fov2 ; angle <= fov2 ; angle += angular_resolution) // { // angles_rad.append(angle * M_PI / 180.0); // } // } // }; // void track_line(const QList& seed, const CT_Image2D* map, const QList& network = nullptr, float fov = 160, float sightline = 100, float min_conductivity = 0.6, float th_conductivity = 0.1); // }; #endif // ONF_STEPVECTORIZECONDUCTIVITYMAP_H