diff --git a/src/LSDCRNParameters.cpp b/src/LSDCRNParameters.cpp index f407149..4a81751 100644 --- a/src/LSDCRNParameters.cpp +++ b/src/LSDCRNParameters.cpp @@ -1144,6 +1144,67 @@ void LSDCRNParameters::set_Braucher_parameters() } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// 10Be production is based on a combination of data from Braucher et al 2011 +// and Borchers et al 2016 as transcribed by Mirjam Schaller +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDCRNParameters::set_BraucherBorchers_parameters() +{ + //S_t = 1; + + // from Vermeesh 2007 + // 10Be from Chmeleff/Korschinek 10Be decay constant; + lambda_10Be = 500e-9; // in yr-1 + lambda_26Al = 980e-9; // in yr-1 + lambda_14C = 121e-6; // in yr-1 + lambda_36Cl = 230e-8; // in yr-1 + + // from the Braucher and Borchers papers + // data compiled by Mirjam Schaller in personal communication + // + // All but 10Be are calibrated to the Stone scaling + // Also linked to the nishizumii standards + P0_10Be = 4.061; // in a/g/yr + P0_26Al = 28.851; // in a/g/yr + P0_14C = 15.21; // in a/g/yr + P0_36Cl = 58.95; // in a/g/yr + P0_21Ne = 18.23; // in a/g/yr + P0_3He = 121.59; // in a/g/yr + + // in g/cm^2 + Gamma[0] = 160; + Gamma[1] = 1500; + Gamma[2] = 1500; + Gamma[3] = 4320; + + // dimensionless + F_10Be[0] = 0.9874; + F_10Be[1] = 0.0030; + F_10Be[2] = 0.0; + F_10Be[3] = 0.0096; + + // dimensionless + F_26Al[0] = 0.9681; + F_26Al[1] = 0.0291; + F_26Al[2] = 0.000; + F_26Al[3] = 0.0028; + + // dimensionless + F_14C[0] = 0.83; + F_14C[1] = 0.15; + F_14C[2] = 0.0; + F_14C[3] = 0.02; + + // dimensionless + F_36Cl[0] = 0.9456; + F_36Cl[1] = 0.0324; + F_36Cl[2] = 0.00; + F_36Cl[3] = 0.022; +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // 10Be is set to a new production curve provided by Shasta Marrero // All others: sets the parameters to those used by Braucher et al 2009 diff --git a/src/LSDCRNParameters.hpp b/src/LSDCRNParameters.hpp index e98ba03..df68b2c 100644 --- a/src/LSDCRNParameters.hpp +++ b/src/LSDCRNParameters.hpp @@ -217,7 +217,14 @@ class LSDCRNParameters /// @author SMM /// @date 27/01/2015 void set_Braucher_parameters(); - + + /// @brief This resets the F, Gamma and P0 values so that they conform to + /// parameters from Braucher et al 2011 and Brchers et al 2016 + /// @detail From personal communication with Mirjam Schaller + /// @author SMM + /// @date 04/02/2022 + void set_BraucherBorchers_parameters(); + /// @brief This resets the F, Gamma and P0 values /// For 10Be, these correspond to new production curves provided by Shasta Marerro // For the rest they conform to diff --git a/src/LSDChiTools.cpp b/src/LSDChiTools.cpp index d0cf0b9..c7fce24 100644 --- a/src/LSDChiTools.cpp +++ b/src/LSDChiTools.cpp @@ -11879,6 +11879,139 @@ void LSDChiTools::print_chi_data_map_to_csv(LSDFlowInfo& FlowInfo, string filena } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +void LSDChiTools::print_chi_data_map_to_csv_with_ni(LSDFlowInfo& FlowInfo, string filename) +{ + + // these are for extracting element-wise data from the channel profiles. + int this_node, row,col; + double latitude,longitude; + LSDCoordinateConverterLLandUTM Converter; + + // find the number of nodes + int n_nodes = (node_sequence.size()); + + // open the data file + ofstream chi_data_out; + chi_data_out.open(filename.c_str()); + chi_data_out << "latitude,longitude,NI,chi,elevation,flow_distance,drainage_area,source_key,basin_key" << endl; + if (n_nodes <= 0) + { + cout << "Cannot print since you have not calculated channel properties yet." << endl; + } + else + { + for (int n = 0; n< n_nodes; n++) + { + this_node = node_sequence[n]; + FlowInfo.retrieve_current_row_and_col(this_node,row,col); + get_lat_and_long_locations(row, col, latitude, longitude, Converter); + + chi_data_out.precision(9); + chi_data_out << latitude << "," + << longitude << "," + << this_node << ","; + chi_data_out.precision(5); + chi_data_out << chi_data_map[this_node] << "," + << elev_data_map[this_node] << "," + << flow_distance_data_map[this_node] << "," + << drainage_area_data_map[this_node] << "," + << source_keys_map[this_node] << "," + << baselevel_keys_map[this_node]; + + chi_data_out << endl; + } + } + + chi_data_out.close(); + +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + +void LSDChiTools::print_chi_data_map_to_csv_with_junction_information(LSDFlowInfo& FlowInfo, LSDJunctionNetwork& JN, string filename) +{ + + // these are for extracting element-wise data from the channel profiles. + int this_node, row,col; + double latitude,longitude; + LSDCoordinateConverterLLandUTM Converter; + + // find the number of nodes + int n_nodes = (node_sequence.size()); + + + // Get vectors for making the maps + vector NIvec; + vector SOvec; + vector JIvec; + JN.GetChannelNodesAndJunctions(FlowInfo, NIvec, JIvec, SOvec); + + // Turn these into maps + map SO_map; + map JI_map; + + int n_NI = int(NIvec.size()); + for(int i = 0; i LSDCosmoData::full_CRN_erosion_analysis_point(double Nuclide_conc { LSDCRNP.set_Braucher_parameters(); } + else if (Muon_scaling == "BraucherBorchers" ) + { + LSDCRNP.set_BraucherBorchers_parameters(); + } else if (Muon_scaling == "Granger" ) { LSDCRNP.set_Granger_parameters(); @@ -4064,7 +4074,7 @@ vector LSDCosmoData::full_CRN_erosion_analysis_point(double Nuclide_conc else { cout << "You didn't set the muon scaling." << endl - << "Options are Schaller, Braucher, newCRONUS, and Granger." << endl + << "Options are Schaller, Braucher, newCRONUS, BraucherBorchers, and Granger." << endl << "You chose: " << Muon_scaling << endl << "Defaulting to Braucher et al (2009) scaling" << endl; LSDCRNP.set_Braucher_parameters(); @@ -4226,6 +4236,10 @@ double LSDCosmoData::predict_CRN_erosion_point(double Nuclide_conc, string Nucli { LSDCRNP.set_Braucher_parameters(); } + else if (Muon_scaling == "BraucherBorchers" ) + { + LSDCRNP.set_BraucherBorchers_parameters(); + } else if (Muon_scaling == "Granger" ) { LSDCRNP.set_Granger_parameters(); @@ -4237,7 +4251,7 @@ double LSDCosmoData::predict_CRN_erosion_point(double Nuclide_conc, string Nucli else { cout << "You didn't set the muon scaling." << endl - << "Options are Schaller, Braucher, newCRONUS and Granger." << endl + << "Options are Schaller, Braucher, BraucherBorchers, newCRONUS and Granger." << endl << "You chose: " << Muon_scaling << endl << "Defaulting to Braucher et al (2009) scaling" << endl; LSDCRNP.set_Braucher_parameters(); @@ -4440,6 +4454,10 @@ double LSDCosmoData::predict_mean_CRN_conc_point(double eff_erosion_rate, string { LSDCRNP.set_Braucher_parameters(); } + else if (Muon_scaling == "BraucherBorchers" ) + { + LSDCRNP.set_BraucherBorchers_parameters(); + } else if (Muon_scaling == "Granger" ) { LSDCRNP.set_Granger_parameters(); @@ -4451,7 +4469,7 @@ double LSDCosmoData::predict_mean_CRN_conc_point(double eff_erosion_rate, string else { cout << "You didn't set the muon scaling." << endl - << "Options are Schaller, Braucher, newCRONUS, and Granger." << endl + << "Options are Schaller, Braucher, BraucherBorchers, newCRONUS, and Granger." << endl << "You chose: " << Muon_scaling << endl << "Defaulting to Braucher et al (2009) scaling" << endl; LSDCRNP.set_Braucher_parameters(); @@ -4973,10 +4991,14 @@ void LSDCosmoData::point_measurements(vector valid_samples,vector s { LSDCRNP.set_Schaller_parameters(); } - else if (Muon_scaling == "Braucher" ) + else if (Muon_scaling == "Braucher" ) { LSDCRNP.set_Braucher_parameters(); } + else if (Muon_scaling == "BraucherBorchers" ) + { + LSDCRNP.set_BraucherBorchers_parameters(); + } else if (Muon_scaling == "Granger" ) { LSDCRNP.set_Granger_parameters(); @@ -4988,7 +5010,7 @@ void LSDCosmoData::point_measurements(vector valid_samples,vector s else { cout << "You didn't set the muon scaling." << endl - << "Options are Schaller, Braucher, newCRONUS, and Granger." << endl + << "Options are Schaller, Braucher, BraucherBorchers, newCRONUS, and Granger." << endl << "You chose: " << Muon_scaling << endl << "Defaulting to Braucher et al (2009) scaling" << endl; LSDCRNP.set_Braucher_parameters(); diff --git a/src/LSDFlowInfo.cpp b/src/LSDFlowInfo.cpp index 8ced554..4b17198 100644 --- a/src/LSDFlowInfo.cpp +++ b/src/LSDFlowInfo.cpp @@ -92,10 +92,13 @@ #include #include #include +#include #include "TNT/tnt.h" #include "LSDFlowInfo.hpp" #include "LSDIndexRaster.hpp" #include "LSDStatsTools.hpp" +#include "LSDRasterInfo.hpp" +#include "LSDSpatialCSVReader.hpp" //#include "LSDRaster.hpp" using namespace std; using namespace TNT; @@ -247,19 +250,19 @@ void LSDFlowInfo::create(vector& temp_BoundaryConditions, // loop through the topo data finding places where there is actually data for (row = 0; row& temp_BoundaryConditions, // this vector starts out empty and then base level nodes are added to it for (row = 0; row& temp_BoundaryConditions, // check for periodic boundary conditions if( BoundaryConditions[0].find("P") == 0 || BoundaryConditions[0].find("p") == 0 ) - { - if( BoundaryConditions[2].find("P") != 0 && BoundaryConditions[2].find("p") != 0 ) { - cout << "WARNING!!! North boundary is periodic! Changing South boundary to periodic" << endl; - BoundaryConditions[2] = "P"; + if( BoundaryConditions[2].find("P") != 0 && BoundaryConditions[2].find("p") != 0 ) + { + cout << "WARNING!!! North boundary is periodic! Changing South boundary to periodic" << endl; + BoundaryConditions[2] = "P"; + } } - } if( BoundaryConditions[1].find("P") == 0 || BoundaryConditions[1].find("p") == 0 ) - { - if( BoundaryConditions[3].find("P") != 0 && BoundaryConditions[3].find("p") != 0 ) { - cout << "WARNING!!! East boundary is periodic! Changing West boundary to periodic" << endl; - BoundaryConditions[3] = "P"; + if( BoundaryConditions[3].find("P") != 0 && BoundaryConditions[3].find("p") != 0 ) + { + cout << "WARNING!!! East boundary is periodic! Changing West boundary to periodic" << endl; + BoundaryConditions[3] = "P"; + } } - } if( BoundaryConditions[2].find("P") == 0 || BoundaryConditions[2].find("p") == 0 ) - { - if( BoundaryConditions[0].find("P") != 0 && BoundaryConditions[0].find("p") != 0 ) { - cout << "WARNING!!! South boundary is periodic! Changing North boundary to periodic" << endl; - BoundaryConditions[0] = "P"; + if( BoundaryConditions[0].find("P") != 0 && BoundaryConditions[0].find("p") != 0 ) + { + cout << "WARNING!!! South boundary is periodic! Changing North boundary to periodic" << endl; + BoundaryConditions[0] = "P"; + } } - } if( BoundaryConditions[3].find("P") == 0 || BoundaryConditions[3].find("p") == 0 ) - { - if( BoundaryConditions[1].find("P") != 0 && BoundaryConditions[1].find("p") != 0 ) { - cout << "WARNING!!! West boundary is periodic! Changing East boundary to periodic" << endl; - BoundaryConditions[1] = "P"; + if( BoundaryConditions[1].find("P") != 0 && BoundaryConditions[1].find("p") != 0 ) + { + cout << "WARNING!!! West boundary is periodic! Changing East boundary to periodic" << endl; + BoundaryConditions[1] = "P"; + } } - } // reset baselevel switch for boundaries one_if_a_baselevel_node = 0; // NORTH BOUNDARY if (row == 0) - { - if( BoundaryConditions[0].find("B") == 0 || BoundaryConditions[0].find("b") == 0 ) - { - one_if_a_baselevel_node = 1; - } - else { - // if periodic, reflect across to south boundary - if( BoundaryConditions[0].find("P") == 0 || BoundaryConditions[0].find("p") == 0 ) - { - row_kernal[0] = NRows-1; - row_kernal[1] = NRows-1; - row_kernal[7] = NRows-1; - } + if( BoundaryConditions[0].find("B") == 0 || BoundaryConditions[0].find("b") == 0 ) + { + one_if_a_baselevel_node = 1; + } else - { - row_kernal[0] = ndv; - row_kernal[1] = ndv; - row_kernal[7] = ndv; - } + { + // if periodic, reflect across to south boundary + if( BoundaryConditions[0].find("P") == 0 || BoundaryConditions[0].find("p") == 0 ) + { + row_kernal[0] = NRows-1; + row_kernal[1] = NRows-1; + row_kernal[7] = NRows-1; + } + else + { + row_kernal[0] = ndv; + row_kernal[1] = ndv; + row_kernal[7] = ndv; + } + } } - } // EAST BOUNDAY if (col == NCols-1) - { - if( BoundaryConditions[1].find("B") == 0 || BoundaryConditions[1].find("b") == 0 ) - { - one_if_a_baselevel_node = 1; - } - else { - if( BoundaryConditions[1].find("P") == 0 || BoundaryConditions[1].find("p") == 0) - { - col_kernal[1] = 0; - col_kernal[2] = 0; - col_kernal[3] = 0; - } + if( BoundaryConditions[1].find("B") == 0 || BoundaryConditions[1].find("b") == 0 ) + { + one_if_a_baselevel_node = 1; + } else - { - col_kernal[1] = ndv; - col_kernal[2] = ndv; - col_kernal[3] = ndv; - } + { + if( BoundaryConditions[1].find("P") == 0 || BoundaryConditions[1].find("p") == 0) + { + col_kernal[1] = 0; + col_kernal[2] = 0; + col_kernal[3] = 0; + } + else + { + col_kernal[1] = ndv; + col_kernal[2] = ndv; + col_kernal[3] = ndv; + } + } } - } // SOUTH BOUNDARY if (row == NRows-1) - { - if( BoundaryConditions[2].find("B") == 0 || BoundaryConditions[2].find("b") == 0 ) - { - one_if_a_baselevel_node = 1; - } - else { - if( BoundaryConditions[2].find("P") == 0 || BoundaryConditions[2].find("p") == 0) - { - row_kernal[3] = 0; - row_kernal[4] = 0; - row_kernal[5] = 0; - } + if( BoundaryConditions[2].find("B") == 0 || BoundaryConditions[2].find("b") == 0 ) + { + one_if_a_baselevel_node = 1; + } else - { - row_kernal[3] = ndv; - row_kernal[4] = ndv; - row_kernal[5] = ndv; - } + { + if( BoundaryConditions[2].find("P") == 0 || BoundaryConditions[2].find("p") == 0) + { + row_kernal[3] = 0; + row_kernal[4] = 0; + row_kernal[5] = 0; + } + else + { + row_kernal[3] = ndv; + row_kernal[4] = ndv; + row_kernal[5] = ndv; + } + } } - } // WEST BOUNDARY if (col == 0) - { - if( BoundaryConditions[3].find("B") == 0 || BoundaryConditions[3].find("b") == 0 ) - { - one_if_a_baselevel_node = 1; - } - else { - if( BoundaryConditions[3].find("P") == 0 || BoundaryConditions[3].find("p") == 0) - { - col_kernal[5] = NCols-1; - col_kernal[6] = NCols-1; - col_kernal[7] = NCols-1; - } + if( BoundaryConditions[3].find("B") == 0 || BoundaryConditions[3].find("b") == 0 ) + { + one_if_a_baselevel_node = 1; + } else - { - col_kernal[5] = ndv; - col_kernal[6] = ndv; - col_kernal[7] = ndv; - } + { + if( BoundaryConditions[3].find("P") == 0 || BoundaryConditions[3].find("p") == 0) + { + col_kernal[5] = NCols-1; + col_kernal[6] = NCols-1; + col_kernal[7] = NCols-1; + } + else + { + col_kernal[5] = ndv; + col_kernal[6] = ndv; + col_kernal[7] = ndv; + } + } } - } // now loop through the surrounding nodes, calculating the slopes // slopes with NoData get NoData slopes @@ -452,86 +455,86 @@ void LSDFlowInfo::create(vector& temp_BoundaryConditions, // 5 4 3 // first logic for baselevel node if (one_if_a_baselevel_node == 1) - { - // get receiver index - FlowDirection[row][col] = -1; - ReceiverVector.push_back(NodeIndex[row][col]); - FlowLengthCode[row][col] = 0; - } + { + // get receiver index + FlowDirection[row][col] = -1; + ReceiverVector.push_back(NodeIndex[row][col]); + FlowLengthCode[row][col] = 0; + } // now the rest of the nodes else - { - FlowLengthCode[row][col] = 0; // set flow length code to 0, this gets reset - // if there is a maximum slope - max_slope = 0; - max_slope_index = -1; - receive_row = row; - receive_col = col; - for (int slope_iter = 0; slope_iter<8; slope_iter++) { - if (row_kernal[slope_iter] == ndv || col_kernal[slope_iter] == ndv) - { - slopes[slope_iter] = NoDataValue; - } - else - { - target_elev = TopoRaster.RasterData[ row_kernal[slope_iter] ][ col_kernal[slope_iter] ]; - if(target_elev == NoDataValue) - { - slopes[slope_iter] = NoDataValue; - } - else + FlowLengthCode[row][col] = 0; // set flow length code to 0, this gets reset + // if there is a maximum slope + max_slope = 0; + max_slope_index = -1; + receive_row = row; + receive_col = col; + for (int slope_iter = 0; slope_iter<8; slope_iter++) { - if(slope_iter%2 == 0) - { - //cout << "LINE 988, cardinal direction, slope iter = " << slope_iter << endl; - slope = TopoRaster.RasterData[row][col]-target_elev; - } - else - { - slope = one_ov_root2*(TopoRaster.RasterData[row][col]-target_elev); - } - - if (slope > max_slope) - { - max_slope_index = slope_iter; - receive_row = row_kernal[slope_iter]; - receive_col = col_kernal[slope_iter]; - max_slope = slope; - if(slope_iter%2 == 0) + if (row_kernal[slope_iter] == ndv || col_kernal[slope_iter] == ndv) { - FlowLengthCode[row][col] = 1; + slopes[slope_iter] = NoDataValue; } - else + else { - FlowLengthCode[row][col] = 2; + target_elev = TopoRaster.RasterData[ row_kernal[slope_iter] ][ col_kernal[slope_iter] ]; + if(target_elev == NoDataValue) + { + slopes[slope_iter] = NoDataValue; + } + else + { + if(slope_iter%2 == 0) + { + //cout << "LINE 988, cardinal direction, slope iter = " << slope_iter << endl; + slope = TopoRaster.RasterData[row][col]-target_elev; + } + else + { + slope = one_ov_root2*(TopoRaster.RasterData[row][col]-target_elev); + } + + if (slope > max_slope) + { + max_slope_index = slope_iter; + receive_row = row_kernal[slope_iter]; + receive_col = col_kernal[slope_iter]; + max_slope = slope; + if(slope_iter%2 == 0) + { + FlowLengthCode[row][col] = 1; + } + else + { + FlowLengthCode[row][col] = 2; + } + } + } } - } } - } - } - // get receiver index - FlowDirection[row][col] = max_slope_index; - ReceiverVector.push_back(NodeIndex[receive_row][receive_col]); - } // end if baselevel boundary conditional + // get receiver index + FlowDirection[row][col] = max_slope_index; + ReceiverVector.push_back(NodeIndex[receive_row][receive_col]); + } // end if baselevel boundary conditional // if the node is a base level node, add it to the base level node list if (FlowLengthCode[row][col] == 0) - { - BaseLevelNodeList.push_back(NodeIndex[row][col]); - } + { + BaseLevelNodeList.push_back(NodeIndex[row][col]); + } } // end if there is data conditional - } // end col loop - } // end row loop + } // end col loop + } // end row loop // first create the number of donors vector // from braun and willett eq. 5 for(int i = 0; i& temp_BoundaryConditions, // from Braun and Willett eq 7 and 8 DeltaVector[NDataNodes] = NDataNodes; for(int i = NDataNodes; i>0; i--) - { - DeltaVector[i-1] = DeltaVector[i] - NDonorsVector[i-1]; - } + { + DeltaVector[i-1] = DeltaVector[i] - NDonorsVector[i-1]; + } // now the DonorStack and the r vectors. These come from Braun and Willett @@ -553,14 +556,14 @@ void LSDFlowInfo::create(vector& temp_BoundaryConditions, int w_index; int delta_index; for (int i = 0; i& temp_BoundaryConditions, j_index = 0; for (int i = 0; i donors = retrieve_donors_to_node( node_list[node]); - // Loop through all the donors to this particular node. We will look through the donors to - // find upslope influence of nodata. - int n_donors = int(donors.size()); - for (int donor = 0; donor donors = retrieve_donors_to_node( node_list[node]); + int n_donors = int(donors.size()); + + if( problem_node) { - if (node_list[nl] == this_d_node) - { - donor_in_node_list = true; - } + cout << "=====================================================================" << endl; + cout << "Inspecting problem nodes: " << row << ", " << col << "," << new_topography_data[row][col] << endl; } - if (donor_in_node_list == false) + // Loop through all the donors to this particular node. We will look through the donors to + // find upslope influence of nodata. + for (int donor = 0; donor influenced_nodes = get_upslope_nodes_include_outlet(this_d_node); - int n_in_basin = int(influenced_nodes.size()); - for (int in = 0; in < n_in_basin; in++) + if (node_list[nl] == this_d_node) { - current_node = influenced_nodes[in]; - retrieve_current_row_and_col(current_node,row,col); - new_topography_data[row][col] = topography.get_data_element(row,col); + donor_in_node_list = true; + + if (problem_node) + { + cout << "This donor is in the channel" << endl; + } } } - } + if (donor_in_node_list == false) + { + bool is_influenced = is_upstream_influenced_by_nodata(this_d_node, topography); + + if (problem_node) + { + cout << "This donor is not in the channel, Is it influenced by nodata? " << is_influenced << endl; + } + + // If this donor is not influenced by nodata anywhere upstream, add all the pixels + // draining to this node to the data. + if (is_influenced == false) + { + vector influenced_nodes = get_upslope_nodes_include_outlet(this_d_node); + int n_in_basin = int(influenced_nodes.size()); + for (int in = 0; in < n_in_basin; in++) + { + current_node = influenced_nodes[in]; + retrieve_current_row_and_col(current_node,row,col); + new_topography_data[row][col] = topography.get_data_element(row,col); + } + } + } + } + } + else + { + cout << "This nodeindex has a nodata value" << endl; } + } LSDRaster temp_topo(NRows,NCols,XMinimum,YMinimum,DataResolution,NoDataValue,new_topography_data,GeoReferencingStrings); @@ -2254,6 +2296,7 @@ vector LSDFlowInfo::Ingest_Channel_Heads(string filename, int input_switch) return Sources; } + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Method to ingest sources from OS MasterMap Water Network Layer (csv) // into a vector of source nodes so that an LSDJunctionNetwork can be created easily @@ -2539,7 +2582,7 @@ LSDIndexRaster LSDFlowInfo::write_NodeIndexVector_to_LSDIndexRaster_Unique(vecto //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // -// This function calcualtes the contributing pixels +// This function calculates the contributing pixels // it can be converted to contributing area by multiplying by the // DataResolution^2 // @@ -2655,13 +2698,20 @@ LSDRaster LSDFlowInfo::write_DrainageArea_to_LSDRaster() float this_DA; Array2D DrainageArea_local(NRows,NCols,ndv); + //cout << "HELLO WHT THE FUCK" << endl; + for(int node = 0; node LSDFlowInfo::get_upslope_nodes(int node_number_outlet) int end_SVector_node = start_SVector_node+NContributingNodes[node_number_outlet]; for(int node = start_SVector_node; node < end_SVector_node; node++) - { - us_nodes.push_back(SVector[node]); - } + { + us_nodes.push_back(SVector[node]); + } return us_nodes; } @@ -2828,18 +2878,18 @@ vector LSDFlowInfo::get_upslope_nodes_include_outlet(int node_number_outlet us_nodes.push_back(node_number_outlet); if(node_number_outlet < 0 || node_number_outlet > NDataNodes-1) - { - cout << "the node index does not exist" << endl; - exit(EXIT_FAILURE); - } + { + cout << "the node index does not exist" << endl; + exit(EXIT_FAILURE); + } int start_SVector_node = SVectorIndex[node_number_outlet]; int end_SVector_node = start_SVector_node+NContributingNodes[node_number_outlet]; for(int node = start_SVector_node; node < end_SVector_node; node++) - { - us_nodes.push_back(SVector[node]); - } + { + us_nodes.push_back(SVector[node]); + } return us_nodes; } @@ -3218,6 +3268,42 @@ LSDRaster LSDFlowInfo::upslope_variable_accumulator_v3(LSDRaster& accum_raster) } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// +// This averages an upslope quantity +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +LSDRaster LSDFlowInfo::upslope_average(LSDRaster& accum_raster) +{ + LSDRaster Accumulated_value = upslope_variable_accumulator_v3(accum_raster); + float raster_NoDataValue = accum_raster.get_NoDataValue(); + int row,col; + // Now loop through nodes getting the average values + // create the data array + Array2D average_data_array(NRows,NCols,raster_NoDataValue); + for (int node = 0; node LSDFlowInfo::sort_node_list_based_on_raster(vector node_vec, LS int LSDFlowInfo::get_node_index_of_coordinate_point(float X_coordinate, float Y_coordinate) { // Shift origin to that of dataset - float X_coordinate_shifted_origin = X_coordinate - XMinimum - DataResolution*0.5; - float Y_coordinate_shifted_origin = Y_coordinate - YMinimum - DataResolution*0.5; + float X_coordinate_shifted_origin = X_coordinate - XMinimum; + float Y_coordinate_shifted_origin = Y_coordinate - YMinimum; // Get row and column of point int col_point = int(X_coordinate_shifted_origin/DataResolution); - int row_point = (NRows-1) - int(ceil(Y_coordinate_shifted_origin/DataResolution)); + int row_point = (NRows - 1) - int(ceil(Y_coordinate_shifted_origin/DataResolution)-0.5); + // Get node of point int CurrentNode; @@ -4511,7 +4598,7 @@ void LSDFlowInfo::D8_Trace(int i, int j, LSDIndexRaster StreamNetwork, float& le vector LSDFlowInfo::get_flow_path(float x_loc, float y_loc) { vector node_list; - + // check to see if the node is in the raster bool is_in_raster = check_if_point_is_in_raster(x_loc,y_loc); @@ -4538,16 +4625,43 @@ vector LSDFlowInfo::get_flow_path(float x_loc, float y_loc) } + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -// Prints a node list +// This gets a flow path. Like D8 trace, but just get a vector of nodeindices rather than anything +// more fancy //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -void LSDFlowInfo::print_vector_of_nodeindices_to_csv_file_with_latlong(vector node_list,string path, string filename) +vector LSDFlowInfo::get_flow_path(int ni) { - // these are for extracting element-wise data from the channel profiles. - int this_node, row,col; - double latitude,longitude; - double x_loc,y_loc; - LSDCoordinateConverterLLandUTM Converter; + vector node_list; + + int receiver_node, current_node; + + current_node = ni; + node_list.push_back(current_node); + + retrieve_receiver_information(current_node,receiver_node); + + while (receiver_node != current_node) + { // need to do edge checking + + current_node = receiver_node; + retrieve_receiver_information(current_node,receiver_node); + + node_list.push_back(current_node); + } + return node_list; +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// Prints a node list +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void LSDFlowInfo::print_vector_of_nodeindices_to_csv_file_with_latlong(vector node_list,string path, string filename) +{ + // these are for extracting element-wise data from the channel profiles. + int this_node, row,col; + double latitude,longitude; + double x_loc,y_loc; + LSDCoordinateConverterLLandUTM Converter; // find the number of nodes int n_nodes = int(node_list.size()); @@ -4631,6 +4745,86 @@ void LSDFlowInfo::print_vector_of_nodeindices_to_csv_file_with_latlong(vector nodeindex_vec, int node_spacing, + bool print_bearings, string outfile_name, + vector& bearing_nodes, vector& bearing_vec) +{ + + ofstream bearings; + if(print_bearings) + { + bearings.open(outfile_name.c_str()); + bearings << "latitude,longitude,bearing,bearing_degree" << endl; + bearings.precision(9); + } + + vector nodes; + vector bearings_vec; + + int top_node; + int middle_node; + int bottom_node; + + int t_row,t_col,m_row,m_col,b_row,b_col; + float t_x_loc,t_y_loc,m_x_loc,m_y_loc,b_x_loc,b_y_loc; + + float bearing_u, bearing_d, bearing; + + double latitude,longitude; + LSDCoordinateConverterLLandUTM Converter; + + int n_nodes = int( nodeindex_vec.size()); + + top_node = 0; + middle_node = node_spacing; + bottom_node = node_spacing*2; + + while (bottom_node < n_nodes) + { + retrieve_current_row_and_col(nodeindex_vec[top_node],t_row,t_col); + get_x_and_y_locations(t_row, t_col, t_x_loc, t_y_loc); + + retrieve_current_row_and_col(nodeindex_vec[middle_node],m_row,m_col); + get_x_and_y_locations(m_row, m_col, m_x_loc, m_y_loc); + + retrieve_current_row_and_col(nodeindex_vec[bottom_node],b_row,b_col); + get_x_and_y_locations(b_row, b_col, b_x_loc, b_y_loc); + + bearing_u = clockwise_angle_between_vector_and_north( t_x_loc, t_y_loc, m_x_loc, m_y_loc); + bearing_d = clockwise_angle_between_vector_and_north( m_x_loc, m_y_loc, b_x_loc, b_y_loc); + + bearing = 0.5*(bearing_u-bearing_d)+bearing_d; + + nodes.push_back(nodeindex_vec[middle_node]); + bearings_vec.push_back(bearing); + + if(print_bearings) + { + get_lat_and_long_locations(m_row, m_col, latitude, longitude, Converter); + bearings << latitude << "," << longitude << "," << bearing << "," << deg(bearing) << endl; + } + + top_node++; + middle_node++; + bottom_node++; + + } + + if(print_bearings) + { + bearings.close(); + } + + bearing_nodes = nodes; + bearing_vec = bearings_vec; + + +} + + + + + // Move the location of the channel head downslope by a user defined distance. // Returns A vector of node indexes pointing to the moved heads. // SWDG 27/11/15 @@ -5810,42 +6004,1360 @@ vector< Array2D > LSDFlowInfo::HilltopFlowRouting(LSDRaster Elevation, LS } WriteTracesFile.close(); - //open output filestream again to coastline data - WriteTracesFile.open(OutputFileName.c_str(), fstream::app|fstream::out); + //open output filestream again to coastline data + WriteTracesFile.open(OutputFileName.c_str(), fstream::app|fstream::out); + + //Check if file exists if not open a new one and write headers + if (WriteTracesFile.is_open()) + { + for (int v = 0; v < count+1; ++v) + { + //get lat long for printing to file + get_lat_and_long_locations(east_vec[v], north_vec[v], latitude, longitude, Converter); + + if (basin_filter_switch == false) + { + WriteTracesFile << ht_count << "," << setiosflags(ios::fixed) << setprecision(10) << east_vec[v] << "," << north_vec[v] << "," << latitude << "," << longitude << "," << length_vec[v] << "," << zeta_vec[v] << endl; + } + else if (basin_filter_switch == true && find(Target_Basin_Vector.begin(), Target_Basin_Vector.end(), basin[a][b]) != Target_Basin_Vector.end() && find(Target_Basin_Vector.begin(), Target_Basin_Vector.end(), basin[i][j]) != Target_Basin_Vector.end()) + { //is this correct? evaulating to not equal one past the end of the vector should equal that the value is found + WriteTracesFile << ht_count << "," << setiosflags(ios::fixed) << setprecision(10) << east_vec[v] << "," << north_vec[v] << "," << latitude << "," << longitude << "," << length_vec[v] << "," << zeta_vec[v] << endl; + } + } + } + WriteTracesFile.close(); + } + } + } // End of path printing logic + } + } //for loop i,j + } + + ofs.close(); + + //add the data arrays to the output vector + OutputArrays.push_back(RoutedHilltops); + OutputArrays.push_back(HillslopeLength_Array); + OutputArrays.push_back(Slope_Array); + OutputArrays.push_back(Relief_Array); + + //Print debugging info to screen + cout << endl; //push output onto new line + cout << "Hilltop count: " << ht_count << endl; + cout << "Stream count: " << s_count << endl; + cout << "Fail count: " << ns_count << endl; + cout << "Uphill count: " << neg_count << endl; + cout << "Edge count: " << edge_count << endl; + + return OutputArrays; +} + + + + + + + + + + + + + + + + + + + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// Hilltop flow routing code built around original code from Martin Hurst. Based on +// Lea (1992), with improvements discussed by Tarboton (1997) and a solution to the +// problem of looping flow paths implemented. +// +// This code is SLOW but robust, a refactored version may appear, but there may not be +// enough whisky in Scotland to support that endeavour. +// +// The algorithm now checks for local uphill flows and in the case of identifying one, +// D8 flow path is used to push the flow into the centre of the steepest downslope +// cell, at which point the trace is restarted. The same technique is used to cope +// with self intersections of the flow path. These problems are not solved in the +// original paper and I think they are caused at least in part by the high resolution +// topogrpahy we are using. +// +// The code is also now built to take a d infinity flow direction raster instead of an +// aspect raster. See Tarboton (1997) for discussions on why this is the best solution. +// +// The Basins input raster is used to code each hilltop into a basin to allow basin +// averaging to take place. +// +// The final 5 parameters are used to set up printing flow paths to files for visualisation, +// if this is not needed simply pass in false to the two boolean switches and empty variables for the +// others, and the code will run as normal. +// +// The structure of the returned vector< Array2D > is as follows: +// [0] Hilltop Network coded with stream ID +// [1] Hillslope Lengths +// [2] Slope +// [3] Relief +// +// SWDG 12/2/14 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +vector< Array2D > LSDFlowInfo::HilltopFlowRouting_Refactored(LSDRaster& Elevation, LSDRaster& Hilltop_ID, LSDRaster& Slope, + LSDRaster& Aspect, LSDRaster& HilltopCurv, LSDRaster& PlanCurvature, + LSDIndexRaster& StreamNetwork, LSDIndexRaster& Basins, + string Prefix, + bool print_paths_switch, int thinning, string trace_path, bool basin_filter_switch, + vector Target_Basin_Vector) +{ + //Declare parameters + int i,j; + int a = 0; + int b = 0; + float X,Y; + float mean_slope, relief; + float length, d; + int flag; + int count = 0; + int DivergentCountFlag = 0; //Flag used to count the number of divergent cells encountered in a trace + int PlanarCountFlag; + float PI = 3.14159265; + float degs, degs_new, theta; + //float s_local; + //float s_edge; + float xo, yo, xi, yi, temp_yo1, temp_yo2, temp_xo1, temp_xo2; + bool skip_trace; //flag used to skip traces where no path to a stream can be found. Will only occur on noisy, raw topography + float E_Star = 0; + float R_Star = 0; + float EucDist = 0; + + //debugging counters + int ns_count = 0; + int s_count = 0; + int neg_count = 0; + int edge_count = 0; + int ht_count = 0; + + // a direction flag numbered 1,2,3,4 for E,S,W,N respectively + int dir; + + float ymax = YMinimum + NRows*DataResolution; + + + // The array of slopes. Hopefully this will save some sime + Array2D tangent_raster = Aspect.get_RasterData(); + + //Get data arrays from LSDRasters + // SMM Not sure why we need these + Array2D zeta = Elevation.get_RasterData(); //elevation + Array2D stnet = StreamNetwork.get_RasterData(); // stream network + Array2D aspect = Aspect.get_RasterData(); //aspect + Array2D hilltop_ID = Hilltop_ID.get_RasterData(); //hilltops + Array2D CHT = HilltopCurv.get_RasterData(); //hilltop curv + Array2D slope = Slope.get_RasterData(); //hilltop slope + Array2D basin = Basins.get_RasterData(); //basins + + //empty arrays for data to be stored in + Array2D rads(NRows,NCols,NoDataValue); + Array2D tangents(NRows,NCols,NoDataValue); + Array2D one_over_tangents(NRows,NCols,NoDataValue); + + + + Array2D path(NRows, NCols, 0.0); + Array2D blank(NRows, NCols, 0.0); + Array2D RoutedHilltops(NRows,NCols,NoDataValue); + Array2D HillslopeLength_Array(NRows,NCols,NoDataValue); + Array2D Slope_Array(NRows,NCols,NoDataValue); + Array2D Relief_Array(NRows,NCols,NoDataValue); + + //vector to store the output data arrays in one vector that can be returned + vector< Array2D > OutputArrays; + vector easting, northing, east_vec, north_vec, zeta_vec, length_vec, empty_vec; + + ofstream ofs; + + //create the output filename from the user supplied filename prefix + stringstream ss_filename; + ss_filename << Prefix << "_HilltopData.csv"; + + ofs.open(ss_filename.str().c_str()); + + if( ofs.fail() ) + { + cout << "\nFATAL ERROR: unable to write to " << ss_filename.str() << endl; + exit(EXIT_FAILURE); + } + ofs << "easting,northing,i,j,hilltop_id,Cht,S,R,Lh,BasinID,a,b,StreamID,HilltopSlope,DivergentCount,PlanarCountFlag,E_Star,R_Star,EucDist\n"; + + //calculate northing and easting + cout << "XMinimum is " << XMinimum << endl; + cout << "YMinimum is " << YMinimum << endl; + cout << "ymax is " << ymax << endl; + + for (i=0;i= 45 && degs < 135) + { + //cout << "\neasterly" << endl; + xo = 1, yo = (1+tangents[i][j])*0.5; + d = abs(1/(2*cos(theta))); + xi = 0, yi = yo; + dir = 1; + east_vec.push_back(easting[b] + half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + ++b; + if (yi == 0) yi = 0.00001; + else if (yi == 1) yi = 1 - 0.00001; + } + //southerly + else if (degs >= 135 && degs < 225) + { + //cout << "\nsoutherly" << endl; + xo = (1-one_over_tangents[i][j])*0.5, yo = 0; + d = abs(1/(2*cos((pi_over_two)-theta))); + xi = xo, yi = 1; + dir = 2; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] - half_datares); + ++a; + if (xi == 0) xi = 0.00001; + else if (xi == 1) xi = 1 - 0.00001; + } + // westerly + else if (degs >= 225 && degs < 315) + { + xo = 0, yo = (1-tangents[i][j])*0.5; + d = abs(1/(2*cos(theta))); + xi = 1, yi = yo; + dir = 3; + east_vec.push_back(easting[b] -half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + --b; + if (yi == 0) yi = 0.00001; + else if (yi == 1) yi = 1 - 0.00001; + } + //northerly + else if (degs >= 315 || degs < 45) + { + xo = (1+one_over_tangents[i][j])*0.5, yo = 1; + d = abs(1/(2*cos((pi_over_two) - theta))); + xi = xo, yi = 0; + dir = 4; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] + half_datares); + --a; + if (xi == 0) xi = 0.00001; + else if (xi == 1) xi = 1 - 0.00001; + } + else + { + cout << "FATAL ERROR, Kinematic routing algorithm enountered null aspect value" << endl; + exit(EXIT_FAILURE); + } + + //collect slopes and totals weighted by path length + length += d; + //s_local = slope[a][b]; + + //update elevation length vectors + zeta_vec.push_back(zeta[a][b]); + length_vec.push_back(length*DataResolution); + + //continue trace until a stream node is encountered + while (flag == true && a > 0 && a < NRows-1 && b > 0 && b < NCols-1) + { //added boudary checking to catch cells which flow off the edge of the DEM tile. + + path[a][b] += 1; + + degs_new = aspect[a][b]; + theta = rads[a][b]; + ++count; + + //Test for perimeter flow paths + if ((dir == 1 && degs_new > 0 && degs_new < 180) + || (dir == 2 && degs_new > 90 && degs_new < 270) + || (dir == 3 && degs_new > 180 && degs_new < 360) + || ((dir == 4 && degs_new > 270) || (dir == 4 && degs_new < 90))) + { + + //DO NORMAL FLOW PATH + //set xo, yo to 0 and 1 in turn and test for true outlet (xi || yi == 0 || 1) + temp_yo1 = yi + (1-xi)*tangents[a][b]; // xo = 1 + temp_xo1 = xi + (1-yi)*(one_over_tangents[a][b]); // yo = 1 + temp_yo2 = yi - xi*tangents[a][b]; // xo = 0 + temp_xo2 = xi - yi*(one_over_tangents[a][b]); // yo = 0 + + // can't outlet at same point as inlet + if (dir == 1) temp_yo2 = -1; + else if (dir == 2) temp_xo1 = -1; + else if (dir == 3) temp_yo1 = -1; + else if (dir == 4) temp_xo2 = -1; + + //s_local = slope[a][b]; + + if (temp_yo1 <= 1 && temp_yo1 > 0) + { + xo = 1, yo = temp_yo1; + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 0, yi = yo, + dir = 1; + east_vec.push_back(easting[b] + half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + ++b; + if (xi== 0 && yi == 0) yi = 0.00001; + else if (xi== 0 && yi == 1) yi = 1 - 0.00001; + } + else if (temp_xo2 <= 1 && temp_xo2 > 0) + { + xo = temp_xo2, yo = 0; + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1, + dir = 2; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] - half_datares); + ++a; + if (xi== 0 && yi == 1) xi = 0.00001; + else if (xi== 1 && yi == 1) xi = 1 - 0.00001; + } + else if (temp_yo2 <= 1 && temp_yo2 > 0) + { + xo = 0, yo = temp_yo2; + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1, yi = yo, + dir = 3; + east_vec.push_back(easting[b] -half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + --b; + if (xi== 1 && yi == 0) yi = 0.00001; + else if (xi== 1 && yi == 1) yi = 1 - 0.00001; + } + else if (temp_xo1 <= 1 && temp_xo1 > 0) + { + xo = temp_xo1, yo = 1; + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 0, + dir = 4; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] + half_datares); + --a; + if (xi == 0 && yi == 0) xi = 0.00001; + else if (xi== 1 && yi == 0) xi = 1 - 0.00001; + } + } + else + { + // ROUTE ALONG EDGES + if (dir == 1) + { + if (degs_new <= 90 || degs_new >= 270) + { + //secondary compenent of flow is north + xo = 0.00001, yo = 1; + //s_edge = abs(s_local*sin(theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1-yo; + dir = 4; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] + half_datares); + --a; + } + else if (degs_new > 90 && degs_new < 270) { //secondary component is south + xo = 0.00001, yo = 0; + //s_edge = abs(s_local*sin((PI/2)-theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1-yo; + dir = 2; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] - half_datares); + ++a; + } + else + { + cout << "Flow unable to route N or S " << endl; //something has gone very wrong... + cout << "Trace skipped.\n" << endl; + skip_trace = true; + //exit(EXIT_FAILURE); + } + } + else if (dir == 2) + { + if (degs_new >= 0 && degs_new <= 180) + { + //secondary component is East + xo = 1, yo = 1-0.00001; + //s_edge = abs(s_local*sin((2/PI)-theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1-xo, yi = yo; + dir = 1; + east_vec.push_back(easting[b] + half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + ++b; + } + else if (degs_new > 180 && degs_new <= 360) + { + //secondary component is West + xo = 0, yo = 1-0.00001; + //s_edge = abs(s_local*sin(theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1-xo, yi = yo; + dir = 3; + east_vec.push_back(easting[b] -half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + --b; + } + else + { + cout << "Flow unable to route E or W" << endl; //something has gone very wrong... + cout << "Trace skipped.\n" << endl; + skip_trace = true; + //exit(EXIT_FAILURE); + } + } + else if (dir == 3) + { + if (degs_new >= 90 && degs_new <= 270) + { + //secondary component is South + xo = 1-0.00001, yo = 0; + //s_edge = abs(s_local*sin(theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1-yo; + dir = 2; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] - half_datares); + ++a; + } + else if (degs_new > 270 || degs_new < 90) + { + //secondary component is North + xo = 1-0.00001, yo = 1; + //s_edge = abs(s_local*sin((2/PI) - theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1- yo; + dir = 4; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] + half_datares); + --a; + } + else + { + cout << "Flow unable to route N or S" << endl; //something has gone very wrong... + cout << "Trace skipped.\n" << endl; + skip_trace = true; + //exit(EXIT_FAILURE); + } + } + else if (dir == 4) + { + if (degs_new >= 180 && degs_new <= 360) + { + //secondary component is West + xo = 0, yo = 0.00001; + //s_edge = abs(s_local*sin((PI/2) - theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1-xo, yi = yo; + dir = 3; + east_vec.push_back(easting[b] -half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + --b; + } + else if (degs_new >= 0 && degs_new < 180) + { + //secondary component is East + xo = 1, yo = 0.00001; + //s_edge = abs(s_local*sin(theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1-xo, yi = yo; + dir = 1; + east_vec.push_back(easting[b] + half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + ++b; + } + else + { + cout << "Flow unable to route E or W" << endl; //something has gone very wrong... + cout << "Trace skipped.\n" << endl; + skip_trace = true; + //exit(EXIT_FAILURE); + } + } + } + + if (path[a][b] < 1) + { // only update length on 'first slosh' + length += d; + } + else if (path[a][b] >= 3) + { //update the skip trace flag so we can categorise each trace + skip_trace = true; + } + degs = degs_new; + + zeta_vec.push_back(zeta[a][b]); + length_vec.push_back(length*DataResolution); + + // test for plan curvature here and set a flag if flow is divergent or convergent but continue trace regardless + // The larger the counter the more convergent or divergent the trace is + if (abs(PlanCurvature.get_data_element(a,b)) > (0.001)) + { + ++DivergentCountFlag; + } + else + { + ++PlanarCountFlag; + } + + if (a == 0 || b == 0 || a == NRows-1 || b == NCols-1 || stnet[a][b] != NoDataValue || path[a][b] >= 3 || skip_trace == true) flag = false; + } + + if (a == 0 || b == 0 || a == NRows-1 || b == NCols-1 ) + { + // avoid going out of bounds. + + // this is caused by having a hilltop on the first row or col away from the border + // eg i or j == 1 or nrows/ncols - 2 and flowing towards the edge. + // can fix with a test here for if streamnet[a][b] != NDV otherwise trace will fail *correctly* + + ++edge_count; + + } + else + { + //if trace finished at a stream, print hillslope info. + if (stnet[a][b] != NoDataValue) // || stnet[a-1][b-1] != NoDataValue || stnet[a][b-1] != NoDataValue || stnet[a+1][b-1] != NoDataValue || stnet[a+1][b] != NoDataValue || stnet[a+1][b+1] != NoDataValue || stnet[a][b+1] != NoDataValue || stnet[a-1][b+1] != NoDataValue || stnet[a-1][b] != NoDataValue) + { + path[a][b] = 1; + + ++s_count; + + X = easting[j]; + Y = northing[i]; + relief = zeta[i][j] - zeta[a][b]; + mean_slope = relief/(length * DataResolution); + + // update arrays with the current metrics + RoutedHilltops[i][j] = 1; + HillslopeLength_Array[i][j] = (length * DataResolution); + Slope_Array[i][j] = mean_slope; + Relief_Array[i][j] = relief; + + //calculate an E* and R* Value assuming S_c of 0.8 + E_Star = (2.0 * abs(CHT[i][j])*(length*DataResolution))/0.8; + R_Star = relief/((length*DataResolution)*0.8); + + //calulate the Euclidean distance between the start and end points of the trace + EucDist = sqrt((pow(((i+0.5)-(a+yo)),2) + pow(((j+0.5)-(b+xo)),2))) * DataResolution; + + if (relief > 0) + { + ofs << X << "," << Y << "," << i << "," << j << "," << hilltop_ID[i][j] << "," << CHT[i][j] << "," << mean_slope << "," << relief << "," << length*DataResolution << "," << basin[a][b] << "," << a << "," << b << "," << stnet[a][b] << "," << slope[i][j] << "," << DivergentCountFlag << "," << PlanarCountFlag << "," << E_Star << "," << R_Star << "," << EucDist << "\n"; + } + else + { + ++neg_count; + } + } + else + { + //unable to route using aspects + //this will encompass skipped traces + //ofs << "fail: " << a << " " << b << " " << i << " " << j << endl; + ++ns_count; + } + } + + //This block checks the various path printing options and writes the data out accordingly + if (print_paths_switch == true) + { + if (ht_count % thinning == 0) + { + if (hilltop_ID[i][j] != NoDataValue) // && skip_trace == false) + { //check that the current i,j tuple corresponds to a hilltop, ie there is actually a trace to write to file, and check that the trace was valid. + + //declare some params for lat long conversion + double latitude,longitude; + LSDCoordinateConverterLLandUTM Converter; + + //create the output filename from the user supplied path + ofstream pathwriter; + string OutputFileName = Prefix+"_hillslope_traces.csv"; + ifstream oftest(OutputFileName.c_str()); + bool FileExists = false; + if (oftest) FileExists = true; + oftest.close(); + + //open the output filestream and write headers + ofstream WriteTracesFile; + if (FileExists == 0) + { + WriteTracesFile.open(OutputFileName.c_str()); + //write headers to allow pandas dataframe reading + if (WriteTracesFile.is_open()) WriteTracesFile << "HilltopID,Easting,Northing,Latitude,Longitude,Distance,Elevation" << endl; + } + WriteTracesFile.close(); + + //open output filestream again to coastline data + WriteTracesFile.open(OutputFileName.c_str(), fstream::app|fstream::out); + + //Check if file exists if not open a new one and write headers + if (WriteTracesFile.is_open()) + { + for (int v = 0; v < count+1; ++v) + { + //get lat long for printing to file + get_lat_and_long_locations(east_vec[v], north_vec[v], latitude, longitude, Converter); + + if (basin_filter_switch == false) + { + WriteTracesFile << ht_count << "," << setiosflags(ios::fixed) << setprecision(10) << east_vec[v] << "," << north_vec[v] << "," << latitude << "," << longitude << "," << length_vec[v] << "," << zeta_vec[v] << endl; + } + else if (basin_filter_switch == true && find(Target_Basin_Vector.begin(), Target_Basin_Vector.end(), basin[a][b]) != Target_Basin_Vector.end() && find(Target_Basin_Vector.begin(), Target_Basin_Vector.end(), basin[i][j]) != Target_Basin_Vector.end()) + { //is this correct? evaulating to not equal one past the end of the vector should equal that the value is found + WriteTracesFile << ht_count << "," << setiosflags(ios::fixed) << setprecision(10) << east_vec[v] << "," << north_vec[v] << "," << latitude << "," << longitude << "," << length_vec[v] << "," << zeta_vec[v] << endl; + } + } + } + WriteTracesFile.close(); + } + } + } // End of path printing logic + } + } //for loop i,j + } + + ofs.close(); + + //add the data arrays to the output vector + OutputArrays.push_back(RoutedHilltops); + OutputArrays.push_back(HillslopeLength_Array); + OutputArrays.push_back(Slope_Array); + OutputArrays.push_back(Relief_Array); + + //Print debugging info to screen + cout << endl; //push output onto new line + cout << "Hilltop count: " << ht_count << endl; + cout << "Stream count: " << s_count << endl; + cout << "Fail count: " << ns_count << endl; + cout << "Uphill count: " << neg_count << endl; + cout << "Edge count: " << edge_count << endl; + + return OutputArrays; +} + + + + + + + + + + + + + + + + + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// Hilltop flow routing code built around original code from Martin Hurst. Based on +// Lea (1992), with improvements discussed by Tarboton (1997) and a solution to the +// problem of looping flow paths implemented. +// +// This code is SLOW but robust, a refactored version may appear, but there may not be +// enough whisky in Scotland to support that endeavour. +// +// The algorithm now checks for local uphill flows and in the case of identifying one, +// D8 flow path is used to push the flow into the centre of the steepest downslope +// cell, at which point the trace is restarted. The same technique is used to cope +// with self intersections of the flow path. These problems are not solved in the +// original paper and I think they are caused at least in part by the high resolution +// topogrpahy we are using. +// +// The code is also now built to take a d infinity flow direction raster instead of an +// aspect raster. See Tarboton (1997) for discussions on why this is the best solution. +// +// The Basins input raster is used to code each hilltop into a basin to allow basin +// averaging to take place. +// +// The final 5 parameters are used to set up printing flow paths to files for visualisation, +// if this is not needed simply pass in false to the two boolean switches and empty variables for the +// others, and the code will run as normal. +// +// +// SWDG 12/2/14 +// SMM refactored to be more memory efficient 28/01/2021 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void LSDFlowInfo::HilltopFlowRouting_TerrifyingRefactored(LSDRaster& Elevation, LSDRaster& Aspect, LSDRaster& PlanCurvature, + LSDIndexRaster& StreamNetwork, string ridges_csv_name,string Prefix, + bool print_paths_switch, int thinning, string trace_path, bool basin_filter_switch) +{ + + // Load the ridges csv + LSDSpatialCSVReader RidgesData(Elevation, ridges_csv_name); + + // Grab the data elements from the Ridges data + vector ht_row_vec = RidgesData.data_column_to_int("row"); + vector ht_col_vec = RidgesData.data_column_to_int("col"); + vector basin_vec = RidgesData.data_column_to_int("basin_id"); + vector ht_slope_vec = RidgesData.data_column_to_float("slope"); + vector ht_curv_vec = RidgesData.data_column_to_float("curvature"); + + vector ht_lat_vec = RidgesData.get_latitude(); + vector ht_long_vec = RidgesData.get_longitude(); + + + LSDCoordinateConverterLLandUTM Converter; + + //Declare parameters + int i,j; + int a = 0; + int b = 0; + float X,Y; + float mean_slope, relief; + float length, d; + int flag; + int count = 0; + int DivergentCountFlag = 0; //Flag used to count the number of divergent cells encountered in a trace + int PlanarCountFlag; + float PI = 3.14159265; + float degs, degs_new, theta; + //float s_local; + //float s_edge; + float xo, yo, xi, yi, temp_yo1, temp_yo2, temp_xo1, temp_xo2; + bool skip_trace; //flag used to skip traces where no path to a stream can be found. Will only occur on noisy, raw topography + float E_Star = 0; + float R_Star = 0; + float EucDist = 0; + + float CHT,HT_slope,trace_length; + double HT_latitude, HT_longitude; + int basin_ID; + + //debugging counters + int ns_count = 0; + int s_count = 0; + int neg_count = 0; + int edge_count = 0; + int ht_count = 0; + + // a direction flag numbered 1,2,3,4 for E,S,W,N respectively + int dir; + float ymax = YMinimum + NRows*DataResolution; + + + //empty arrays for data to be stored in + Array2D rads(NRows,NCols,NoDataValue); + Array2D tangents(NRows,NCols,NoDataValue); + Array2D one_over_tangents(NRows,NCols,NoDataValue); + + + Array2D path(NRows, NCols, 0.0); + Array2D blank(NRows, NCols, 0.0); + Array2D RoutedHilltops(NRows,NCols,NoDataValue); + + //vector to store the output data arrays in one vector that can be returned + vector< Array2D > OutputArrays; + vector easting, northing, east_vec, north_vec, zeta_vec, length_vec, empty_vec; + + ofstream ofs; + + //create the output filename from the user supplied filename prefix + stringstream ss_filename; + ss_filename << Prefix << "_HilltopData_TN.csv"; + + ofs.open(ss_filename.str().c_str()); + + if( ofs.fail() ) + { + cout << "\nFATAL ERROR: unable to write to " << ss_filename.str() << endl; + exit(EXIT_FAILURE); + } + ofs << "latitude,longitude,easting,northing,row,col,Cht,S,R,Lh,BasinID,channel_row,channel_col,StreamID,HilltopSlope,DivergentCount,PlanarCountFlag,E_Star,R_Star,EucDist\n"; + + //calculate northing and easting + cout << "XMinimum is " << XMinimum << endl; + cout << "YMinimum is " << YMinimum << endl; + cout << "ymax is " << ymax << endl; + + for (i=0;i= 45 && degs < 135) + { + //cout << "\neasterly" << endl; + xo = 1, yo = (1+tangents[i][j])*0.5; + d = abs(1/(2*cos(theta))); + xi = 0, yi = yo; + dir = 1; + east_vec.push_back(easting[b] + half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + ++b; + if (yi == 0) yi = 0.00001; + else if (yi == 1) yi = 1 - 0.00001; + } + //southerly + else if (degs >= 135 && degs < 225) + { + //cout << "\nsoutherly" << endl; + xo = (1-one_over_tangents[i][j])*0.5, yo = 0; + d = abs(1/(2*cos((pi_over_two)-theta))); + xi = xo, yi = 1; + dir = 2; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] - half_datares); + ++a; + if (xi == 0) xi = 0.00001; + else if (xi == 1) xi = 1 - 0.00001; + } + // westerly + else if (degs >= 225 && degs < 315) + { + xo = 0, yo = (1-tangents[i][j])*0.5; + d = abs(1/(2*cos(theta))); + xi = 1, yi = yo; + dir = 3; + east_vec.push_back(easting[b] -half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + --b; + if (yi == 0) yi = 0.00001; + else if (yi == 1) yi = 1 - 0.00001; + } + //northerly + else if (degs >= 315 || degs < 45) + { + xo = (1+one_over_tangents[i][j])*0.5, yo = 1; + d = abs(1/(2*cos((pi_over_two) - theta))); + xi = xo, yi = 0; + dir = 4; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] + half_datares); + --a; + if (xi == 0) xi = 0.00001; + else if (xi == 1) xi = 1 - 0.00001; + } + else + { + cout << "FATAL ERROR, Kinematic routing algorithm enountered null aspect value" << endl; + exit(EXIT_FAILURE); + } + + //collect slopes and totals weighted by path length + length += d; + //s_local = slope[a][b]; + + //update elevation length vectors + zeta_vec.push_back(Elevation.get_data_element(a,b)); + length_vec.push_back(length*DataResolution); + + //continue trace until a stream node is encountered + while (flag == true && a > 0 && a < NRows-1 && b > 0 && b < NCols-1) + { //added boudary checking to catch cells which flow off the edge of the DEM tile. + + path[a][b] += 1; + + degs_new = Aspect.get_data_element(a,b); + theta = rads[a][b]; + ++count; + + //Test for perimeter flow paths + if ((dir == 1 && degs_new > 0 && degs_new < 180) + || (dir == 2 && degs_new > 90 && degs_new < 270) + || (dir == 3 && degs_new > 180 && degs_new < 360) + || ((dir == 4 && degs_new > 270) || (dir == 4 && degs_new < 90))) + { + + //DO NORMAL FLOW PATH + //set xo, yo to 0 and 1 in turn and test for true outlet (xi || yi == 0 || 1) + temp_yo1 = yi + (1-xi)*tangents[a][b]; // xo = 1 + temp_xo1 = xi + (1-yi)*(one_over_tangents[a][b]); // yo = 1 + temp_yo2 = yi - xi*tangents[a][b]; // xo = 0 + temp_xo2 = xi - yi*(one_over_tangents[a][b]); // yo = 0 + + // can't outlet at same point as inlet + if (dir == 1) temp_yo2 = -1; + else if (dir == 2) temp_xo1 = -1; + else if (dir == 3) temp_yo1 = -1; + else if (dir == 4) temp_xo2 = -1; + + //s_local = slope[a][b]; + + if (temp_yo1 <= 1 && temp_yo1 > 0) + { + xo = 1, yo = temp_yo1; + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 0, yi = yo, + dir = 1; + east_vec.push_back(easting[b] + half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + ++b; + if (xi== 0 && yi == 0) yi = 0.00001; + else if (xi== 0 && yi == 1) yi = 1 - 0.00001; + } + else if (temp_xo2 <= 1 && temp_xo2 > 0) + { + xo = temp_xo2, yo = 0; + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1, + dir = 2; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] - half_datares); + ++a; + if (xi== 0 && yi == 1) xi = 0.00001; + else if (xi== 1 && yi == 1) xi = 1 - 0.00001; + } + else if (temp_yo2 <= 1 && temp_yo2 > 0) + { + xo = 0, yo = temp_yo2; + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1, yi = yo, + dir = 3; + east_vec.push_back(easting[b] -half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + --b; + if (xi== 1 && yi == 0) yi = 0.00001; + else if (xi== 1 && yi == 1) yi = 1 - 0.00001; + } + else if (temp_xo1 <= 1 && temp_xo1 > 0) + { + xo = temp_xo1, yo = 1; + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 0, + dir = 4; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] + half_datares); + --a; + if (xi == 0 && yi == 0) xi = 0.00001; + else if (xi== 1 && yi == 0) xi = 1 - 0.00001; + } + } + else + { + // ROUTE ALONG EDGES + if (dir == 1) + { + if (degs_new <= 90 || degs_new >= 270) + { + //secondary compenent of flow is north + xo = 0.00001, yo = 1; + //s_edge = abs(s_local*sin(theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1-yo; + dir = 4; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] + half_datares); + --a; + } + else if (degs_new > 90 && degs_new < 270) { //secondary component is south + xo = 0.00001, yo = 0; + //s_edge = abs(s_local*sin((PI/2)-theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1-yo; + dir = 2; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] - half_datares); + ++a; + } + else + { + cout << "Flow unable to route N or S " << endl; //something has gone very wrong... + cout << "Trace skipped.\n" << endl; + skip_trace = true; + //exit(EXIT_FAILURE); + } + } + else if (dir == 2) + { + if (degs_new >= 0 && degs_new <= 180) + { + //secondary component is East + xo = 1, yo = 1-0.00001; + //s_edge = abs(s_local*sin((2/PI)-theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1-xo, yi = yo; + dir = 1; + east_vec.push_back(easting[b] + half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + ++b; + } + else if (degs_new > 180 && degs_new <= 360) + { + //secondary component is West + xo = 0, yo = 1-0.00001; + //s_edge = abs(s_local*sin(theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1-xo, yi = yo; + dir = 3; + east_vec.push_back(easting[b] -half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + --b; + } + else + { + cout << "Flow unable to route E or W" << endl; //something has gone very wrong... + cout << "Trace skipped.\n" << endl; + skip_trace = true; + //exit(EXIT_FAILURE); + } + } + else if (dir == 3) + { + if (degs_new >= 90 && degs_new <= 270) + { + //secondary component is South + xo = 1-0.00001, yo = 0; + //s_edge = abs(s_local*sin(theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1-yo; + dir = 2; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] - half_datares); + ++a; + } + else if (degs_new > 270 || degs_new < 90) + { + //secondary component is North + xo = 1-0.00001, yo = 1; + //s_edge = abs(s_local*sin((2/PI) - theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = xo, yi = 1- yo; + dir = 4; + east_vec.push_back(easting[b] + (xo - 0.5)*DataResolution); + north_vec.push_back(northing[a] + half_datares); + --a; + } + else + { + cout << "Flow unable to route N or S" << endl; //something has gone very wrong... + cout << "Trace skipped.\n" << endl; + skip_trace = true; + //exit(EXIT_FAILURE); + } + } + else if (dir == 4) + { + if (degs_new >= 180 && degs_new <= 360) + { + //secondary component is West + xo = 0, yo = 0.00001; + //s_edge = abs(s_local*sin((PI/2) - theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1-xo, yi = yo; + dir = 3; + east_vec.push_back(easting[b] -half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + --b; + } + else if (degs_new >= 0 && degs_new < 180) + { + //secondary component is East + xo = 1, yo = 0.00001; + //s_edge = abs(s_local*sin(theta)); + d = sqrt((xo-xi)*(xo-xi) + (yo-yi)*(yo-yi)); + xi = 1-xo, yi = yo; + dir = 1; + east_vec.push_back(easting[b] + half_datares); + north_vec.push_back(northing[a] + (yo - 0.5)*DataResolution); + ++b; + } + else + { + cout << "Flow unable to route E or W" << endl; //something has gone very wrong... + cout << "Trace skipped.\n" << endl; + skip_trace = true; + //exit(EXIT_FAILURE); + } + } + } + + if (path[a][b] < 1) + { // only update length on 'first slosh' + length += d; + } + else if (path[a][b] >= 3) + { //update the skip trace flag so we can categorise each trace + skip_trace = true; + } + degs = degs_new; + + zeta_vec.push_back(Elevation.get_data_element(a,b)); + length_vec.push_back(length*DataResolution); + + // test for plan curvature here and set a flag if flow is divergent or convergent but continue trace regardless + // The larger the counter the more convergent or divergent the trace is + if (abs(PlanCurvature.get_data_element(a,b)) > (0.001)) + { + ++DivergentCountFlag; + } + else + { + ++PlanarCountFlag; + } + + if (a == 0 || b == 0 || a == NRows-1 || b == NCols-1 + || StreamNetwork.get_data_element(a,b) != NoDataValue + || path[a][b] >= 3 || skip_trace == true) + { + flag = false; + } + } + + if (a == 0 || b == 0 || a == NRows-1 || b == NCols-1 ) + { + // avoid going out of bounds. + + // this is caused by having a hilltop on the first row or col away from the border + // eg i or j == 1 or nrows/ncols - 2 and flowing towards the edge. + // can fix with a test here for if streamnet[a][b] != NDV otherwise trace will fail *correctly* + + ++edge_count; + } + else + { + //if trace finished at a stream, print hillslope info. + if (StreamNetwork.get_data_element(a,b) != NoDataValue) + { + path[a][b] = 1; + + ++s_count; + + X = easting[j]; + Y = northing[i]; + relief = Elevation.get_data_element(i,j) - Elevation.get_data_element(a,b); + trace_length = length * DataResolution; + mean_slope = relief/(trace_length); + + + //calculate an E* and R* Value assuming S_c of 0.8 + E_Star = (2.0 * abs(CHT)*(length*DataResolution))/0.8; + R_Star = relief/((length*DataResolution)*0.8); + + //calulate the Euclidean distance between the start and end points of the trace + EucDist = sqrt( ((i+0.5)-(a+yo))*((i+0.5)-(a+yo)) + ((j+0.5)-(b+xo))*((j+0.5)-(b+xo)) ) * DataResolution; + + if (relief > 0) + { + trace_sucess = true; + ofs.precision(9); + ofs << HT_latitude << "," << HT_longitude << ","; + ofs.precision(6); + ofs << X << "," << Y << "," << i << "," << j << "," << CHT + << "," << mean_slope << "," << relief << "," << length*DataResolution << "," + << basin_ID << "," << a << "," << b << "," << StreamNetwork.get_data_element(a,b) + << "," << HT_slope << "," << DivergentCountFlag << "," << PlanarCountFlag << "," + << E_Star << "," << R_Star << "," << EucDist << "\n"; + } + else + { + ++neg_count; + } + } + else + { + //unable to route using aspects + //this will encompass skipped traces + //ofs << "fail: " << a << " " << b << " " << i << " " << j << endl; + ++ns_count; + } + } + + //This block checks the various path printing options and writes the data out accordingly + if (print_paths_switch == true) + { + if (ht_count % thinning == 0) + { + if (trace_sucess) // && skip_trace == false) + { //check that the current i,j tuple corresponds to a hilltop, ie there is actually a trace to write to file, and check that the trace was valid. + + //declare some params for lat long conversion + double latitude,longitude; + + //create the output filename from the user supplied path + ofstream pathwriter; + string OutputFileName = Prefix+"_hillslope_traces.csv"; + ifstream oftest(OutputFileName.c_str()); + bool FileExists = false; + if (oftest) FileExists = true; + oftest.close(); + + //open the output filestream and write headers + ofstream WriteTracesFile; + if (FileExists == 0) + { + WriteTracesFile.open(OutputFileName.c_str()); + //write headers to allow pandas dataframe reading + if (WriteTracesFile.is_open()) WriteTracesFile << "HilltopID,Easting,Northing,Latitude,Longitude,Distance,Elevation" << endl; + } + WriteTracesFile.close(); + + //open output filestream again to coastline data + WriteTracesFile.open(OutputFileName.c_str(), fstream::app|fstream::out); - //Check if file exists if not open a new one and write headers - if (WriteTracesFile.is_open()) - { - for (int v = 0; v < count+1; ++v) - { - //get lat long for printing to file - get_lat_and_long_locations(east_vec[v], north_vec[v], latitude, longitude, Converter); + //Check if file exists if not open a new one and write headers + if (WriteTracesFile.is_open()) + { + for (int v = 0; v < count+1; ++v) + { + //get lat long for printing to file + get_lat_and_long_locations(east_vec[v], north_vec[v], latitude, longitude, Converter); + + WriteTracesFile << ht_count << "," << setiosflags(ios::fixed) << setprecision(10) << east_vec[v] << "," + << north_vec[v] << "," << latitude << "," << longitude << "," << length_vec[v] << "," + << zeta_vec[v] << endl; - if (basin_filter_switch == false) - { - WriteTracesFile << ht_count << "," << setiosflags(ios::fixed) << setprecision(10) << east_vec[v] << "," << north_vec[v] << "," << latitude << "," << longitude << "," << length_vec[v] << "," << zeta_vec[v] << endl; - } - else if (basin_filter_switch == true && find(Target_Basin_Vector.begin(), Target_Basin_Vector.end(), basin[a][b]) != Target_Basin_Vector.end() && find(Target_Basin_Vector.begin(), Target_Basin_Vector.end(), basin[i][j]) != Target_Basin_Vector.end()) - { //is this correct? evaulating to not equal one past the end of the vector should equal that the value is found - WriteTracesFile << ht_count << "," << setiosflags(ios::fixed) << setprecision(10) << east_vec[v] << "," << north_vec[v] << "," << latitude << "," << longitude << "," << length_vec[v] << "," << zeta_vec[v] << endl; - } - } - } - WriteTracesFile.close(); } } - } // End of path printing logic + WriteTracesFile.close(); + } } - } //for loop i,j + } } ofs.close(); - //add the data arrays to the output vector - OutputArrays.push_back(RoutedHilltops); - OutputArrays.push_back(HillslopeLength_Array); - OutputArrays.push_back(Slope_Array); - OutputArrays.push_back(Relief_Array); //Print debugging info to screen cout << endl; //push output onto new line @@ -5855,9 +7367,60 @@ vector< Array2D > LSDFlowInfo::HilltopFlowRouting(LSDRaster Elevation, LS cout << "Uphill count: " << neg_count << endl; cout << "Edge count: " << edge_count << endl; - return OutputArrays; } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Hilltop flow routing code built around original code from Martin Hurst. Based on // Lea (1992), with improvements discussed by Tarboton (1997) and a solution to the @@ -7205,6 +8768,239 @@ vector< Array2D > LSDFlowInfo::HilltopFlowRouting_Profile(LSDRaster Eleva + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// +// This function takes an integer raster that has nodata and classification +// codes. These are then propagated upslope, so all upslope nodes have +// this code. Nodes that have recieves a classification do not reclassify: +// the downstream node classification "wins" +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +LSDIndexRaster LSDFlowInfo::tag_nodes_upstream_of_baselevel(LSDIndexRaster& tagged_raster) +{ + // We just double check if the dimensions are the same + int NRows_ir = tagged_raster.get_NRows(); + int NCols_ir = tagged_raster.get_NCols(); + int NDV = tagged_raster.get_NoDataValue(); + + Array2D output_array(NRows_ir,NCols_ir,NDV); + + if ( (NRows_ir != NRows) || (NCols_ir != NCols) ) + { + cout << "Fatal error. You are trying to make a tagged raster using LSDFlowInfo::tag_nodes_upstream_of_baselevel" << endl; + cout << "but the raster and the flow info object do not have the same rows and columns." << endl; + exit(0); + } + + // Okay, now we go through the stack and tag the upstream nodes + int row,col; + int this_tag; + int s_node = 0; + int this_node = SVector[s_node]; + + + vector bln = get_BaseLevelNodeList(); + vector upslope_nodes; + + for (int bl = 0; bl< int(bln.size()); bl++) + { + this_node = bln[bl]; + retrieve_current_row_and_col(this_node, row, col); + this_tag = tagged_raster.get_data_element(row,col); + + cout << "baselevel: " << bl << endl; + + if(this_tag != NDV) + { + upslope_nodes = get_upslope_nodes(this_node); + for (int n = 0; n< int(upslope_nodes.size()); n++) + { + retrieve_current_row_and_col(upslope_nodes[n], row, col); + output_array[row][col] = this_tag; + } + } + } + + + // now write the mask as an LSDIndexRaster + LSDIndexRaster final_tagged(NRows,NCols,XMinimum,YMinimum, + DataResolution,int(NoDataValue),output_array,GeoReferencingStrings); + return final_tagged; + + +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// +// This function takes an integer raster that has nodata and classification +// codes. These are then propagated upslope, so all upslope nodes have +// this code. Nodes that have recieves a classification do not reclassify: +// the downstream node classification "wins" +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +LSDIndexRaster LSDFlowInfo::tag_upstream_nodes(LSDIndexRaster& tagged_raster, float crit_upslope_distance) +{ + // We just double check if the dimensions are the same + int NRows_ir = tagged_raster.get_NRows(); + int NCols_ir = tagged_raster.get_NCols(); + int NDV = tagged_raster.get_NoDataValue(); + + Array2D output_array(NRows_ir,NCols_ir,NDV); + + if ( (NRows_ir != NRows) || (NCols_ir != NCols) ) + { + cout << "Fatal error. You are trying to make a tagged raster using LSDFlowInfo::tag_upslope_nodes" << endl; + cout << "but the raster and the flow info object do not have the same rows and columns." << endl; + exit(0); + } + + // get the flow distance and contributing pixel rasters + LSDRaster fd = distance_from_outlet(); + + // Loop through the stack, collecting tagged pixels. + // This should have the effect of ordering the pixels + vector tagged_node_list; + int row,col; + for (int si = 0; si upslope_nodes; + for(int tn = 0; tn< NTN; tn++) + { + // get the flow distance of this node + retrieve_current_row_and_col(tagged_node_list[tn], row, col); + tagged_node_FD = fd.get_data_element(row,col); + + // get the tag + this_tag = tagged_raster.get_data_element(row,col); + + upslope_nodes = get_upslope_nodes(tagged_node_list[tn]); + for (int n = 0; n< int(upslope_nodes.size()); n++) + { + retrieve_current_row_and_col(upslope_nodes[n], row, col); + + node_FD = fd.get_data_element(row,col); + + FD_diff = node_FD - tagged_node_FD; + + if (FD_diff < crit_upslope_distance) + { + output_array[row][col] = this_tag; + } + } + } + + // now write the mask as an LSDIndexRaster + LSDIndexRaster final_tagged(NRows,NCols,XMinimum,YMinimum, + DataResolution,int(NoDataValue),output_array,GeoReferencingStrings); + return final_tagged; + +} + + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// +// This function takes an integer raster that has nodata and classification +// codes. These are then propagated downslope, so all downslope nodes have +// this code. Nodes that have receives a classification do not reclassify: +// the downstream node classification "wins" +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +LSDIndexRaster LSDFlowInfo::tag_downstream_nodes(LSDIndexRaster& tagged_raster, float crit_downslope_distance) +{ + // We just double check if the dimensions are the same + int NRows_ir = tagged_raster.get_NRows(); + int NCols_ir = tagged_raster.get_NCols(); + int NDV = tagged_raster.get_NoDataValue(); + + Array2D output_array(NRows_ir,NCols_ir,NDV); + + if ( (NRows_ir != NRows) || (NCols_ir != NCols) ) + { + cout << "Fatal error. You are trying to make a tagged raster using LSDFlowInfo::tag_downstream_nodes" << endl; + cout << "but the raster and the flow info object do not have the same rows and columns." << endl; + exit(0); + } + + // get the flow distance and contributing pixel rasters + LSDRaster fd = distance_from_outlet(); + + // Loop through the stack, collecting tagged pixels. + // This should have the effect of ordering the pixels + vector tagged_node_list; + int row,col; + for (int si = 0; si nodes_in_basin = this->get_upslope_nodes_include_outlet(node); - int min_row = std::numeric_limits::max(); + int min_row = std::numeric_limits::max(); int max_row = std::numeric_limits::min(); - int min_col = std::numeric_limits::max(); + int min_col = std::numeric_limits::max(); int max_col = std::numeric_limits::min(); // first I am getting the min and max row @@ -9407,7 +11203,216 @@ LSDRaster LSDFlowInfo::get_raster_draining_to_node(int node, LSDRaster& elevatio } - // map > burn_raster_to_ +// This function takes a nodelist and determines which of the nodes in the +// nodelist are internal nodes +// Used to get basin outlines +vector LSDFlowInfo::internal_nodes(vector node_list) +{ + int n_nodes = int(node_list.size()); + + // this speeds up the searching + unordered_set node_set(node_list.begin(),node_list.end()); + vector is_internal(n_nodes,false); + + //for(int i = 0; i node_vector) +{ + int this_node, nearest_node; + float this_dist; + float min_dist = 10000000000000; + for (int i = 0; i < int(node_vector.size()); i++) + { + this_node = node_vector[i]; + this_dist = get_Euclidian_distance(this_node, target_node); + if (this_dist < min_dist) + { + min_dist = this_dist; + nearest_node = this_node; + } + } + return nearest_node; + +} + +//-----------------------------------------------------------------------------// +// Function to calculate the upstream hypsometric integral from a node +// HI = (Z_mean - Z_min) / (Z_max - Z_min) +//-----------------------------------------------------------------------------// +float LSDFlowInfo::calculate_upstream_HI(int node, LSDRaster& Elevation) +{ + float HI; + // get the vector of upstream nodes + vector upstream_nodes = get_upslope_nodes_include_outlet(node); + // get a vector of elevation of each of these nodes + int this_elev, this_row, this_col; + vector upstream_elevs; + for (int i = 0; i < int(upstream_nodes.size()); i++) + { + retrieve_current_row_and_col(upstream_nodes[i], this_row, this_col); + this_elev = Elevation.get_data_element(this_row, this_col); + upstream_elevs.push_back(this_elev); + } + + // now work out the HI from the mean, max and min elevations + float mean_elev = get_mean_ignore_ndv(upstream_elevs, NoDataValue); + float min_elev = Get_Minimum(upstream_elevs, NoDataValue); + float max_elev = Get_Maximum(upstream_elevs, NoDataValue); + HI = (mean_elev - min_elev)/(max_elev - min_elev); + + return HI; +} + +//-----------------------------------------------------------------------------// +// Function to calculate the % upstream elevation within specific elevation +// bands for a node +// FJC +//-----------------------------------------------------------------------------// +void LSDFlowInfo::calculate_upstream_elevations(int node, LSDRaster& Elevation, float bin_width, float lower_limit, float upper_limit, vector& Midpoints, vector& ProbabilityDensity) +{ + float HI; + // get the vector of upstream nodes + vector upstream_nodes = get_upslope_nodes_include_outlet(node); + // get a vector of elevation of each of these nodes + int this_elev, this_row, this_col; + vector upstream_elevs; + for (int i = 0; i < int(upstream_nodes.size()); i++) + { + retrieve_current_row_and_col(upstream_nodes[i], this_row, this_col); + this_elev = Elevation.get_data_element(this_row, this_col); + upstream_elevs.push_back(this_elev); + } + + // calculate histogram from the vector of upstream elevations + vector LLims, ULims; + vector Count; + calculate_histogram_fixed_limits(upstream_elevs, bin_width, lower_limit, upper_limit, Midpoints, LLims, ULims, Count, ProbabilityDensity); + +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This calculates the elevation difference between each point in the swath and +// the channel +// FJC +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// LSDRaster LSDFlowInfo::calculate_channel_relief(vector & swath_rasters, LSDRaster& topography_raster, vector channel_node_list) +// { +// Array2D ChannelRelief_array(NRows, NCols, NoDataValue); +// +// int channel_node, channel_row, channel_col, channel_idx; +// float channel_elev, this_elev; +// for (int row = 0; row < NRows; row++) +// { +// for (int col = 0; col < NCols; col++) +// { +// channel_idx = swath_rasters[2].get_data_element(row, col); +// if (channel_idx != NoDataValue) +// { +// channel_node = channel_node_list[channel_idx]; +// retrieve_current_row_and_col(channel_node, channel_row, channel_col); +// channel_elev = topography_raster.get_data_element(channel_row, channel_col); +// this_elev = topography_raster.get_data_element(row, col); +// ChannelRelief_array[row][col] = this_elev - channel_elev; +// } +// } +// } +// +// LSDRaster ChannelRelief(NRows,NCols, XMinimum, YMinimum, DataResolution, NoDataValue, ChannelRelief_array, GeoReferencingStrings); +// +// return ChannelRelief; +// } #endif diff --git a/src/LSDFlowInfo.hpp b/src/LSDFlowInfo.hpp index 5a9f0bb..3abc59e 100644 --- a/src/LSDFlowInfo.hpp +++ b/src/LSDFlowInfo.hpp @@ -199,7 +199,7 @@ class LSDFlowInfo /// Note: this is a double, because a float does not have sufficient precision /// relative to a UTM location (which is in metres) /// @param long the longitude of the node (in decimal degrees, replaced by function) - /// Note: this is a double, because a float does not have sufficient precision + /// Note: this is a double, because a float does not have sufficient precision /// @param X the Easting of the location /// @param Y the Northing of the location /// @author SMM @@ -312,7 +312,7 @@ class LSDFlowInfo /// @param drainage_area /// @author SMM /// @date 30/09/19 - void print_vector_of_nodeindices_to_csv_file_with_latlong(vector node_list,string path, string filename, LSDRaster& Elevation, LSDRaster& FlowDistance, + void print_vector_of_nodeindices_to_csv_file_with_latlong(vector node_list,string path, string filename, LSDRaster& Elevation, LSDRaster& FlowDistance, LSDRaster& drainage_area); ///@brief This function takes a vector of node indices and prints a csv @@ -325,6 +325,20 @@ class LSDFlowInfo ///@date 2/2/16 void print_vector_of_nodeindices_to_csv_file_Unique(vector& nodeindex_vec, string outfilename); + ///@brief This function takes a vector of node indices and moves + /// down the list, getting the bearings of each segment subject to an offset + ///@param nodeindex vec is a vector of nodeindices (which are ints) + ///@param node_spacing how many nodes between the two nodevecs + ///@param print_bearings if true, prints to a geojson + ///@param outfileprefix the prefix of the outfile (.geojson gets added) + ///@param bearing_nodes The list of nodes from which the bearing is taken + ///@param bearings the actual bearings + ///@author SMM + ///@date 4/3/21 + void calculate_bearings_from_nodelist(vector nodeindex_vec, int node_spacing, + bool print_bearings, string outfileprefix, + vector& bearing_nodes, vector& bearing_vec); + ///@brief Get the number of pixels flowing into a node. ///@param node Integer of node index value. @@ -409,6 +423,8 @@ class LSDFlowInfo int get_NDataNodes () const { return NDataNodes; } /// @return Vector of all base level nodes. vector get_BaseLevelNodeList () { return BaseLevelNodeList; } + /// @return get the number of baselevel nodes + int get_NBaseLevelNodes () { return int( BaseLevelNodeList.size() );} /// @return donor stack vector (depth first search sequence of nodes) vector get_donorStack() const { return DonorStackVector; } @@ -543,8 +559,8 @@ class LSDFlowInfo /// @details Assumes the FlowInfo object has the same dimensions as the channel heads raster. /// @param filename of the channel heads raster. /// @param extension of the channel heads raster. - /// @param (optional) input_switch, ONLY NEEDED FOR LOADING .csv FILES! - /// An integer to determine whether to use the node index (0 -> default), row and column indices (1), + /// @param (optional) input_switch, ONLY NEEDED FOR LOADING .csv FILES! + /// An integer to determine whether to use the node index (0 -> default), row and column indices (1), /// or point coordinates from .csv file (2) to locate the channel heads /// @return Vector of source nodes. /// @author SWDG updated SMM updated DTM @@ -561,7 +577,7 @@ class LSDFlowInfo /// @details Assumes the FlowInfo object has the same dimensions as the channel heads raster. /// @param filename of the channel heads raster. /// @param extension of the channel heads raster. - /// @param (optional) input_switch, ONLY NEEDED FOR LOADING .csv FILES! + /// @param (optional) input_switch, ONLY NEEDED FOR LOADING .csv FILES! /// An integer to determine whether to use the node index (0 -> default), /// row and column indices (1), or point coordinates from .csv file (2) to locate the channel heads /// @return Vector of source nodes. @@ -582,7 +598,7 @@ class LSDFlowInfo // Using xy allows a "universal" method that can ingest external or internal data // B.G. 11/11/2018 //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - vector Ingest_Channel_Heads(vector& x_coord, vector& y_coord); + vector Ingest_Channel_Heads(vector& x_coord, vector& y_coord); // functions for getting flow, discharge, sediment flux, etc @@ -624,7 +640,7 @@ class LSDFlowInfo /// @brief This creates a new raster. It takes a node list (usually a channel) and then /// looks at all the donors to that channel and sees if any are influenced upslope /// by nodata. It then removes all these pixels. This way you can isolate both - /// nodes drainaing to a local base level but eliminate nodes draining from the edge. + /// nodes drainaing to a local base level but eliminate nodes draining from the edge. /// @detail uses D8 flow routing to do this /// @param topography A topography raster (must be same dimension as that used to make flowinfo) /// @param node_list a list of nodes (often read by an LSDSpatialCSVReader object) @@ -697,9 +713,19 @@ class LSDFlowInfo ///@date 19/11/2019 LSDRaster upslope_variable_accumulator_v3(LSDRaster& accum_raster); + ///@brief This function returns the upslope average value of + /// some raster bassed on the flow info + ///@detail Uses the stack vector to get the accumulation + ///@param A raster that contains the variable to be averaged (e.g., precipitation) + ///@return A raster containing the uplsope average + ///@author SMM + ///@date 25/06/2021 + LSDRaster upslope_average(LSDRaster& accum_raster); + + ///@brief This function tests whether one node is upstream of another node ///@param current_node - ///@param test_node + ///@param test_node The node which we want to test whether it is upstream or not ///@return Boolean indicating whether node is upstream or not ///@author FC ///@date 08/10/13 @@ -1043,11 +1069,22 @@ void get_nodeindices_from_csv(string csv_filename, vector& NIs, vector get_flow_path(float latitude, float longitude); + vector get_flow_path(float latitude, float longitude); + + + /// @brief Perform a downslope trace using D8 from a given point source. + /// @details This version uses a nodeindex + /// @param ni the nodeindex of the starting point + /// @return A vector of nodeindices that contain the nodes from start to finish that are + /// on the flow path + /// @author SMM + /// @date 12/03/2021 + vector get_flow_path(int ni); + /// @brief Move the location of the channel head downslope by a user defined distance. /// @param Sources a vector of node indexes of the channel heads to be moved. @@ -1070,6 +1107,74 @@ void get_nodeindices_from_csv(string csv_filename, vector& NIs, vector > is as follows: \n\n + /// [0] Hilltop Network coded with stream ID \n + /// [1] Hillslope Lengths \n + /// [2] Slope \n + /// [3] Relief \n + /// + /// @param Elevation LSDRaster of elevation values. + /// @param Slope LSDRaster of slope values. + /// @param Hilltops LSDRaster of hilltops. + /// @param StreamNetwork LSDIndexRaster of the stream network. + /// @param Aspect LSDRaster of Aspect. + /// @param Prefix String Prefix for output data filename. + /// @param Basins LSDIndexRaster of basin outlines. + /// @param PlanCurvature LSDRaster of planform curvature. + /// @param print_paths_switch If true paths will be printed. + /// @param thinning Thinning factor, value used to skip hilltops being printed, use 1 to print every hilltop. + /// @param trace_path The file path to be used to write the path files to, must end with a slash. + /// @param basin_filter_switch If this switch is true only basins in Target_Basin_Vector will have their paths printed. + /// @param Target_Basin_Vector Vector of Basin IDs that the user wants to print traces for. + /// @return Vector of Array2D containing hillslope metrics. + /// @author SWDG, SMM + /// @date 12/2/14, 20/10/2021 + vector< Array2D > HilltopFlowRouting_Refactored(LSDRaster& Elevation, LSDRaster& Hilltop_ID, LSDRaster& Slope, + LSDRaster& Aspect, LSDRaster& HilltopCurv, LSDRaster& PlanCurvature, LSDIndexRaster& StreamNetwork, LSDIndexRaster& Basins, + string Prefix, bool print_paths_switch, int thinning, string trace_path, bool basin_filter_switch, + vector Target_Basin_Vector); + + + + + /// @ brief a refactored HFR routine that is much more memory efficient + void HilltopFlowRouting_TerrifyingRefactored(LSDRaster& Elevation, LSDRaster& Aspect, LSDRaster& PlanCurvature, + LSDIndexRaster& StreamNetwork, + string ridges_csv_name,string Prefix, + bool print_paths_switch, int thinning, string trace_path, bool basin_filter_switch); + + + + + /// @brief Hilltop flow routing. /// /// @details Hilltop flow routing code built around original code from Martin Hurst. Based on @@ -1270,6 +1375,36 @@ void get_nodeindices_from_csv(string csv_filename, vector& NIs, vector basin_edge_extractor(int outlet_node, LSDRaster& Topography); + /// @brief This function takes an integer raster that has some tags + /// and then tags all upstream nodes with that tag + /// designed to help extract the main drainage divide of landscapes + /// @param tagged_raster an LSDIndexRaster containing the pixels from which we search upstream + /// @return An index raster that has all pixels upslope of the tagged regions taking the tagged value + /// @author SMM + /// @date 11/11/2020 + LSDIndexRaster tag_nodes_upstream_of_baselevel(LSDIndexRaster& tagged_raster); + + /// @brief This function takes an integer raster that has some tags + /// and then tags all upstream nodes with that tag + /// designed to help extract the main drainage divide of landscapes + /// @param tagged_raster an LSDIndexRaster containing the pixels from which we search upstream + /// @param crit_downslope_distance The distance beyond which we don't tag the raster. + /// @return An index raster that has all pixels upslope of the tagged regions taking the tagged value + /// @author SMM + /// @date 11/11/2020 + LSDIndexRaster tag_upstream_nodes(LSDIndexRaster& tagged_raster, float crit_downslope_distance); + + + /// @brief This function takes an integer raster that has some tags + /// and then tags all downstream nodes with that tag. It stops when you have got too far downstream + /// @param tagged_raster an LSDIndexRaster containing the pixels from which we search upstream + /// @param crit_downslope_distance The distance beyond which we don't tag the raster. + /// @return An index raster that has all pixels downstream of the tagged regions taking the tagged value + /// within a flow distance window + /// @author SMM + /// @date 11/12/2020 + LSDIndexRaster tag_downstream_nodes(LSDIndexRaster& tagged_raster, float crit_downslope_distance); + /// @brief This function returns all the values from a raster for a corresponding /// input vector of node indices. @@ -1377,30 +1512,61 @@ void get_nodeindices_from_csv(string csv_filename, vector& NIs, vector > get_vectors_of_flow(LSDRaster& topo); /// Accessor for the DonorStackVector() - vector get_DonorStackVector() {return DonorStackVector;} + vector get_DonorStackVector() {return DonorStackVector;} /// Accessor for the DonorStackVector() - vector get_RowIndex() {return RowIndex;} + vector get_RowIndex() {return RowIndex;} /// Accessor for the DonorStackVector() - vector get_ColIndex() {return ColIndex;} + vector get_ColIndex() {return ColIndex;} - /// @brief This is a function that produces a **HUGE** data map, but + /// @brief This is a function that produces a **HUGE** data map, but /// speeds up a number of computations (i.e., memory unfriendly, but computation speed friendly) /// It produces a map that has vectors of vecors where each vector is a baselevel node /// and the map strings are the various data elements such as stack order, inverted stack order, rows, columns, etc - /// @return A map of vecvecs where the key is the name of the data member and the vector into the vectors is indexed by the baselevel node. + /// @return A map of vecvecs where the key is the name of the data member and the vector into the vectors is indexed by the baselevel node. /// @author BG /// @date 01/04/2019 map > > get_map_of_vectors(); - ///@brief This return an LSDRaster draining to a specific node, this only fill it with the node ctually in the basin - ///@brief And fill the rest with nodata. To be used with test_edge deactivated - ///@param int node, the node index of the outlet - ///@param LSDRaster the base elevation raster - ///@return a LSDRater trimmed to the closest extents + /// @brief This return an LSDRaster draining to a specific node, this only fill it with the node ctually in the basin + /// @brief And fill the rest with nodata. To be used with test_edge deactivated + /// @param int node, the node index of the outlet + /// @param LSDRaster the base elevation raster + /// @return a LSDRater trimmed to the closest extents LSDRaster get_raster_draining_to_node(int node, LSDRaster& elevation_raster); + /// @brief This takes a node list and returns true if the node is surrounded by + /// other nodes in the node list. + /// @param node_list, a vector of nodes. This will almost always be a basin derived from get_upslope_nodes + /// @return a vector of bools. True if an internal node + /// @author SMM + /// @date 11/01/2021 + vector internal_nodes(vector node_list); + + /// @brief This takes a node and a vector of nodes and finds the nearest node in the vector to the target node. + /// @author FJC + /// @date 04/06/21 + int find_nearest_node_in_vector(int target_node, vector node_vector); + + /// @brief function to calculate the hypsometric integral for a node + /// @param node starting node to calculate HI for + /// @param FlowInfo + /// @param Elevation elev raster + /// @author FJC + /// @date 26/01/22 + float calculate_upstream_HI(int node, LSDRaster& Elevation); + + void calculate_upstream_elevations(int node, LSDRaster& Elevation, float bin_width, float lower_limit, float upper_limit, vector& Midpoints, vector& ProbabilityDensity); + + + /// @brief Find the elevation difference between the channel and surrounding pixels using the + /// swath tool. + /// @return raster of channel relief. + /// @author FJC + /// @date 09/03/2021 + // LSDRaster calculate_channel_relief(vector & swath_rasters, LSDRaster& topography_raster, vector channel_node_list); + protected: diff --git a/src/LSDIndexRaster.cpp b/src/LSDIndexRaster.cpp index e23671f..c2b405a 100644 --- a/src/LSDIndexRaster.cpp +++ b/src/LSDIndexRaster.cpp @@ -243,21 +243,31 @@ void LSDIndexRaster::create(LSDRaster& NonIntLSDRaster) XMinimum = NonIntLSDRaster.get_XMinimum(); YMinimum = NonIntLSDRaster.get_YMinimum(); DataResolution = NonIntLSDRaster.get_DataResolution(); - NoDataValue = NonIntLSDRaster.get_NoDataValue(); + NoDataValue = int(NonIntLSDRaster.get_NoDataValue()); GeoReferencingStrings = NonIntLSDRaster.get_GeoReferencingStrings(); - Array2D RasterDataFloat = NonIntLSDRaster.get_RasterData(); vector list_unique_values; //Declarations + //cout << "NDV is: " << NoDataValue << endl; Array2D RasterDataInt(NRows,NCols,NoDataValue); - for (int i=0; i 0 && col_point < NCols-1) + if(col_point >= 0 && col_point <= NCols-1) { this_col = col_point; } - if(row_point > 0 && row_point < NRows -1) + if(row_point >= 0 && row_point <= NRows -1) { this_row = row_point; } @@ -3015,6 +3025,265 @@ LSDIndexRaster LSDIndexRaster::remove_checkerboard_pattern() } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// More complex nodatafiller for binary raster (fills with 1s) +// FJC 29/03/2021 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +LSDIndexRaster LSDIndexRaster::alternating_direction_nodata_fill_binary_raster(int window_width) +{ + + + // check argument + if(window_width <1) + { + cout << "You need a positive window width, defaulting to 1" << endl; + window_width = 1; + } + + cout << "Sweeping nodata, window width is: " << window_width << endl; + + // This function loops in alternating directions until there is no more nodata + int NNoData = 0; + //float this_window_sum; + int this_window_ndata; + int window_row, window_col; + + // set up data to be + Array2D this_sweep_data = RasterData.copy();; + Array2D updated_raster; + + // set the sweep number to 0 + int nsweep = 0; + + do + { + // reset the number of nodata points in this sweep to zero + NNoData = 0; + + cout << "LINE 8268, Sweep number: " << nsweep << endl; + cout << "Line 8275, switch is: " << nsweep%4 << endl; + + // copy over the updated raster + updated_raster = this_sweep_data.copy(); + + int begin_ndv = 0; + for(int row = 0; row 0 && window_row < NRows-1 + && window_col > 0 && window_col < NCols-1) + { + //cout << "wr: " << window_row << " wc: " << window_col << " data: " << updated_raster[window_row][window_col] << endl; + if(updated_raster[window_row][window_col] != NoDataValue) + { + //this_window_sum += updated_raster[window_row][window_col]; + this_window_ndata += 1; + } + } + } + } + + // now get the average + if(this_window_ndata>0) + { + //cout << "Found a nodata replacement" << endl; + //local_average = this_window_sum/float(this_window_ndata ); + this_sweep_data[row][col] = 1; + } + } + } + } + break; + } + case(1): + { + // sweep 1 + for (int col=0; col 0 && window_row < NRows-1 && window_col > 0 && window_col < NCols-1) + { + if(updated_raster[window_row][window_col] != NoDataValue) + { + //this_window_sum += updated_raster[window_row][window_col]; + this_window_ndata += 1; + } + } + } + } + + // now get the average + if(this_window_ndata>0) + { + //local_average = this_window_sum/float(this_window_ndata ); + this_sweep_data[row][col] = 1; + } + } + } + } + break; + } + case(2): + { + // sweep 2 + for (int row=0; row0; --col) + { + // if the node contains nodata, search the surrounding nodes + if(updated_raster[row][col] == 0) + { + NNoData++; + //this_window_sum = 0; + this_window_ndata = 0; + for(int r = -window_width; r<=window_width; r++) + { + for(int c = -window_width; c<=window_width; c++) + { + window_row = r+row; + window_col = c+col; + + if(window_row > 0 && window_row < NRows-1 && window_col > 0 && window_col < NCols-1) + { + if(updated_raster[window_row][window_col] != NoDataValue) + { + //this_window_sum += updated_raster[window_row][window_col]; + this_window_ndata += 1; + } + } + } + } + + // now get the average + if(this_window_ndata>0) + { + //local_average = this_window_sum/float(this_window_ndata ); + this_sweep_data[row][col] = 1; + } + } + } + } + break; + } + case(3): + { + // sweep 3 + for (int col=0; col0; --row) + { + // if the node contains nodata, search the surrounding nodes + if(updated_raster[row][col] == 0) + { + NNoData++; + //this_window_sum = 0; + this_window_ndata = 0; + for(int r = -window_width; r<=window_width; r++) + { + for(int c = -window_width; c<=window_width; c++) + { + window_row = r+row; + window_col = c+col; + + if(window_row > 0 && window_row < NRows-1 && window_col > 0 && window_col < NCols-1) + { + if(updated_raster[window_row][window_col] != NoDataValue) + { + //this_window_sum += updated_raster[window_row][window_col]; + this_window_ndata += 1; + } + } + } + } + + // now get the average + if(this_window_ndata>0) + { + //local_average = this_window_sum/float(this_window_ndata ); + this_sweep_data[row][col] = 1; + } + } + } + } + } + break; + } + + int test_ndv = 0; + for(int row = 0; row 0); + + LSDIndexRaster Hole_filled_Raster(NRows,NCols,XMinimum,YMinimum,DataResolution, + int(NoDataValue),this_sweep_data,GeoReferencingStrings); + return Hole_filled_Raster; + +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // QUALITY ANALYSIS ROUTINES // These functions can be used to compare two rasters and calculate the reliability, diff --git a/src/LSDIndexRaster.hpp b/src/LSDIndexRaster.hpp index 4c041f3..38e6edf 100644 --- a/src/LSDIndexRaster.hpp +++ b/src/LSDIndexRaster.hpp @@ -579,6 +579,11 @@ class LSDIndexRaster /// @date 30/10/15 LSDIndexRaster remove_checkerboard_pattern(); + /// @brief More complex nodatafiller for binary raster + /// @author FJC + /// @date 29/03/21 + LSDIndexRaster alternating_direction_nodata_fill_binary_raster(int window_width); + /// @brief Function to calculate the reliability of floodplain method /// @param ActualRaster raster of actual values /// @author FJC diff --git a/src/LSDJunctionNetwork.cpp b/src/LSDJunctionNetwork.cpp index 646b4de..21e27e2 100644 --- a/src/LSDJunctionNetwork.cpp +++ b/src/LSDJunctionNetwork.cpp @@ -229,6 +229,9 @@ void LSDJunctionNetwork::create(vector Sources, LSDFlowInfo& FlowInfo) current_col = FlowInfo.ColIndex[current_node]; receiver_node = FlowInfo.ReceiverVector[current_node]; + //cout << "current node " << current_node << endl; + //cout << "current row " << current_row << " current col " << current_col << endl; + current_stream_order = 1; if (current_node == receiver_node) @@ -389,6 +392,8 @@ void LSDJunctionNetwork::create(vector Sources, LSDFlowInfo& FlowInfo) // get the next current node, which is this nodes receiver current_node = FlowInfo.ReceiverVector[current_node]; + // cout << "receiver node " << current_node << endl; + // cout << "receiver row " << current_row << " receiver col " << current_col << endl; // get the next receiver node, which is the next node receiver_node = FlowInfo.ReceiverVector[current_node]; @@ -425,8 +430,8 @@ void LSDJunctionNetwork::create(vector Sources, LSDFlowInfo& FlowInfo) current_col = FlowInfo.ColIndex[current_node]; receiver_node = FlowInfo.ReceiverVector[current_node]; - //cout << "LINE 257 ChNet, SOURCE: " << src << " n_src: " << n_sources << " current_node: " << current_node - // << " and rnode: " << receiver_node << endl; + // cout << "LINE 257 ChNet, SOURCE: " << src << " n_src: " << n_sources << " current_node: " << current_node + // << " and rnode: " << receiver_node << endl; //each source is a junction node. Push back the junction vector JunctionVector.push_back(current_node); @@ -455,7 +460,7 @@ void LSDJunctionNetwork::create(vector Sources, LSDFlowInfo& FlowInfo) // the next element is the receiver junction, we need to follow the path to this receiver // the routine continues until the junction has been visited or until it hits // a baselevel node - //cout << "LINE 280" << endl; + //cout << "LINE 463" << endl; while (baselevel_switch == 0 && junction_switch <2) { //cout << "Line 283" << endl; @@ -516,8 +521,8 @@ void LSDJunctionNetwork::create(vector Sources, LSDFlowInfo& FlowInfo) // is a visited junction if(JunctionArray[current_row][current_col] != NoDataValue) { - //cout << "LINE 338, found a junction at node: " << current_node - // << " JArray: " << JunctionArray[current_row][current_col] << endl; + // cout << "LINE 524, found a junction at node: " << current_node + // << " JArray: " << JunctionArray[current_row][current_col] << endl; junction_switch = JunctionArray[current_row][current_col]; JunctionArray[current_row][current_col] ++; // increment the junction array // it will be greater than 1 if @@ -554,8 +559,8 @@ void LSDJunctionNetwork::create(vector Sources, LSDFlowInfo& FlowInfo) } // end baselevel logic } // end sources loop - //cout << "ChanNet; LINE 368; sz ReceiverVec: " << ReceiverVector.size() << " sz JuncVec: " << JunctionVector.size() - // << " sz SOVec: " << StreamOrderVector.size() << endl; + // cout << "ChanNet; LINE 562; sz ReceiverVec: " << ReceiverVector.size() << " sz JuncVec: " << JunctionVector.size() + // << " sz SOVec: " << StreamOrderVector.size() << endl; //for(int i = 0; i< int(BaseLevelJunctions.size()); i++) //{ @@ -2302,7 +2307,7 @@ void LSDJunctionNetwork::print_complete_junction_angles_to_csv(vector Junct LSDRaster& FlowDistance, float vertical_interval, string csv_name) { - cout << "Let me fun the full junction angle code that includes elevations and flow distances." << endl; + cout << "Let me run the full junction angle code that includes elevations and flow distances." << endl; ofstream csv_out; csv_out.open(csv_name.c_str()); @@ -3294,6 +3299,35 @@ int LSDJunctionNetwork::get_downstream_junction(int starting_junction, LSDFlowIn return receiver_junction; } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-= +// Function to get the junction downstream of a specified node +// Added by FJC 29/01/21 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-= +int LSDJunctionNetwork::get_junction_downstream_of_node(int CurrentNode, LSDFlowInfo& FlowInfo) +{ + int junction; + // check if this node is already a junction + int junc = get_Junction_of_Node(CurrentNode, FlowInfo); + if (junc != NoDataValue) + { + junction = junc; + } + else + { + int at_junction = 0; + int ReceiverRow, ReceiverCol, ReceiverNode; + while(at_junction<1) + { + FlowInfo.retrieve_receiver_information(CurrentNode, ReceiverNode, ReceiverRow, ReceiverCol); + CurrentNode = ReceiverNode; + junction = retrieve_junction_number_at_row_and_column(ReceiverRow,ReceiverCol); + //test to see if receiver node is in channel + if(junction != NoDataValue) ++at_junction; + } + } + return junction; +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-= // This function extracts the junctions of a given basin order that are the lowermost // junction in the basin @@ -5469,6 +5503,153 @@ LSDRaster LSDJunctionNetwork::ExtractRidges(LSDFlowInfo& FlowInfo, int& min_orde //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// ExtractAllRidges +//------------------------------------------------------------------------------ +// This uses a different strategy to previous ridgetop extraction methods +// It loops through all junctions. +// It uses the junction stack so that the biggest junctions are done last +// It then extracts the ridgelines based on the nodes contributing to +// these junctions, placing them in a map. +// +// SMM 11/01/2021 +//------------------------------------------------------------------------------ +map LSDJunctionNetwork::ExtractAllRidges(LSDFlowInfo& FlowInfo) +{ + + vector upslope_junc_nodes; + + // get the penultamite node from each junction + // we use the stack to get the junction ordering + vector pen_nodes = get_node_list_of_penultimate_node_from_junction_list(SVector,FlowInfo); + + // get the stream orders of these vectors + vector so_stack; + int row,col; + + map node_map; + + + for(int i = NJunctions-1; i>= 0; i--) + { + so_stack.push_back(StreamOrderVector[ SVector[i] ]); + + // for debugging + //cout << "O: " << StreamOrderVector[ SVector[i] ] << endl; + + + upslope_junc_nodes = FlowInfo.get_upslope_nodes(pen_nodes[i]); + + // now get the nodes that are internal + vector is_internal = FlowInfo.internal_nodes(upslope_junc_nodes); + + // now remove all interior nodes + vector external_nodes; + for (int usn = 0; usn < int(upslope_junc_nodes.size()); usn++) + { + if(is_internal[usn] == false) + { + // These are from an old version that made a raster and kept here for reference if you + // want to replicate the code elsewhere + //FlowInfo.retrieve_current_row_and_col(upslope_junc_nodes[usn],row,col); + //RidgeNetwork[row][col] = StreamOrderVector[ SVector[i] ]; + + node_map[upslope_junc_nodes[usn]] = StreamOrderVector[ SVector[i] ]; + } + } + } + + //LSDRaster ridge_raster(NRows,NCols,XMinimum,YMinimum,DataResolution,NoDataValue,RidgeNetwork,GeoReferencingStrings); + return node_map; +} + + +map LSDJunctionNetwork::ExtractAllRidges(LSDFlowInfo& FlowInfo, string csv_filename) +{ + + ofstream csv_out; + csv_out.open(csv_filename.c_str()); + csv_out.precision(8); + + + vector upslope_junc_nodes; + + // get the penultamite node from each junction + // we use the stack to get the junction ordering + vector pen_nodes = get_node_list_of_penultimate_node_from_junction_list(SVector,FlowInfo); + + // get the stream orders of these vectors + vector so_stack; + int row,col; + + map node_map; + map > node_map_full; + + vector node_data(5,-9999); + for(int i = NJunctions-1; i>= 0; i--) + { + so_stack.push_back(StreamOrderVector[ SVector[i] ]); + + // for debugging + //cout << "O: " << StreamOrderVector[ SVector[i] ] << endl; + + + upslope_junc_nodes = FlowInfo.get_upslope_nodes(pen_nodes[i]); + + // now get the nodes that are internal + vector is_internal = FlowInfo.internal_nodes(upslope_junc_nodes); + + // now remove all interior nodes + vector external_nodes; + for (int usn = 0; usn < int(upslope_junc_nodes.size()); usn++) + { + if(is_internal[usn] == false) + { + + + node_map[upslope_junc_nodes[usn]] = StreamOrderVector[ SVector[i] ]; + + // This populates some data members for the ridge network + FlowInfo.retrieve_current_row_and_col(upslope_junc_nodes[usn],row,col); + node_data[0] = SVector[i]; + node_data[1] = StreamOrderVector[ SVector[i] ]; + node_data[2] = row; + node_data[3] = col; + node_data[4] = FlowInfo.retrieve_contributing_pixels_of_node(upslope_junc_nodes[usn]); + + node_map_full[upslope_junc_nodes[usn]] = node_data; + + } + } + } + + + // now we print these to a csv file + csv_out << "latitude,longitude,row,col,junction,stream_order,contributing_pixels"< >::iterator it; + LSDCoordinateConverterLLandUTM Converter; + double latitude, longitude; + int curr_row,curr_col; + + int this_node; + for (it = node_map_full.begin(); it != node_map_full.end(); it++) + { + this_node = it->first; + node_data = it->second; + + curr_row = node_data[2]; + curr_col = node_data[3]; + + get_lat_and_long_locations(curr_row, curr_col, latitude,longitude, Converter); + + csv_out << latitude << "," << longitude << "," << curr_row << "," << curr_col + << "," << node_data[0] << "," << node_data[1] << "," << node_data[4] << endl; + + } + csv_out.close(); + return node_map; +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // ExtractHilltops //------------------------------------------------------------------------------ @@ -7047,6 +7228,122 @@ void LSDJunctionNetwork::PrintChannelNetworkToCSV_WithElevation(LSDFlowInfo& flo } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-= +// This prints a channel network to csv in WGS84 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-= +void LSDJunctionNetwork::PrintChannelNetworkToCSV_WithElevation_WithDonorJunction(LSDFlowInfo& flowinfo, string fname_prefix, LSDRaster& Elevation) +{ + + // first get the vectors + vector NIvec; + vector SOvec; + vector JIvec; + GetChannelNodesAndJunctions(flowinfo, NIvec, JIvec, SOvec); + + // Deal with setting up the file + // append csv to the filename + string FileName = fname_prefix+".csv"; + + //open a file to write + ofstream WriteData; + WriteData.open(FileName.c_str()); + + WriteData.precision(8); + WriteData << "latitude,longitude,Junction Index,Stream Order,NI,receiver_NI,elevation(m),receiver_JI,upstream_JI" << endl; + + // the x and y locations + double latitude,longitude; + + // this is for latitude and longitude + LSDCoordinateConverterLLandUTM Converter; + + + // now get the number of channel nodes + int this_NI, receiver_NI; + int row,col; + int NNodes = int(NIvec.size()); + float this_elev; + int RJ; + int UJ; + //cout << "The number of nodes is: " << NNodes << endl; + for(int node = 0; node NIvec; + vector SOvec; + vector JIvec; + GetChannelNodesAndJunctions(flowinfo, NIvec, JIvec, SOvec); + + // Deal with setting up the file + // append csv to the filename + string FileName = fname_prefix+".csv"; + + //open a file to write + ofstream WriteData; + WriteData.open(FileName.c_str()); + + WriteData.precision(8); + WriteData << "latitude,longitude,Junction Index,Stream Order,NI,receiver_NI,elevation(m),receiver_JI,slope,relief(m),curvature" << endl; + + // the x and y locations + double latitude,longitude; + + // this is for latitude and longitude + LSDCoordinateConverterLLandUTM Converter; + + + // now get the number of channel nodes + int this_NI, receiver_NI; + int row,col; + int NNodes = int(NIvec.size()); + float this_elev,this_slope,this_relief,this_curv; + int RJ; + //cout << "The number of nodes is: " << NNodes << endl; + for(int node = 0; node LSDJunctionNetwork::Prune_Junctions_By_Contributing_Pixel_Window_Rem } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This function loops through all basins and gets basins within the +// contributing pixels between the upper an lower limits +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +vector LSDJunctionNetwork::Prune_Junctions_By_Contributing_Pixel_Window_Remove_Nested_Keep_Edge(LSDFlowInfo& FlowInfo, + LSDRaster& TestRaster, LSDIndexRaster& FlowAcc, + int lower_limit, int upper_limit) +{ + + // We use ALL the junctions for this operation + vector Junctions_Initial; + for (int i = 0; i< NJunctions; i++) + { + Junctions_Initial.push_back(i); + } + + // get the flow accumulation (in pixels) from each of these basins + cout << "First let me prune within the contributing area window." << endl; + cout << " I'm starting with " << Junctions_Initial.size() << " junctions." << endl; + cout << "The lower limit is: " << lower_limit << " and upper limit "<< upper_limit << endl; + vector first_pruning = Prune_Junctions_By_Contributing_Pixel_Window(Junctions_Initial,FlowInfo, + FlowAcc, lower_limit, upper_limit); + cout << "Right, I've pruned those and have " << first_pruning.size() << " junctions left." << endl; + + + // Now prune based on nesting + cout << "Now I'm pruning out any nested junctions." << endl; + vector third_pruning = Prune_Junctions_If_Nested(first_pruning,FlowInfo, FlowAcc); + cout << "Finished with pruning, I have " << third_pruning.size() << " junctions left." << endl; + return third_pruning; +} + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // This function takes a list of junctions and then prunes them on the basis // of whether they are nested. Nested junctions are eliminated. @@ -8187,6 +8518,175 @@ int LSDJunctionNetwork::get_nodeindex_of_nearest_channel_for_specified_coordinat } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + + + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// GET node index of nearest channel using a pixel nodeindex +// +// SMM 02/07/2021 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +int LSDJunctionNetwork::get_nodeindex_of_nearest_channel(int starting_nodeindex, int threshold_stream_order, LSDFlowInfo& FlowInfo) +{ + + int ReceiverRow, ReceiverCol, ReceiverNode, CurrentCol, CurrentRow; + int chan_node; + int this_SO; + + bool found_channel; + + int CurrentNode = starting_nodeindex; + + while (found_channel == false) + { + FlowInfo.retrieve_receiver_information(CurrentNode, ReceiverNode, ReceiverRow, ReceiverCol); + + if (ReceiverNode == CurrentNode) + { + // This is a baselevel + chan_node = NoDataValue; + found_channel = true; + } + else + { + this_SO = StreamOrderArray[ReceiverRow][ReceiverCol]; + if (this_SO >= threshold_stream_order) + { + chan_node = ReceiverNode; + found_channel == true; + } + else + { + CurrentNode = ReceiverNode; + } + } + } + + return chan_node; +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// GET node index of nearest channel using a pixel nodeindex +// +// SMM 02/07/2021 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +float LSDJunctionNetwork::get_distance_to_nearest_channel(int starting_nodeindex, int threshold_stream_order, LSDFlowInfo& FlowInfo, LSDRaster& FlowDist) +{ + int channel_node = get_nodeindex_of_nearest_channel(starting_nodeindex, threshold_stream_order, FlowInfo); + float dist_to_channel = float(NoDataValue); + + int this_row,this_col, chan_row,chan_col; + + if (channel_node != NoDataValue) + { + FlowInfo.retrieve_current_row_and_col(starting_nodeindex,this_row,this_col); + FlowInfo.retrieve_current_row_and_col(channel_node,chan_row,chan_col); + + dist_to_channel = FlowDist.get_data_element(this_row,this_col) - FlowDist.get_data_element(chan_row,chan_col); + } + + return dist_to_channel; + +} + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// GET distance to nearest channel as a raster +// +// SMM 02/07/2021 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +LSDRaster LSDJunctionNetwork::get_distance_to_nearest_channel_raster(int threshold_stream_order, LSDFlowInfo& FlowInfo, LSDRaster& FlowDist) +{ + Array2D distance_to_channel_raster(NRows,NCols,float(NoDataValue)); + + int NNodes = FlowInfo.get_NDataNodes(); + int this_row,this_col; + float this_distance; + + for(int i = 0; i< NNodes; i++) + { + FlowInfo.retrieve_current_row_and_col(i,this_row,this_col); + this_distance = get_distance_to_nearest_channel(i, threshold_stream_order, FlowInfo, FlowDist); + distance_to_channel_raster[this_row][this_col] = this_distance; + } + + + LSDRaster distance_raster(NRows,NCols,XMinimum,YMinimum,DataResolution,NoDataValue,distance_to_channel_raster,GeoReferencingStrings); + return distance_raster; +} + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// GET relief to nearest channel using a pixel nodeindex +// +// SMM 02/07/2021 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +float LSDJunctionNetwork::get_relief_to_nearest_channel(int starting_nodeindex, int threshold_stream_order, LSDFlowInfo& FlowInfo, LSDRaster& Elevation) +{ + int channel_node = get_nodeindex_of_nearest_channel(starting_nodeindex, threshold_stream_order, FlowInfo); + float elev = float(NoDataValue); + + int this_row,this_col, chan_row,chan_col; + + if (channel_node != NoDataValue) + { + FlowInfo.retrieve_current_row_and_col(starting_nodeindex,this_row,this_col); + FlowInfo.retrieve_current_row_and_col(channel_node,chan_row,chan_col); + + elev = Elevation.get_data_element(this_row,this_col) - Elevation.get_data_element(chan_row,chan_col); + } + + return elev; + +} + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// GET relief to nearest channel as a raster +// +// SMM 02/07/2021 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +LSDRaster LSDJunctionNetwork::get_relief_to_nearest_channel_raster(int threshold_stream_order, LSDFlowInfo& FlowInfo, LSDRaster& Elevation) +{ + Array2D relief_to_channel_raster(NRows,NCols,float(NoDataValue)); + + int NNodes = FlowInfo.get_NDataNodes(); + int this_row,this_col; + float this_relief; + + for(int i = 0; i< NNodes; i++) + { + FlowInfo.retrieve_current_row_and_col(i,this_row,this_col); + this_relief = get_relief_to_nearest_channel(i, threshold_stream_order, FlowInfo, Elevation); + relief_to_channel_raster[this_row][this_col] = this_relief; + } + + + LSDRaster relief_raster(NRows,NCols,XMinimum,YMinimum,DataResolution,NoDataValue,relief_to_channel_raster,GeoReferencingStrings); + return relief_raster; +} + + + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // @@ -8737,7 +9237,7 @@ vector LSDJunctionNetwork::snap_point_locations_to_upstream_junctions_from_ int search_radius_nodes, int threshold_stream_order, LSDFlowInfo& FlowInfo, LSDRasterInfo& RI) { - // read the csv file + // read the csv file LSDSpatialCSVReader outlets_data( RI, csv_filename ); // Now get the lat long points @@ -8803,7 +9303,7 @@ vector LSDJunctionNetwork::snap_point_locations_to_upstream_junctions_from_ cout << "Snapping: Got channel!, channel node is: " << this_chan_node << endl; if(this_chan_node != NoDataValue) { - cout << "Snapping, got the node index and the junction" << endl; + cout << "Snapping, got the node index and the junction" << endl; // now get the upslope junction int temp_junc = find_upstream_junction_from_channel_nodeindex(this_chan_node, FlowInfo); snapped_junction_indices.push_back(temp_junc); @@ -8826,7 +9326,7 @@ vector LSDJunctionNetwork::snap_point_locations_to_upstream_junctions_from_ - } + } return snapped_junction_indices; @@ -9392,7 +9892,7 @@ void LSDJunctionNetwork::print_junctions_to_csv(LSDFlowInfo& FlowInfo, string fn // print to file sources_out << JunctionList[i] << "," << this_node << "," << x_loc << "," - << y_loc << "," << latitude << "," << longitude << "," << StreamOrderVector[ JunctionList[i] ] + << y_loc << "," << latitude << "," << longitude << "," << StreamOrderVector[ JunctionList[i] ] << "," << RJ << endl; } @@ -9450,7 +9950,7 @@ void LSDJunctionNetwork::print_junctions_to_csv(LSDFlowInfo& FlowInfo, vector& input_nodes, vector& sources, vector& baselevel_nodes, vector& baselevel_junctions,vector& outlet_nodes,LSDFlowInfo& FlowInfo, +void LSDJunctionNetwork::select_basin_from_nodes(vector& input_nodes, vector& sources, vector& baselevel_nodes, vector& baselevel_junctions,vector& outlet_nodes,LSDFlowInfo& FlowInfo, LSDRaster& DistanceFromOutlet, bool check_edges) { @@ -9874,7 +10374,7 @@ void LSDJunctionNetwork::select_basin_from_nodes(vector& input_nodes, vecto vector checker; if(check_edges) checker = this->check_nodata_influence(FlowInfo, DistanceFromOutlet); - + vector newput_doe(input_nodes.size()); for(size_t i=0; i& input_nodes, vecto // { // if(v == -9999) // { - // v = + // v = // } // } @@ -10038,7 +10538,7 @@ vector LSDJunctionNetwork::check_nodata_influence(LSDFlowInfo& FlowInfo, L // this_rast.write_raster("FLUB","bil"); return is_blurp; -} +} //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -10069,7 +10569,7 @@ void LSDJunctionNetwork::basin_from_node_snap_to_largest_surrounding_DA(vector LSDJunctionNetwork::basin_from_node_minimum_DA(vector& sources, const vector& stack = FlowInfo.get_SVector(); for(size_t i =0; i LSDJunctionNetwork::basin_from_node_range_DA(vector& sources, v double min_DA, double max_DA, bool check_edges) { // Getting the stack - const vector& stack = FlowInfo.get_SVector(); + const vector& stack = FlowInfo.get_SVector(); // Initialising the target nodes, reserve memory for worst case scenario () vector target_nodes;target_nodes.reserve(stack.size()); @@ -10172,7 +10672,7 @@ vector LSDJunctionNetwork::basin_from_node_range_DA(vector& sources, v { // influenced by edges if(check_edges) - { + { if(checker[stack[i]]) { continue; @@ -10199,7 +10699,7 @@ vector LSDJunctionNetwork::basin_from_node_range_DA(vector& sources, v } this->select_basin_from_nodes( target_nodes, sources, baselevel_nodes, baselevel_junctions, outlet_nodes, FlowInfo, DistanceFromOutlet, false); - + return target_nodes; } @@ -10621,4 +11121,134 @@ void LSDJunctionNetwork::write_river_profiles_to_csv_all_sources(float channel_l chan_out.close(); } +//------------------------------------------------------------------------------------------------------------------------------------------// +// Function to calculate the hypsometric integral for each point along the channel network and write to csv +// FJC 26/01/22 +//------------------------------------------------------------------------------------------------------------------------------------------// +void LSDJunctionNetwork::calculate_hypsometric_integral(string fname_prefix, LSDFlowInfo& FlowInfo, LSDRaster& Elevation) +{ + + // first get the vectors + vector NIvec; + vector SOvec; + vector JIvec; + GetChannelNodesAndJunctions(FlowInfo, NIvec, JIvec, SOvec); + + // Deal with setting up the file + // append csv to the filename + string FileName = fname_prefix+".csv"; + + //open a file to write + ofstream WriteData; + WriteData.open(FileName.c_str()); + + WriteData.precision(8); + WriteData << "latitude,longitude,Junction Index,Stream Order,NI,receiver_NI,receiver_JI,HI" << endl; + + // the x and y locations + double latitude,longitude; + + // this is for latitude and longitude + LSDCoordinateConverterLLandUTM Converter; + + + // now get the number of channel nodes + int this_NI, receiver_NI, RJ; + int row,col; + int NNodes = int(NIvec.size()); + float HI; + //cout << "The number of nodes is: " << NNodes << endl; + for(int node = 0; node NIvec; + vector SOvec; + vector JIvec; + GetChannelNodesAndJunctions(FlowInfo, NIvec, JIvec, SOvec); + + // Deal with setting up the file + // append csv to the filename + string FileName = fname_prefix+".csv"; + + //open a file to write + ofstream WriteData; + WriteData.open(FileName.c_str()); + + WriteData.precision(8); + WriteData << "latitude,longitude,Junction Index,Stream Order,NI,receiver_NI,receiver_JI,"; + + // get the bin midpoints for writing to the header + vector bin_midpoints; + int n_bins = int((upper_limit - lower_limit)/bin_width)+1; + int mp = bin_width/2; + for (int i = 0; i < n_bins; i++) + { + if (i < n_bins-1) { WriteData << "midpoint_" << to_string(mp) << ","; } + else { WriteData << "midpoint_" << to_string(mp);} + mp+=bin_width; + } + WriteData << endl; + + + // the x and y locations + double latitude,longitude; + + // this is for latitude and longitude + LSDCoordinateConverterLLandUTM Converter; + + + // now get the number of channel nodes + int this_NI, receiver_NI, RJ; + int row,col; + int NNodes = int(NIvec.size()); + float HI; + //cout << "The number of nodes is: " << NNodes << endl; + // vectors for histogram + vector Midpoints, ProbabilityDensity; + for(int node = 0; node JunctionList, + void calculate_junction_angles_complete(vector JunctionList, LSDFlowInfo& FlowInfo, map >& JA_int_info, map >& JA_float_info ); @@ -274,23 +274,23 @@ class LSDJunctionNetwork /// @brief This is a more complete function for junction angles /// It overwirtes two maps, containing all sorts of information about - /// the junction angles. + /// the junction angles. /// This is an overloaded version that gives some slope and flow distance information /// @param JunctionList a list of junctions /// @param FlowInfo an LSDFlowInfo object - /// @param JA_int_info A map where the key is the junction number + /// @param JA_int_info A map where the key is the junction number /// and the vector is a series of integer data about that juction - /// @param JA_float_info A map where the key is the junction number + /// @param JA_float_info A map where the key is the junction number /// and the vector is a series of float data about that juction /// @author SMM /// @date 17/11/2019 - void calculate_junction_angles_complete(vector JunctionList, + void calculate_junction_angles_complete(vector JunctionList, LSDFlowInfo& FlowInfo, LSDRaster& Elevation, LSDRaster& FlowDistance, float vertical_interval, map >& JA_int_info, map >& JA_float_info ); - + /// @brief This function gets the mean and standard error of every junction angle /// upslope of a given junction /// @param target_junction The target junction @@ -350,7 +350,7 @@ class LSDJunctionNetwork string csv_name); /// @brief This prints the junction angles to a csv file - /// It uses the much more complete junction angle code + /// It uses the much more complete junction angle code /// @param JunctionList The list of junctions to analyze. If this is an empty vector, /// the code analyses all junctions in the DEM /// @param FlowInfo The LSDFlowInfo object @@ -373,7 +373,7 @@ class LSDJunctionNetwork /// @author SMM /// @date 07/12/2019 void print_complete_junction_angles_to_csv(vector JunctionList, - LSDFlowInfo& FlowInfo, LSDRaster& Elevations, + LSDFlowInfo& FlowInfo, LSDRaster& Elevations, LSDRaster& FlowDistance, float vertical_interval, string csv_name); @@ -445,6 +445,24 @@ class LSDJunctionNetwork /// @date 04/03/2020 void PrintChannelNetworkToCSV_WithElevation(LSDFlowInfo& flowinfo, string fname_prefix,LSDRaster& elevation); + /// @brief This prints a stream network to a csv in WGS84. It includes the elevation data and the donor junction + /// @param FlowInfo the flow info object which translates node indices to actual points + /// @param FileName_prefix The prefix of the file to write, if no path is included it will write to the current directory. + /// The csv extension is added automatically. + /// @param elevation The elevation raster + /// @author SMM + /// @date 08/11/2020 + void PrintChannelNetworkToCSV_WithElevation_WithDonorJunction(LSDFlowInfo& flowinfo, string fname_prefix,LSDRaster& elevation); + + /// @brief This prints a stream network to a csv in WGS84. It includes surface fitting metrics along the channel + /// @param FlowInfo the flow info object which translates node indices to actual points + /// @param FileName_prefix The prefix of the file to write, if no path is included it will write to the current directory. + /// The csv extension is added automatically. + /// @param elevation The elevation raster + /// @author FJC + /// @date 28/01/2022 + void PrintChannelNetworkToCSV_WithSurfaceMetrics(LSDFlowInfo& flowinfo, string fname_prefix, LSDRaster& Elevation, LSDRaster& Slope, LSDRaster& Relief, LSDRaster& Curvature); + /// @brief This sends the JunctionArray to a LSDIndexRaster. /// @return LSDIndexRaster of JunctionArray. @@ -562,6 +580,24 @@ class LSDJunctionNetwork LSDRaster& TestRaster, LSDIndexRaster& FlowAcc, int lower_limit, int upper_limit); + /// @brief This function removes basins that fall outside a contributing pixel + /// Window and those that + /// are nested. A rather intensive pruning process that hopeuflly results + /// in a number of basins that are a similar size + /// @detail This doesn't just look for baselevel junctions: it goes through + /// all junctions in the DEM. Warning: computationally expensive! + /// @param FlowInfo The LSDFlowInfo object + /// @param TestRaster A raster that is just used to look for nodata + /// @param FlowAcc an LSDIndexRaster with the number of pixels for flow accumulation + /// @param lower_limit The minimum number of contributing pixels + /// @param upper_limit The maximum number of contributing pixels + /// @return a pruned list of base level nodes + /// @author SMM + /// @date 26/06/17 + vector Prune_Junctions_By_Contributing_Pixel_Window_Remove_Nested_Keep_Edge(LSDFlowInfo& FlowInfo, + LSDRaster& TestRaster, LSDIndexRaster& FlowAcc, + int lower_limit, int upper_limit); + /// @brief This function removes basins that are nested within any other /// basin in the list /// @param Junctions_Initial a vector of integers containg an inital @@ -1281,7 +1317,30 @@ vector GetChannelHeadsChiMethodFromValleys(vector ValleyNodes, /// @date 18/10/2012, 28/03/2013 LSDRaster ExtractRidges(LSDFlowInfo& FlowInfo, int& min_order, int& max_order); + /// @brief This extracts all ridges defined by every junction. It takes the penultamate node + /// before the next downstream junction, and then extract that basin. It then gets the outline + /// of that basin for tagging as ridgeline nodes. Therefore every junction, except for baselevel junctions, + /// will have ridgelines. + /// @detail Overlapping is based on the stack so higher order junctions will overwrite the ridgelines of + /// lower order junctions. + /// @param FlowInfo LSDFlowInfo object. + /// @return A map with the key being the node index and the value being the largest stream order of the ridge + /// @author SMM + /// @date 11/01/2021 + map ExtractAllRidges(LSDFlowInfo& FlowInfo); + /// @brief This extracts all ridges defined by every junction. It takes the penultamate node + /// before the next downstream junction, and then extract that basin. It then gets the outline + /// of that basin for tagging as ridgeline nodes. Therefore every junction, except for baselevel junctions, + /// will have ridgelines. + /// @detail Overlapping is based on the stack so higher order junctions will overwrite the ridgelines of + /// lower order junctions. + /// @param FlowInfo LSDFlowInfo object. + /// @param csv_name the name of the csv (with extension) to which to print the data + /// @return A map with the key being the node index and the value being the largest stream order of the ridge + /// @author SMM + /// @date 11/01/2021 + map ExtractAllRidges(LSDFlowInfo& FlowInfo, string csv_name); /// @brief This last function gets the hilltops: ridges limited by a maximum slope. /// @@ -1309,7 +1368,7 @@ vector GetChannelHeadsChiMethodFromValleys(vector ValleyNodes, /// @date 04/04/13 LSDIndexRaster ChannelIndexer(LSDFlowInfo& flowinfo); - /// @brief This extracts vectors containing node incidex, junction indices + /// @brief This extracts vectors containing node indices, junction indices /// and stream orders of pixels in the channel network. /// @detail The vectors are replaced by the method /// @author SMM @@ -1482,6 +1541,53 @@ vector GetChannelHeadsChiMethodFromValleys(vector ValleyNodes, /// @date 21/10/2013 int get_nodeindex_of_nearest_channel_for_specified_coordinates(float X_coordinate,float Y_coordinate, int threshold_stream_order, int search_radius_nodes,LSDFlowInfo& FlowInfo); + /// @brief Function to get the nodeindex of the nearest channel node for a given nodeindex + /// @param starting_nodeindex does what it says on the tin + /// @param threshold_stream_order The minimum stream order that will be considers a 'channel' by the algorithm + /// @param FlowInfo LSDFlowInfo object. + /// @return Returns the NodeIndex of the nearest channel node. + /// @author SMM + /// @date 02/07/2021 + int get_nodeindex_of_nearest_channel(int starting_nodeindex, int threshold_stream_order, LSDFlowInfo& FlowInfo); + + /// @brief Function to get the distance to the nearest channel + /// @param starting_nodeindex does what it says on the tin + /// @param threshold_stream_order The minimum stream order that will be considers a 'channel' by the algorithm + /// @param FlowInfo LSDFlowInfo object. + /// @param FlowDist the distance from outlet raster + /// @return Returns the distance to the nearest channel node following a flow path + /// @author SMM + /// @date 02/07/2021 + float get_distance_to_nearest_channel(int starting_nodeindex, int threshold_stream_order, LSDFlowInfo& FlowInfo, LSDRaster& FlowDist); + + /// @brief Function to get the distance to the nearest channel + /// @param threshold_stream_order The minimum stream order that will be considers a 'channel' by the algorithm + /// @param FlowInfo LSDFlowInfo object. + /// @param FlowDist the flow distance raster + /// @return Returns the distance of all pixels to the nearest channel node as a raster + /// @author SMM + /// @date 02/07/2021 + LSDRaster get_distance_to_nearest_channel_raster(int threshold_stream_order, LSDFlowInfo& FlowInfo, LSDRaster& FlowDist); + + /// @brief Function to get the releif to the nearest channel + /// @param starting_nodeindex does what it says on the tin + /// @param threshold_stream_order The minimum stream order that will be considers a 'channel' by the algorithm + /// @param FlowInfo LSDFlowInfo object. + /// @param Elevation the elevation raster + /// @return Returns the relief of this pixel to the nearest channel node following a flow path + /// @author SMM + /// @date 02/07/2021 + float get_relief_to_nearest_channel(int starting_nodeindex, int threshold_stream_order, LSDFlowInfo& FlowInfo, LSDRaster& Elevation); + + /// @brief Function to get the distance to the nearest channel + /// @param threshold_stream_order The minimum stream order that will be considers a 'channel' by the algorithm + /// @param FlowInfo LSDFlowInfo object. + /// @param Elevation the elevation raster + /// @return Returns the relief of this pixel to the nearest channel node as a raster + /// @author SMM + /// @date 02/07/2021 + LSDRaster get_relief_to_nearest_channel_raster(int threshold_stream_order, LSDFlowInfo& FlowInfo, LSDRaster& Elevation); + /// @brief Function to snap input coordinates to the nearest channel node from latitude and longitude /// @param latitude /// @param longitude @@ -1624,8 +1730,8 @@ vector GetChannelHeadsChiMethodFromValleys(vector ValleyNodes, /// @brief This function takes the name of a csv file that contains /// latitude and longitude information and then finds the junction - /// numbers upslope. - /// @param csv_filename The name of the file from which the lat-long will be read. + /// numbers upslope. + /// @param csv_filename The name of the file from which the lat-long will be read. /// @param search_radius_nodes the number of nodes around the point to search /// for a channel /// @param threshold_stream_order the minimum stream order to which the point @@ -1652,7 +1758,7 @@ vector GetChannelHeadsChiMethodFromValleys(vector ValleyNodes, ///@author B.G. ///@date 09/12/2019 void select_basin_from_nodes(vector& input_nodes, vector& sources, vector& baselevel_nodes, - vector& baselevel_junctions,vector& outlet_nodes,LSDFlowInfo& FlowInfo, + vector& baselevel_junctions,vector& outlet_nodes,LSDFlowInfo& FlowInfo, LSDRaster& DistanceFromOutlet, bool check_edges); ///@brief This function extract basins from list of nodes and overwrite given sources, @@ -1670,7 +1776,7 @@ vector GetChannelHeadsChiMethodFromValleys(vector ValleyNodes, ///@param LSDRaster: DistanceFromOutlet, a raster with the distance from oulet ///@author B.G. ///@date 09/12/2019 - void basin_from_node_snap_to_largest_surrounding_DA(vector& input_nodes, vector& sources, vector& baselevel_nodes, + void basin_from_node_snap_to_largest_surrounding_DA(vector& input_nodes, vector& sources, vector& baselevel_nodes, vector& baselevel_junctions,vector& outlet_nodes, LSDFlowInfo& FlowInfo, LSDRaster& DistanceFromOutlet, LSDRaster& DrainageArea, int n_pixels, bool check_edges); @@ -1687,7 +1793,7 @@ vector GetChannelHeadsChiMethodFromValleys(vector ValleyNodes, ///@return a vector with the selected baselevel nodes ///@author B.G ///@date December 2019 - vector basin_from_node_minimum_DA(vector& sources, vector& baselevel_nodes, + vector basin_from_node_minimum_DA(vector& sources, vector& baselevel_nodes, vector& baselevel_junctions,vector& outlet_nodes, LSDFlowInfo& FlowInfo, LSDRaster& DistanceFromOutlet, LSDRaster& DrainageArea, double min_DA, bool check_edges); @@ -1705,7 +1811,7 @@ vector GetChannelHeadsChiMethodFromValleys(vector ValleyNodes, ///@return a vector with the selected baselevel nodes ///@author B.G ///@date December 2019 - std::vector basin_from_node_minimum_DA_draining_to_list_of_nodes(vector& input_nodes, vector& sources, vector& baselevel_nodes, + std::vector basin_from_node_minimum_DA_draining_to_list_of_nodes(vector& input_nodes, vector& sources, vector& baselevel_nodes, vector& baselevel_junctions,vector& outlet_nodes, LSDFlowInfo& FlowInfo, LSDRaster& DistanceFromOutlet, LSDRaster& DrainageArea, double min_DA); @@ -1723,7 +1829,7 @@ vector GetChannelHeadsChiMethodFromValleys(vector ValleyNodes, ///@return a vector with the selected baselevel nodes ///@author B.G ///@date December 2019 - std::vector basin_from_node_all_minimum_DA_for_one_watershed(int outlet_node_of_the_watershed, vector& sources, vector& baselevel_nodes, + std::vector basin_from_node_all_minimum_DA_for_one_watershed(int outlet_node_of_the_watershed, vector& sources, vector& baselevel_nodes, vector& baselevel_junctions,vector& outlet_nodes, LSDFlowInfo& FlowInfo, LSDRaster& DistanceFromOutlet, LSDRaster& DrainageArea, double min_DA, double max_DA); @@ -1843,6 +1949,14 @@ double min_DA, double max_DA); /// @date 08/10/15 int get_downstream_junction(int starting_junction, LSDFlowInfo& FlowInfo); + /// @details Get downstream junction for a specified node + /// @param CurrentNode starting node + /// @param FlowInfo LSDFlowInfo object + /// @return integer with downstream junction number + /// @author FJC + /// @date 29/01/21 + int get_junction_downstream_of_node(int CurrentNode, LSDFlowInfo& FlowInfo); + /// @details Gets the stream order of a node /// @param FlowInfo LSDFlowInfo object /// @param node node of interest @@ -2116,6 +2230,19 @@ void write_river_profiles_to_csv_all_sources(float channel_length, int slope_win //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-= map GetMapOfChannelNodes(LSDFlowInfo& flowinfo); +/// @brief Calculate the hypsometric integral for every point in the channel network and write to csv +/// @param fname_prefix csv filename prefix +/// @param FlowInfo +/// @param Elevation elev raster +/// @author FJC +/// @date 26/01/22 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-= +void calculate_hypsometric_integral(string fname_prefix, LSDFlowInfo& FlowInfo, LSDRaster& Elevation); + +void calculate_upstream_elevations_network(string fname_prefix, LSDFlowInfo& FlowInfo, LSDRaster& Elevation, float bin_width, float lower_limit, float upper_limit); + + + protected: ///Number of rows. diff --git a/src/LSDLithoCube.cpp b/src/LSDLithoCube.cpp index d271670..ec80b27 100644 --- a/src/LSDLithoCube.cpp +++ b/src/LSDLithoCube.cpp @@ -35,7 +35,58 @@ using namespace TNT; +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// operators +// SMM, 2012 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +LSDLithoCube& LSDLithoCube::operator=(const LSDLithoCube& rhs) +{ + if (&rhs != this) + { + create(rhs.get_NRows(),rhs.get_NCols(),rhs.get_NLayers(), + rhs.get_XMinimum(),rhs.get_YMinimum(),rhs.get_ZMinimum(), + rhs.get_DataResolution(), + rhs.get_XSpacing(),rhs.get_YSpacing(),rhs.get_ZSpacing(), + rhs.get_loaded_vo_file(), rhs.get_NoDataValue(), + rhs.get_GeoReferencingStrings(), + rhs.get_LithoLayers(), + rhs.get_bottom_elevations(), + rhs.get_layer_thicknesses(), + rhs.get_StratiToK(), + rhs.get_StratiToSc() ); + } + return *this; +} + +// This is the create function that is used by the copy constructor +void LSDLithoCube::create(int nrows, int ncols, int nlayers, float xmin, float ymin, float zmin, + float cellsize, float xspace, float yspace, float zspace, + bool loaded, int ndv, map temp_GRS, + vector< Array2D > ll, vector be, vector lt, + map StK, map StS) +{ + NRows = nrows; + NCols = ncols; + NLayers = nlayers, + XMinimum = xmin; + YMinimum = ymin; + ZMinimum = zmin; + DataResolution = cellsize; + XSpacing = xspace; + YSpacing = yspace; + ZSpacing = zspace; + loaded_vo_file = loaded; + NoDataValue = ndv; + GeoReferencingStrings = temp_GRS; + LithoLayers = ll; + bottom_elevations = be; + layer_thicknesses = lt; + StratiToK = StK; + StratiToSc = StS; +} + +// A minimal create function. void LSDLithoCube::create() { NRows = 100; @@ -516,11 +567,11 @@ void LSDLithoCube::get_row_and_col_of_a_point(float X_coordinate,float Y_coordin //cout << "Getting row and col, " << row_point << " " << col_point << endl; - if(col_point > 0 && col_point < NCols-1) + if(col_point >= 0 && col_point <= NCols-1) { this_col = col_point; } - if(row_point > 0 && row_point < NRows -1) + if(row_point >= 0 && row_point <= NRows -1) { this_row = row_point; } @@ -583,6 +634,87 @@ int LSDLithoCube::get_layer_from_elevation(float elevation) } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// +// This gets the elevation from a layer number +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +float LSDLithoCube::get_elevation_from_layer(int layer_number) +{ + float this_elevation; + + this_elevation = ZMinimum + (layer_number * ZSpacing) + 0.5 * ZSpacing; + cout << "Layer is: " << layer_number << " and elevation is: " << this_elevation << endl; + + return this_elevation; +} + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// +// This gets an elevation raster of the lithocube surface. +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +LSDRaster LSDLithoCube::get_lithocube_surface_elevation(LSDIndexRaster& SurfaceLayerRaster) +{ + int SLR_NRows = SurfaceLayerRaster.get_NRows(); + int SLR_NCols = SurfaceLayerRaster.get_NCols(); + float SLR_NDV = SurfaceLayerRaster.get_NoDataValue(); + + Array2D elevation(SLR_NRows,SLR_NCols,SLR_NDV); + + + // now loop through topmost layer raster, getting the information + for(int row = 0; row < SLR_NRows; row++) + { + for(int col = 0; col < SLR_NCols; col++) + { + elevation[row][col] = get_elevation_from_layer(SurfaceLayerRaster.get_data_element(row,col)); + } + } + cout << "I have assigned the elevations." << endl; + // now make the raster of the stratigraphic information + LSDRaster ThisSurfaceElevation(SLR_NRows, SLR_NCols, SurfaceLayerRaster.get_XMinimum(), SurfaceLayerRaster.get_YMinimum(), + SurfaceLayerRaster.get_DataResolution(), SLR_NDV, elevation, SurfaceLayerRaster.get_GeoReferencingStrings()); + + return ThisSurfaceElevation; +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// +// This gets the topmost layer in the lithocube that represents stratigraphy +// (i.e. for each location, gets the layer number that represents the surface) +// It has logic to "drill" down the lithocube so that it returns the +// topmost allowed values +// the forbidden values are in the list called forbidden_codes +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +LSDIndexRaster LSDLithoCube::get_lithocube_surface_layers(list forbidden_codes) +{ + int starting_layer = NLayers-1; + Array2D layer_number(NRows,NCols,NoDataValue); + + cout << "Starting at layer " << starting_layer << endl; + + + // now loop through topmost layer raster, getting the information + for(int row = 0; row < NRows; row++) + { + for(int col = 0; col < NCols; col++) + { + layer_number[row][col] = get_layer_from_location(starting_layer, row, col, forbidden_codes); + } + } + cout << "I have assigned the layer numbers." << endl; + // now make the raster of the stratigraphic information + LSDIndexRaster ThisSurfaceLayer(NRows, NCols, XMinimum, YMinimum, XSpacing, NoDataValue, layer_number, GeoReferencingStrings); + + + return ThisSurfaceLayer; + + +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // // This gets a lithocode from a location @@ -622,6 +754,37 @@ int LSDLithoCube::get_lithocode_from_location(int LLayer, int LRow, int LCol, li } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// +// This gets the layer number from a location +// It has logic to "drill" down the lithocube so that it returns the +// topmost layer with allowed lithocode values +// the forbidden values are in the list called forbidden_codes +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +int LSDLithoCube::get_layer_from_location(int LLayer, int LRow, int LCol, list forbidden_codes) +{ + + int this_lcode = LithoLayers[LLayer][LRow][LCol]; + int this_layer = LLayer; + + + if (forbidden_codes.size() > 0) + { + cout << "This lithocode is: " << this_lcode << endl; + while ( (find(forbidden_codes.begin(),forbidden_codes.end(),this_lcode) != forbidden_codes.end()) && this_layer > 0) + { + cout << "Got a forbidden code! Let me dig a layer!" << endl; + this_layer--; + this_lcode = LithoLayers[this_layer][LRow][LCol]; + cout << "New layer is: " << this_layer << " and new code is: " << this_lcode << endl; + } + } + + return this_layer; + +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -699,7 +862,7 @@ LSDIndexRaster LSDLithoCube::get_lithology_codes_from_raster(LSDRaster& Elevatio } } } - cout << "I have assigned the lithocodes." << endl; + //cout << "I have assigned the lithocodes." << endl; // now make the raster of the stratigraphic information LSDIndexRaster ThisStrat(ER_NRows, ER_NCols, ElevationRaster.get_XMinimum(), ElevationRaster.get_YMinimum(), ElevationRaster.get_DataResolution(), LR_NDV, lithocode, ElevationRaster.get_GeoReferencingStrings()); @@ -1109,7 +1272,7 @@ void LSDLithoCube::XY_to_rowcol_careful(float X_coord, float Y_coord, int& row, if(snap_to_closest_point_on_raster) { is_X_recasted = true; - std::cout << "WARNING::X coordinate is West to the X minimum by " << corrected_X << ", I am recasting to column 0" << std::endl; + //std::cout << "WARNING::X coordinate is West to the X minimum by " << corrected_X << ", I am recasting to column 0" << std::endl; col = 0; } else @@ -1123,7 +1286,7 @@ void LSDLithoCube::XY_to_rowcol_careful(float X_coord, float Y_coord, int& row, if(snap_to_closest_point_on_raster) { is_X_recasted = true; - std::cout << "WARNING::X coordinate is East to the X minimum by " << corrected_X - ((ncols+1) * cellsize) << ", I am recasting to column " << ncols - 1 << std::endl; + //std::cout << "WARNING::X coordinate is East to the X minimum by " << corrected_X - ((ncols+1) * cellsize) << ", I am recasting to column " << ncols - 1 << std::endl; col = ncols - 1; } else @@ -1139,7 +1302,7 @@ void LSDLithoCube::XY_to_rowcol_careful(float X_coord, float Y_coord, int& row, if(snap_to_closest_point_on_raster) { is_Y_recasted = true; - std::cout << "WARNING::Y coordinate is South to the Y minimum by " << - corrected_Y << ", I am recasting to row " << nrows - 1 << std::endl; + //std::cout << "WARNING::Y coordinate is South to the Y minimum by " << - corrected_Y << ", I am recasting to row " << nrows - 1 << std::endl; row = nrows - 1; } else @@ -1153,7 +1316,7 @@ void LSDLithoCube::XY_to_rowcol_careful(float X_coord, float Y_coord, int& row, if(snap_to_closest_point_on_raster) { is_Y_recasted = true; - std::cout << "WARNING::X coordinate is North to the Y maximum by " << (nrows+1) * cellsize - corrected_Y << ", I am recasting to row " << 0 << std::endl; + //std::cout << "WARNING::X coordinate is North to the Y maximum by " << (nrows+1) * cellsize - corrected_Y << ", I am recasting to row " << 0 << std::endl; row = 0; } else diff --git a/src/LSDLithoCube.hpp b/src/LSDLithoCube.hpp index e08ef0e..a9202ff 100644 --- a/src/LSDLithoCube.hpp +++ b/src/LSDLithoCube.hpp @@ -38,6 +38,9 @@ class LSDLithoCube { public: + /// Assignment operator. + LSDLithoCube& operator=(const LSDLithoCube& LSDLC); + /// @brief Constructor. /// @return LSDLithoCube LSDLithoCube() @@ -54,8 +57,6 @@ class LSDLithoCube create(An_LSDRaster); } - - /// @brief Constructor. Create an LSDLithoCube from a file. /// Uses a filename and file extension /// @return LSDLithoCube @@ -66,6 +67,52 @@ class LSDLithoCube create(filename); } + // Get functions + // Need these for the copy constructor + /// @return Number of rows as an integer. + int get_NRows() const { return NRows; } + /// @return Number of columns as an integer. + int get_NCols() const { return NCols; } + /// @return Number of layers as an integer. + int get_NLayers() const { return NLayers; } + /// @return Minimum X coordinate as a float. + float get_XMinimum() const { return XMinimum; } + /// @return Minimum Y coordinate as a float. + float get_YMinimum() const { return YMinimum; } + /// @return Minimum Z coordinate as a float. + float get_ZMinimum() const { return ZMinimum; } + + /// @return Data resolution as a float. + float get_DataResolution() const { return DataResolution; } + + /// @return X spacing as a float. + float get_XSpacing() const { return XSpacing; } + /// @return X spacing as a float. + float get_YSpacing() const { return YSpacing; } + /// @return X spacing as a float. + float get_ZSpacing() const { return ZSpacing; } + + /// @return Return the bool flag that we loaded the vo file + bool get_loaded_vo_file() const { return loaded_vo_file; } + + /// @return No Data Value as an integer. + int get_NoDataValue() const { return NoDataValue; } + + /// @return map containing the georeferencing strings + map get_GeoReferencingStrings() const { return GeoReferencingStrings; } + + /// @return the integer arrays that have the lithocodes (this is the main dataset) + vector< Array2D > get_LithoLayers() const { return LithoLayers; } + /// @return a float vector of the elevations of the bottom of each layer + vector get_bottom_elevations() const { return bottom_elevations; } + /// @return a float vector of the layer thicknesses + vector get_layer_thicknesses() const { return layer_thicknesses; } + + /// @return a map that connects lithocodes to K values + map get_StratiToK() const { return StratiToK; } + /// @return a map that connects lithocodes to Sc values + map get_StratiToSc() const { return StratiToSc; } + /// @brief This takes a litho layer and writes the layer to a raster /// @param layer The layer number you want /// @param fname the filename (without extension but with path) @@ -140,6 +187,28 @@ class LSDLithoCube /// @date 30/01/2020 int get_layer_from_elevation(float elevation); + /// @brief Gets the elevation from the layer number + /// @param layer_number - the number of the layer. the layer's number. the number belonging to the layer + /// @return the elevation + /// @author ELSG + /// @date 14/04/2021 + float get_elevation_from_layer(int layer_number); + + + /// @brief Gets the elevation raster of the lithocube surface (or any given layer raster really) + /// @param SurfaceLayerRaster - a raster of layer numbers (e.g. representing the topographic surface of the lithocube) + /// @return an elevation raster + /// @author ELSG + /// @date 14/04/2021 + LSDRaster get_lithocube_surface_elevation(LSDIndexRaster& SurfaceLayerRaster); + + /// @brief Gets the layer raster of the lithocube surface + /// @param forbidden_codes - a list of litho codes that do not represent bedrock in the lithocube + /// @return a raster of the layer numbers representing the lithocube surface + /// @author ELSG + /// @date 14/04/2021 + LSDIndexRaster get_lithocube_surface_layers(list forbidden_codes); + /// @brief Gets the lithocode from a location. /// @detail Includes logic to drill through forbidden lithocode values until /// it finds a valid value @@ -153,6 +222,19 @@ class LSDLithoCube /// @date 31/08/2020 int get_lithocode_from_location(int LLayer, int LRow, int LCol, list forbidden_codes); + /// @brief Gets the layer from a location. + /// @detail Includes logic to drill through forbidden lithocode values until + /// it finds a valid value + /// @param LLayer the starting layer + /// @param LRow the row in the lithocube + /// @param LCol the col in the lithocube + /// @param forbidden_codes the list of codes that are not allowed to be returned + /// for example if you want to remove "air" or quaternary pixels in the lithocube + /// @return the layer + /// @author ELSG + /// @date 14/04/2021 + int get_layer_from_location(int LLayer, int LRow, int LCol, list forbidden_codes); + /// @brief Takes an elevation raster and returns an erodibility raster /// @param ElevationRaster A raster of elevations /// @return a raster of erodbility (K) values @@ -302,6 +384,12 @@ class LSDLithoCube /// @brief Make a rastermaker from another raster void create(LSDRaster& An_LSDRaster); + void create(int nrows, int ncols, int nlayers, float xmin, float ymin, float zmin, + float cellsize, float xspace, float yspace, float zspace, + bool loaded, int ndv, map temp_GRS, + vector< Array2D > ll, vector be, vector lt, + map StK, map StS); + }; diff --git a/src/LSDParameterParser.cpp b/src/LSDParameterParser.cpp index d4146ef..548e76e 100644 --- a/src/LSDParameterParser.cpp +++ b/src/LSDParameterParser.cpp @@ -139,6 +139,81 @@ vector DriverIngestor(int nNumberofArgs,char *argv[]) path_and_file.push_back(path_name); path_and_file.push_back(file_name); + if (path_and_file[0] == "-h" || path_and_file[0] == "--h" || + path_and_file[0] == "-H" || path_and_file[0] == "--H" || + path_and_file[0] == "--help" || path_and_file[0] == "-help" || + path_and_file[0] == "help" || path_and_file[0] == "Help" || + path_and_file[0] == "HELP" || path_and_file[0] == "-help" || + path_and_file[0] == "-WTF" || + path_and_file[1] == "-h" || path_and_file[1] == "--h" || + path_and_file[0] == "-H" || path_and_file[0] == "--H" || + path_and_file[1] == "--help" || path_and_file[1] == "-help" || + path_and_file[1] == "help" || path_and_file[1] == "Help" || + path_and_file[1] == "HELP" || path_and_file[1] == "-help" || + path_and_file[1] == "-WTF") + { + cout << "I am going to print a help file. There will be a .csv and a .html version. " << endl; + cout << "These files have README in the filename." << endl; + path_and_file[0] = "./"; + path_and_file[1] = "cry_for_help.txt"; + + ofstream ofs; + ofs.open("./cry_for_help.txt"); + ofs << "# The user has cried for help. " << endl; + ofs << "# You can find the help file with README in the filename." << endl; + ofs.close(); + } + + + if (path_and_file[0] == "-c" || path_and_file[0] == "--c" || + path_and_file[0] == "--C" || path_and_file[0] == "-C" || + path_and_file[0] == "cite" || path_and_file[0] == "Cite" || + path_and_file[0] == "-cite" || path_and_file[0] == "-Cite" || + path_and_file[0] == "--cite" || path_and_file[0] == "--Cite" || + path_and_file[0] == "citation" || path_and_file[0] == "Citation" || + path_and_file[0] == "-citation" || path_and_file[0] == "-Citation" || + path_and_file[0] == "--citation" || path_and_file[0] == "--Citation" || + path_and_file[1] == "-c" || path_and_file[0] == "--c" || + path_and_file[1] == "--C" || path_and_file[0] == "-C" || + path_and_file[1] == "cite" || path_and_file[0] == "Cite" || + path_and_file[1] == "-cite" || path_and_file[0] == "-Cite" || + path_and_file[1] == "--cite" || path_and_file[0] == "--Cite" || + path_and_file[1] == "citation" || path_and_file[0] == "Citation" || + path_and_file[1] == "-citation" || path_and_file[0] == "-Citation" || + path_and_file[1] == "--citation" || path_and_file[0] == "--Citation" ) + { + //cout << "You have chosen to print the citation information. " << endl; + path_and_file[0] = "./"; + path_and_file[1] = "lsdtt_citation.txt"; + + ofstream ofs; + ofs.open("./lsdtt_citation.txt"); + ofs << "# The user has asked for a citation. " << endl; + ofs.close(); + } + + if (path_and_file[0] == "-v" || path_and_file[0] == "--v" || + path_and_file[0] == "-V" || path_and_file[0] == "--V" || + path_and_file[0] == "version" || path_and_file[0] == "Version" || + path_and_file[0] == "-version" || path_and_file[0] == "-Version" || + path_and_file[0] == "--version" || path_and_file[0] == "--Version" || + path_and_file[1] == "-v" || path_and_file[0] == "--v" || + path_and_file[1] == "-V" || path_and_file[0] == "--V" || + path_and_file[1] == "version" || path_and_file[0] == "Version" || + path_and_file[1] == "-version" || path_and_file[0] == "-Version" || + path_and_file[1] == "--version" || path_and_file[0] == "--Version" ) + { + //cout << "You have chosen to print the version. " << endl; + path_and_file[0] = "./"; + path_and_file[1] = "lsdtt_version.txt"; + + ofstream ofs; + ofs.open("./lsdtt_version.txt"); + ofs << "# The user has asked for the version. " << endl; + ofs.close(); + } + + return path_and_file; } @@ -193,8 +268,9 @@ void LSDParameterParser::create(string PathName, string FileName) void LSDParameterParser::force_bil_extension() { cout << "===============================" << endl; - cout << "WARNING!!! This program requires georeferencing so only bil format" << endl; + cout << "WARNING!!! This program requires georeferencing so only ENVI bil format" << endl; cout << "Topographic data will be allowed!!" << endl; + cout << "This is not the same as ESRI bil. In gdal use -of ENVI to ouput to ENVI bil" << endl; cout << "===============================" << endl; dem_read_extension = "bil"; dem_write_extension = "bil"; @@ -392,10 +468,7 @@ void LSDParameterParser::parse_file_IO() } found_cheads = true; } - else - { - cout << "I didn't find CHeads_file in the parameter map" << endl; - } + if(parameter_map.find("channel heads fname") != parameter_map.end()) @@ -494,6 +567,9 @@ void LSDParameterParser::parse_all_parameters(map default_map_f, } + + + //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // This parses all the default parameter maps. //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= @@ -510,6 +586,136 @@ void LSDParameterParser::parse_all_parameters(map default_map_f, } + +void LSDParameterParser::print_help(map< string, vector > help_map, string help_prefix, string version, string citation) +{ + + // Open the help file + ofstream help_ofs; + string help_fname = write_path+help_prefix+".csv"; + + help_ofs.open(help_fname.c_str()); + vector this_string_vec; + + help_ofs << "name,type,default,description,guidance" << endl; + help_ofs <<"version,"<< version << ",citation,"<< citation << ", please also look at statements printed by code for citations to specific routines." << endl; + + // get the parameters + vector these_keys = extract_keys(parameters_read_map); + //cout << "The number of read keys are: " << these_keys.size() << endl; + + vector more_keys = extract_keys(defaults_used_map); + //cout << "The number of defaults are: " << more_keys.size() << endl; + + these_keys.insert( these_keys.end(), more_keys.begin(), more_keys.end() ); + //cout << "The updated size is: " << these_keys.size() << endl; + for(int i = 0; i< int(these_keys.size()); i++) + { + //cout << "The key is: " << these_keys[i] << endl; + if ( help_map.find( these_keys[i] ) == help_map.end() ) + { + help_ofs << these_keys[i] << ",not available,not available,not available,not available" << endl; + } + else + { + this_string_vec = help_map[ these_keys[i] ]; + help_ofs << these_keys[i] << "," << this_string_vec[0] << "," + << this_string_vec[1] << "," << this_string_vec[2] << "," + << this_string_vec[3] << endl; + } + + } + + help_ofs.close(); + + + // Now we do the html file + ofstream help_ofs_html; + string help_fname_html = write_path+help_prefix+".html"; + help_ofs_html.open(help_fname_html.c_str()); + + string program_name = help_prefix.erase(help_prefix.length()-7); + + cout << "Printing the help files to: " << endl; + cout << help_fname << endl; + cout << help_fname_html << endl; + + help_ofs_html << "\n\n\n\tHelp information for " << program_name << ""; + help_ofs_html << "\n\n" << endl << endl; + + help_ofs_html << "

Help information for " << program_name << "

" << endl << endl; + help_ofs_html << "

You are using version " << version << " of the lsdtopotools command line program " << program_name << ".

" << endl; + help_ofs_html << "

If the use of this software results in a publication, please cite " << citation << ". In addition please also look at statements printed by the code for citations to specific routines and algorithms.

" << endl; + + help_ofs_html << "

How this software works

" << endl; + help_ofs_html << "

This software is called from a command line." << endl; + help_ofs_html << "We will designate a command prompt in these instructions with the # symbol.

" << endl; + help_ofs_html << "

The program does not work on its own, it needs instructions. You give it instructions using a parameter file." << endl; + help_ofs_html << "Usually you will keep the parameter file in the same directory as your data." << endl; + help_ofs_html << "You will need to write the parameter file yourself using a text editor (for example brackets, atom, sublime text, pspad, notepad, emacs or similar." << endl; + help_ofs_html << "If your parameter file is called my_parameter_file.driver then the call to the program would look like this:

" << endl; + help_ofs_html << "

# "<< program_name << " my_parameter_file.driver

" << endl; + help_ofs_html << "

The file extension of the parameter file can be anything (we commonly use the extension driver but you can add any extension you want).

" << endl; + + help_ofs_html << "

Data input format

" << endl; + help_ofs_html << "

This program reads ENVI bil format rasters projected into UTM (the WGS84 version). You must convert and project your DEM before using these tools. ENVI bil and ESRI bil are two different file formats so make sure you use ENVI bil." << endl; + help_ofs_html << "You can read about converting file formats at the lsdtopotools documentation site.

" << endl; + + help_ofs_html << "

Data output format

" << endl; + help_ofs_html << "

This program writes ENVI bil format rasters projected into UTM (the WGS84 version).

" << endl; + help_ofs_html << "This program also writes csv and geojson files for point data, which are projected into geographic WGS 84 (EPSG:4326)

" << endl; + + help_ofs_html << "

Making the parameter file

" << endl; + help_ofs_html << "

You call lsdtt command line tools with a parameter file. This file can be made in a text editor. " << endl; + help_ofs_html << "Lines in the parameter file that start with the hash symbol (#) are ignored. " << endl; + help_ofs_html << "Lines that are read have a keyword followed by a colon and then a value:

" << endl; + help_ofs_html << "

keyword: value

" << endl; + help_ofs_html << "

If you scroll down in this file you will see the parameters available for this program.

" << endl; + help_ofs_html << "

In addition you need to tell the program what your data is called. " << endl; + help_ofs_html << "

For this you need this line:

" << endl; + help_ofs_html << "

read fname: FILE_PREFIX

" << endl; + help_ofs_html << "

where FILE_PREFIX is the name of the raster without the .bil extension

" << endl; + help_ofs_html << "

The rest of the parameter file has the format:

\n

PARAMETER_NAME: VALUE

That is, there needs to be a colon and a space after the parameter name." << endl; + help_ofs_html << "The default parameter value will be used if you don't give the parameter file the name and value of a parameter

" << endl << endl; + + // And now for the table. + // First the header + help_ofs_html << "

Parameter table

" << endl; + help_ofs_html << "\n\t\n\t\t" + << "\n\t\t" + << "\n\t\t" + << "\n\t\t" + << "\n\t\t" + << "\n\t" << endl; + + help_ofs_html << "\n\t\n\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t" << endl; + + // And now for the rest of it. + for(int i = 0; i< int(these_keys.size()); i++) + { + //cout << "The key is: " << these_keys[i] << endl; + if ( help_map.find( these_keys[i] ) == help_map.end() ) + { + help_ofs_html << "\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t" << endl; + } + else + { + this_string_vec = help_map[ these_keys[i] ]; + + help_ofs_html << "\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t" << endl; + } + + } + + // and now the end + help_ofs_html << "\n\t\n\t
param nametypedefaultdescriptionguidance
" << these_keys[i] << "not availablenot availablenot availablenot available
" << these_keys[i] << "" << this_string_vec[0] << "" + << this_string_vec[1] << "" << this_string_vec[2] << "" + << this_string_vec[3] <<"
\n\t\n" << endl; + + help_ofs_html.close(); + +} + //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // These two functions takes a map of defualt parameters and returns the parameters for the // current implementation @@ -524,17 +730,17 @@ void LSDParameterParser::parse_float_parameters(map default_map) int n_keys = int(these_keys.size()); for(int i = 0; i default_map) int n_keys = int(these_keys.size()); for(int i = 0; i default_map) int n_keys = int(these_keys.size()); for(int i = 0; i default_map) int n_keys = int(these_keys.size()); for(int i = 0; i default_map) int n_keys = int(these_keys.size()); for(int i = 0; i temp_bc(4); for (int i = 0; i< 4; i++) { @@ -794,10 +1000,10 @@ void LSDParameterParser::check_boundary_conditions() boundary_conditions = temp_bc; } - for (int i =0; i< 4; i++) - { - cout << "Boundary["< default_map_i, map default_map_b, map default_map_s, map default_map_d); + /// @brief This processes the help information + /// @param help_map the map of string vectors containing help information. + /// @param the file prefix of the help file + /// @param the version the help file refers to + /// @param the appropriate citation for the code + /// @author SMM + /// @date 21/07/2021 + void print_help(map< string, vector > help_map, string file_prefix, string version, string citation); + /// @brief This function takes a default map and converts it into the parameters /// by comparing the keys to the parsed parameter file diff --git a/src/LSDRaster.cpp b/src/LSDRaster.cpp index 29b20fd..dd343a6 100644 --- a/src/LSDRaster.cpp +++ b/src/LSDRaster.cpp @@ -2060,11 +2060,11 @@ void LSDRaster::get_row_and_col_of_a_point(float X_coordinate,float Y_coordinate //cout << "Getting row and col, " << row_point << " " << col_point << endl; - if(col_point > 0 && col_point < NCols-1) + if(col_point >= 0 && col_point <= NCols-1) { this_col = col_point; } - if(row_point > 0 && row_point < NRows -1) + if(row_point >= 0 && row_point <= NRows -1) { this_row = row_point; } @@ -2088,11 +2088,11 @@ void LSDRaster::get_row_and_col_of_a_point(double X_coordinate,double Y_coordina //cout << "Getting row and col, " << row_point << " " << col_point << endl; - if(col_point > 0 && col_point < NCols-1) + if(col_point >= 0 && col_point <= NCols-1) { this_col = col_point; } - if(row_point > 0 && row_point < NRows -1) + if(row_point >= 0 && row_point <= NRows -1) { this_row = row_point; } @@ -2101,6 +2101,72 @@ void LSDRaster::get_row_and_col_of_a_point(double X_coordinate,double Y_coordina col = this_col; } +void LSDRaster::replace_pixels(string replace_filename) +{ + ifstream ifs; + ifs.open(replace_filename.c_str()); + + if( ifs.fail() ) + { + cout << "\nFATAL ERROR: Trying to load csv data file, but the file" << replace_filename + << " doesn't exist; LLSDRaster::replace_points" << endl; + exit(EXIT_FAILURE); + } + else + { + cout << "I have opened the csv file." << endl; + } + + // get the headers from the first line + // we just ignore this. + // initiate the strings to hold the file + string line_from_file; + vector empty_string_vec; + vector this_string_vec; + string temp_string; + getline(ifs, line_from_file); + + // now loop through the rest of the lines, getting the data. + while( getline(ifs, line_from_file)) + { + //cout << "Getting line, it is: " << line_from_file << endl; + // reset the string vec + this_string_vec = empty_string_vec; + + // create a stringstream + stringstream ss(line_from_file); + ss.precision(9); + + // create a stringstream + while( ss.good() ) + { + string substr; + getline( ss, substr, ',' ); + + // remove the spaces + substr.erase(remove_if(substr.begin(), substr.end(), ::isspace), substr.end()); + + // remove control characters + substr.erase(remove_if(substr.begin(), substr.end(), ::iscntrl), substr.end()); + + // add the string to the string vec + this_string_vec.push_back( substr ); + } + + // now convert to pixel locations + float easting = atof(this_string_vec[0].c_str()); + float northing = atof(this_string_vec[1].c_str()); + float new_value = atof(this_string_vec[2].c_str()); + + if (check_if_point_is_in_raster(easting,northing)) + { + int r,c; + get_row_and_col_of_a_point(easting,northing,r,c); + RasterData[r][c] = new_value; + } + } + +} //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // Snap to point with greatest value within a given window size @@ -2307,6 +2373,57 @@ float LSDRaster::max_elevation( void ) } return max_elevation; } + +float LSDRaster::min_elevation() +{ + float min_elevation = 9999999999999; + for (int i=0; i max_elevation) + { + max_elevation = RasterData[i][j]; + row = i; + col = j; + } + } + } + return max_elevation; +} + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -2564,7 +2681,7 @@ void LSDRaster::AdjustElevation(float elevation_change) RasterData[row][col] = RasterData[row][col]+elevation_change; } } - } + } } @@ -6625,6 +6742,43 @@ LSDRaster LSDRaster::mask_to_nodata_with_mask_raster(LSDIndexRaster& Mask_raster } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +LSDRaster LSDRaster::isolate_to_smaller_raster(LSDRaster& Mask_raster) +{ + Array2D new_data_raster; + new_data_raster = RasterData.copy(); + + // first check to see if the rasters are the same size + int IR_NRows = Mask_raster.get_NRows(); + int IR_NCols = Mask_raster.get_NCols(); + int IR_NDV = Mask_raster.get_NoDataValue(); + + if(IR_NRows == NRows && IR_NCols == NCols) + { + for(int row = 0; rowNCols) + { + max_offset_counter = NCols/2; + } + else + { + max_offset_counter = NRows/2; + } + + + //cout << "Finding the nearest data value to the nodata pixels. The max offset is " << max_offset_counter << endl; + + bool havent_found_nearest = true; + int row,col; + int data_row,data_col; + while(offset_counter <= max_offset_counter && havent_found_nearest) + { + //cout << "The offset counter is " << offset_counter << endl; + if (RasterData[this_row][this_col] != NoDataValue) + { + //cout << "This pixel has data " << endl; + nearest_value = RasterData[this_row][this_col]; + nearest_distance = 0; + havent_found_nearest = false; + } + else + { + vector row_vec; + vector col_vec; + + // left column + col = -offset_counter; + for (row = -offset_counter; row<= offset_counter; row++) + { + row_vec.push_back(this_row+row); + col_vec.push_back(this_col+col); + } + // right column + col = offset_counter; + for (row = -offset_counter; row<= offset_counter; row++) + { + row_vec.push_back(this_row+row); + col_vec.push_back(this_col+col); + } + // top row + row = offset_counter; + for (col = -offset_counter+1; col<= offset_counter-1; col++) + { + row_vec.push_back(this_row+row); + col_vec.push_back(this_col+col); + } + // bottom row + row = -offset_counter; + for (col = -offset_counter+1; col<= offset_counter-1; col++) + { + row_vec.push_back(this_row+row); + col_vec.push_back(this_col+col); + } + + // now find the closet neighbour + nearest_value = NoDataValue; + nearest_distance = NoDataValue; + int n_nodes = int(row_vec.size()); + //cout << "looping though adjacent pixels" << endl; + //cout << "This pixel at: " << this_row << "," << this_col << endl; + for(int n = 0; n= 0 && row < NRows && col >= 0 && col < NCols) + { + if (RasterData[row][col] != NoDataValue) + { + //cout << "Found a pixel with some data! "; + float radial_dist = sqrt(pow(((row-this_row)*DataResolution),2) + pow(((col-this_col)*DataResolution),2)); + //cout << "dist: " << radial_dist << " val: " << RasterData[row][col] << endl; + if(nearest_distance == NoDataValue) + { + nearest_distance = radial_dist; + nearest_value = RasterData[row][col]; + } + else if (nearest_distance > radial_dist) + { + nearest_distance = radial_dist; + nearest_value = RasterData[row][col]; + } + havent_found_nearest = false; + } + } + } + offset_counter++; + } + } + distance = nearest_distance; + value = nearest_value; +} + +// This returns two raster that are the nearest value and nearest distance to nodata nodes +vector LSDRaster::get_nearest_distance_and_value_masks() +{ + Array2D Distances(NRows, NCols, NoDataValue); + Array2D Values(NRows, NCols, NoDataValue); + + float distance,value; + for(int row = 0; row R_vec; + R_vec.push_back(DRaster); + R_vec.push_back(VRaster); + + return R_vec; + +} + + +// This returns two raster that are the nearest value and nearest distance to nodata nodes +// This is similar to above but uses another raster and ignores any points where there is nodata +// in the NoDataIgnore_raster +vector LSDRaster::get_nearest_distance_and_value_masks(LSDRaster& NoDataIgnore_raster) +{ + Array2D Distances(NRows, NCols, NoDataValue); + Array2D Values(NRows, NCols, NoDataValue); + + float distance,value; + for(int row = 0; row R_vec; + R_vec.push_back(DRaster); + R_vec.push_back(VRaster); + + return R_vec; + +} + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -// THis pads a smaller index raster to the same extent as a bigger raster by adding +/// This takes a list of points. Then, for every pixel in the +/// raster it finds the point amongst that list that is closest to the given pixel. +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +vector< LSDRaster > LSDRaster::find_nearest_point_from_list_of_points(vector Eastings, vector Northings, + vector values, float swath_width) +{ + Array2D Distances(NRows, NCols, NoDataValue); + Array2D Values(NRows, NCols, NoDataValue); + Array2D Nodes(NRows, NCols, NoDataValue); + + // Get the easting and northing vectors from the raster + vector raster_eastings; + vector raster_northings; + get_easting_and_northing_vectors(raster_eastings, raster_northings); + + int n_nodes = int(Eastings.size()); + + float this_min_distance, this_value, this_node, this_distance; + float x_0,y_0,x_1,y_1; + + // Now we loop through all pixels in the raster + cout << "I am going to find the closest point in a list of points to every pixel in your raster." << endl; + cout << "This has not been programmed in an efficient way," << endl; + cout << "so if this is a big DEM it will take a while" << endl; + for(int row = 0; row return_rasters; + return_rasters.push_back(DistRaster); + return_rasters.push_back(ValuesRaster); + return_rasters.push_back(NodesRaster); + + return return_rasters; + +} + + +void LSDRaster::make_swath(vector Eastings, vector Northings, + vector values, float swath_width, float bin_width, + string swath_data_prefix, bool print_swath_rasters) +{ + // first we get the swath rasters + vector< LSDRaster > swath_rasther_vec = find_nearest_point_from_list_of_points(Eastings, Northings,values, swath_width); + + string swath_data_name = swath_data_prefix+"_swath.csv"; + if (print_swath_rasters) + { + cout << "I am now going to print your swath rasters." << endl; + string dist_fname = swath_data_prefix+"_swathdist"; + string val_fname = swath_data_prefix+"_swathval"; + string node_fname = swath_data_prefix+"_swathnode"; + + swath_rasther_vec[0].write_raster(dist_fname,"bil"); + swath_rasther_vec[1].write_raster(val_fname,"bil"); + swath_rasther_vec[2].write_raster(node_fname,"bil"); + } + + // now we need to vectorize the data + vector distance_vector; + vector elevation_vector; + float this_distance; + for(int row = 0; row midpoints_output; + vector MeanX_output; + vector MedianX_output; + vector StandardDeviationX_output; + vector StandardErrorX_output; + vector MADX_output; + vector MeanY_output; + vector MinimumY_output; + vector FirstQuartileY_output; + vector MedianY_output; + vector ThirdQuartileY_output; + vector MaximumY_output; + vector StandardDeviationY_output; + vector StandardErrorY_output; + vector MADY_output; + vector number_observations_output; + + + bin_data(distance_vector, elevation_vector, bin_width, midpoints_output, MeanX_output, + MedianX_output, StandardDeviationX_output, StandardErrorX_output, MADX_output, + MeanY_output, MinimumY_output, FirstQuartileY_output, MedianY_output, + ThirdQuartileY_output, MaximumY_output, StandardDeviationY_output, StandardErrorY_output, + MADY_output, number_observations_output, NoDataValue); + + + ofstream bin_data_out(swath_data_name); + bin_data_out << "distance,mean_elevation,minimum_z,first_quartile_z,median_z,third_quartile_z,max_z,n_samples"<< endl; + int n_bins = (midpoints_output.size()); + // For some reason the last bin is always empty so I just don't print it + for(int i = 0; i filtered = RasterData.copy(); Array2D gaussian_kernel_weights(kw,kw,0.0); @@ -11141,8 +11674,6 @@ LSDRaster LSDRaster::PeronaMalikFilter(int timesteps, float percentile_for_lambd return PM_FilteredTopo; } - - //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Method to turn a point shapefile into an LSDIndexRaster. // @@ -11394,6 +11925,217 @@ LSDRaster LSDRaster::neighbourhood_statistics_spatial_average(float window_radiu return SpatialAverage; } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// spatial_average +// Calculates a spatial average using a specified moving window. Uses a neighbourhood +// switch to select circular (1) vs square window (0) +// DTM 19/06/2014 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +LSDRaster LSDRaster::neighbourhood_statistics_local_min_max(float window_radius, int neighbourhood_switch, bool find_maximum) +{ + Array2D SpatialMinMaxArray(NRows,NCols,NoDataValue); +// Array2D StandardDeviationArray(NRows,NCols,NoDataValue); + + // catch if the supplied window radius is less than the data resolution and + // set it to equal the data resolution - SWDG + if (window_radius < DataResolution) + { + cout << "Supplied window radius: " << window_radius << " is less than the data resolution: " << + DataResolution << ".\nWindow radius has been set to data resolution." << endl; + window_radius = DataResolution; + } + + // Prepare kernel + int kr = int(ceil(window_radius/DataResolution)); // Set radius of kernel + int kw=2*kr+1; // width of kernel + Array2D data_kernel(kw,kw,NoDataValue); + Array2D mask = create_mask(window_radius, neighbourhood_switch); + + // Move window over DEM and extract neighbourhood pixels + cout << "\n\tRunning neighbourhood statistics..." << endl; + cout << "\t\tDEM size = " << NRows << " x " << NCols << endl; + + + float extreme; + float value; + int k_row,k_col; + + + vector data; + for(int i=0;i= 0 && k_row < NRows && k_col >=0 && k_col < NCols) + { + value = RasterData[k_row][k_col]; + if(value!=NoDataValue && mask[i_kernel][j_kernel]==1) + { + //cout << "Value here." << endl; + if (find_maximum) + { + if (value > extreme) + { + extreme = value; + } + } + else + { + // get the minimum + if (value < extreme) + { + extreme = value; + } + } + + // sedt the value in this pixel after going through the kernal + //cout << "Value is: " << extreme << endl; + SpatialMinMaxArray[i][j]=extreme; + } + } + } + } + //cout << "And this extreme is: " << SpatialMinMaxArray[i][j] << endl; + } + } + } + + LSDRaster SpatialMinMax(NRows,NCols,XMinimum,YMinimum,DataResolution, + NoDataValue,SpatialMinMaxArray,GeoReferencingStrings); + return SpatialMinMax; +} + +//------------------------------------------------------------------------------ +// Function to return an array with the location of the pixel with the minimum or +// maximum value in a neighbourhood +// FJC 30/01/21 +//------------------------------------------------------------------------------ +LSDRaster LSDRaster::neighbourhood_statistics_local_min_max_location(Array2D& TargetRasterData, float window_radius, int neighbourhood_switch, bool find_maximum) +{ + Array2D SpatialMinMaxArray(NRows,NCols,NoDataValue); +// Array2D StandardDeviationArray(NRows,NCols,NoDataValue); + + // catch if the supplied window radius is less than the data resolution and + // set it to equal the data resolution - SWDG + if (window_radius < DataResolution) + { + cout << "Supplied window radius: " << window_radius << " is less than the data resolution: " << + DataResolution << ".\nWindow radius has been set to data resolution." << endl; + window_radius = DataResolution; + } + + // Prepare kernel + int kr = int(ceil(window_radius/DataResolution)); // Set radius of kernel + int kw=2*kr+1; // width of kernel + Array2D data_kernel(kw,kw,NoDataValue); + Array2D mask = create_mask(window_radius, neighbourhood_switch); + + // Move window over DEM and extract neighbourhood pixels + cout << "\n\tRunning neighbourhood statistics..." << endl; + cout << "\t\tDEM size = " << NRows << " x " << NCols << endl; + + + float extreme; + float value; + int k_row,k_col,extreme_row,extreme_col; + + + vector data; + for(int i=0;i= 0 && k_row < NRows && k_col >=0 && k_col < NCols) + { + value = TargetRasterData[k_row][k_col]; + if(value!=NoDataValue && mask[i_kernel][j_kernel]==1) + { + //cout << "Value here." << endl; + if (find_maximum) + { + if (value > extreme) + { + extreme = value; + extreme_row = k_row; + extreme_col = k_col; + } + } + else + { + // get the minimum + if (value < extreme) + { + extreme = value; + extreme_row = k_row; + extreme_col = k_col; + } + } + } + } + } + } + // set the value in the pixel. We use the pixel that the value is taken from, + // rather than the centre of the kernal. + //cout << "Value is: " << extreme << endl; + SpatialMinMaxArray[extreme_row][extreme_col]=extreme; + //cout << "And this extreme is: " << SpatialMinMaxArray[i][j] << endl; + } + } + } + + LSDRaster SpatialMinMax(NRows,NCols,XMinimum,YMinimum,DataResolution, + NoDataValue,SpatialMinMaxArray,GeoReferencingStrings); + return SpatialMinMax; +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // overloaded function to kick out 2 rasters -> local standard deviation & average @@ -13635,6 +14377,8 @@ LSDIndexRaster LSDRaster::ConvertToBinary(int Value, int ndv){ return binmask; } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Method to merge data from two LSDRasters WITH SAME EXTENT together. The data from the // raster specified as an argument will be added (will overwrite the original raster if there @@ -13915,13 +14659,13 @@ string LSDRaster::ChannelLengthByOrder(LSDIndexRaster& StreamNetwork, Array2D Gauss(NRows, NCols, NoDataValue); - for (int i = 1; i < NRows - 1; ++i){ - for (int j = 1; j < NCols - 1; ++j){ + for (int i = 0; i < NRows; ++i){ + for (int j = 0; j < NCols; ++j){ if (RasterData[i][j] != NoDataValue){ Gauss[i][j] = getGaussianRandom(minimum, mean, allowNegative); @@ -13937,15 +14681,18 @@ LSDRaster LSDRaster::PoupulateRasterGaussian(float minimum, float mean){ //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= /// Populate a raster with a given value. /// SWDG 9/6/16 +/// updated SMM 07/05/2021 to get edges //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -LSDRaster LSDRaster::PoupulateRasterSingleValue(float value){ - +LSDRaster LSDRaster::PopulateRasterSingleValue(float value) +{ Array2D Single(NRows, NCols, NoDataValue); - for (int i = 1; i < NRows - 1; ++i){ - for (int j = 1; j < NCols - 1; ++j){ - - if (RasterData[i][j] != NoDataValue){ + for (int i = 0; i < NRows; ++i) + { + for (int j = 0; j < NCols; ++j) + { + if (RasterData[i][j] != NoDataValue) + { Single[i][j] = value; } } @@ -14167,7 +14914,7 @@ LSDRaster LSDRaster::Breaching_Lindsay2016() Array2D backlinks(NRows,NCols, NO_BACK_LINK); // Visited represent the stage of all cells // 0-> Unprocessed - // 1-> EDGE + // 1-> EDGE Array2D visited(NRows,NCols); Array2D pits(NRows,NCols); // cout << "1" << endl; @@ -14214,7 +14961,7 @@ LSDRaster LSDRaster::Breaching_Lindsay2016() //Determine if this is an edge cell, gather information used to determine if //it is a pit cell - float lowest_neighbour = std::numeric_limits::max(); + float lowest_neighbour = std::numeric_limits::max(); for(int n=-1;n<=1;n++) for(int n2=-1;n2<=1;n2++) { @@ -14223,12 +14970,12 @@ LSDRaster LSDRaster::Breaching_Lindsay2016() //const int nx = x+n; //const int ny = y+n2; - + //No need for an inGrid check here because edge cells are filtered above // TOCHECK -> BG //Cells which can drain into NoData go on priority-queue as edge cells - if(tRasterData[y][x]==NoDataValue){ + if(tRasterData[y][x]==NoDataValue){ pq.push(guest); visited[y][x] = 1; goto nextcell; //VELOCIRAPTOR @@ -14291,7 +15038,7 @@ LSDRaster LSDRaster::Breaching_Lindsay2016() //Looks for neighbours which are either unvisited or pits for(int n=-1;n<=1;n++) - for(int n2=-1;n2<=1;n2++) + for(int n2=-1;n2<=1;n2++) { if(n==0 && n2==0) continue; diff --git a/src/LSDRaster.hpp b/src/LSDRaster.hpp index fccf3f7..818d5b4 100644 --- a/src/LSDRaster.hpp +++ b/src/LSDRaster.hpp @@ -501,6 +501,12 @@ class LSDRaster void get_row_and_col_of_a_point(float X_coordinate,float Y_coordinate,int& row, int& col); void get_row_and_col_of_a_point(double X_coordinate,double Y_coordinate,int& row, int& col); + /// @brief This reads a csv with x,y,value and finds the pixels at those locations and replaces the value + /// @detail the csv has columns X,Y,new_value + /// @param replace_filename the name of the csv file (with full path and csv extension) + /// @author SMM + /// @date 06/10/2021 + void replace_pixels(string replace_filename); void snap_to_row_col_with_greatest_value_in_window(int input_row, int input_col, int&out_row, int& out_col, int n_pixels); @@ -583,6 +589,14 @@ class LSDRaster /// @date 5/11/14 LSDRaster RasterTrimmerSpiral(); + /// @brief This replaces the first and last row and column with nodata + /// used in drainage extraction where removal of all pixels that are + /// influenced by the edge is important + /// @return void but updates the data in the raster object + /// @author SMM + /// @date 15/01/2022 + void replace_edges_with_nodata(); + /// @brief This returns a clipped raster that has the same dimensions as the /// smaller raster /// @param smaller_raster the raster to which the bigger raster should be @@ -622,6 +636,67 @@ class LSDRaster /// @date 10/02/17 LSDRaster BufferRasterData(float window_radius); + /// @brief This finds the nearest value of a pixel that has data to a pixel in this_row,this_col; + /// it is used to find the nearest value and distance to nodata nodes. Used in routines for + /// masking then filling channels + /// @param this_row the row to be investigated + /// @param this_col the col to be investigated + /// @param distance the distance to the nearest occupied pixel (returned by value) + /// @param value the value of the nearest occupied pixel (returned by value) + /// @author SMM + /// @date 26/01/2021 + void find_nearest_data(int this_row,int this_col, float& distance, float& value); + + + /// @brief This takes a list of points. Then, for every pixel in the + /// raster it finds the point amongst that list that is closest to the given pixel. + /// @param Eastings a vector of easting locations + /// @param Northings a vector of northing locations + /// @param values The value of the list of points to map onto the raster. + /// This functions is most commonly used for swath mapping so the value is usually a distance + /// @param swath_width the width of the swath in metres + /// @return A vector or rasters. The first is the closest distance to each pixel in the list + /// the second is the value of the pixel in the list, and the third is the index in the list + /// of the closest pixel + /// @author SMM + /// @date 15/02/2021 + vector< LSDRaster > find_nearest_point_from_list_of_points(vector Eastings, vector Northings, + vector values, float swath_width); + + /// @brief This takes a list of points and makes a swath profile around them. + /// @param Eastings a vector of easting locations + /// @param Northings a vector of northing locations + /// @param values The value of the list of points to map onto the raster. + /// This functions is most commonly used for swath mapping so the value is usually a distance + /// @param swath_width the width of the swath in metres + /// @param bin_width the distance between bins in the (i.e., the distance between points) + /// @param swath_data_prefix the prefix for the swath filenames + /// @param print_swath_rasters A boolean that controls if the swath rasters are printed + /// @return A vector or rasters. The first is the closest distance to each pixel in the list + /// the second is the value of the pixel in the list, and the third is the index in the list + /// of the closest pixel + /// @author SMM + /// @date 15/02/2021 + void make_swath(vector Eastings, vector Northings, + vector values, float swath_width, float bin_width, + string swath_data_prefix, bool print_swath_rasters); + + /// @brief Finds all nodata nodes and gets rasters of the nearest points value and + /// its distance + /// @return a vector of two rasters, the first is the distance and the second is the value + /// @author SMM + /// @date 26/01/2021 + vector get_nearest_distance_and_value_masks(); + + /// @brief Finds all nodata nodes and gets rasters of the nearest points value and + /// its distance + /// @param NoDataIgnore_raster a raster where the NaData values in that raster + /// are ignored by the nearest to value data raster + /// @return a vector of two rasters, the first is the distance and the second is the value + /// @author SMM + /// @date 05/03/2021 + vector get_nearest_distance_and_value_masks(LSDRaster& NoDataIgnore_raster); + /// @brief Pad one smaller raster to the same extent as a bigger raster by adding /// no data around the edges LSDIndexRaster PadSmallerRaster(LSDIndexRaster& smaller_raster); @@ -655,6 +730,22 @@ class LSDRaster /// @date 01/02/2014 float max_elevation(void); + + /// @brief Calculates minimum elevation of a raster + /// @return Minimum elevation + /// @author SMM + /// @date 08/10/2021 + float min_elevation(); + + + /// @brief Calculates max elevation of a raster + /// @param row overwritten row of the maximum elevation + /// @param col overwritten col of the maximum elevation + /// @return The spatially distributed relief + /// @author SMM + /// @date 12/03/2021 + float max_elevation(int& row, int& col); + /// @brief Calculates mean relief of a raster, it defaults to a circular kernal /// @return The spatially distributed relief /// @author JAJ (entered into trunk SMM) @@ -1405,6 +1496,11 @@ class LSDRaster /// @date 4/11/2014 LSDRaster mask_to_nodata_with_mask_raster(LSDIndexRaster& Mask_raster, int mask_value); + /// @brief This function masks a raster to only include points with data in the second raster + /// @author FJC + /// @date 09/03/21 + LSDRaster isolate_to_smaller_raster(LSDRaster& Mask_raster); + ///@brief This function fills pits/sinks in a DEM by incrementing elevations for cells with ///no downslope neighbour. The process is repeated adnausium until no cells require ///incrementing. @@ -1950,7 +2046,7 @@ class LSDRaster //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= ///@brief Wrapper Function to create a D-infinity flow accumulation and drainage area raster ///@return vector of LSDRaster (0 is acc, 1 is DA) - ///@author BG + ///@author BG ///@date 09/01/2018 //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= vector D_inf_flowacc_DA(); @@ -2057,7 +2153,7 @@ class LSDRaster vector neighbourhood_statistics_spatial_average_and_SD(float window_radius, int neighbourhood_switch); - /// @brief gets relief within value specified circular neighbourhood + /// @brief gets relief within value specified neighbourhood /// /// @details The second argument (neighbourhood_switch) specifies the neighbourhood type: /// 0 Square neighbourhood @@ -2070,6 +2166,32 @@ class LSDRaster LSDRaster neighbourhood_statistics_local_relief(float window_radius, int neighbourhood_switch); + /// @brief gets minimum or maximum value in a neighbourhood + /// + /// @details The second argument (neighbourhood_switch) specifies the neighbourhood type: + /// 0 Square neighbourhood + /// 1 Circular window + /// @param float window_radius -> radius of neighbourhood + /// @param int neighbourhood_switch -> see above + /// @param bool find_maximum -> if true, find the maximum, if false, find the minimum + /// @return LSDRaster contianing the maximum or minimum within a neighbourhood + /// @author SMM + /// @date 27/01/2021 + LSDRaster neighbourhood_statistics_local_min_max(float window_radius, int neighbourhood_switch, bool find_maximum); + + /// @brief Function to return an array with the location of the pixel with the minimum or + /// maximum value in a neighbourhood + /// @details The second argument (neighbourhood_switch) specifies the neighbourhood type: + /// 0 Square neighbourhood + /// 1 Circular window + /// @param float window_radius -> radius of neighbourhood + /// @param int neighbourhood_switch -> see above + /// @param bool find_maximum -> if true, find the maximum, if false, find the minimum + /// @return LSDRaster contianing the location and value of the maximum or minimum within a neighbourhood + /// @author FJC + /// @date 30/01/21 + LSDRaster neighbourhood_statistics_local_min_max_location(Array2D& TargetRasterData, float window_radius, int neighbourhood_switch, bool find_maximum); + /// @brief tests neighbourhood for the fraction of values for which the specified /// condition is met. /// @@ -2351,13 +2473,13 @@ class LSDRaster /// @param mean Mean value of the distribution to draw values from. /// @author SWDG /// @date 9/6/16 - LSDRaster PoupulateRasterGaussian(float minimum, float mean); + LSDRaster PopulateRasterGaussian(float minimum, float mean); /// @brief Populate a raster with a single value. /// @param value Value to populate all non nodata cells with. - /// @author SWDG - /// @date 9/6/16 - LSDRaster PoupulateRasterSingleValue(float value); + /// @author SWDG SMM + /// @date 9/6/16 update 07/05/2021 to get edges + LSDRaster PopulateRasterSingleValue(float value); /// @brief Write CHT and hilltop gradient data to a *.csv file, coded by UTM coordinates as well as lat/long. /// diff --git a/src/LSDRasterMaker.cpp b/src/LSDRasterMaker.cpp index b2d5256..567da21 100644 --- a/src/LSDRasterMaker.cpp +++ b/src/LSDRasterMaker.cpp @@ -128,6 +128,70 @@ void LSDRasterMaker::resize_and_reset( int new_rows, int new_cols, float new_res } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// +// Gets the row and column of a point +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDRasterMaker::get_row_and_col_of_a_point(float X_coordinate,float Y_coordinate,int& row, int& col) +{ + int this_row = NoDataValue; + int this_col = NoDataValue; + + // Shift origin to that of dataset + float X_coordinate_shifted_origin = X_coordinate - XMinimum; + float Y_coordinate_shifted_origin = Y_coordinate - YMinimum; + + // Get row and column of point + int col_point = int(X_coordinate_shifted_origin/DataResolution); + int row_point = (NRows - 1) - int(ceil(Y_coordinate_shifted_origin/DataResolution)-0.5); + + //cout << "Getting row and col, " << row_point << " " << col_point << endl; + + if(col_point >= 0 && col_point <= NCols-1) + { + this_col = col_point; + } + if(row_point >= 0 && row_point <= NRows -1) + { + this_row = row_point; + } + + row = this_row; + col = this_col; +} + +void LSDRasterMaker::get_row_and_col_of_a_point(double X_coordinate,double Y_coordinate,int& row, int& col) +{ + int this_row = NoDataValue; + int this_col = NoDataValue; + + // Shift origin to that of dataset + double X_coordinate_shifted_origin = X_coordinate - XMinimum; + double Y_coordinate_shifted_origin = Y_coordinate - YMinimum; + + // Get row and column of point + int col_point = int(X_coordinate_shifted_origin/DataResolution); + int row_point = (NRows - 1) - int(ceil(Y_coordinate_shifted_origin/DataResolution)-0.5); + + //cout << "Getting row and col, " << row_point << " " << col_point << endl; + + if(col_point >= 0 && col_point <= NCols-1) + { + this_col = col_point; + } + if(row_point >= 0 && row_point <= NRows -1) + { + this_row = row_point; + } + + row = this_row; + col = this_col; +} + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // Gets the minimum and maximum values in the raster //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -181,6 +245,82 @@ void LSDRasterMaker::set_to_constant_value(float new_value) } +// Add a float to all pixels in the raster +void LSDRasterMaker::add_value(float value_to_add) +{ + // now loop through the matrix rescaling the values. + for (int row = 0; row< NRows; row++) + { + for(int col = 0; col < NCols; col++) + { + if(RasterData[row][col] != NoDataValue) + { + RasterData[row][col] = RasterData[row][col]+value_to_add; + } + } + } +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This function add strips of a given value. +// This happily overwrites NoData +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDRasterMaker::add_strip(int start_row_or_col, int end_row_or_col, bool horizontal, float value) +{ + if (start_row_or_col < 0) + { + start_row_or_col = 0; + } + + if (horizontal) + { + if(start_row_or_col >= NRows) + { + start_row_or_col = NRows-1; + } + if(end_row_or_col > NRows) + { + end_row_or_col = NRows-1; + } + } + else + { + if(start_row_or_col >= NCols) + { + start_row_or_col = NCols-1; + } + if(end_row_or_col > NCols) + { + end_row_or_col = NCols-1; + } + } + + if (horizontal) + { + for (int row = start_row_or_col; row <= end_row_or_col; row++) + { + for (int col = 0; col < NCols; col++) + { + RasterData[row][col] = value; + } + } + } + else + { + for (int row = 0; row < NRows; row++) + { + for (int col = start_row_or_col; end_row_or_col<= NCols; col++) + { + RasterData[row][col] = value; + } + } + } + + + + + +} //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // This function takes the existing raster data and then linearly scales it @@ -223,43 +363,617 @@ void LSDRasterMaker::scale_to_new_minimum_and_maximum_value(float new_minimum, f //// impose_channels: this imposes channels onto the landscape //// You need to print a channel to csv and then load the data ////------------------------------------------------------------------------------ -void LSDRasterMaker::impose_channels(LSDSpatialCSVReader& source_points_data) +void LSDRasterMaker::impose_channels(LSDSpatialCSVReader& source_points_data, string column_name) { + // string column_name = "elevation(m)"; + + + Array2D zeta=RasterData.copy(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + + // Get the local coordinate as well as the elevations + vector UTME, UTMN; + source_points_data.get_x_and_y_from_latlong(UTME,UTMN); + vector elev = source_points_data.data_column_to_float(column_name); + + + // make the map + cout << "I am making an elevation map. This will not work if points in the raster lie outside of the csv channel points." << endl; + int row,col; + for(int i = 0; i< int(elev.size()); i++) + { + + source_points_data.get_row_and_col_of_a_point(UTME[i],UTMN[i],row, col); + zeta[row][col] = elev[i]; + } + + + this->RasterData = zeta.copy(); + + RasterData = zeta.copy(); +} + + +////------------------------------------------------------------------------------ +//// This takes a raster an increases the elevation of nodata pixels at the edge nodes +//// so that the outside pixels always drain inwards +////------------------------------------------------------------------------------ +void LSDRasterMaker::buffer_basin_to_single_outlet(LSDSpatialCSVReader& source_points_data, float slope) +{ + string column_name = "elevation(m)"; + float elev_diff = 10; //DataResolution*sqrt(2)*slope; + + vector min_elev_nd_rows; + vector min_elev_nd_cols; + + Array2D zeta=RasterData.copy(); + Array2D old_zeta = RasterData.copy(); + + // Get the local coordinate as well as the elevations + vector UTME, UTMN; + source_points_data.get_x_and_y_from_latlong(UTME,UTMN); + + int min_row,min_col; + int rp1,rm1,cp1,cm1; + + vector elev; + if ( UTME.size() == 1) + { + cout << "Only one element!" << endl; + elev.push_back(0); + } + else + { + elev = source_points_data.data_column_to_float(column_name); + cout << "There are " << elev.size() << " data points" << endl; + } + + // find the minimum elevation + int n_elev = int(elev.size()); + float min_elev = 10000000000; + int node_of_min_elev = 0; + cout << "n_elev: " << n_elev << endl; + for(int i = 0; i< n_elev; i++) + { + cout << "elev: " << elev[i] << endl; + if(elev[i] < min_elev) + { + min_elev = elev[i]; + node_of_min_elev = i; + } + } + get_row_and_col_of_a_point(UTME[node_of_min_elev],UTMN[node_of_min_elev],min_row, min_col); + cout << "The minimum elevation of the source point is: " << min_elev << " at node: " << node_of_min_elev << endl; + cout << "minimum elev in source points is at " << UTME[node_of_min_elev] << " , " << UTMN[node_of_min_elev] << endl; + + // now logic for finding the nodata around the minimum elevation + rp1 = min_row+1; + if(rp1 == NRows) + { + rp1 = min_row; + } + rm1 = min_row-1; + if (rm1 == -1) + { + rm1 = 0; + } + cp1 = min_col+1; + if (cp1 == NCols) + { + cp1 = min_col; + } + cm1 = min_col-1; + if (cm1 == -1) + { + cm1 = 0; + } + + // now search all adjacent nodes + if ( old_zeta[rp1][cp1] == NoDataValue) + { + cout << "Buffering, found ndv" << endl; + min_elev_nd_rows.push_back(rp1); + min_elev_nd_cols.push_back(cp1); + } + if ( old_zeta[rp1][min_col] == NoDataValue) + { + cout << "Buffering, found ndv" << endl; + min_elev_nd_rows.push_back(rp1); + min_elev_nd_cols.push_back(min_col); + } + if ( old_zeta[rp1][cm1] == NoDataValue) + { + cout << "Buffering, found ndv" << endl; + min_elev_nd_rows.push_back(rp1); + min_elev_nd_cols.push_back(cm1); + } + if ( old_zeta[min_row][cp1] == NoDataValue) + { + cout << "Buffering, found ndv" << endl; + min_elev_nd_rows.push_back(min_row); + min_elev_nd_cols.push_back(cp1); + } + if ( old_zeta[min_row][cm1] == NoDataValue) + { + cout << "Buffering, found ndv" << endl; + min_elev_nd_rows.push_back(min_row); + min_elev_nd_cols.push_back(cm1); + } + if ( old_zeta[rm1][cp1] == NoDataValue) + { + cout << "Buffering, found ndv" << endl; + min_elev_nd_rows.push_back(rm1); + min_elev_nd_cols.push_back(cm1); + } + if ( old_zeta[rm1][min_col] == NoDataValue) + { + cout << "Buffering, found ndv" << endl; + min_elev_nd_rows.push_back(rm1); + min_elev_nd_cols.push_back(min_col); + } + if ( old_zeta[rm1][cm1] == NoDataValue) + { + cout << "Buffering, found ndv" << endl; + min_elev_nd_rows.push_back(rm1); + min_elev_nd_cols.push_back(cm1); + } + + // Now crinkle up the side + for(int row = 0; row< NRows; row++) + { + for(int col = 0; colRasterData = zeta.copy(); + + RasterData = zeta.copy(); +} + + + +void LSDRasterMaker::buffer_basin_to_single_outlet(float slope) +{ + Array2D zeta=RasterData.copy(); + + vector edge_rows; + vector edge_cols; + + float elev_diff = DataResolution*sqrt(2)*slope; + + // we look for any edge nodes. We don't look along the edge of the DEM + // any node that is not itself nodata but has an edge pixel as nodata + // gets added as an edge node + int rp1,rm1,cp1,cm1; + bool is_edge_node; + int min_edge_row = 0; + int min_edge_col = 0; + float min_edge_elev = 99999999999999; + for(int row = 1; row < NRows-1; row++) + { + for(int col = 1; col < NCols-1; col++) + { + if (zeta[row][col] != NoDataValue) + { + is_edge_node = false; + rp1 = row+1; + rm1 = row-1; + cp1 = col+1; + cm1 = col-1; + + // now search all adjacent nodes + if ( zeta[rp1][cp1] == NoDataValue) + { + is_edge_node = true; + } + if ( zeta[rp1][col] == NoDataValue) + { + is_edge_node = true; + } + if ( zeta[rp1][cm1] == NoDataValue) + { + is_edge_node = true; + } + if ( zeta[row][cp1] == NoDataValue) + { + is_edge_node = true; + } + if ( zeta[row][cm1] == NoDataValue) + { + is_edge_node = true; + } + if ( zeta[rm1][cp1] == NoDataValue) + { + is_edge_node = true; + } + if ( zeta[rm1][col] == NoDataValue) + { + is_edge_node = true; + } + if ( zeta[rm1][cm1] == NoDataValue) + { + is_edge_node = true; + } + + if (is_edge_node) + { + edge_rows.push_back(row); + edge_cols.push_back(col); + + if (min_edge_elev > zeta[row][col]) + { + min_edge_elev = zeta[row][col]; + min_edge_row = row; + min_edge_col = col; + } + } + } + } + } + + + // Okay, now look through the edge rows and columns, buffering up the nodes. + int n_edge_nodes = int(edge_rows.size()); + int row,col; + for(int i = 0; i< n_edge_nodes; i++) + { + row = edge_rows[i]; + col = edge_cols[i]; + + rp1 = row+1; + rm1 = row-1; + cp1 = col+1; + cm1 = col-1; + + if (row == min_edge_row && col == min_edge_col) + { + cout << "Found the minimum elevation edge node." << endl; + } + else + { + // now search all adjacent nodes + if ( zeta[rp1][cp1] == NoDataValue) + { + zeta[rp1][cp1] = zeta[row][col]+elev_diff; + } + if ( zeta[rp1][col] == NoDataValue) + { + zeta[rp1][col] = zeta[row][col]+elev_diff; + } + if ( zeta[rp1][cm1] == NoDataValue) + { + zeta[rp1][cm1] = zeta[row][col]+elev_diff; + } + if ( zeta[row][cp1] == NoDataValue) + { + zeta[row][cp1] = zeta[row][col]+elev_diff; + } + if ( zeta[row][cm1] == NoDataValue) + { + zeta[row][cm1] = zeta[row][col]+elev_diff; + } + if ( zeta[rm1][cp1] == NoDataValue) + { + zeta[rm1][cp1] = zeta[row][col]+elev_diff; + } + if ( zeta[rm1][col] == NoDataValue) + { + zeta[rm1][col] = zeta[row][col]+elev_diff; + } + if ( zeta[rm1][cm1] == NoDataValue) + { + zeta[rm1][cm1] = zeta[row][col]+elev_diff; + } + } + } + + this->RasterData = zeta.copy(); + + RasterData = zeta.copy(); +} + +////------------------------------------------------------------------------------ +//// impose_channels: this imposes channels onto the landscape +//// You need to print a channel to csv and then load the data +////------------------------------------------------------------------------------ +void LSDRasterMaker::impose_channels_with_buffer(LSDSpatialCSVReader& source_points_data, float slope, string column_name) +{ + + // string column_name = "elevation(m)"; + + float elev_diff = DataResolution*sqrt(2)*slope; + Array2D zeta=RasterData.copy(); // Step one, create donor "stack" etc. via FlowInfo LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); - // need to fill the raster to ensure there are no internal base level nodes - cout << "I am going to fill" << endl; - float slope_for_fill = 0.0001; - cout << "Filling." << endl; - LSDRaster filled_topography = temp.fill(slope_for_fill); + // Get the local coordinate as well as the elevations + vector UTME, UTMN; + source_points_data.get_x_and_y_from_latlong(UTME,UTMN); + vector elev = source_points_data.data_column_to_float(column_name); + + // find the minimum elevation + float min_elev = 10000000000; + int node_of_min_elev = 0; + for(int i = 0; i< int(elev.size()); i++) + { + if(elev[i] < min_elev) + { + min_elev = elev[i]; + node_of_min_elev = i; + } + } + + cout << "The minimum elevation is: " << min_elev << endl; - vector bc(4, "b"); // Initialise boundaries to baselevel + // make the map + cout << "I am making an elevation map. This will not work if points in the raster lie outside of the csv channel points." << endl; + int row,col; + int rp1,rm1,cp1,cm1; + for(int i = 0; i< int(elev.size()); i++) + { + + source_points_data.get_row_and_col_of_a_point(UTME[i],UTMN[i],row, col); + zeta[row][col] = elev[i]; - cout << "Getting the flow info. This might take some time." << endl; - LSDFlowInfo flow(bc, filled_topography); - // update the raster - zeta = filled_topography.get_RasterData(); + if (i != node_of_min_elev) + { + // now search neighbouring nodes for nodata + rp1 = row+1; + if(rp1 == NRows) + { + rp1 = row; + } + rm1 = row-1; + if (rm1 == -1) + { + rm1 = 0; + } + cp1 = col+1; + if (cp1 == NCols) + { + cp1 = col; + } + cm1 = col-1; + if (cm1 == -1) + { + cm1 = 0; + } - // Get the local node index as well as the elevations - vector ni = source_points_data.get_nodeindices_from_lat_long(flow); + // now search all adjacent nodes + if ( zeta[rp1][cp1] == NoDataValue) + { + zeta[rp1][cp1] = elev[i]+elev_diff; + } + if ( zeta[rp1][col] == NoDataValue) + { + zeta[rp1][col] = elev[i]+elev_diff; + } + if ( zeta[rp1][cm1] == NoDataValue) + { + zeta[rp1][cm1] = elev[i]+elev_diff; + } + if ( zeta[row][cp1] == NoDataValue) + { + zeta[row][cp1] = elev[i]+elev_diff; + } + if ( zeta[row][cm1] == NoDataValue) + { + zeta[row][cm1] = elev[i]+elev_diff; + } + if ( zeta[rm1][cp1] == NoDataValue) + { + zeta[rm1][cp1] = elev[i]+elev_diff; + } + if ( zeta[rm1][col] == NoDataValue) + { + zeta[rm1][col] = elev[i]+elev_diff; + } + if ( zeta[rm1][cm1] == NoDataValue) + { + zeta[rm1][cm1] = elev[i]+elev_diff; + } + } + } + + + this->RasterData = zeta.copy(); + + RasterData = zeta.copy(); +} + +////------------------------------------------------------------------------------ +//// impose_channels: this imposes channels onto the landscape using XY data +//// You need to print a channel to csv and then load the data +//// ELSG 23/02/2021 +////------------------------------------------------------------------------------ +void LSDRasterMaker::impose_channels_with_buffer_use_XY(LSDSpatialCSVReader& source_points_data, float slope, string column_name) +{ + + // string column_name = "elevation(m)"; + string x_column_name = "X"; + string y_column_name = "Y"; + + float elev_diff = DataResolution*sqrt(2)*slope; + + + Array2D zeta=RasterData.copy(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + + // Get the local coordinate as well as the elevations + vector X = source_points_data.data_column_to_float(x_column_name); + vector Y = source_points_data.data_column_to_float(y_column_name); + //source_points_data.get_x_and_y_from_latlong(X,Y); vector elev = source_points_data.data_column_to_float(column_name); + + // find the minimum elevation + float min_elev = 10000000000; + int node_of_min_elev = 0; + for(int i = 0; i< int(elev.size()); i++) + { + if(elev[i] < min_elev) + { + min_elev = elev[i]; + node_of_min_elev = i; + } + } + + cout << "The minimum elevation is: " << min_elev << endl; + + // make the map cout << "I am making an elevation map. This will not work if points in the raster lie outside of the csv channel points." << endl; int row,col; - for(int i = 0; i< int(ni.size()); i++) + int rp1,rm1,cp1,cm1; + for(int i = 0; i< int(elev.size()); i++) { - - flow.retrieve_current_row_and_col( ni[i], row, col); - // cout << "I am in row " << row << " and col " << col << endl; + cout << "At i " << i << ", X is: " << X[i] << ", and Y is: " << Y[i] << "; elevation is: " << elev[i] << endl; + source_points_data.get_row_and_col_of_a_point(X[i],Y[i],row, col); + cout << "Row is: " << row << ", and col is: " << col << endl; zeta[row][col] = elev[i]; + + if (i != node_of_min_elev) + { + // now search neighbouring nodes for nodata + rp1 = row+1; + if(rp1 == NRows) + { + rp1 = row; + } + rm1 = row-1; + if (rm1 == -1) + { + rm1 = 0; + } + cp1 = col+1; + if (cp1 == NCols) + { + cp1 = col; + } + cm1 = col-1; + if (cm1 == -1) + { + cm1 = 0; + } + + // now search all adjacent nodes + if ( zeta[rp1][cp1] == NoDataValue) + { + zeta[rp1][cp1] = elev[i]+elev_diff; + } + if ( zeta[rp1][col] == NoDataValue) + { + zeta[rp1][col] = elev[i]+elev_diff; + } + if ( zeta[rp1][cm1] == NoDataValue) + { + zeta[rp1][cm1] = elev[i]+elev_diff; + } + if ( zeta[row][cp1] == NoDataValue) + { + zeta[row][cp1] = elev[i]+elev_diff; + } + if ( zeta[row][cm1] == NoDataValue) + { + zeta[row][cm1] = elev[i]+elev_diff; + } + if ( zeta[rm1][cp1] == NoDataValue) + { + zeta[rm1][cp1] = elev[i]+elev_diff; + } + if ( zeta[rm1][col] == NoDataValue) + { + zeta[rm1][col] = elev[i]+elev_diff; + } + if ( zeta[rm1][cm1] == NoDataValue) + { + zeta[rm1][cm1] = elev[i]+elev_diff; + } + } } @@ -270,6 +984,28 @@ void LSDRasterMaker::impose_channels(LSDSpatialCSVReader& source_points_data) + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// Cap elevations using an initial raster +//------------------------------------------------------------------------------ +void LSDRasterMaker::cap_elevations(LSDRaster& InitialRaster) +{ + cout << "Capping elevations. WARNING: no checking rasters are same dimension" << endl; + for(int row=0; row InitialRaster.get_data_element(row,col)) + { + RasterData[row][col] = InitialRaster.get_data_element(row,col); + } + } + } + } +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // This smooths the raster by taking a weighted average of the given pixel // and neighboring pixels. diff --git a/src/LSDRasterMaker.hpp b/src/LSDRasterMaker.hpp index 24325e7..869226c 100644 --- a/src/LSDRasterMaker.hpp +++ b/src/LSDRasterMaker.hpp @@ -97,6 +97,16 @@ class LSDRasterMaker: public LSDRaster /// @date 01/09/2017 void resize_and_reset( int new_rows, int new_cols, float new_resolution, float new_value ); + /// @brief Gets the row and column of a point in the raster + /// @param X_coordinate the x location of the point + /// @param Y_coordinate the y location of the point + /// @param row the row of the point, replaced upon running the routine + /// @param col the col of the point, replaced upon running the routine + /// @author SMM + /// @date 22/01/2016 + void get_row_and_col_of_a_point(float X_coordinate,float Y_coordinate,int& row, int& col); + void get_row_and_col_of_a_point(double X_coordinate,double Y_coordinate,int& row, int& col); + /// @brief Gets the minimum and maximum values from a raster /// @return vector with the first element is the minimum and second is the maximum /// @author SMM @@ -109,6 +119,21 @@ class LSDRasterMaker: public LSDRaster /// @date 18/11/2018 void set_to_constant_value(float new_value); + /// @brief This just adds elevation to any non nodata pixel + /// @param value_to_add The value to add. To subtract, this will be negative. + /// @author SMM + /// @date 09/02/2021 + void add_value(float value_to_add); + + /// @brief Adds a strip of values to the raster + /// @param start_row_or_col the starting row or column + /// @param end_row_or_col the ending row or column + /// @param use_rows if true this creates horizontal strips + /// @param value the new value of the strip + /// @author SMM + /// @date 11/11/2020 + void add_strip(int start_row_or_col, int end_row_or_col, bool horizontal, float value); + /// @brief This linearly scales the raster to new minimum and maximum values /// @param new_minimum does what it says on the tin. /// @param new_maxuimum does what it says on the tin @@ -119,11 +144,46 @@ class LSDRasterMaker: public LSDRaster /// @brief This fixes a channel, derived from source points data /// onto the model DEM /// @param source_points_data an LSDSpatialCSVReader object. It needs lat and long and elevation columns + /// @param column_name the name of the elevation column /// @author SMM /// @date 04/03/2020 - void impose_channels(LSDSpatialCSVReader& source_points_data); + void impose_channels(LSDSpatialCSVReader& source_points_data, string column_name); + + /// @brief This fixes a channel, derived from source points data. It also makes sure none of these nodes is + /// adjacent to a nodata pixel + /// @param source_points_data an LSDSpatialCSVReader object. It needs lat and long and elevation columns + /// @param slope the slope between the data point and the adjacent pixel + /// @param column_name the name of the elevation column + /// @author SMM + /// @date 21/11/2020 + void impose_channels_with_buffer(LSDSpatialCSVReader& source_points_data, float slope, string column_name); + + /// @brief This fixes a channel, derived from source points data. It also makes sure none of these nodes is + /// adjacent to a nodata pixel. Same as the above function, but uses XY data instead of lat-long + /// @param source_points_data an LSDSpatialCSVReader object. It needs XY and elevation columns + /// @param slope the slope between the data point and the adjacent pixel + /// @param column_name the name of the elevation column + /// @author ELSG + /// @date 23/02/2021 + void impose_channels_with_buffer_use_XY(LSDSpatialCSVReader& source_points_data, float slope, string column_name); + + /// @brief This function adds elevation to the nodata pixels around the edge of a DEM + /// It is used to crinkle up the edge of of a raster to ensure drainage + /// @param slope the slope between the data point and the adjacent pixel + /// @param source_points_data this gives a channel that defines the outlet + /// @author SMM + /// @date 19/03/2021 + void buffer_basin_to_single_outlet(LSDSpatialCSVReader& source_points_data, float slope); + /// @brief This function adds elevation to the nodata pixels around the edge of a DEM + /// it is used on a DEM derived from a basin to ensure internal drainage to + /// a single outlet + /// @param slope the slope between the data point and the adjacent pixel + /// @author SMM + /// @date 10/02/2020 + void buffer_basin_to_single_outlet(float slope); + /// @brief This smooths the raster. At some point in the future I'll /// add more options but at the moment it just uses 4 neighbours and has /// double weighting on the central pixel. It assumes periodic boundaries @@ -133,6 +193,12 @@ class LSDRasterMaker: public LSDRaster /// @date 03/09/2017 void smooth(int boundary_type); + /// @brief Caps elevations using the initial raster + /// WARNING no testing if the raster is the correct shape! + /// @param InitialRaster The initial raster above which the new surface cannot rise. + /// @author SMM + /// @date 31/08/2020 + void cap_elevations(LSDRaster& InitialRaster); //void random_horizontal_strips(int minimum_strip_size, int maximum_strip_size, float minimum_value, float maximum_value); diff --git a/src/LSDRasterModel.cpp b/src/LSDRasterModel.cpp index fee4e9d..b8f792c 100644 --- a/src/LSDRasterModel.cpp +++ b/src/LSDRasterModel.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -241,7 +242,6 @@ void LSDRasterModel::default_parameters( void ) set_S_c( 1.0 ); // 45 degrees, slope of 1 set_print_interval( 10 ); // number of timesteps - set_float_print_interval (5000); // this is in years set_next_printing_time (0); set_steady_state_tolerance( 0.00001 ); @@ -297,6 +297,25 @@ void LSDRasterModel::set_raster_data(LSDRaster& Raster) } +LSDRaster LSDRasterModel::make_constant_raster(float value) +{ + + Array2D temp_data = RasterData.copy(); + for(int row = 0; row< NRows; row++) + { + for (int col = 0; col < NCols; col++) + { + temp_data[row][col]= value; + } + } + + LSDRaster NewRaster(NRows, NCols, XMinimum, YMinimum, + DataResolution, NoDataValue, temp_data, + GeoReferencingStrings); + return NewRaster; + +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // This adds a path to the run name and the report name //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -318,6 +337,210 @@ void LSDRasterModel::add_path_to_names( string pathname) } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// Some tools for transient model runs +// The phase_rates are derived from elevations through time. +// These you obtain from the function +// LSDRasterModel::calculate_phase_rates_from_elevations +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +float LSDRasterModel::calculate_bl_drop_rate( vector phase_start, vector phase_rates) +{ + + int n_phases = int(phase_start.size()); + // get the current time + + float this_rate; + if (n_phases == 1) + { + this_rate = phase_rates[0]; + } + else + { + if (current_time >= phase_start[n_phases-1]) + { + this_rate = phase_rates[n_phases-1]; + } + else + { + bool not_got_rate = true; + int phase = 0; + do + { + if (current_time >= phase_start[phase] && current_time < phase_start[phase+1]) + { + not_got_rate = false; + this_rate = phase_rates[phase]; + } + else + { + phase++; + } + + } while (not_got_rate); + } + } + return this_rate; + +} + + +float LSDRasterModel::calculate_bl_drop_rate_from_elevations( vector phase_start, vector phase_elevations) +{ + + int n_phases = int(phase_start.size()); + // get the current time + + float this_rate; + if (n_phases == 1) + { + cout << "You need an initial elevation and a final elevation. You only have one data points. FATAL" << endl; + cout << "Check your transient baselevel fall file." << endl; + exit(0); + } + else + { + if (current_time >= phase_start[n_phases-1]) + { + // Negative is because lowering elevations are positive in our convention + this_rate = -(phase_elevations[n_phases-1]-phase_elevations[n_phases-2])/(phase_start[n_phases-1]-phase_start[n_phases-2]); + } + else + { + bool not_got_rate = true; + int phase = 0; + do + { + if (current_time >= phase_start[phase] && current_time < phase_start[phase+1]) + { + not_got_rate = false; + // Negative is because lowering elevations are positive in our convention + this_rate = -(phase_elevations[phase+1]-phase_elevations[phase])/(phase_start[phase+1]-phase_start[phase]); + } + else + { + phase++; + } + + } while (not_got_rate); + } + } + return this_rate; + +} + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This gets a vecvec of drop rates. +// The convention here is that if the base level is lowering, +// the rate is positive. +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +vector< vector > LSDRasterModel::calculate_phase_rates_from_elevations( vector phase_start, vector< vector > phase_elevations) +{ + int n_phases = int(phase_start.size()); + int n_nodes = int( phase_elevations[0].size() ); + float dt; + + vector empty_vec; + vector rate_vec; + vector last_elev; + vector next_elev; + vector< vector > rate_vecvec; + + if (n_phases == 1) + { + cout << "I can't run the channel elevation rate extration routine with only one phase." << endl; + cout << "Exiting" << endl; + exit(0); + } + for(int i = 0; i< n_phases-1; i++) + { + last_elev= phase_elevations[i]; + next_elev = phase_elevations[i+1]; + + dt = phase_start[i+1]-phase_start[i]; + cout << "Phase " << i << ", dt: " << dt << endl; + + rate_vec = empty_vec; + + for (int n = 0; n LSDRasterModel::calculate_bl_drop_rate( vector phase_start, vector< vector > phase_rates) +{ + + //cout << "This is the vecvec version of the routine. " << endl; + int n_phases = int(phase_start.size()); + int n_nodes = int( phase_rates[0].size() ); + int accepted_phase = 0; + + //cout << "N phases: " << n_phases << " and n_nodes: " << n_nodes << endl; + vector zero_vec(n_nodes,0.0); + vector rate_vec; + + // get the current time + float this_rate; + if (n_phases == 1) + { + rate_vec = zero_vec; + } + else + { + if (current_time >= phase_start[n_phases-1]) + { + rate_vec = phase_rates[n_phases-1]; + accepted_phase = n_phases-1; + } + else + { + bool not_got_rate = true; + int phase = 0; + do + { + if (current_time >= phase_start[phase] && current_time < phase_start[phase+1]) + { + not_got_rate = false; + rate_vec = phase_rates[phase]; + accepted_phase = phase; + } + else + { + phase++; + } + + } while (not_got_rate); + } + } + //cout << "This time is " << current_time << " and the phase is: " << accepted_phase << endl; + return rate_vec; + +} + + + + + + + + + + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // INITIALISATION MODULE //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -616,6 +839,44 @@ void LSDRasterModel::random_surface_noise() } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// Similar to above, but uses the noise parameter stored in the object +// but understands nodata and you pass it a seed +// SMM 24/6/2021 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void LSDRasterModel::random_surface_noise(long seed) +{ + // check on the noise data member. It needs to be bigger than 10-6) + // if not set 1 mm as default + if (noise < 0.000001) + { + noise = 0.001; + } + + // we set the min and max between zero and noise. We don't go between + // -noise/2 and noise/2 just because we don't want negative elevations near + // a base level node. + float min = 0; + float max = noise; + + + // Add random float to each pixel + for(int row = 0; row < NRows; row++) + { + for (int col = 0; col < NCols; col++) + { + if (RasterData[row][col] != NoDataValue) + { + RasterData[row][col] += ran3(&seed)*(max-min) + min; + } + } + } +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // initializes a parabolic surface with elevations on the north and south edges at zero and // elevation in the middle of 'peak elevation' @@ -1030,21 +1291,27 @@ void LSDRasterModel::raise_and_fill_raster() { for(int col = 0; colget_RasterData(); + delete temp; +} + + + +void LSDRasterModel::add_fixed_elevation(float adjustment) +{ + Array2D zeta=RasterData.copy(); + + for(int row = 0; row zeta=RasterData.copy(); + + for(int row = 0; row zeta=RasterData.copy(); @@ -1900,11 +2244,11 @@ void LSDRasterModel::impose_channels(LSDSpatialCSVReader& source_points_data) //vector ni = source_points_data.get_nodeindices_from_lat_long(flow); vector elev = source_points_data.data_column_to_float(column_name); // make the map - cout << "I am making an elevation map. This will not work if points in the raster lie outside of the csv channel points." << endl; + //cout << "I am making an elevation map. This will not work if points in the raster lie outside of the csv channel points." << endl; int row,col; for(int i = 0; i< int(elev.size()); i++) { - + source_points_data.get_row_and_col_of_a_point(UTME[i],UTMN[i],row, col); if(zeta[row][col] == NoDataValue) @@ -1919,9 +2263,6 @@ void LSDRasterModel::impose_channels(LSDSpatialCSVReader& source_points_data) zeta[row][col] = elev[i]; } - - this->RasterData = zeta.copy(); - RasterData = zeta.copy(); } @@ -1930,35 +2271,113 @@ void LSDRasterModel::impose_channels(LSDSpatialCSVReader& source_points_data) //// impose_channels: this imposes channels onto the landscape //// You need to print a channel to csv and then load the data ////------------------------------------------------------------------------------ -void LSDRasterModel::impose_channels_and_lift_raster(LSDSpatialCSVReader& source_points_data) +void LSDRasterModel::impose_channels(LSDSpatialCSVReader& source_points_data, string column_name, vector bl_code) { - string column_name = "elevation(m)"; - + // string column_name = "elevation(m)"; + Array2D zeta=RasterData.copy(); // Get the local node index as well as the elevations vector UTME, UTMN; source_points_data.get_x_and_y_from_latlong(UTME,UTMN); - vector elev = source_points_data.data_column_to_float(column_name); - // find minimum values - float min_chan_elev = 9999999999; - for(int i = 0;i< int(elev.size()); i++) + // check to see if bl_code vector makes sense + if ( UTME.size() != bl_code.size()) { - if (min_chan_elev > elev[i]) - { - min_chan_elev = elev[i]; - } + cout << "Fatal error, the baselevel code vector is not the same size as the number of nodes in the channel points file." << endl; + exit(0); } - float min_rast_elev = 999999999; - for(int row = 0; row ni = source_points_data.get_nodeindices_from_lat_long(flow); + vector elev = source_points_data.data_column_to_float(column_name); + // make the map + //cout << "I am making an elevation map. This will not work if points in the raster lie outside of the csv channel points." << endl; + int row,col; + + + //cout << "IMPOSING CHANNELS, the crazy node has an elevation of: " << zeta[617][761] << endl; + + float zurich_crazy_node_elev = zeta[617][761]; + + for(int i = 0; i< int(elev.size()); i++) + { + + + + source_points_data.get_row_and_col_of_a_point(UTME[i],UTMN[i],row, col); + + //if (i == 0) + //{ + // cout << "Outlet, elevation is: " << elev[i] << " and bl_code is " << bl_code[i] << endl; + // cout << "r,c of outlet is: " << row << "," << col << endl; + //} + + if(row == 617 && col == 761) + { + cout << endl << endl << "==================================================" << endl; + cout << "WARNING" << endl; + cout << "In the NAGRA zurich scenario the pixel at 617,761 does crazy things that have no explanation." << endl; + cout << "I have to force the elevation of this pixel." << endl; + cout << "If you are getting this message it means you might get some breakage in your imposed channel" << endl; + cout << "==================================================" << endl << endl; + + } + + if(zeta[row][col] == NoDataValue) + { + cout << "Imposing channels, I seem to be on a nodata node. Check your single channel data." << endl; + } + else if (bl_code[i] == 0) + { + //cout << "Setting BL node to " << elev[i] << endl; + zeta[row][col] = elev[i]; + } + } + + + //cout << "DONE IMPOSING CHANNELS, the crazy node has an elevation of: " << zeta[617][761] << endl; + //cout << "FORCING THE ZURICH CRAZY NODE." << endl; + zeta[617][761] = zurich_crazy_node_elev; + + RasterData = zeta.copy(); +} + + +////------------------------------------------------------------------------------ +//// impose_channels: this imposes channels onto the landscape +//// You need to print a channel to csv and then load the data +////------------------------------------------------------------------------------ +void LSDRasterModel::impose_channels_and_lift_raster(LSDSpatialCSVReader& source_points_data, string column_name) +{ + + // string column_name = "elevation(m)"; + + Array2D zeta=RasterData.copy(); + + // Get the local node index as well as the elevations + vector UTME, UTMN; + source_points_data.get_x_and_y_from_latlong(UTME,UTMN); + vector elev = source_points_data.data_column_to_float(column_name); + + + // find minimum values + float min_chan_elev = 9999999999; + for(int i = 0;i< int(elev.size()); i++) + { + if (min_chan_elev > elev[i]) + { + min_chan_elev = elev[i]; + } + } + + float min_rast_elev = 999999999; + for(int row = 0; row zeta[row][col] ) { @@ -2016,7 +2435,7 @@ void LSDRasterModel::impose_channels_and_lift_raster(LSDSpatialCSVReader& source //// channel network to a CSV file as well as returning a csvreader object //// that can be used to burn raster values onto this csv. ////------------------------------------------------------------------------------ -LSDSpatialCSVReader LSDRasterModel::get_channels_for_burning(int contributing_pixels) +LSDSpatialCSVReader LSDRasterModel::get_channels_for_burning(int contributing_pixels, string temp_channel_path, string temp_channel_fname) { string column_name = "elevation(m)"; @@ -2047,8 +2466,8 @@ LSDSpatialCSVReader LSDRasterModel::get_channels_for_burning(int contributing_pi LSDJunctionNetwork ChanNetwork(sources, FlowInfo); // print the network - string chan_fname = "./temp_channels"; - string full_chan_fname = "./temp_channels.csv"; + string chan_fname = temp_channel_path+temp_channel_fname; + string full_chan_fname = temp_channel_path+temp_channel_fname+".csv"; ChanNetwork.PrintChannelNetworkToCSV_WithElevation(FlowInfo, chan_fname,filled_topography); // Now load a csv object @@ -2114,6 +2533,44 @@ Array2D LSDRasterModel::calculate_erosion_rates( void ) } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// CALCULATE EROSION RATES +// Simple function that creates an array with the erosion rates for a given +// timestep, calculated by diffencing elevation rasters with consecutive +// timesteps. +// Uses an uplift raster instead of calculating from a model uplift field +//------------------------------------------------------------------------------ +Array2D LSDRasterModel::calculate_erosion_rates( LSDRaster& Uplift_raster ) +{ + // create the erosion array + Array2D ErosionRateArray(NRows,NCols,NoDataValue); + + // first check to see if zeta_old exists + if (zeta_old.dim1() != NRows || zeta_old.dim2() != NCols) + { + cout << "LSDRasterModel::calculate_erosion_rates, WARNING zeta_old doesn't exist" << endl; + } + else + { + // loop through all the raster data getting erosion rate using the + // get_erosion_at_cell data member + for(int row=0; row& CRNColumns, - vector& eroded_cells, - int startType, double startDepth, double particle_spacing, - LSDCRNParameters& CRNParams) +void LSDRasterModel::run_components_combined_imposed_baselevel( LSDRaster& URaster, LSDRaster& KRaster, + bool use_adaptive_timestep, LSDSpatialCSVReader& source_points_data, + vector phase_time, vector phase_rates, string column_name) { + //recording = false; + cycle_number = 1; + total_erosion = 0; + max_erosion = 0; + min_erosion = -99; + switch_delay = 0; + time_delay = 0; - // first you need to get the locations of the columns - int N_pcolumns = CRNColumns.size(); - vector row_vec; - vector col_vec; + stringstream ss, ss_root; - vector e_cells(N_pcolumns); + // some fields for uplift and fluvial incision + Array2D uplift_field; + Array2D fluvial_incision_rate_field; - for (int i = 0; i next_time) + { + cout << "Your rate comes from the future! current_time > next_time. " << endl; + cout << "I don't know how to deal with this and need to exit. " << endl; + exit(0); + } + + bool let_timestep_increase; do { + + // Get the current baselevel fall rate + float fall_rate = calculate_bl_drop_rate( phase_time, phase_rates); + + //if (timestep_counter % 100 == 0) + //{ + // cout << "Time: " << current_time << " and fall rate: " << fall_rate << endl; + //} + + // Record current topography zeta_old = RasterData.copy(); @@ -3608,18 +4112,17 @@ void LSDRasterModel::run_components_combined_cell_tracker( vector= next_time-timeStep) + { + cout << "Updating the next time." << endl; + cout << "current time: " << current_time << " with timestep " << timeStep << endl; + timeStep = next_time - current_time; + cout << "Updated timeStep: " << timeStep << endl; - double this_zeta_old = zeta_old[row][col]; - double this_zeta_new = RasterData[row][col]; + if (current_time > next_time) + { + cout << "WARNING: I have overshot!!" << endl; + } + next_time_ticker++; + if(next_time_ticker > n_phases -1) + { + next_time = endTime+10*timeStep; + } + else + { + next_time = phase_time[next_time_ticker]; + } + + } - //double test = RasterData[row][col]; - //cout << "zo " << this_zeta_old << " RD: " << RasterData[row][col] << endl; - //test = 5; - //cout << "test is: " << test << " RD: " << RasterData[row][col] << endl; + // now see if the time has exceeded the next print time + if (current_time > next_printing_time) + { + do + { + next_printing_time+=float_print_interval; + } while(next_printing_time < current_time); + print_rasters_and_csv( frame ); + ++frame; + } + if (quiet == false) cout << "\rTime: " << current_time << " years" << flush; + } while (check_end_condition() == false); - double this_uplift_rate = get_uplift_rate_at_cell(row,col); + // reset the current frame + current_frame = frame; +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - //cout << "\n\n\nCRN at [" << row << "]["< 0 && (print % print_interval) == 0) - { - // calculate erosion rate and place it in the erosion data member for - // raster printing - erosion = get_total_erosion_rate_over_timestep(); - print_rasters( frame ); - print_average_erosion_and_apparent_erosion( frame, CRNColumns, CRNParams); - //print_column_erosion_and_apparent_erosion( frame, CRNColumns, CRNParams); - ++frame; - } - if ( quiet == false) cout << "\rTime: " << current_time << " years" << flush; - ++print; - //cout << "Line 2693, data[10][10]: " << RasterData[10][10] << endl; - // check to see if steady state has been achieved - //check_steady_state(); - } while ( check_end_condition() == false); - eroded_cells = e_cells; -} -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -// This function intiates a vector of LSDParticleColumns at steady state -// the zeta values are distrubuted over the surface +// This is a wrapper function used to drive a model run +// it checks parameters and flags from the data members +// +// Similar to run_componets but instead of running fluvial +// and uplift in seperate steps it inserts the fluvial +// and uplift matrices into the nonlinear hillslope +// solver. +// +// This version allows variable K and U rasters to be used. +// You can also switch on an adaptive timestep +// +// It also allows an imposed base level +// //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -vector LSDRasterModel::initiate_steady_CRN_columns(int column_spacing, - vector& CRNcol_rows, vector& CRNcol_cols, - double rho_r, double this_U, int startType, double startDepth, - double particle_spacing, LSDCRNParameters& CRNParam) +void LSDRasterModel::run_components_combined_imposed_baselevel( LSDRaster& URaster, LSDRaster& KRaster, + bool use_adaptive_timestep, LSDSpatialCSVReader& source_points_data, + vector phase_time, vector< vector > phase_elevations, + float minimum_slope, string column_name) { - cout << "Initialising some CRN columns." << endl; + //recording = false; + cycle_number = 1; + total_erosion = 0; + max_erosion = 0; + min_erosion = -99; + switch_delay = 0; + time_delay = 0; - vector CRNParticleColumns_vec; + stringstream ss, ss_root; - // get the number of particle rows and columns - // note there are less rows since these are boundary nodes that - // have zero erosion and are not included - int NPRows = (NRows-2)/column_spacing+1; - int NPCols = NCols/column_spacing+1; - cout << "Rows: " << NRows << " and NPRows: " << NPRows << endl; - cout << "Cols: " << NCols << " and NPCols: " << NPCols << endl; - vector rows_vec; - vector cols_vec; + // some fields for uplift and fluvial incision + Array2D uplift_field; + Array2D fluvial_incision_rate_field; - int this_row, this_col; - double this_x,this_y; - double zeta_at_cell; - double eff_U = rho_r*this_U/10; // this converts the uplift to effective - // uplift in units g/cm^2/yr, - // required by the cosmo particles + // set the frame to the current frame plus 1 + int timestep_counter = 0; // for debugging + int frame = current_frame; - // loop through the columns, creating steady particles - for (int prow = 0; prow > phase_rates_vecvec = calculate_phase_rates_from_elevations( phase_time, phase_elevations); + cout << "Okay, I have got the phase rates. " << endl; + cout << "There are " << n_phases << " phases." << endl; + cout << "confirming with the vecvec, which has " << phase_rates_vecvec.size() << " phases." << endl; + + // This fixes the first timestep + bool fix_step_0 = true; + if (fix_step_0) { - this_row = prow*column_spacing+1; - for (int pcol = 0; pcol bl_code = source_points_data.data_column_to_int(bl_switch); + + phase_rates_vecvec[0] = phase_rates_vecvec[1]; + vector update_elev = phase_elevations[1]; + vector start_elev = phase_elevations[0]; + float phase_dt = phase_time[1] - phase_time[0]; + vector this_pr = phase_rates_vecvec[0]; + for(int i = 0; i< int(update_elev.size()); i++) { - this_col = pcol*column_spacing; - // make sure you dont go out of bounds - if(this_row >= NRows-1) + if( bl_code[i] == 0) // this is for places you use the reported elevations { - cout << "Danger, you were about to go out of the raster domain when making a CRN column\n"; - this_row = NRows-2; + update_elev[i] = update_elev[i]+this_pr[i]*phase_dt; } - if(this_col >= NCols) + else // for these nodes we only retain the area. This is tuned to actual data at Jura ost and isn't general { - cout << "Danger, you were about to go out of the raster domain when making a CRN column\n"; - this_col = NCols-1; + update_elev[i] = start_elev[i]+2.6; } + + } - // push back the row and col vec - rows_vec.push_back(this_row); - cols_vec.push_back(this_col); - - // get the x and y locations - this_x = double(this_col)*DataResolution+XMinimum; - this_y = double(NRows-this_row-1)*DataResolution+YMinimum; - - // initiate an empty column - LSDParticleColumn temp_col; - temp_col.set_Row_and_Col(this_row,this_col); - - // get the elevation of the surface - zeta_at_cell = float(RasterData[this_row][this_col]); - - // make a steady state column - //cout << "LINE 3032, initiating SS column" << endl; - temp_col.initiate_SS_cosmo_column_3CRN(startType, this_x, this_y, startDepth, particle_spacing, - zeta_at_cell, eff_U, CRNParam); - //cout << "LINE 3035, Initiated column" << endl; + string column_name = "elevation(m)"; + source_points_data.data_column_replace(column_name, update_elev); + } - // make sure this is a rock only simulation - double ST = 0; - temp_col.set_SoilThickness(ST); - temp_col.set_RockDensity(rho_r); + cout << "Starting the transient loop. " << endl; + cout << "The starting time is: " << current_time << endl; + cout << "The timestep is: " << timeStep << endl; - // add the column to the vector - CRNParticleColumns_vec.push_back(temp_col); - //cout << "Got col, row: " << this_row << " and col: " << this_col << endl; - } + float next_time; + int next_time_ticker = 1; + if (n_phases == 1) + { + next_time = endTime+10*timeStep; + } + else + { + next_time = phase_time[1]; } - //cout << "/n/nLine 2788, data[10][10]: " << RasterData[10][10] << endl; - CRNcol_rows = rows_vec; - CRNcol_cols = cols_vec; - return CRNParticleColumns_vec; -} + if (current_time > next_time) + { + cout << "Your rate comes from the future! current_time > next_time. " << endl; + cout << "I don't know how to deal with this and need to exit. " << endl; + exit(0); + } + cout << "The next time is: " << next_time << endl; -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -// This function simply calls the run_components function -// JAJ, sometime 2014 -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -void LSDRasterModel::run_model( void ) -{ - // file to write details of each time step - // This will be opened by the write_report method - int run = 1; - // loop through the number of runs. The number of runs is stored as a data member + + bool let_timestep_increase; do { - //initial_steady_state = false; // SMM: not clear why this is set to false - // SMM: I have turned this off because it was overriding the periodic forcing - recording = false; // SMM: not clear why this is set to false + // Get the current baselevel fall rate + cout << "Getting the fall rates." << endl; + vector fall_rate = calculate_bl_drop_rate( phase_time, phase_rates_vecvec); - if ( initialized == false && quiet == false) + int n_fr_nodes = int(fall_rate.size()); + cout << "The fall rates at 5 and 10 : " << fall_rate[5] << ", " << fall_rate[10] << endl; + //for (int n = 0; n= next_time-timeStep) + { + cout << endl << endl << "======================================================" << endl; + cout << "Updating the next time for the incision and/or uplift phasing." << endl; + cout << "current time: " << current_time << " with timestep " << timeStep << endl; + timeStep = next_time - current_time; + cout << "Updated timeStep: " << timeStep << endl; - if ( initialized == false && quiet == false) - { - cout << "Model has not been initialized with a parameter file." << endl; - cout << "All values used are defaults" << endl; - } + if (current_time > next_time) + { + cout << "WARNING: I have overshot!!" << endl; + } - // Generate random noise - random_surface_noise(0, noise); - // Fill the topography - LSDRaster *temp; - temp = new LSDRaster(*this); - float thresh_slope = 0.00001; - *temp = fill(thresh_slope); - RasterData = temp->get_RasterData(); - delete temp; + next_time_ticker++; + if(next_time_ticker > n_phases -1) + { + next_time = endTime+10*timeStep; + } + else + { + next_time = phase_time[next_time_ticker]; + } + + } - // Run model with some modest fluvial forcing - K_mode = 1; // THis means a sinusoidal variation in - // channel forcing - K_amplitude = K_fluv * 0.3; - endTime = 0; // SMM: not sure why this is set to zero. - cout << "Running to steady state, periodicity is: " << periodicity << endl; - //periodicity = 2000; - period_mode = 1; - cycle_steady_check = true; // this means that the steady state check - // is run based on erosion from one cycle - // to the next - print_interval = 0; - reporting = false; - if ( quiet == false) - { - cout << "Producing steady state profile" << endl; - } - // because the cycle_steady_check is true, it means that this will run - // through several periods until the surface does not change between cycles - - // switch the end time mode to be periodic - short endTimeModeSwap = endTime_mode; - endTime_mode = 2; + // now see if the time has exceeded the next print time + print_erosion = true; + if (current_time > next_printing_time) + { + do + { + next_printing_time+=float_print_interval; + } while(next_printing_time < current_time); + print_rasters_and_csv( frame ); + ++frame; + } + if (quiet == false) cout << "\rTime: " << current_time << " years" << flush; - // run components - run_components(); + } while (check_end_condition() == false); - if ( quiet == false) - { - cout << "Finished producing cyclic steady" << endl; - } + // reset the current frame + current_frame = frame; +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - // switch back to the original endTime_mode - endTime_mode = endTimeModeSwap; - // Now run with static forcing - K_mode = K_mode_swap; - D_mode = D_mode_swap; - K_amplitude = K_amp_swap; - endTime = timeStep*10; - cycle_steady_check = false; - initial_steady_state = false; - current_time = 0; - if ( quiet == false) cout << "Did cyclic steady, now running at constant forcing for a while." << endl; - cout << "Timestep is: " << timeStep << endl; - run_components(); - if ( quiet == false) cout << "Forced from constant steady state, exiting steady state routine." << endl; - endTime = endTime_swap; - periodicity = period_swap; - period_mode = period_mode_swap; - cycle_steady_check = false; - print_interval = print_interval_swap; - reporting = reporting_swap; - steady_state_data = Array2D (NRows, NCols, 0.0); - steady_state_data = RasterData; - // set the initial steady state flag to true - initial_steady_state = true; - cout << "Line 2526 Finished run_to_steady_state, initial_steady_state: " << initial_steady_state << endl; -} -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -void LSDRasterModel::soil_diffusion_fv( void ) -{ - static bool defined = false; - static int problem_dimension; - static float inv_dx_S_c_squared, inv_dy_S_c_squared, dx_front_term, dy_front_term; - static vector vec_k_value_i_j; - static vector vec_k_value_ip1_j; - static vector vec_k_value_im1_j; - static vector vec_k_value_i_jp1; - static vector vec_k_value_i_jm1; - static float iteration_tolerance = 0.01; - Array2D fluvial_temp(NRows, NCols, 0.0); - float south, north; - if (boundary_conditions[2][0] == 'b') - { - south = 0.0; - north = current_time*get_max_uplift(); +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This is a wrapper function used to drive a model run +// it checks parameters and flags from the data members +// +// Similar to run_componets but instead of running fluvial +// and uplift in seperate steps it inserts the fluvial +// and uplift matrices into the nonlinear hillslope +// solver. +// +// This version allows variable K and U rasters to be used. +// You can also switch on an adaptive timestep +// +// It also allows an imposed base level +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDRasterModel::run_components_combined_imposed_baselevel( LSDRaster& URaster, + bool use_adaptive_timestep, LSDSpatialCSVReader& source_points_data, + vector phase_time, vector< vector > phase_elevations, + LSDRaster& cumulative_uplift, + LSDLithoCube& LSDLC, list forbidden_lithocodes, + bool print_lithocode_raster, + bool use_hillslope_hybrid, + int threshold_contributing_pixels, + float minimum_slope, + bool print_exhumation_and_cumulative_uplift, + string column_name) +{ + + //cout << endl << endl << "I am checking the baselevel again LINE 4270" << endl; + //Array2D zeta=RasterData.copy(); + //LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); + //cout << "The nodatavalue is: " << NoDataValue << endl; + //LSDFlowInfo FI(boundary_conditions, temp); + //cout << "Number of BL nodes is: " << FI.get_NBaseLevelNodes() << endl << endl << endl; + + //recording = false; + cycle_number = 1; + total_erosion = 0; + max_erosion = 0; + min_erosion = -99; + switch_delay = 0; + time_delay = 0; - cout << south << ", " << north << endl; - } - else - { - cout << "Model currently not built to cope with hillslope diffusion using these boundary conditions" << endl; - cout << "Feature implementation required" << endl; - exit(1); - } + // These are for imposing all the lithocode information + LSDIndexRaster lithocodes_index_raster; + LSDRaster K_values; + LSDRaster Sc_values; + LSDRaster exhumation_surface; - if (defined == false) - { - mtl_initiate_assembler_matrix(problem_dimension, inv_dx_S_c_squared, inv_dy_S_c_squared, - dx_front_term, dy_front_term, vec_k_value_i_j, vec_k_value_ip1_j, - vec_k_value_im1_j, vec_k_value_i_jp1, vec_k_value_i_jm1); - } - defined = true; + stringstream ss, ss_root; - nonlinear_creep_timestep(fluvial_temp, iteration_tolerance, problem_dimension, - inv_dx_S_c_squared, inv_dy_S_c_squared, dx_front_term, dy_front_term, vec_k_value_i_j, - vec_k_value_ip1_j, vec_k_value_im1_j, vec_k_value_i_jp1, vec_k_value_i_jm1, - south, north); -} + // some fields for uplift and fluvial incision + Array2D uplift_field; + Array2D fluvial_incision_rate_field; + // set the frame to the current frame plus 1 + int timestep_counter = 0; // for debugging + int frame = current_frame; + int n_phases = int(phase_time.size()); + // get the rate vecvec + cout << "Let me get the phase rates" << endl; + vector< vector > phase_rates_vecvec = calculate_phase_rates_from_elevations( phase_time, phase_elevations); + cout << "Okay, I have got the phase rates. " << endl; + cout << "There are " << n_phases << " phases." << endl; + cout << "confirming with the vecvec, which has " << phase_rates_vecvec.size() << " phases." << endl; -mtl::compressed2D LSDRasterModel::generate_fd_matrix( int dimension, int size, bool periodic ) -{ - int num_neighbours, num_neighbours_; - int row, col; - float r = get_D() * timeStep / (DataResolution * DataResolution); - float r_ = get_D() * timeStep / pow(DataResolution * 1.4142135623, 2); - int width, height; + cout << "Starting the transient loop. " << endl; + cout << "The starting time is: " << current_time << endl; + cout << "The timestep is: " << timeStep << endl; - if (dimension == 0) // North - south + float next_time; + int next_time_ticker = 1; + if (n_phases == 1) { - width = NCols; - height = NRows - 2; + next_time = endTime+10*timeStep; } - else // East - west + else { - width = NCols - 2; - height = NRows; + next_time = phase_time[1]; } - mtl::compressed2D matrix(size, size); - matrix = 0.0; - mtl::mat::inserter< mtl::compressed2D > ins(matrix); + if (current_time > next_time) + { + cout << "Your rate comes from the future! current_time > next_time. " << endl; + cout << "I don't know how to deal with this and need to exit. " << endl; + exit(0); + } - for (int i=0; i 0) - { - ins[i][i-1] << -r; - } - else if (dimension == 0) - { - if ( periodic == false) - --num_neighbours; - else - ins[i][i+width-1] << -r; - } - // right - if (col < width - 1) - { - ins[i][i+1] << -r; - } - else if (dimension == 0) + // Get the current baselevel fall rate + //cout << "Getting the fall rates." << endl; + vector fall_rate = calculate_bl_drop_rate( phase_time, phase_rates_vecvec); + + // Record current topography + zeta_old = RasterData.copy(); + + // run active model components + // first diffuse the hillslopes. Currently two options. Perhaps a flag could be + // added so that we don't have to keep adding if statements as more + // hillslope rules are added? + if (hillslope) { - if ( periodic == false) - --num_neighbours; + if (nonlinear) + { + //soil_diffusion_fv_nonlinear(); + MuddPILE_nl_soil_diffusion_nouplift(); + } else - ins[i][i-width+1] << -r; + { + soil_diffusion_fd_linear(); + } } - // up - if (row > 0) - { - ins[i][i-width] << -r; - } - else if (dimension == 1) + // sediment will have moved into channels. This is assumed to + // be immediately removed by fluvial processes, so we use the + // 'wash out' member function + wash_out(); + + // now for fluvial erosion + if (fluvial) { - if ( periodic == false) - --num_neighbours; - else - ins[i][i+(width*(NCols-1))] << -r; + // currently the only option is stream power using the FASTSCAPE + // algorithm so there are no choices here + + //cout << "I am using an adaptive timestep" << endl; + //fluvial_incision_with_variable_uplift_and_variable_K_adaptive_timestep( URaster, KRaster ); + + let_timestep_increase = true; + if (next_time - current_time == timeStep) + { + cout << "I'm close to the next time so I'm stopping the timestep from increaseing." << endl; + let_timestep_increase = false; + } + + // This calculates the exhumation surface for the lithocube. + // It is the elevation minus the cumulative uplift (so you dig into deeper lithocube + // laytes to simulate the additional exhumation from uplfit) + exhumation_surface = calculate_exhumation_surface_from_cumulative_uplft(cumulative_uplift); + + //cout << "Now I'll check where in the lithocube we are and make a raster of lithocodes." << endl; + lithocodes_index_raster = LSDLC.get_lithology_codes_from_raster(exhumation_surface,forbidden_lithocodes); + + //cout << "Converting the litho codes to K values" << endl; + K_values = LSDLC.index_to_K(lithocodes_index_raster, 0.000003); + + + //cout << "Doing a timestep" << endl; + //cout << "Entering timestep, the fall rate in the outlet is: " << fall_rate[0] << endl; + fluvial_incision_with_variable_uplift_and_variable_K_adaptive_timestep_impose_baselevel( URaster, K_values, + source_points_data,fall_rate, + let_timestep_increase,use_adaptive_timestep, + minimum_slope, column_name); + + //cout << "Finished fluvial timestep, the Raster pixel upstream of outlet is: " << RasterData[627][1] << endl; + } - // down - if (row < height-1) + if(use_hillslope_hybrid) { - ins[i][i+width] << -r; + //cout << "Using hillslope hybrid!" << endl; + Sc_values = LSDLC.index_to_Sc(lithocodes_index_raster, 0.21); + bool carve_before_fill = true; + hillslope_hybrid_module(Sc_values, URaster, threshold_contributing_pixels, source_points_data); + + //cout << "Finished hillslope timestep, the Raster pixel upstream of outlet is: " << RasterData[627][1] << endl; + } - else if (dimension == 1) + + + + // need a function to calculate the cumulative uplift here + update_cumulative_uplift(cumulative_uplift, URaster, timeStep); + + //update the time + current_time += timeStep; + + + + // Manage the timestepping + if( current_time >= next_time-timeStep) { - if (periodic == false) - --num_neighbours; + cout << endl << endl << "======================================================" << endl; + cout << "Updating the next time for the incision and/or uplift phasing." << endl; + cout << "current time: " << current_time << " with timestep " << timeStep << endl; + timeStep = next_time - current_time; + cout << "Updated timeStep: " << timeStep << endl; + + if (current_time > next_time) + { + cout << "WARNING: I have overshot!!" << endl; + } + + next_time_ticker++; + if(next_time_ticker > n_phases -1) + { + next_time = endTime+10*timeStep; + } else - ins[i][i-(width*(NCols-1))] << -r; + { + next_time = phase_time[next_time_ticker]; + } + } - // Diagonals - // Upper left - if (row > 0 && col > 0) - ins[i][i-width-1] << -r_; - else if (dimension == 0 && row > 0) - if (periodic == false) - --num_neighbours_; - else{ - ins[i][i-1] << -r_;} - else if (dimension == 1 && col > 0) + + // now see if the time has exceeded the next print time + print_erosion = true; + if (current_time > next_printing_time) { - if (periodic == false) - --num_neighbours_; - else - ins[i][i+(width*(NCols-1))-1] << -r; - } + // We fill and impose the base level + //fill_raster(minimum_slope); + //impose_channels(source_points_data, column_name); - // Upper right - if (row > 0 && col < width-1) - ins[i][i-width+1] << -r_; - else if (dimension == 0 && row > 0) - { - if (periodic == false) - --num_neighbours_; - else - ins[i][i-(2*width)+1] << -r_; - } - else if (dimension == 1 && col < width-1) - { - if (periodic == false) - --num_neighbours_; - else - ins[i][i+(width*(NCols-1))+1] << -r_; - } + do + { + next_printing_time+=float_print_interval; + } while(next_printing_time < current_time); + print_rasters_and_csv( frame ); - // Lower left - if (row < height-1 && col > 0) - ins[i][i+width-1] << -r_; - else if (dimension == 0 && row < height-1) - { - if (periodic == false) - --num_neighbours_; - else - ins[i][i+(2*width)-1] << -r_; - } + if (print_exhumation_and_cumulative_uplift) + { + string CU_fname = name+"_cumulativeuplift_"+itoa(frame); + string exhu_fname = name+"_exhumationsurface_"+itoa(frame); - else if (dimension == 1 && col > 0) - { - if (periodic == false) - --num_neighbours_; - else - ins[i][col-1] << -r_; - } + cumulative_uplift.write_raster(CU_fname,"bil"); + exhumation_surface.write_raster(exhu_fname,"bil"); + } - // Lower right - if (row < height-1 && col < width-1) - { - ins[i][i+width+1] << -r_; - } - else if (dimension == 0 && row < height-1) - { - if (periodic == false) - --num_neighbours_; - else - ins[i][i+1] << -r_; - } + if (print_lithocode_raster) + { + string litho_fname = name+"_lithocode_"+itoa(frame); + lithocodes_index_raster.write_raster(litho_fname,"bil"); + } - else if (dimension == 1 && col < width-1) - { - if (periodic == false) - --num_neighbours_; - else - ins[i][col+1] << -r_; + ++frame; } + if (quiet == false) cout << "\rTime: " << current_time << " years" << flush; - ins[i][i] << num_neighbours*r + 1 + num_neighbours_ * r_; - } - return matrix; + } while (check_end_condition() == false); + + // reset the current frame + current_frame = frame; } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -mtl::dense_vector LSDRasterModel::build_fd_vector(int dimension, int size) + + + + + + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This is a wrapper function used to drive a model run +// it checks parameters and flags from the data members +// +// Similar to run_componets but instead of running fluvial +// and uplift in seperate steps it inserts the fluvial +// and uplift matrices into the nonlinear hillslope +// solver. +// +// This version allows variable K and U rasters to be used. +// You can also switch on an adaptive timestep +// +// It also allows an imposed base level +// +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDRasterModel::run_components_combined_imposed_baselevel( LSDRaster& URaster, + bool use_adaptive_timestep, LSDSpatialCSVReader& source_points_data, + vector phase_time, vector< float > outlet_elevations, + LSDRaster& cumulative_uplift, + LSDLithoCube& LSDLC, list forbidden_lithocodes, + bool print_lithocode_raster, + bool use_hillslope_hybrid, + int threshold_contributing_pixels, + float minimum_slope, + bool print_exhumation_and_cumulative_uplift, + string column_name) { - int vector_pos = 0; - mtl::dense_vector data_vector(size); - float push_val; - float r = get_D() * timeStep / (DataResolution * DataResolution); - int start_i, end_i; - int start_j, end_j; + + //cout << endl << endl << "I am checking the baselevel again LINE 4270" << endl; + //Array2D zeta=RasterData.copy(); + //LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); + //cout << "The nodatavalue is: " << NoDataValue << endl; + //LSDFlowInfo FI(boundary_conditions, temp); + //cout << "Number of BL nodes is: " << FI.get_NBaseLevelNodes() << endl << endl << endl; + + //recording = false; + cycle_number = 1; + total_erosion = 0; + max_erosion = 0; + min_erosion = -99; + switch_delay = 0; + time_delay = 0; - if (dimension == 0) + // These are for imposing all the lithocode information + LSDIndexRaster lithocodes_index_raster; + LSDRaster K_values; + LSDRaster Sc_values; + + LSDRaster exhumation_surface; + + stringstream ss, ss_root; + + // some fields for uplift and fluvial incision + Array2D uplift_field; + Array2D fluvial_incision_rate_field; + + // set the frame to the current frame plus 1 + int timestep_counter = 0; // for debugging + int frame = current_frame; + + int n_phases = int(phase_time.size()); + + // get the baselevel codes + cout << "Getting the baselevel code." << endl; + string bl_switch = "baselevel_code"; + vector bl_code = source_points_data.data_column_to_int(bl_switch); + + + cout << "Starting the transient loop. " << endl; + cout << "The starting time is: " << current_time << endl; + cout << "The timestep is: " << timeStep << endl; + + float next_time; + int next_time_ticker = 1; + if (n_phases == 1) { - start_i = 1; end_i = NRows-2; - start_j = 0; end_j = NCols-1; + next_time = endTime+10*timeStep; } - else // East - west + else { - start_i = 0; end_i = NRows-1; - start_j = 1; end_j = NCols-2; + next_time = phase_time[1]; } - for (int i=start_i; i<=end_i; ++i) + if (current_time > next_time) { - for (int j=start_j; j<=end_j; ++j) + cout << "Your rate comes from the future! current_time > next_time. " << endl; + cout << "I don't know how to deal with this and need to exit. " << endl; + exit(0); + } + + cout << "The next time I will update the baselevel fall rate is: " << next_time << endl; + + + bool let_timestep_increase; + do + { + + // Get the current baselevel fall rate + //cout << "Getting the fall rates." << endl; + float this_fall_rate = calculate_bl_drop_rate_from_elevations( phase_time, outlet_elevations); + vector fall_rate; + + int n_fr_nodes = 1; + // We just set all the fall rates the same. + // Only the pixels with the correct base level code will fall at this rate + int n_bl_nodes = int(bl_code.size()); + for (int bl_node = 0; bl_node &data_vector, int dimension) -{ - int vector_pos = 0; - if (dimension == 1) // East - west - { - for (int i=0; i matrix = generate_fd_matrix(dimension, size, periodic); - // Unpack data - mtl::dense_vector data_vector = build_fd_vector(dimension, size); + // need a function to calculate the cumulative uplift here + update_cumulative_uplift(cumulative_uplift, URaster, timeStep); - if ( quiet == false && name == "debug" && size < 100) - { - cout << "Data: " << endl; - for (int i=0; i= next_time-timeStep) { - for (int j=0; j next_time) { - cout << RasterData[i][j] << " "; + cout << "WARNING: I have overshot!!" << endl; } - cout << endl; - } - cout << "Matrix: " << endl; - for (int i=0; i n_phases -1) { - cout << matrix[i][j] << " "; + next_time = endTime+10*timeStep; } - cout << endl; + else + { + next_time = phase_time[next_time_ticker]; + } + } - cout << "Vector: " << endl; - for (int i=0; i output(size); - // Set up preconditioner - itl::pc::ilu_0 < mtl::compressed2D > P(matrix); - // Iterator condition - itl::basic_iteration iter(data_vector, 200, 1e-6); - // Matrix solver - itl::bicgstab(matrix, output, data_vector, P, iter); + // now see if the time has exceeded the next print time + print_erosion = true; + if (current_time > next_printing_time) + { + // We fill and impose the base level + fill_raster(minimum_slope); + impose_channels(source_points_data, column_name); - repack_vector(output, dimension); - if (quiet == false && name == "debug" && size <100) - { - cout << "Output: " << endl; - for (int i=0; i LSDRasterModel::generate_fv_matrix( int dimension, int size, bool periodic ) -{ - float A, B, C, D; - float front = timeStep * get_D() / (DataResolution*DataResolution); - float inv_term = 1 / (DataResolution * DataResolution * S_c * S_c); - int p = 0; // positioner for matrix insertion - int offset; - int start_i, start_j, end_i, end_j; - mtl::compressed2D matrix(size, size); - matrix = 0.0; - mtl::mat::inserter< mtl::compressed2D > ins(matrix); - if (dimension == 0) - { - start_i = 1; end_i = NRows-2; - start_j = 0; end_j = NCols-1; - offset = NCols; - } - else - { - start_i = 0; end_i = NRows-1; - start_j = 1; end_j = NCols-2; - offset = NCols - 2; - } - for (int i=start_i; i<=end_i; ++i) - { - for (int j=start_j; j<=end_j; ++j) - { - A = (i==0) ? 0 : front / (1 - pow(RasterData[i][j] - RasterData[i-1][j], 2) * inv_term); - B = (j==NCols-1) ? 0 : front / (1 - pow(RasterData[i][j] - RasterData[i][j+1], 2) * inv_term); - C = (i==NRows-1) ? 0 : front / (1 - pow(RasterData[i][j] - RasterData[i+1][j], 2) * inv_term); - D = (j==0) ? 0 : front / (1 - pow(RasterData[i][j] - RasterData[i][j-1], 2) * inv_term); - if (periodic) - { - if (i==0) A = front / (1 - pow(RasterData[i][j] - RasterData[NRows-1][j], 2) * inv_term); - else if (j==NCols-1) B = front / (1 - pow(RasterData[i][j] - RasterData[i][0], 2) * inv_term); - else if (i==NRows-1) C = front / (1 - pow(RasterData[i][j] - RasterData[0][j], 2) * inv_term); - else if (j==0) D = front / (1 - pow(RasterData[i][j] - RasterData[i][NCols-1], 2) * inv_term); - } - ins[p][p] << 1 + A + B + C + D; - if (j != start_j) - ins[p][p-1] << -D; - else if (periodic && dimension == 0 ) - ins[p][p+offset-1] << -D; - if (j != end_j) - ins[p][p+1] << -B; - else if (periodic && dimension == 0 ) - ins[p][p-offset+1] << -B; - if (i != start_i) - ins[p][p-offset] << -A; - else if (periodic && dimension == 1 ) - ins[p][p+(offset*(NCols-1))] << -A; - if (i != end_i) - ins[p][p+offset] << -C; - else if (periodic && dimension == 1 ) - ins[p][p-(offset*(NCols-1))] << -C; - ++p; - } - } - return matrix; -} -mtl::dense_vector LSDRasterModel::build_fv_vector( int dimension, int size ) -{ - float front = timeStep * get_D() / (DataResolution*DataResolution); - float inv_term = 1 / (DataResolution * DataResolution * S_c * S_c); - mtl::dense_vector data_vector(size); - int p = 0; // vector positioner - int start_i, end_i; - int start_j, end_j; - float push_val; - if (dimension == 0) - { - start_i = 1; end_i = NRows-2; - start_j = 0; end_j = NCols-1; - } - else - { - start_i = 0; end_i = NRows-1; - start_j = 1; end_j = NCols-2; - } - for (int i=start_i; i<=end_i; ++i) - { - for (int j=start_j; j<=end_j; ++j) - { - push_val = zeta_old[i][j]; -// cout << push_val << endl; - if (dimension == 0) - { - if (i==1) - push_val += zeta_old[0][j] * front / - (1 - pow(RasterData[i][j]-RasterData[0][j],2) * inv_term); - if (i==NRows-2) - push_val += zeta_old[NRows-1][j] * front / - (1 - pow(RasterData[i][j]-RasterData[NRows-1][j],2) * inv_term); - } - else if (dimension == 1) - { - if (j==1) - push_val += zeta_old[i][0] * front / - (1 - pow(RasterData[i][j]-RasterData[i][0],2) * inv_term); - if (j==NCols-2) - push_val += zeta_old[i][NCols-1] * front / - (1 - pow(RasterData[i][j]-RasterData[i][NCols-1],2) * inv_term); - } - data_vector[p] = push_val; - ++p; - } - } - return data_vector; -} -void LSDRasterModel::repack_vector(mtl::dense_vector &data_vector, int dimension) -{ - int start_i, end_i; - int start_j, end_j; - int p = 0; - if (dimension == 0) - { - start_i = 1; end_i = NRows-2; - start_j = 0; end_j = NCols-1; - } - else - { - start_i = 0; end_i = NRows-1; - start_j = 1; end_j = NCols-2; - } - for (int i = start_i; i<=end_i; ++i) - { - for (int j = start_j; j<=end_j; ++j) - { - RasterData[i][j] = data_vector[p]; - ++p; - } - } -} -void LSDRasterModel::soil_diffusion_fv_nonlinear( void ) -{ - int max_iter = 200, iter = 0; - float epsilon = 0.00001; - float max_diff; - Array2D last_iteration; - short dimension = 0; - bool periodic; - int size; - interpret_boundary(dimension, periodic, size); - do { - last_iteration = RasterData.copy(); - // A - mtl::compressed2D matrix = generate_fv_matrix(dimension, size, periodic); - // b - mtl::dense_vector data_vector = build_fv_vector(dimension, size); - if (quiet == false && name == "debug" && NRows <= 10 && NCols <= 10) - { - cout << "Data: " << endl; - for (int i=0; i output(size); - // Set up preconditioner - itl::pc::ilu_0 < mtl::compressed2D > P(matrix); - // Iterator condition - itl::basic_iteration iter(data_vector, 200, 1e-6); - // Matrix solver - itl::bicgstab(matrix, output, data_vector, P, iter); - repack_vector(output, dimension); - /* - if (NRows <= 10 && NCols <= 10) - { - cout << "Output: " << endl; - for (int i=0; i max_diff) - max_diff = RasterData[i][j] - last_iteration[i][j]; - } - } - if (quiet == false && name == "debug" && size <100) - { - cout << "Output: " << endl; - for (int i=0; i epsilon && iter < max_iter); - -} -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -// This takes the model and calculates the steady state fluvial surface derived from -// a chi map -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -void LSDRasterModel::fluvial_snap_to_steady_state(float U) -{ - Array2D zeta=RasterData.copy(); - // Step one, create donor "stack" etc. via FlowInfo - LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); - // need to fill the raster to ensure there are no internal base level nodes - float slope_for_fill = 0.0001; - LSDRaster filled = temp.fill(slope_for_fill); - LSDFlowInfo flow(boundary_conditions, filled); - float K = get_K(); - float m_exp = get_m(); - float n_exp = get_n(); - float area_threshold = 0; - float m_over_n = m_exp/n_exp; - float A_0 = 1; - float one_over_n = 1/n_exp; - LSDRaster ChiValues = flow.get_upslope_chi_from_all_baselevel_nodes(m_over_n, A_0,area_threshold); - float thisChi; - // now we calculate the elevations assuming that the elevation at chi = 0 is 0 - // This is based on equation 4 from mudd et al 2014 JGR-ES - for (int row = 0; row& CRNColumns, + vector& eroded_cells, + int startType, double startDepth, double particle_spacing, + LSDCRNParameters& CRNParams) { - string column_name = "elevation(m)"; - - // impose the channel before you fill - impose_channels(source_points_data); - - - Array2D zeta=RasterData.copy(); - - // Step one, create donor "stack" etc. via FlowInfo - LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + // first you need to get the locations of the columns + int N_pcolumns = CRNColumns.size(); + vector row_vec; + vector col_vec; - // need to fill the raster to ensure there are no internal base level nodes - cout << "I am going to carve and fill" << endl; - float slope_for_fill = 0.0001; - LSDRaster filled_topography,carved_topography; - if(carve_before_fill) - { - cout << "Carving and filling." << endl; - carved_topography = temp.Breaching_Lindsay2016(); - filled_topography = carved_topography.fill(slope_for_fill); - } - else - { - cout << "Filling." << endl; - filled_topography = temp.fill(slope_for_fill); - } - cout << "Getting the flow info. This might take some time." << endl; - LSDFlowInfo flow(boundary_conditions, filled_topography); - // update the raster - zeta = filled_topography.get_RasterData(); + vector e_cells(N_pcolumns); - // Get the local node index as well as the elevations - vector ni = source_points_data.get_nodeindices_from_lat_long(flow); - vector elev = source_points_data.data_column_to_float(column_name); - // make the map - cout << "I am making an elevation map. This will not work if points in the raster lie outside of the csv channel points." << endl; - map elevation_map; - for(int i = 0; i< int(ni.size()); i++) + for (int i = 0; i nodeList = flow.get_SVector(); - int numNodes = nodeList.size(); - int node, row, col, receiver, receiver_row, receiver_col; - float drainageArea, dx; - // these save a bit of computational expense. - float root_2 = pow(2, 0.5); - float dx_root2 = root_2*DataResolution; - float DR2 = DataResolution*DataResolution; + //cout << "\n\nLine 2604, data[10][10]: " << RasterData[10][10] << endl; - // Step two calculate new height - //for (int i=numNodes-1; i>=0; --i) - for (int i=0; iRasterData = zeta.copy(); + // get the erosion rates at the cells + for (int i = 0; i zeta=RasterData.copy(); - - // Step one, create donor "stack" etc. via FlowInfo - LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); - - // need to fill the raster to ensure there are no internal base level nodes - float slope_for_fill = 0.0001; - LSDRaster filled_topography,carved_topography; - if(carve_before_fill) - { - cout << "Carving and filling." << endl; - carved_topography = temp.Breaching_Lindsay2016(); - filled_topography = carved_topography.fill(slope_for_fill); - } - else - { - cout << "Filling." << endl; - filled_topography = temp.fill(slope_for_fill); - } - cout << "Getting the flow info. This might take some time." << endl; - LSDFlowInfo flow(boundary_conditions, filled_topography); - // update the raster - zeta = filled_topography.get_RasterData(); - - - // Get the local node index as well as the elevations - vector ni = source_points_data.get_nodeindices_from_lat_long(flow); - vector elev = source_points_data.data_column_to_float(column_name); - // make the map - cout << "I am making an elevation map. This will not work if points in the raster lie outside of the csv channel points." << endl; - map elevation_map; - for(int i = 0; i< int(ni.size()); i++) - { - elevation_map[ ni[i] ] = elev[i]; - } - - float m_exp = get_m(); - float n_exp = get_n(); - float one_over_n = 1/n_exp; - - //float FP_NDV = K_values.get_NoDataValue(); - float FP_value, K_value, U_value, receiver_elev, parenth_term, area_pow; - - - // Step one, create donor "stack" etc. via FlowInfo - vector nodeList = flow.get_SVector(); - int numNodes = nodeList.size(); - int node, row, col, receiver, receiver_row, receiver_col; - float drainageArea, dx; - - // these save a bit of computational expense. - float root_2 = pow(2, 0.5); - float dx_root2 = root_2*DataResolution; - float DR2 = DataResolution*DataResolution; + //cout << "\n\n\nCRN at [" << row << "]["<=0; --i) - float new_zeta; - for (int i=0; i 0 && (print % print_interval) == 0) + { + // calculate erosion rate and place it in the erosion data member for + // raster printing + erosion = get_total_erosion_rate_over_timestep(); + print_rasters( frame ); + print_average_erosion_and_apparent_erosion( frame, CRNColumns, CRNParams); + //print_column_erosion_and_apparent_erosion( frame, CRNColumns, CRNParams); + ++frame; } - + if ( quiet == false) cout << "\rTime: " << current_time << " years" << flush; + ++print; + //cout << "Line 2693, data[10][10]: " << RasterData[10][10] << endl; + // check to see if steady state has been achieved + //check_steady_state(); - } - //return LSDRasterModel(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); - this->RasterData = zeta.copy(); + } while ( check_end_condition() == false); - RasterData = zeta.copy(); + eroded_cells = e_cells; } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -// This takes the model and calculates the steady state fluvial surface derived from -// a chi map -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -float LSDRasterModel::fluvial_snap_to_steady_state_tune_K_for_relief(float U, float desired_relief) +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This function intiates a vector of LSDParticleColumns at steady state +// the zeta values are distrubuted over the surface +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +vector LSDRasterModel::initiate_steady_CRN_columns(int column_spacing, + vector& CRNcol_rows, vector& CRNcol_cols, + double rho_r, double this_U, int startType, double startDepth, + double particle_spacing, LSDCRNParameters& CRNParam) { - Array2D zeta=RasterData.copy(); + cout << "Initialising some CRN columns." << endl; - // Step one, create donor "stack" etc. via FlowInfo - LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + vector CRNParticleColumns_vec; - // We need to fill the raster so we don't get internally drained catchments - float slope_for_fill = 0.0001; - LSDRaster filled = temp.fill(slope_for_fill); - LSDFlowInfo flow(boundary_conditions, filled); + // get the number of particle rows and columns + // note there are less rows since these are boundary nodes that + // have zero erosion and are not included + int NPRows = (NRows-2)/column_spacing+1; + int NPCols = NCols/column_spacing+1; + cout << "Rows: " << NRows << " and NPRows: " << NPRows << endl; + cout << "Cols: " << NCols << " and NPCols: " << NPCols << endl; + vector rows_vec; + vector cols_vec; - float K = get_K(); - float m_exp = get_m(); - float n_exp = get_n(); - float area_threshold = 0; - float m_over_n = m_exp/n_exp; - float A_0 = 1; - float one_over_n = 1/n_exp; + int this_row, this_col; + double this_x,this_y; + double zeta_at_cell; + double eff_U = rho_r*this_U/10; // this converts the uplift to effective + // uplift in units g/cm^2/yr, + // required by the cosmo particles - LSDRaster ChiValues = flow.get_upslope_chi_from_all_baselevel_nodes(m_over_n, A_0,area_threshold); - float thisChi; - float MaxChi = 0; - // now we calculate the elevations assuming that the elevation at chi = 0 is 0 - // This is based on equation 4 from mudd et al 2014 JGR-ES - for (int row = 0; row= NRows-1) { - if (thisChi > MaxChi) - { - MaxChi = thisChi; - } + cout << "Danger, you were about to go out of the raster domain when making a CRN column\n"; + this_row = NRows-2; } - } - } - - // calculate K (you need to do some algebra on equation 4 from mudd et al 2014 JGR-ES) - K = U * pow((desired_relief/MaxChi),-n_exp); - cout << "I am updating K to " << K << " to get your desired relief." << endl; - - - // now recalculate the elevations - // This is based on equation 4 from mudd et al 2014 JGR-ES - for (int row = 0; row= NCols) { - zeta[row][col] = NoDataValue; + cout << "Danger, you were about to go out of the raster domain when making a CRN column\n"; + this_col = NCols-1; } - else - { - if (n_exp == 1) - { - zeta[row][col] = (U/K)*thisChi; - } - else - { - zeta[row][col] = pow( (U/K),one_over_n)*thisChi; - } - } - } - } + // push back the row and col vec + rows_vec.push_back(this_row); + cols_vec.push_back(this_col); - set_K(K); - RasterData = zeta.copy(); - return(K); -} + // get the x and y locations + this_x = double(this_col)*DataResolution+XMinimum; + this_y = double(NRows-this_row-1)*DataResolution+YMinimum; + // initiate an empty column + LSDParticleColumn temp_col; + temp_col.set_Row_and_Col(this_row,this_col); -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -// This takes the model and calculates the K needed for a fixed relief -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -float LSDRasterModel::fluvial_calculate_K_for_steady_state_relief(float U, float desired_relief) -{ + // get the elevation of the surface + zeta_at_cell = float(RasterData[this_row][this_col]); + + // make a steady state column + //cout << "LINE 3032, initiating SS column" << endl; + temp_col.initiate_SS_cosmo_column_3CRN(startType, this_x, this_y, startDepth, particle_spacing, + zeta_at_cell, eff_U, CRNParam); + //cout << "LINE 3035, Initiated column" << endl; + + // make sure this is a rock only simulation + double ST = 0; + temp_col.set_SoilThickness(ST); + temp_col.set_RockDensity(rho_r); + + // add the column to the vector + CRNParticleColumns_vec.push_back(temp_col); + //cout << "Got col, row: " << this_row << " and col: " << this_col << endl; + } + } + + //cout << "/n/nLine 2788, data[10][10]: " << RasterData[10][10] << endl; + CRNcol_rows = rows_vec; + CRNcol_cols = cols_vec; + return CRNParticleColumns_vec; +} + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This function simply calls the run_components function +// JAJ, sometime 2014 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDRasterModel::run_model( void ) +{ + // file to write details of each time step + // This will be opened by the write_report method + int run = 1; + + // loop through the number of runs. The number of runs is stored as a data member + do + { + //initial_steady_state = false; // SMM: not clear why this is set to false + // SMM: I have turned this off because it was overriding the periodic forcing + + recording = false; // SMM: not clear why this is set to false + + if ( initialized == false && quiet == false) + { + cout << "Model has not been initialized with a parameter file." << endl; + cout << "All values used are defaults" << endl; + } + + current_time = 0; + run_components(); + ++run; + } while (run <= num_runs); + + final_report(); +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// This function starts from a steady state. +// apparently the steady state data is stored as a data member. +// SMM: not sure if this is the best thing to do since it adds a lot +// of memory overhead. Could this be done by using a steady state raster +// as an argument? +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void LSDRasterModel::run_model_from_steady_state( void ) +{ + // file to write details of each time step + // This will be opened by the write_report method + RasterData = steady_state_data; + reset_model(); + + if ( initialized == false && quiet == false) + { + cout << "Model has not been initialized with a parameter file." << endl; + cout << "All values used are defaults" << endl; + } + if ( initial_steady_state == false) + { + cout << "Model has not been set to steady state yet" << endl; + cout << "Run LSDRasterModel::reach_steady_state( float tolerance ) first" << endl; + } + // Generate random noise + + int run = 1; + stringstream ss, ss_root; + do{ + current_time = 0; + run_components(); + ++run; + } while (run <= num_runs); + + // If the last output wasn't written, write it + + if ( quiet == false) cout << "\nModel finished!\n" << endl; + final_report(); +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// This function brings a model run to steady state +// It does not simply run at a constant erosion rate, but rather +// runs using periodic forcing. Tests have shown this can greatly reduce +// the time required to reach steady state +// JAJ, sometime 2014 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void LSDRasterModel::reach_steady_state( void ) +{ + // file to write details of each time step + // This will be opened by the write_report method + initial_steady_state = false; // this means that the model + // knows SS has not been reached so it + // will not 'record' model results + current_time = 0; + total_erosion = 0; + max_erosion = 0; + min_erosion = -99; + + // these 'swap' functions are here because the run to steady state + // uses default paramaters but the original model parameters are + // stored in these 'swap' data members and restored after running + // the model to steady state. + int K_mode_swap = K_mode; + int D_mode_swap = D_mode; + float K_amp_swap = K_amplitude; + float endTime_swap = endTime; + float period_swap = periodicity; + int period_mode_swap = period_mode; + float print_interval_swap = print_interval; + bool reporting_swap = reporting; + string name_swap = name; + + if ( initialized == false && quiet == false) + { + cout << "Model has not been initialized with a parameter file." << endl; + cout << "All values used are defaults" << endl; + } + + // Generate random noise + random_surface_noise(0, noise); + // Fill the topography + LSDRaster *temp; + temp = new LSDRaster(*this); + float thresh_slope = 0.00001; + *temp = fill(thresh_slope); + RasterData = temp->get_RasterData(); + delete temp; + + // Run model with some modest fluvial forcing + K_mode = 1; // THis means a sinusoidal variation in + // channel forcing + K_amplitude = K_fluv * 0.3; + endTime = 0; // SMM: not sure why this is set to zero. + cout << "Running to steady state, periodicity is: " << periodicity << endl; + //periodicity = 2000; + period_mode = 1; + cycle_steady_check = true; // this means that the steady state check + // is run based on erosion from one cycle + // to the next + print_interval = 0; + reporting = false; + + if ( quiet == false) + { + cout << "Producing steady state profile" << endl; + } + // because the cycle_steady_check is true, it means that this will run + // through several periods until the surface does not change between cycles + + // switch the end time mode to be periodic + short endTimeModeSwap = endTime_mode; + endTime_mode = 2; + + // run components + run_components(); + + if ( quiet == false) + { + cout << "Finished producing cyclic steady" << endl; + } + + // switch back to the original endTime_mode + endTime_mode = endTimeModeSwap; + + // Now run with static forcing + K_mode = K_mode_swap; + D_mode = D_mode_swap; + K_amplitude = K_amp_swap; + endTime = timeStep*10; + cycle_steady_check = false; + initial_steady_state = false; + current_time = 0; + + if ( quiet == false) cout << "Did cyclic steady, now running at constant forcing for a while." << endl; + cout << "Timestep is: " << timeStep << endl; + run_components(); + if ( quiet == false) cout << "Forced from constant steady state, exiting steady state routine." << endl; + + endTime = endTime_swap; + periodicity = period_swap; + period_mode = period_mode_swap; + cycle_steady_check = false; + print_interval = print_interval_swap; + reporting = reporting_swap; + + steady_state_data = Array2D (NRows, NCols, 0.0); + steady_state_data = RasterData; + + // set the initial steady state flag to true + initial_steady_state = true; + cout << "Line 2526 Finished run_to_steady_state, initial_steady_state: " << initial_steady_state << endl; +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + +void LSDRasterModel::soil_diffusion_fv( void ) +{ + static bool defined = false; + + static int problem_dimension; + static float inv_dx_S_c_squared, inv_dy_S_c_squared, dx_front_term, dy_front_term; + static vector vec_k_value_i_j; + static vector vec_k_value_ip1_j; + static vector vec_k_value_im1_j; + static vector vec_k_value_i_jp1; + static vector vec_k_value_i_jm1; + static float iteration_tolerance = 0.01; + + Array2D fluvial_temp(NRows, NCols, 0.0); + float south, north; + + if (boundary_conditions[2][0] == 'b') + { + south = 0.0; + north = current_time*get_max_uplift(); + + cout << south << ", " << north << endl; + } + else + { + cout << "Model currently not built to cope with hillslope diffusion using these boundary conditions" << endl; + cout << "Feature implementation required" << endl; + exit(1); + } + + + if (defined == false) + { + mtl_initiate_assembler_matrix(problem_dimension, inv_dx_S_c_squared, inv_dy_S_c_squared, + dx_front_term, dy_front_term, vec_k_value_i_j, vec_k_value_ip1_j, + vec_k_value_im1_j, vec_k_value_i_jp1, vec_k_value_i_jm1); + } + defined = true; + + nonlinear_creep_timestep(fluvial_temp, iteration_tolerance, problem_dimension, + inv_dx_S_c_squared, inv_dy_S_c_squared, dx_front_term, dy_front_term, vec_k_value_i_j, + vec_k_value_ip1_j, vec_k_value_im1_j, vec_k_value_i_jp1, vec_k_value_i_jm1, + south, north); +} + + + + +mtl::compressed2D LSDRasterModel::generate_fd_matrix( int dimension, int size, bool periodic ) +{ + int num_neighbours, num_neighbours_; + int row, col; + float r = get_D() * timeStep / (DataResolution * DataResolution); + float r_ = get_D() * timeStep / pow(DataResolution * 1.4142135623, 2); + int width, height; + + if (dimension == 0) // North - south + { + width = NCols; + height = NRows - 2; + } + else // East - west + { + width = NCols - 2; + height = NRows; + } + + mtl::compressed2D matrix(size, size); + matrix = 0.0; + mtl::mat::inserter< mtl::compressed2D > ins(matrix); + + for (int i=0; i 0) + { + ins[i][i-1] << -r; + } + else if (dimension == 0) + { + if ( periodic == false) + --num_neighbours; + else + ins[i][i+width-1] << -r; + } + + // right + if (col < width - 1) + { + ins[i][i+1] << -r; + } + else if (dimension == 0) + { + if ( periodic == false) + --num_neighbours; + else + ins[i][i-width+1] << -r; + } + + // up + if (row > 0) + { + ins[i][i-width] << -r; + } + else if (dimension == 1) + { + if ( periodic == false) + --num_neighbours; + else + ins[i][i+(width*(NCols-1))] << -r; + } + + // down + if (row < height-1) + { + ins[i][i+width] << -r; + } + else if (dimension == 1) + { + if (periodic == false) + --num_neighbours; + else + ins[i][i-(width*(NCols-1))] << -r; + } + + // Diagonals + // Upper left + if (row > 0 && col > 0) + ins[i][i-width-1] << -r_; + else if (dimension == 0 && row > 0) + if (periodic == false) + --num_neighbours_; + else{ + ins[i][i-1] << -r_;} + else if (dimension == 1 && col > 0) + { + if (periodic == false) + --num_neighbours_; + else + ins[i][i+(width*(NCols-1))-1] << -r; + } + + + // Upper right + if (row > 0 && col < width-1) + ins[i][i-width+1] << -r_; + else if (dimension == 0 && row > 0) + { + if (periodic == false) + --num_neighbours_; + else + ins[i][i-(2*width)+1] << -r_; + } + else if (dimension == 1 && col < width-1) + { + if (periodic == false) + --num_neighbours_; + else + ins[i][i+(width*(NCols-1))+1] << -r_; + } + + // Lower left + if (row < height-1 && col > 0) + ins[i][i+width-1] << -r_; + else if (dimension == 0 && row < height-1) + { + if (periodic == false) + --num_neighbours_; + else + ins[i][i+(2*width)-1] << -r_; + } + + else if (dimension == 1 && col > 0) + { + if (periodic == false) + --num_neighbours_; + else + ins[i][col-1] << -r_; + } + + // Lower right + if (row < height-1 && col < width-1) + { + ins[i][i+width+1] << -r_; + } + else if (dimension == 0 && row < height-1) + { + if (periodic == false) + --num_neighbours_; + else + ins[i][i+1] << -r_; + } + + else if (dimension == 1 && col < width-1) + { + if (periodic == false) + --num_neighbours_; + else + ins[i][col+1] << -r_; + } + + ins[i][i] << num_neighbours*r + 1 + num_neighbours_ * r_; + } + return matrix; +} + +mtl::dense_vector LSDRasterModel::build_fd_vector(int dimension, int size) +{ + int vector_pos = 0; + mtl::dense_vector data_vector(size); + float push_val; + float r = get_D() * timeStep / (DataResolution * DataResolution); + int start_i, end_i; + int start_j, end_j; + + if (dimension == 0) + { + start_i = 1; end_i = NRows-2; + start_j = 0; end_j = NCols-1; + } + else // East - west + { + start_i = 0; end_i = NRows-1; + start_j = 1; end_j = NCols-2; + } + + for (int i=start_i; i<=end_i; ++i) + { + for (int j=start_j; j<=end_j; ++j) + { + push_val = RasterData[i][j]; + if (dimension == 0) + { + if (i==1) + push_val += RasterData[0][j] * r; + else if (j==NRows-2) + push_val += RasterData[NRows-1][j] * r; + } + + else if (dimension == 1) + { + if (j == 1) + push_val += RasterData[i][0] * r; + else if (j == NCols-2) + push_val += RasterData[i][NCols-1] * r; + } + + data_vector[vector_pos] = push_val; + ++vector_pos; + } + } + + return data_vector; +} + +/* +void LSDRasterModel::repack_fd_vector(mtl::dense_vector &data_vector, int dimension) +{ + int vector_pos = 0; + if (dimension == 1) // East - west + { + for (int i=0; i matrix = generate_fd_matrix(dimension, size, periodic); + // Unpack data + mtl::dense_vector data_vector = build_fd_vector(dimension, size); + + if ( quiet == false && name == "debug" && size < 100) + { + cout << "Data: " << endl; + for (int i=0; i output(size); + // Set up preconditioner + itl::pc::ilu_0 < mtl::compressed2D > P(matrix); + // Iterator condition + itl::basic_iteration iter(data_vector, 200, 1e-6); + // Matrix solver + itl::bicgstab(matrix, output, data_vector, P, iter); + + + repack_vector(output, dimension); + if (quiet == false && name == "debug" && size <100) + { + cout << "Output: " << endl; + for (int i=0; i LSDRasterModel::generate_fv_matrix( int dimension, int size, bool periodic ) +{ + float A, B, C, D; + float front = timeStep * get_D() / (DataResolution*DataResolution); + float inv_term = 1 / (DataResolution * DataResolution * S_c * S_c); + + int p = 0; // positioner for matrix insertion + int offset; + int start_i, start_j, end_i, end_j; + mtl::compressed2D matrix(size, size); + matrix = 0.0; + mtl::mat::inserter< mtl::compressed2D > ins(matrix); + + if (dimension == 0) + { + start_i = 1; end_i = NRows-2; + start_j = 0; end_j = NCols-1; + offset = NCols; + } + else + { + start_i = 0; end_i = NRows-1; + start_j = 1; end_j = NCols-2; + offset = NCols - 2; + } + + for (int i=start_i; i<=end_i; ++i) + { + for (int j=start_j; j<=end_j; ++j) + { + A = (i==0) ? 0 : front / (1 - pow(RasterData[i][j] - RasterData[i-1][j], 2) * inv_term); + B = (j==NCols-1) ? 0 : front / (1 - pow(RasterData[i][j] - RasterData[i][j+1], 2) * inv_term); + C = (i==NRows-1) ? 0 : front / (1 - pow(RasterData[i][j] - RasterData[i+1][j], 2) * inv_term); + D = (j==0) ? 0 : front / (1 - pow(RasterData[i][j] - RasterData[i][j-1], 2) * inv_term); + + if (periodic) + { + if (i==0) A = front / (1 - pow(RasterData[i][j] - RasterData[NRows-1][j], 2) * inv_term); + else if (j==NCols-1) B = front / (1 - pow(RasterData[i][j] - RasterData[i][0], 2) * inv_term); + else if (i==NRows-1) C = front / (1 - pow(RasterData[i][j] - RasterData[0][j], 2) * inv_term); + else if (j==0) D = front / (1 - pow(RasterData[i][j] - RasterData[i][NCols-1], 2) * inv_term); + + } + + ins[p][p] << 1 + A + B + C + D; + if (j != start_j) + ins[p][p-1] << -D; + else if (periodic && dimension == 0 ) + ins[p][p+offset-1] << -D; + if (j != end_j) + ins[p][p+1] << -B; + else if (periodic && dimension == 0 ) + ins[p][p-offset+1] << -B; + if (i != start_i) + ins[p][p-offset] << -A; + else if (periodic && dimension == 1 ) + ins[p][p+(offset*(NCols-1))] << -A; + if (i != end_i) + ins[p][p+offset] << -C; + else if (periodic && dimension == 1 ) + ins[p][p-(offset*(NCols-1))] << -C; + + ++p; + } + } + return matrix; +} + +mtl::dense_vector LSDRasterModel::build_fv_vector( int dimension, int size ) +{ + float front = timeStep * get_D() / (DataResolution*DataResolution); + float inv_term = 1 / (DataResolution * DataResolution * S_c * S_c); + + mtl::dense_vector data_vector(size); + int p = 0; // vector positioner + int start_i, end_i; + int start_j, end_j; + float push_val; + + if (dimension == 0) + { + start_i = 1; end_i = NRows-2; + start_j = 0; end_j = NCols-1; + } + else + { + start_i = 0; end_i = NRows-1; + start_j = 1; end_j = NCols-2; + } + + for (int i=start_i; i<=end_i; ++i) + { + for (int j=start_j; j<=end_j; ++j) + { + push_val = zeta_old[i][j]; +// cout << push_val << endl; + + if (dimension == 0) + { + if (i==1) + push_val += zeta_old[0][j] * front / + (1 - pow(RasterData[i][j]-RasterData[0][j],2) * inv_term); + if (i==NRows-2) + push_val += zeta_old[NRows-1][j] * front / + (1 - pow(RasterData[i][j]-RasterData[NRows-1][j],2) * inv_term); + } + else if (dimension == 1) + { + if (j==1) + push_val += zeta_old[i][0] * front / + (1 - pow(RasterData[i][j]-RasterData[i][0],2) * inv_term); + if (j==NCols-2) + push_val += zeta_old[i][NCols-1] * front / + (1 - pow(RasterData[i][j]-RasterData[i][NCols-1],2) * inv_term); + } + + data_vector[p] = push_val; + ++p; + } + } + return data_vector; +} + +void LSDRasterModel::repack_vector(mtl::dense_vector &data_vector, int dimension) +{ + int start_i, end_i; + int start_j, end_j; + int p = 0; + + if (dimension == 0) + { + start_i = 1; end_i = NRows-2; + start_j = 0; end_j = NCols-1; + } + else + { + start_i = 0; end_i = NRows-1; + start_j = 1; end_j = NCols-2; + } + + for (int i = start_i; i<=end_i; ++i) + { + for (int j = start_j; j<=end_j; ++j) + { + RasterData[i][j] = data_vector[p]; + ++p; + } + } +} + + +void LSDRasterModel::soil_diffusion_fv_nonlinear( void ) +{ + int max_iter = 200, iter = 0; + float epsilon = 0.00001; + float max_diff; + Array2D last_iteration; + + short dimension = 0; + bool periodic; + int size; + interpret_boundary(dimension, periodic, size); + + do { + last_iteration = RasterData.copy(); + // A + mtl::compressed2D matrix = generate_fv_matrix(dimension, size, periodic); + // b + mtl::dense_vector data_vector = build_fv_vector(dimension, size); + + if (quiet == false && name == "debug" && NRows <= 10 && NCols <= 10) + { + cout << "Data: " << endl; + for (int i=0; i output(size); + // Set up preconditioner + itl::pc::ilu_0 < mtl::compressed2D > P(matrix); + // Iterator condition + itl::basic_iteration iter(data_vector, 200, 1e-6); + // Matrix solver + itl::bicgstab(matrix, output, data_vector, P, iter); + + repack_vector(output, dimension); + /* + if (NRows <= 10 && NCols <= 10) + { + cout << "Output: " << endl; + for (int i=0; i max_diff) + max_diff = RasterData[i][j] - last_iteration[i][j]; + } + } + if (quiet == false && name == "debug" && size <100) + { + cout << "Output: " << endl; + for (int i=0; i epsilon && iter < max_iter); + +} + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// This takes the model and calculates the steady state fluvial surface derived from +// a chi map +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void LSDRasterModel::fluvial_snap_to_steady_state(float U) +{ + Array2D zeta=RasterData.copy(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + + // need to fill the raster to ensure there are no internal base level nodes + float slope_for_fill = 0.0001; + LSDRaster filled = temp.fill(slope_for_fill); + LSDFlowInfo flow(boundary_conditions, filled); + + float K = get_K(); + float m_exp = get_m(); + float n_exp = get_n(); + float area_threshold = 0; + float m_over_n = m_exp/n_exp; + float A_0 = 1; + float one_over_n = 1/n_exp; + + LSDRaster ChiValues = flow.get_upslope_chi_from_all_baselevel_nodes(m_over_n, A_0,area_threshold); + float thisChi; + // now we calculate the elevations assuming that the elevation at chi = 0 is 0 + // This is based on equation 4 from mudd et al 2014 JGR-ES + for (int row = 0; row zeta=RasterData.copy(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + + // need to fill the raster to ensure there are no internal base level nodes + cout << "I am going to carve and fill" << endl; + float slope_for_fill = 0.0001; + LSDRaster filled_topography,carved_topography; + if(carve_before_fill) + { + cout << "Carving and filling." << endl; + carved_topography = temp.Breaching_Lindsay2016(); + filled_topography = carved_topography.fill(slope_for_fill); + } + else + { + cout << "Filling." << endl; + filled_topography = temp.fill(slope_for_fill); + } + cout << "Getting the flow info. This might take some time." << endl; + LSDFlowInfo flow(boundary_conditions, filled_topography); + // update the raster + zeta = filled_topography.get_RasterData(); + + // Get the local node index as well as the elevations + vector ni = source_points_data.get_nodeindices_from_lat_long(flow); + vector elev = source_points_data.data_column_to_float(column_name); + // make the map + cout << "I am making an elevation map. This will not work if points in the raster lie outside of the csv channel points." << endl; + map elevation_map; + for(int i = 0; i< int(ni.size()); i++) + { + elevation_map[ ni[i] ] = elev[i]; + } + + float m_exp = get_m(); + float n_exp = get_n(); + float one_over_n = 1/n_exp; + + //float FP_NDV = K_values.get_NoDataValue(); + float FP_value, K_value, U_value, receiver_elev, parenth_term, area_pow; + + + // Step one, create donor "stack" etc. via FlowInfo + vector nodeList = flow.get_SVector(); + int numNodes = nodeList.size(); + int node, row, col, receiver, receiver_row, receiver_col; + float drainageArea, dx; + + // these save a bit of computational expense. + float root_2 = pow(2, 0.5); + float dx_root2 = root_2*DataResolution; + float DR2 = DataResolution*DataResolution; + + // Step two calculate new height + //for (int i=numNodes-1; i>=0; --i) + for (int i=0; iRasterData = zeta.copy(); + + RasterData = zeta.copy(); +} + + + +void LSDRasterModel::hillslope_hybrid_module(LSDRaster& Sc_values, LSDRaster& Uplift, int threshold_pixels,LSDSpatialCSVReader& source_points_data) +{ + //cout << "I'm in the hillslope routine, ZNO node is: " << RasterData[627][1] << endl; + + // we use the old zeta as this is meant to be used after the fluvial step + Array2D zeta_fluvial=RasterData.copy(); + Array2D zeta_new=zeta_old.copy(); + Array2D zeta_test=zeta_old.copy(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta_new, GeoReferencingStrings); + + // Create the laplacian function + // This is from the updated data + LSDRaster curvature = basic_curvature(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDFlowInfo flow(boundary_conditions, temp); + vector ni = source_points_data.get_nodeindices_from_lat_long(flow); + vector::iterator it; + + cout << "HS NBaseLevel: " << flow.get_NBaseLevelNodes() << endl; + + //float FP_NDV = K_values.get_NoDataValue(); + float Sc_value, receiver_elev; + + + // Step one, create donor "stack" etc. via FlowInfo + vector nodeList = flow.get_SVector(); + int numNodes = nodeList.size(); + int node, row, col, receiver, receiver_row, receiver_col, contributing_pixels; + float dx; + + // these save a bit of computational expense. + float root_2 = pow(2, 0.5); + float dx_root2 = root_2*DataResolution; + float DR2 = DataResolution*DataResolution; + + // Step two calculate new height + //for (int i=numNodes-1; i>=0; --i) + float new_zeta; + for (int i=0; iRasterData = zeta_new.copy(); + +} + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// This snaps to steady based on an input file with elevations and node indicies +// overloaded from the previous function +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void LSDRasterModel::hillslope_snap_to_steady_variable_Sc(LSDRaster& Sc_values, bool carve_before_fill, int threshold_pixels) +{ + Array2D zeta=RasterData.copy(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + + // need to fill the raster to ensure there are no internal base level nodes + float slope_for_fill = 0.0001; + LSDRaster filled_topography,carved_topography; + if(carve_before_fill) + { + cout << "Carving and filling." << endl; + carved_topography = temp.Breaching_Lindsay2016(); + filled_topography = carved_topography.fill(slope_for_fill); + } + else + { + cout << "Filling." << endl; + filled_topography = temp.fill(slope_for_fill); + } + cout << "Getting the flow info. This might take some time." << endl; + LSDFlowInfo flow(boundary_conditions, filled_topography); + // update the raster + zeta = filled_topography.get_RasterData(); + + //float FP_NDV = K_values.get_NoDataValue(); + float Sc_value, receiver_elev; + + + // Step one, create donor "stack" etc. via FlowInfo + vector nodeList = flow.get_SVector(); + int numNodes = nodeList.size(); + int node, row, col, receiver, receiver_row, receiver_col, contributing_pixels; + float dx; + + // these save a bit of computational expense. + float root_2 = pow(2, 0.5); + float dx_root2 = root_2*DataResolution; + float DR2 = DataResolution*DataResolution; + + // Step two calculate new height + //for (int i=numNodes-1; i>=0; --i) + float new_zeta; + for (int i=0; iRasterData = zeta.copy(); + + RasterData = zeta.copy(); +} + + + + + + + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// This takes the model and calculates the steady state fluvial surface derived from +// a chi map +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +float LSDRasterModel::fluvial_snap_to_steady_state_tune_K_for_relief(float U, float desired_relief) +{ + Array2D zeta=RasterData.copy(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + + // We need to fill the raster so we don't get internally drained catchments + float slope_for_fill = 0.0001; + LSDRaster filled = temp.fill(slope_for_fill); + LSDFlowInfo flow(boundary_conditions, filled); + + float K = get_K(); + float m_exp = get_m(); + float n_exp = get_n(); + float area_threshold = 0; + float m_over_n = m_exp/n_exp; + float A_0 = 1; + float one_over_n = 1/n_exp; + + LSDRaster ChiValues = flow.get_upslope_chi_from_all_baselevel_nodes(m_over_n, A_0,area_threshold); + float thisChi; + float MaxChi = 0; + // now we calculate the elevations assuming that the elevation at chi = 0 is 0 + // This is based on equation 4 from mudd et al 2014 JGR-ES + for (int row = 0; row MaxChi) + { + MaxChi = thisChi; + } + } + } + } + + // calculate K (you need to do some algebra on equation 4 from mudd et al 2014 JGR-ES) + K = U * pow((desired_relief/MaxChi),-n_exp); + cout << "I am updating K to " << K << " to get your desired relief." << endl; + + + // now recalculate the elevations + // This is based on equation 4 from mudd et al 2014 JGR-ES + for (int row = 0; row zeta=RasterData.copy(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + + // We need to fill the raster so we don't get internally drained catchments + float slope_for_fill = 0.0001; + LSDRaster filled = temp.fill(slope_for_fill); + LSDFlowInfo flow(boundary_conditions, filled); + + float K = get_K(); + float m_exp = get_m(); + float n_exp = get_n(); + float area_threshold = 0; + float m_over_n = m_exp/n_exp; + float A_0 = 1; + + LSDRaster ChiValues = flow.get_upslope_chi_from_all_baselevel_nodes(m_over_n, A_0,area_threshold); + float thisChi; + float MaxChi = 0; + // now we calculate the elevations assuming that the elevation at chi = 0 is 0 + // This is based on equation 4 from mudd et al 2014 JGR-ES + for (int row = 0; row MaxChi) + { + MaxChi = thisChi; + } + } + } + } + + // calculate K (you need to do some algebra on equation 4 from mudd et al 2014 JGR-ES) + K = U * pow((desired_relief/MaxChi),-n_exp); + cout << "I am updating K to " << K << " to get your desired relief." << endl; + + return(K); +} + + + + + +//============================================================================================================================ +// WORKING HERE +// TRYING TO GET CRITICAL SLOPES WORKING +//============================================================================================================================ + + +// Just a structure that define a node by ID and elevation +// Same principle that Martin's filling algorithm +struct MyNode +{ + float elevation; + std::pair ID; +}; +bool operator>( const MyNode& lhs, const MyNode& rhs ) +{ + return lhs.elevation > rhs.elevation; +} +bool operator<( const MyNode& lhs, const MyNode& rhs ) +{ + return lhs.elevation < rhs.elevation; +} + + + +LSDRaster LSDRasterModel::basic_valley_fill_critical_slope(float critical_slope, int contributing_pixel_threshold) +{ + Array2D zeta=RasterData.copy(); + float sqrt2 = sqrt(2); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + + // We need to fill the raster so we don't get internally drained catchments + cout << "I'm getting the flow info" << endl; + float slope_for_fill = 0.0001; + LSDRaster filled = temp.fill(slope_for_fill); + LSDFlowInfo flow(boundary_conditions, filled); + int contributing_pixels; + + vector S_vec = flow.get_SVector(); + + // also I want to keep track on which nodes have been processed + map ,bool> has_been_proc; + //And a map that check whether a node is in the river + map ,bool> is_river; + // This tests the exsitence of the node in a stack + map ,bool> is_in_DEM; + // Let's initialise my output as well + Array2D output(NRows,NCols, NoDataValue); + // Before processing my nodes, I need to set up my priority queue + // And set 2 priority queues that will work together + priority_queue< MyNode, vector, greater > PriorityQueue_1, PriorityQueue_2; + // I'll need a switch to know which PQ is getting filled + int working_PQ = 1; + + + // let's go through the stack and get the river nodes + // I'll fill my PQ1 with the river_nodes + cout << "Filling the priority queues. The no data value is: " << NoDataValue < this_node = {this_row, this_col}; + + is_in_DEM[this_node] = true; + + // Initialising my node + MyNode this_MyNode; + this_MyNode.ID = this_node; + this_MyNode.elevation = this_elev; + + // is it a river + if(contributing_pixels>=contributing_pixel_threshold) + { + PriorityQueue_1.push(this_MyNode); + is_river[this_node] = true; + output[this_row][this_col] = this_elev; + has_been_proc[this_node] = true; + } + else + { + is_river[this_node] = false; + output[this_row][this_col] = NoDataValue; + has_been_proc[this_node] = false; + } + } + // std::cout << "is empty??" + + //cout << "Now it is time to create the slopes" << endl; + // Alright, I have my list of node ordered by elevation (thanks to fastscape and stuff) + while(PriorityQueue_1.empty() == false || PriorityQueue_2.empty() == false) + { + //cout << "Whohoo, starting with the priority queueueueueue" << endl; + // I'll want my node + pair this_node; + MyNode this_MyNode; + if(working_PQ == 1) + { + this_MyNode = PriorityQueue_1.top(); + PriorityQueue_1.pop(); + } + else + { + this_MyNode = PriorityQueue_2.top(); + PriorityQueue_2.pop(); + } + this_node = this_MyNode.ID; + float this_elevation = this_MyNode.elevation; + int this_row = this_node.first; + int this_col = this_node.second; + // let me go through the neighbors + //cout << "z: " << this_elevation << " r: " << this_row << " c: " << this_col << endl; + vector row_neighbors = {this_row-1,this_row,this_row+1}; + vector col_neighbors = {this_col-1,this_col,this_col+1}; + for(int nR=0;nR= 0 && neighb_row <= NRows - 1 && neighb_col >= 0 && neighb_col <= NCols - 1) + { - LSDRaster ChiValues = flow.get_upslope_chi_from_all_baselevel_nodes(m_over_n, A_0,area_threshold); - float thisChi; - float MaxChi = 0; - // now we calculate the elevations assuming that the elevation at chi = 0 is 0 - // This is based on equation 4 from mudd et al 2014 JGR-ES - for (int row = 0; row nenode = {neighb_row,neighb_col}; + + if(not is_in_DEM[nenode]) + { + //cout << "Hey this neighbour isn't in the DEM" << endl; + } + else + { + if(not is_river[nenode] && not has_been_proc[nenode]) + { + // If we got this far it means the node needs to be processed. + float dx; + if((nR == 0 || nR == 2) && (nC == 0 || nC == 2)) + { + dx = sqrt2*DataResolution; + } + else + { + dx = DataResolution; + } + // backcalculating the slope + //cout << "I'm getting a slope at r: " << neighb_row << " c: " << neighb_col << " elev: " << this_elevation << endl; + float new_elev = critical_slope * dx + this_elevation; + //cout << "1.."; + output[neighb_row][neighb_col] = new_elev; + //cout << "2.."; + has_been_proc[nenode] = true; + //cout << "done" < new_data(NRows,NCols,NoDataValue); + + + // These are the north, south, east and west slopes + float f_N, f_S, f_E, f_W; + float dxsquared = DataResolution*DataResolution; + + for(int row = 0; row < NRows; row++) { - for(int col = 0; col MaxChi) + // Northern pixel (remember row 0 is the north and row NRows-1 is the south) + if ( row == 0 || RasterData[row-1][col] == NoDataValue) { - MaxChi = thisChi; + f_N = RasterData[row][col]; + } + else + { + f_N = RasterData[row-1][col]; + } + + // Southern pixel (remember row 0 is the north and row NRows-1 is the south) + if ( row == NRows-1 || RasterData[row+1][col] == NoDataValue) + { + f_S = RasterData[row][col]; + } + else + { + f_S = RasterData[row+1][col]; + } + + // eastern pixel + if ( col == 0 || RasterData[row][col-1] == NoDataValue) + { + f_E = RasterData[row][col]; + } + else + { + f_E = RasterData[row][col-1]; + } + + // western pixel + if ( col == NRows-1 || RasterData[row][col+1] == NoDataValue) + { + f_W = RasterData[row][col]; } + else + { + f_W = RasterData[row][col+1]; + } + + new_data[row][col] = (f_N+f_S+f_E+f_W - 4*RasterData[row][col] ) / dxsquared; + + + + } } } - // calculate K (you need to do some algebra on equation 4 from mudd et al 2014 JGR-ES) - K = U * pow((desired_relief/MaxChi),-n_exp); - cout << "I am updating K to " << K << " to get your desired relief." << endl; + LSDRaster output_raster(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, new_data, GeoReferencingStrings); + return output_raster; - return(K); } +LSDRaster LSDRasterModel::basic_smooth(float central_pixel_weighting) +{ + + // at the moment the boundary type can only be 0 and this is a periodic + // boundary type at the E and W boundaries. + Array2D new_data(NRows,NCols,NoDataValue); + float total_weighting; + float total_sum; + int rp1, rm1,cp1, cm1; + int boundary_type = 0; // This later allows the code to be flexible in terms of the boundary type. + // Currently only periodic boundaries are on offer. -//============================================================================================================================ -// WORKING HERE -// TRYING TO GET CRITICAL SLOPES WORKING -//============================================================================================================================ + for(int row = 0; row ID; -}; -bool operator>( const MyNode& lhs, const MyNode& rhs ) -{ - return lhs.elevation > rhs.elevation; -} -bool operator<( const MyNode& lhs, const MyNode& rhs ) -{ - return lhs.elevation < rhs.elevation; + // implement boundary conditions. + if(boundary_type == 0) + { + if (rp1 == NRows) + { + rp1 = rm1; + } + if (rm1 == -1) + { + rm1 = rp1; + } + if (cp1 == NCols) + { + cp1 = 0; + } + if(cm1 == -1) + { + cm1 = NCols-1; + } + } + else + { + if (rp1 == NRows) + { + rp1 = rm1; + } + if (rm1 == -1) + { + rm1 = rp1; + } + if (cp1 == NCols) + { + cp1 = 0; + } + if(cm1 == -1) + { + cm1 = NCols-1; + } + } + + if( RasterData[row][col] != NoDataValue) + { + total_weighting += central_pixel_weighting; + total_sum += central_pixel_weighting*RasterData[row][col]; + + // now go through all the other directions. + if (RasterData[row][cp1] != NoDataValue) + { + total_weighting +=1; + total_sum += RasterData[row][cp1]; + } + if (RasterData[row][cm1] != NoDataValue) + { + total_weighting +=1; + total_sum += RasterData[row][cm1]; + } + if (RasterData[rp1][col] != NoDataValue) + { + total_weighting +=1; + total_sum += RasterData[rp1][col]; + } + if (RasterData[rm1][col] != NoDataValue) + { + total_weighting +=1; + total_sum += RasterData[rm1][col]; + } + } + // Now update the array + + new_data[row][col] = total_sum/total_weighting; + } + } + + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, new_data, GeoReferencingStrings); + return temp; } -LSDRaster LSDRasterModel::basic_valley_fill_critical_slope(float critical_slope, int contributing_pixel_threshold) +LSDRaster LSDRasterModel::basic_valley_fill_critical_slope(LSDRaster& S_c_raster, int contributing_pixel_threshold) { Array2D zeta=RasterData.copy(); @@ -5232,7 +7108,7 @@ LSDRaster LSDRasterModel::basic_valley_fill_critical_slope(float critical_slope, } // backcalculating the slope //cout << "I'm getting a slope at r: " << neighb_row << " c: " << neighb_col << " elev: " << this_elevation << endl; - float new_elev = critical_slope * dx + this_elevation; + float new_elev = S_c_raster.get_data_element(neighb_row,neighb_col) * dx + this_elevation; //cout << "1.."; output[neighb_row][neighb_col] = new_elev; //cout << "2.."; @@ -5272,422 +7148,475 @@ LSDRaster LSDRasterModel::basic_valley_fill_critical_slope(float critical_slope, } -LSDRaster LSDRasterModel::basic_smooth(float central_pixel_weighting) -{ - // at the moment the boundary type can only be 0 and this is a periodic - // boundary type at the E and W boundaries. - Array2D new_data(NRows,NCols,NoDataValue); - float total_weighting; - float total_sum; - int rp1, rm1,cp1, cm1; - int boundary_type = 0; // This later allows the code to be flexible in terms of the boundary type. - // Currently only periodic boundaries are on offer. - for(int row = 0; row elev = RasterData.copy(); + Array2D new_elev(NRows, NCols, NoDataValue); + + // loop through each possible tilt direction and get the new array of elevations + // after tilting + if (tilt_boundary == "N") // north is tilt boundary - max elevation at the S + { + for (int i = 0; i < NRows; i++) + { + for (int j = 0; j < NCols; j++) { - if (rp1 == NRows) - { - rp1 = rm1; - } - if (rm1 == -1) - { - rm1 = rp1; - } - if (cp1 == NCols) - { - cp1 = 0; - } - if(cm1 == -1) + // first row stays at same elevation + if (i == 0) { new_elev[i][j] = elev[i][j]; } + // other rows, calculate based on angle + else { - cm1 = NCols-1; + float this_elev = elev[i][j]; + float length = (i + 1) * DataResolution; + new_elev[i][j] = (length * tan(angle)) + this_elev; + cout << "old elev: " << this_elev << " new elev: " << (length * tan(angle)) + this_elev << endl; } } - else + } + } + else if (tilt_boundary == "S") // south is tilt boundary - max elevation at the N + { + for (int i = 0; i < NRows; i++) + { + for (int j = 0; j < NCols; j++) { - if (rp1 == NRows) - { - rp1 = rm1; - } - if (rm1 == -1) + // last row stays at same elevation + if (i == NRows-1) { new_elev[i][j] = elev[i][j]; } + // other rows, calculate based on angle + else { - rm1 = rp1; + float this_elev = elev[i][j]; + float length = (NRows - i) * DataResolution; + new_elev[i][j] = (length * tan(angle)) + this_elev; + cout << "old elev: " << this_elev << " new elev: " << (length * tan(angle)) + this_elev << endl; } - if (cp1 == NCols) + } + } + } + else if (tilt_boundary == "E") // east is tilt boundary - max elevation at the W + { + for (int i = 0; i < NRows; i++) + { + for (int j = 0; j < NCols; j++) + { + // last col stays at same elevation + if (j == NCols-1) { new_elev[i][j] = elev[i][j]; } + // other cols, calculate based on angle + else { - cp1 = 0; + float this_elev = elev[i][j]; + float length = (NCols - i) * DataResolution; + new_elev[i][j] = (length * tan(angle)) + this_elev; + cout << "old elev: " << this_elev << " new elev: " << (length * tan(angle)) + this_elev << endl; } - if(cm1 == -1) + } + } + } + else if (tilt_boundary == "W") // west is tilt boundary - max elevation at the E + { + for (int i = 0; i < NRows; i++) + { + for (int j = 0; j < NCols; j++) + { + // first col stays at same elevation + if (j == 0) { new_elev[i][j] = elev[i][j]; } + // other cols, calculate based on angle + else { - cm1 = NCols-1; + float this_elev = elev[i][j]; + float length = (j + 1) * DataResolution; + new_elev[i][j] = (length * tan(angle)) + this_elev; + cout << "old elev: " << this_elev << " new elev: " << (length * tan(angle)) + this_elev << endl; } } + } + } + else + { + cout << "Warning - you haven't set your boundary to N, W, E, or S. Returning the original raster" << endl; + new_elev = elev; + } - if( RasterData[row][col] != NoDataValue) + // set the model to the array of new elevations + RasterData = new_elev; +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// This is the component of the model that is solved using the +// FASTSCAPE algorithm of Willett and Braun (2013) +// Uses Newton's method to solve incision if the slope exponent != 1 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void LSDRasterModel::fluvial_incision( void ) +{ + Array2D zeta=RasterData.copy(); + + // Step one, create donor "stack" etc. via FlowInfo + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); + LSDFlowInfo flow(boundary_conditions, temp); + vector nodeList = flow.get_SVector(); + int numNodes = nodeList.size(); + int node, row, col, receiver, receiver_row, receiver_col; + float drainageArea, dx, streamPowerFactor; + float K = get_K(); + + // these save a bit of computational expense. + float root_2 = pow(2, 0.5); + float dx_root2 = root_2*DataResolution; + float DR2 = DataResolution*DataResolution; + + + // this is only for bug checking + if (quiet == false && name == "debug" && NRows <= 10 && NCols <= 10) + { + cout << "Drainage area: " << endl; + for (int i=0; i=0; --i) + for (int i=0; i 100) { - total_weighting +=1; - total_sum += RasterData[rm1][col]; + //cout << "Too many iterations! epsilon is: " << abs(epsilon) << endl; + epsilon = 0.5e-6; } + } while (abs(epsilon) > 1e-6); + zeta[row][col] = new_zeta; + + // check for overexcavation + if(zeta[row][col] < zeta[receiver_row][receiver_col]) + { + //cout << "Warning, overexcavation. Setting to minimum slope." << endl; + zeta[row][col] = zeta[receiver_row][receiver_col]+(0.00001)*dx; } - // Now update the array - new_data[row][col] = total_sum/total_weighting; } } - - - // Step one, create donor "stack" etc. via FlowInfo - LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, new_data, GeoReferencingStrings); - return temp; + //return LSDRasterModel(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); + this->RasterData = zeta.copy(); } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - - -LSDRaster LSDRasterModel::basic_valley_fill_critical_slope(LSDRaster& S_c_raster, int contributing_pixel_threshold) +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// This is the component of the model that is solved using the +// FASTSCAPE algorithm of Willett and Braun (2013) +// Uses Newton's method to solve incision if the slope exponent != 1 +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void LSDRasterModel::fluvial_incision_with_uplift( void ) { - Array2D zeta=RasterData.copy(); - float sqrt2 = sqrt(2); // Step one, create donor "stack" etc. via FlowInfo - LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta, GeoReferencingStrings); + LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); + //cout << "The nodatavalue is: " << NoDataValue << endl; + LSDFlowInfo flow(boundary_conditions, temp); - // We need to fill the raster so we don't get internally drained catchments - cout << "I'm getting the flow info" << endl; - float slope_for_fill = 0.0001; - LSDRaster filled = temp.fill(slope_for_fill); - LSDFlowInfo flow(boundary_conditions, filled); - int contributing_pixels; + //for(int i = 0; i<4; i++) + //{ + // cout << "bc["< S_vec = flow.get_SVector(); + vector nodeList = flow.get_SVector(); + int numNodes = nodeList.size(); + int node, row, col, receiver, receiver_row, receiver_col; + float drainageArea, dx, streamPowerFactor; + float K = get_K(); + float U; - // also I want to keep track on which nodes have been processed - map ,bool> has_been_proc; - //And a map that check whether a node is in the river - map ,bool> is_river; - // This tests the exsitence of the node in a stack - map ,bool> is_in_DEM; - // Let's initialise my output as well - Array2D output(NRows,NCols, NoDataValue); - // Before processing my nodes, I need to set up my priority queue - // And set 2 priority queues that will work together - priority_queue< MyNode, vector, greater > PriorityQueue_1, PriorityQueue_2; - // I'll need a switch to know which PQ is getting filled - int working_PQ = 1; - - - // let's go through the stack and get the river nodes - // I'll fill my PQ1 with the river_nodes - cout << "Filling the priority queues. The no data value is: " << NoDataValue < this_node = {this_row, this_col}; + // these save a bit of computational expense. + float root_2 = pow(2, 0.5); + float dx_root2 = root_2*DataResolution; + float DR2 = DataResolution*DataResolution; - is_in_DEM[this_node] = true; - - // Initialising my node - MyNode this_MyNode; - this_MyNode.ID = this_node; - this_MyNode.elevation = this_elev; - // is it a river - if(contributing_pixels>=contributing_pixel_threshold) - { - PriorityQueue_1.push(this_MyNode); - is_river[this_node] = true; - output[this_row][this_col] = this_elev; - has_been_proc[this_node] = true; - } - else - { - is_river[this_node] = false; - output[this_row][this_col] = NoDataValue; - has_been_proc[this_node] = false; - } - } - // std::cout << "is empty??" - - //cout << "Now it is time to create the slopes" << endl; - // Alright, I have my list of node ordered by elevation (thanks to fastscape and stuff) - while(PriorityQueue_1.empty() == false || PriorityQueue_2.empty() == false) + // this is only for bug checking + if (quiet == false && name == "debug" && NRows <= 10 && NCols <= 10) { - //cout << "Whohoo, starting with the priority queueueueueue" << endl; - // I'll want my node - pair this_node; - MyNode this_MyNode; - if(working_PQ == 1) - { - this_MyNode = PriorityQueue_1.top(); - PriorityQueue_1.pop(); - } - else - { - this_MyNode = PriorityQueue_2.top(); - PriorityQueue_2.pop(); - } - this_node = this_MyNode.ID; - float this_elevation = this_MyNode.elevation; - int this_row = this_node.first; - int this_col = this_node.second; - // let me go through the neighbors - //cout << "z: " << this_elevation << " r: " << this_row << " c: " << this_col << endl; - vector row_neighbors = {this_row-1,this_row,this_row+1}; - vector col_neighbors = {this_col-1,this_col,this_col+1}; - for(int nR=0;nR= 0 && neighb_row <= NRows - 1 && neighb_col >= 0 && neighb_col <= NCols - 1) - { - - - //cout << "nr: " << neighb_row << " nc: " << neighb_col << endl; - pair nenode = {neighb_row,neighb_col}; - - if(not is_in_DEM[nenode]) - { - //cout << "Hey this neighbour isn't in the DEM" << endl; - } - else - { - if(not is_river[nenode] && not has_been_proc[nenode]) - { - // If we got this far it means the node needs to be processed. - float dx; - if((nR == 0 || nR == 2) && (nC == 0 || nC == 2)) - { - dx = sqrt2*DataResolution; - } - else - { - dx = DataResolution; - } - // backcalculating the slope - //cout << "I'm getting a slope at r: " << neighb_row << " c: " << neighb_col << " elev: " << this_elevation << endl; - float new_elev = S_c_raster.get_data_element(neighb_row,neighb_col) * dx + this_elevation; - //cout << "1.."; - output[neighb_row][neighb_col] = new_elev; - //cout << "2.."; - has_been_proc[nenode] = true; - //cout << "done" <=0; --i) + for (int i=0; i elev = RasterData.copy(); - Array2D new_elev(NRows, NCols, NoDataValue); + float epsilon; // in newton's method, z_n+1 = z_n - f(z_n)/f'(z_n) + // and here epsilon = f(z_n)/f'(z_n) + // f(z_n) = -z_n + z_old - dt*K*A^m*( (z_n-z_r)/dx )^n + // We differentiate the above equation to get f'(z_n) + // the resulting equation f(z_n)/f'(z_n) is seen below + float streamPowerFactor = K * pow(drainageArea, m) * timeStep; + float slope; - // loop through each possible tilt direction and get the new array of elevations - // after tilting - if (tilt_boundary == "N") // north is tilt boundary - max elevation at the S - { - for (int i = 0; i < NRows; i++) - { - for (int j = 0; j < NCols; j++) + // iterate until you converge on a solution. Uses Newton's method. + int iter_count = 0; + do { - // first row stays at same elevation - if (i == 0) { new_elev[i][j] = elev[i][j]; } - // other rows, calculate based on angle - else + slope = (new_zeta - zeta[receiver_row][receiver_col]) / dx; + + if(slope < 0) { - float this_elev = elev[i][j]; - float length = (i + 1) * DataResolution; - new_elev[i][j] = (length * tan(angle)) + this_elev; - cout << "old elev: " << this_elev << " new elev: " << (length * tan(angle)) + this_elev << endl; + epsilon = 0; } - } - } - } - else if (tilt_boundary == "S") // south is tilt boundary - max elevation at the N - { - for (int i = 0; i < NRows; i++) - { - for (int j = 0; j < NCols; j++) - { - // last row stays at same elevation - if (i == NRows-1) { new_elev[i][j] = elev[i][j]; } - // other rows, calculate based on angle else { - float this_elev = elev[i][j]; - float length = (NRows - i) * DataResolution; - new_elev[i][j] = (length * tan(angle)) + this_elev; - cout << "old elev: " << this_elev << " new elev: " << (length * tan(angle)) + this_elev << endl; + // Get epsilon based on f(z_n)/f'(z_n) + epsilon = (new_zeta - old_zeta + + streamPowerFactor * pow(slope, n) - timeStep*U) / + (1 + streamPowerFactor * (n/dx) * pow(slope, n-1)); + //cout << "slope: " << slope << " epsilon: " << epsilon << endl; } - } - } - } - else if (tilt_boundary == "E") // east is tilt boundary - max elevation at the W - { - for (int i = 0; i < NRows; i++) - { - for (int j = 0; j < NCols; j++) - { - // last col stays at same elevation - if (j == NCols-1) { new_elev[i][j] = elev[i][j]; } - // other cols, calculate based on angle - else + + new_zeta -= epsilon; + + iter_count++; + if(iter_count > 100) { - float this_elev = elev[i][j]; - float length = (NCols - i) * DataResolution; - new_elev[i][j] = (length * tan(angle)) + this_elev; - cout << "old elev: " << this_elev << " new elev: " << (length * tan(angle)) + this_elev << endl; + //cout << "Too many iterations! epsilon is: " << abs(epsilon) << endl; + epsilon = 0.5e-6; } - } - } - } - else if (tilt_boundary == "W") // west is tilt boundary - max elevation at the E - { - for (int i = 0; i < NRows; i++) - { - for (int j = 0; j < NCols; j++) + + } while (abs(epsilon) > 1e-6); + zeta[row][col] = new_zeta; + + // check for overexcavation + if(zeta[row][col] < zeta[receiver_row][receiver_col]) { - // first col stays at same elevation - if (j == 0) { new_elev[i][j] = elev[i][j]; } - // other cols, calculate based on angle - else - { - float this_elev = elev[i][j]; - float length = (j + 1) * DataResolution; - new_elev[i][j] = (length * tan(angle)) + this_elev; - cout << "old elev: " << this_elev << " new elev: " << (length * tan(angle)) + this_elev << endl; - } + //cout << "Warning, overexcavation. Setting to minimum slope." << endl; + zeta[row][col] = zeta[receiver_row][receiver_col]+(0.00001)*dx; } } } - else - { - cout << "Warning - you haven't set your boundary to N, W, E, or S. Returning the original raster" << endl; - new_elev = elev; - } - // set the model to the array of new elevations - RasterData = new_elev; + //return LSDRasterModel(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); + this->RasterData = zeta.copy(); + } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // This is the component of the model that is solved using the // FASTSCAPE algorithm of Willett and Braun (2013) // Uses Newton's method to solve incision if the slope exponent != 1 //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -void LSDRasterModel::fluvial_incision( void ) +void LSDRasterModel::fluvial_incision_with_uplift_and_variable_K( LSDRaster& K_raster ) { Array2D zeta=RasterData.copy(); // Step one, create donor "stack" etc. via FlowInfo LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); + //cout << "The nodatavalue is: " << NoDataValue << endl; LSDFlowInfo flow(boundary_conditions, temp); + + //for(int i = 0; i<4; i++) + //{ + // cout << "bc["< nodeList = flow.get_SVector(); int numNodes = nodeList.size(); int node, row, col, receiver, receiver_row, receiver_col; float drainageArea, dx, streamPowerFactor; - float K = get_K(); + float U; // these save a bit of computational expense. float root_2 = pow(2, 0.5); @@ -5731,17 +7660,17 @@ void LSDRasterModel::fluvial_incision( void ) switch (flow.retrieve_flow_length_code_of_node(node)) { case 0: - dx = -99; - break; + dx = -99; + break; case 1: dx = DataResolution; - break; + break; case 2: dx = dx_root2; - break; + break; default: - dx = -99; - break; + dx = -99; + break; } // some logic if n is close to 1. Saves a bit of computational expense. @@ -5753,14 +7682,20 @@ void LSDRasterModel::fluvial_incision( void ) // compute new elevation if node is not a base level node if (node != receiver) { - streamPowerFactor = K * pow(drainageArea, m) * (timeStep / dx); - zeta[row][col] = (zeta[row][col] + zeta[receiver_row][receiver_col] * streamPowerFactor) / + // get the uplift rate + U = get_uplift_rate_at_cell(row,col); + + // get the stream power factor + streamPowerFactor = K_raster.get_data_element(row,col) * pow(drainageArea, m) * (timeStep / dx); + + // calculate elevation + zeta[row][col] = (zeta[row][col] + + zeta[receiver_row][receiver_col]*streamPowerFactor + + timeStep*U) / (1 + streamPowerFactor); - // check for overexcavation if(zeta[row][col] < zeta[receiver_row][receiver_col]) { - //cout << "Warning, overexcavation. Setting to minimum slope." << endl; zeta[row][col] = zeta[receiver_row][receiver_col]+(0.00001)*dx; } } @@ -5768,16 +7703,22 @@ void LSDRasterModel::fluvial_incision( void ) else // this else loop is for when n is not close to one and you need an iterative solution { if (dx == -99) + { continue; + } float new_zeta = zeta[row][col]; + //float old_iter_zeta = zeta[row][col]; float old_zeta = zeta[row][col]; + // get the uplift rate + U = get_uplift_rate_at_cell(row,col); + float epsilon; // in newton's method, z_n+1 = z_n - f(z_n)/f'(z_n) // and here epsilon = f(z_n)/f'(z_n) // f(z_n) = -z_n + z_old - dt*K*A^m*( (z_n-z_r)/dx )^n // We differentiate the above equation to get f'(z_n) // the resulting equation f(z_n)/f'(z_n) is seen below - float streamPowerFactor = K * pow(drainageArea, m) * timeStep; + float streamPowerFactor = K_raster.get_data_element(row,col) * pow(drainageArea, m) * timeStep; float slope; // iterate until you converge on a solution. Uses Newton's method. @@ -5792,41 +7733,45 @@ void LSDRasterModel::fluvial_incision( void ) } else { - epsilon = (new_zeta - old_zeta + streamPowerFactor * pow(slope, n)) / + // Get epsilon based on f(z_n)/f'(z_n) + epsilon = (new_zeta - old_zeta + + streamPowerFactor * pow(slope, n) - timeStep*U) / (1 + streamPowerFactor * (n/dx) * pow(slope, n-1)); } + new_zeta -= epsilon; - // This limits the number of iterations iter_count++; if(iter_count > 100) { - //cout << "Too many iterations! epsilon is: " << abs(epsilon) << endl; epsilon = 0.5e-6; } + } while (abs(epsilon) > 1e-6); zeta[row][col] = new_zeta; // check for overexcavation if(zeta[row][col] < zeta[receiver_row][receiver_col]) { - //cout << "Warning, overexcavation. Setting to minimum slope." << endl; zeta[row][col] = zeta[receiver_row][receiver_col]+(0.00001)*dx; } - } } - //return LSDRasterModel(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); this->RasterData = zeta.copy(); + } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + + + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // This is the component of the model that is solved using the // FASTSCAPE algorithm of Willett and Braun (2013) // Uses Newton's method to solve incision if the slope exponent != 1 -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -void LSDRasterModel::fluvial_incision_with_uplift( void ) +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K( LSDRaster& Urate_raster, LSDRaster& K_raster ) { Array2D zeta=RasterData.copy(); @@ -5844,7 +7789,6 @@ void LSDRasterModel::fluvial_incision_with_uplift( void ) int numNodes = nodeList.size(); int node, row, col, receiver, receiver_row, receiver_col; float drainageArea, dx, streamPowerFactor; - float K = get_K(); float U; // these save a bit of computational expense. @@ -5871,7 +7815,7 @@ void LSDRasterModel::fluvial_incision_with_uplift( void ) for (int i=0; i 100) { - //cout << "Too many iterations! epsilon is: " << abs(epsilon) << endl; epsilon = 0.5e-6; } @@ -5989,29 +7926,22 @@ void LSDRasterModel::fluvial_incision_with_uplift( void ) // check for overexcavation if(zeta[row][col] < zeta[receiver_row][receiver_col]) { - //cout << "Warning, overexcavation. Setting to minimum slope." << endl; zeta[row][col] = zeta[receiver_row][receiver_col]+(0.00001)*dx; } } } - - //return LSDRasterModel(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); this->RasterData = zeta.copy(); } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - - - - -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // This is the component of the model that is solved using the // FASTSCAPE algorithm of Willett and Braun (2013) // Uses Newton's method to solve incision if the slope exponent != 1 -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -void LSDRasterModel::fluvial_incision_with_uplift_and_variable_K( LSDRaster& K_raster ) +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K_adaptive_timestep( LSDRaster& Urate_raster, LSDRaster& K_raster ) { Array2D zeta=RasterData.copy(); @@ -6036,7 +7966,6 @@ void LSDRasterModel::fluvial_incision_with_uplift_and_variable_K( LSDRaster& K_r float dx_root2 = root_2*DataResolution; float DR2 = DataResolution*DataResolution; - // this is only for bug checking if (quiet == false && name == "debug" && NRows <= 10 && NCols <= 10) { @@ -6050,130 +7979,348 @@ void LSDRasterModel::fluvial_incision_with_uplift_and_variable_K( LSDRaster& K_r } } - // Step two calculate new height - //for (int i=numNodes-1; i>=0; --i) - for (int i=0; i 100) + { + cout << "There is an overexcavation that has not been fixed by a very small timestep." << endl; + cout << "I think there is a numerical instability and I am killing the computation." << endl; + cout << "This does not mean that you are a bad person." << endl; + cout << "zeta old is" << zeta_old << endl; + cout << "zeta_ reciever is: " << zeta[receiver_row][receiver_col] << endl; + cout << "Streampower factor: " << streamPowerFactor << endl; + cout << "denominator is: " << (1 + streamPowerFactor) << endl; + cout << "uplift term is: " << timeStep*U << endl; + cout << "reciever term is: " << zeta[receiver_row][receiver_col]*streamPowerFactor << endl; + exit(EXIT_FAILURE); + } + + + } + } + } + else // this else loop is for when n is not close to one and you need an iterative solution + { + if (dx == -99) + { + continue; + } + float new_zeta = zeta[row][col]; + //float old_iter_zeta = zeta[row][col]; + float old_zeta = zeta[row][col]; + + // get the uplift rate + U = Urate_raster.get_data_element(row,col); + + float epsilon; // in newton's method, z_n+1 = z_n - f(z_n)/f'(z_n) + // and here epsilon = f(z_n)/f'(z_n) + // f(z_n) = -z_n + z_old - dt*K*A^m*( (z_n-z_r)/dx )^n + // We differentiate the above equation to get f'(z_n) + // the resulting equation f(z_n)/f'(z_n) is seen below + float streamPowerFactor = K_raster.get_data_element(row,col) * pow(drainageArea, m) * timeStep; + float slope; + + // iterate until you converge on a solution. Uses Newton's method. + int iter_count = 0; + do + { + slope = (new_zeta - zeta[receiver_row][receiver_col]) / dx; + + if(slope < 0) + { + epsilon = 0; + } + else + { + // Get epsilon based on f(z_n)/f'(z_n) + epsilon = (new_zeta - old_zeta + + streamPowerFactor * pow(slope, n) - timeStep*U) / + (1 + streamPowerFactor * (n/dx) * pow(slope, n-1)); + } + + new_zeta -= epsilon; + + iter_count++; + if(iter_count > 100) + { + epsilon = 0.5e-6; + } + + } while (abs(epsilon) > 1e-6); + zeta[row][col] = new_zeta; + + // check for overexcavation + if(zeta[row][col] <= zeta[receiver_row][receiver_col]) + { + it_has_overexcavated = true; + + // kill the program if the number of overexcavation steps get too small + if (timestep_iterator> 100) + { + cout << "There is an overexcavation that has not been fixed by a very small timestep." << endl; + cout << "I think there is a numerical instability and I am killing the computation." << endl; + cout << "This does not mean that you are a bad person." << endl; + exit(EXIT_FAILURE); + } + } + } // end logic for n not equal to one + + if (it_has_overexcavated) + { + //cout << "Whoops I had an overexcavation! " << endl; + //cout << " The number of times this has happend this timestep is: " << timestep_iterator << endl; + timeStep = timeStep*0.25; + timestep_iterator++; + i = numNodes; + } + + } // end logic for node loop + + } while ( it_has_overexcavated); + + // if the model didn't overexcavate at all, then increase the timestep. + //cout << "The timestep iterator is: " << timestep_iterator << endl; + if(timestep_iterator ==0) + { + timeStep = 2*timeStep; + if (timeStep > maxtimeStep) { - case 0: - dx = -99; - break; - case 1: - dx = DataResolution; - break; - case 2: - dx = dx_root2; - break; - default: - dx = -99; - break; + timeStep = maxtimeStep; } + } - // some logic if n is close to 1. Saves a bit of computational expense. - if (abs(n - 1) < 0.0001) + this->RasterData = zeta.copy(); + +} +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + + + + + +void LSDRasterModel::process_transient_file(string transient_infile_name, vector& phases,vector& outlet_elevations) +{ + vector these_phases; + vector these_elevs; + + // make sure the filename works + ifstream ifs(transient_infile_name.c_str()); + if( ifs.fail() ) + { + cout << "\nFATAL ERROR: Trying to load csv data file, but the file" << transient_infile_name + << " doesn't exist; LSDRasterModel::process_transient_file" << endl; + exit(EXIT_FAILURE); + } + else + { + cout << "I have opened the csv file." << endl; + } + + // initiate the string to hold the file + string line_from_file; + vector empty_string_vec; + vector this_string_vec; + string temp_string; + + // get the headers from the first line + getline(ifs, line_from_file); + + // reset the string vec + this_string_vec = empty_string_vec; + + // now loop through the rest of the lines, getting the data. + while( getline(ifs, line_from_file)) + { + //cout << "Getting line, it is: " << line_from_file << endl; + // reset the string vec + this_string_vec = empty_string_vec; + + // create a stringstream + stringstream ss(line_from_file); + + while( ss.good() ) { - if (dx == -99) - continue; + string substr; + getline( ss, substr, ',' ); + + // remove the spaces + substr.erase(remove_if(substr.begin(), substr.end(), ::isspace), substr.end()); + + // remove control characters + substr.erase(remove_if(substr.begin(), substr.end(), ::iscntrl), substr.end()); + + // add the string to the string vec + this_string_vec.push_back( substr ); + } + + these_phases.push_back( atof( this_string_vec[0].c_str() ) ); + these_elevs.push_back( atof( this_string_vec[1].c_str() ) ); + + + } - // compute new elevation if node is not a base level node - if (node != receiver) - { - // get the uplift rate - U = get_uplift_rate_at_cell(row,col); - // get the stream power factor - streamPowerFactor = K_raster.get_data_element(row,col) * pow(drainageArea, m) * (timeStep / dx); + ifs.close(); + phases = these_phases; + outlet_elevations = these_elevs; - // calculate elevation - zeta[row][col] = (zeta[row][col] - + zeta[receiver_row][receiver_col]*streamPowerFactor - + timeStep*U) / - (1 + streamPowerFactor); +} - if(zeta[row][col] < zeta[receiver_row][receiver_col]) - { - zeta[row][col] = zeta[receiver_row][receiver_col]+(0.00001)*dx; - } - } - } - else // this else loop is for when n is not close to one and you need an iterative solution - { - if (dx == -99) - { - continue; - } - float new_zeta = zeta[row][col]; - //float old_iter_zeta = zeta[row][col]; - float old_zeta = zeta[row][col]; - // get the uplift rate - U = get_uplift_rate_at_cell(row,col); +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This processes the baselevel file so you get the timesteps and a vector of +// vectors with the elevations of the channel at each time +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDRasterModel::process_baselevel( LSDSpatialCSVReader& source_points_data, string timing_prefix, + float timing_multiplier, int n_time_columns, int phase_steps, + vector& phases_vec, vector< vector >& elevation_vecvec) +{ + // loop through the timings - float epsilon; // in newton's method, z_n+1 = z_n - f(z_n)/f'(z_n) - // and here epsilon = f(z_n)/f'(z_n) - // f(z_n) = -z_n + z_old - dt*K*A^m*( (z_n-z_r)/dx )^n - // We differentiate the above equation to get f'(z_n) - // the resulting equation f(z_n)/f'(z_n) is seen below - float streamPowerFactor = K_raster.get_data_element(row,col) * pow(drainageArea, m) * timeStep; - float slope; + cout << endl << endl << "===================================" << endl; + cout << "Processing baselevel. I will give you the outlet only" << endl; - // iterate until you converge on a solution. Uses Newton's method. - int iter_count = 0; - do - { - slope = (new_zeta - zeta[receiver_row][receiver_col]) / dx; + vector< vector > elevations; + vector phases; - if(slope < 0) - { - epsilon = 0; - } - else - { - // Get epsilon based on f(z_n)/f'(z_n) - epsilon = (new_zeta - old_zeta - + streamPowerFactor * pow(slope, n) - timeStep*U) / - (1 + streamPowerFactor * (n/dx) * pow(slope, n-1)); - } + string num_string; + string col_string; + float this_phase; + vector this_elevation; - new_zeta -= epsilon; + string elevation_string = "elevation(m)"; - iter_count++; - if(iter_count > 100) - { - epsilon = 0.5e-6; - } + phases.push_back(0); + elevations.push_back(source_points_data.data_column_to_float(elevation_string)); - } while (abs(epsilon) > 1e-6); - zeta[row][col] = new_zeta; + for (int i = 1 ; i <= n_time_columns; i++) + { + num_string = itoa(i*phase_steps); + col_string = timing_prefix+num_string; - // check for overexcavation - if(zeta[row][col] < zeta[receiver_row][receiver_col]) - { - zeta[row][col] = zeta[receiver_row][receiver_col]+(0.00001)*dx; - } + this_phase = float(i*phase_steps)*timing_multiplier; + + if (source_points_data.is_column_in_csv(col_string)) + { + //cout << "I found the column: " << col_string << " that is at time: " << this_phase << endl; + + phases.push_back(this_phase); + this_elevation = source_points_data.data_column_to_float(col_string); + elevations.push_back( this_elevation ); + + cout << "Phase: " << this_phase << " and outlet elevation: " << this_elevation[0] << endl; } + + cout << "==========================================" << endl << endl; + + + } - this->RasterData = zeta.copy(); + + phases_vec = phases; + elevation_vecvec = elevations; } + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +// This processes the baselevel file so you get the timesteps and a vector of +// vectors with the elevations of the channel at each time +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void LSDRasterModel::baselevel_run_area_switch( LSDSpatialCSVReader& source_points_data, string column_name) +{ + + vector category_vector = source_points_data.data_column_to_float(column_name); + vector category_switch; + + cout << endl << endl << "==========================================" << endl; + cout << "Setting baselevel category, I am using column: " << column_name << endl; + cout << "Any values in this column that are less than or equal to 0 will lead to a baselevel channel." << endl; + cout << "==========================================" << endl << endl << endl; + + for (int i = 0; i< int(category_vector.size()); i++) + { + //cout << i << ": " << category_vector[i] << endl; + + if (category_vector[i] <= 0) + { + category_switch.push_back("0"); + } + else + { + category_switch.push_back("1"); + } + } + source_points_data.add_data_column("baselevel_code", category_switch); +} + @@ -6183,9 +8330,21 @@ void LSDRasterModel::fluvial_incision_with_uplift_and_variable_K( LSDRaster& K_r // This is the component of the model that is solved using the // FASTSCAPE algorithm of Willett and Braun (2013) // Uses Newton's method to solve incision if the slope exponent != 1 +// +// IMPORTANT: Convention is that the fall rate is positive for a dropping base level +// So positive fall rates will lower the base level nodes //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K( LSDRaster& Urate_raster, LSDRaster& K_raster ) +void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K_adaptive_timestep_impose_baselevel( LSDRaster& Urate_raster, + LSDRaster& K_raster, LSDSpatialCSVReader& source_points_data, + float bl_fall_rate, + bool let_timestep_increase, string column_name) { + + + // We need to impose the baselevel channels first + impose_channels(source_points_data, column_name); + + // now we get the raster Array2D zeta=RasterData.copy(); // Step one, create donor "stack" etc. via FlowInfo @@ -6193,10 +8352,12 @@ void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K( LSDRa //cout << "The nodatavalue is: " << NoDataValue << endl; LSDFlowInfo flow(boundary_conditions, temp); - //for(int i = 0; i<4; i++) - //{ - // cout << "bc["< ni = source_points_data.get_nodeindices_from_lat_long(flow); + vector start_elev = source_points_data.data_column_to_float(column_name); + vector end_elev = start_elev; vector nodeList = flow.get_SVector(); int numNodes = nodeList.size(); @@ -6209,7 +8370,6 @@ void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K( LSDRa float dx_root2 = root_2*DataResolution; float DR2 = DataResolution*DataResolution; - // this is only for bug checking if (quiet == false && name == "debug" && NRows <= 10 && NCols <= 10) { @@ -6223,150 +8383,313 @@ void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K( LSDRa } } - // Step two calculate new height - //for (int i=numNodes-1; i>=0; --i) - for (int i=0; i bl_map; + map::iterator it; + for (int n = 0; n < int(ni.size()); n++) { - case 0: - dx = -99; - break; - case 1: - dx = DataResolution; - break; - case 2: - dx = dx_root2; - break; - default: - dx = -99; - break; + bl_map[ ni[n] ] = start_elev[n] - bl_drop; + end_elev[n] = start_elev[n] - bl_drop; } - - // some logic if n is close to 1. Saves a bit of computational expense. - if (abs(n - 1) < 0.0001) + + // Calculate new heights + for (int i=0; i 100) + { + cout << "There is an overexcavation that has not been fixed by a very small timestep." << endl; + cout << "I think there is a numerical instability and I am killing the computation." << endl; + cout << "This does not mean that you are a bad person." << endl; + cout << "zeta old is: " << zeta_old << endl; + cout << "zeta reciever is: " << zeta[receiver_row][receiver_col] << endl; + cout << "Streampower factor: " << streamPowerFactor << endl; + cout << "denominator is: " << (1 + streamPowerFactor) << endl; + cout << "uplift term is: " << timeStep*U << endl; + cout << "reciever term is: " << zeta[receiver_row][receiver_col]*streamPowerFactor << endl; + + if (bl_map.find( receiver ) != bl_map.end()) + { + cout << "The reciever of this problem node is in the imposed channel." << endl; + } + + exit(EXIT_FAILURE); + } + } + } } - else + else // this else loop is for when n is not close to one and you need an iterative solution { - // Get epsilon based on f(z_n)/f'(z_n) - epsilon = (new_zeta - old_zeta - + streamPowerFactor * pow(slope, n) - timeStep*U) / - (1 + streamPowerFactor * (n/dx) * pow(slope, n-1)); - } + if (dx == -99) + { + continue; + } + float new_zeta = zeta[row][col]; + //float old_iter_zeta = zeta[row][col]; + float old_zeta = zeta[row][col]; - new_zeta -= epsilon; + // get the uplift rate + U = Urate_raster.get_data_element(row,col); - iter_count++; - if(iter_count > 100) + float epsilon; // in newton's method, z_n+1 = z_n - f(z_n)/f'(z_n) + // and here epsilon = f(z_n)/f'(z_n) + // f(z_n) = -z_n + z_old - dt*K*A^m*( (z_n-z_r)/dx )^n + // We differentiate the above equation to get f'(z_n) + // the resulting equation f(z_n)/f'(z_n) is seen below + float streamPowerFactor = K_raster.get_data_element(row,col) * pow(drainageArea, m) * timeStep; + float slope; + + // iterate until you converge on a solution. Uses Newton's method. + int iter_count = 0; + do + { + slope = (new_zeta - zeta[receiver_row][receiver_col]) / dx; + + if(slope < 0) + { + epsilon = 0; + } + else + { + // Get epsilon based on f(z_n)/f'(z_n) + epsilon = (new_zeta - old_zeta + + streamPowerFactor * pow(slope, n) - timeStep*U) / + (1 + streamPowerFactor * (n/dx) * pow(slope, n-1)); + } + + new_zeta -= epsilon; + + iter_count++; + if(iter_count > 100) + { + epsilon = 0.5e-6; + } + + } while (abs(epsilon) > 1e-6); + zeta[row][col] = new_zeta; + + // check for overexcavation + if(zeta[row][col] <= zeta[receiver_row][receiver_col]) + { + it_has_overexcavated = true; + + // kill the program if the number of overexcavation steps get too small + if (timestep_iterator> 100) + { + cout << "There is an overexcavation that has not been fixed by a very small timestep." << endl; + cout << "I think there is a numerical instability and I am killing the computation." << endl; + cout << "I am so sorry for your simulation." << endl; + cout << "This does not mean that you are a bad person." << endl; + exit(EXIT_FAILURE); + } + } + } // end logic for n not equal to one + + if (it_has_overexcavated) { - epsilon = 0.5e-6; + //cout << "Whoops I had an overexcavation! " << endl; + //cout << " The number of times this has happend this timestep is: " << timestep_iterator << endl; + timeStep = timeStep*0.25; + timestep_iterator++; + i = numNodes; } + } // end logic for the if it is in the baselevel node list + } // end logic for node loop - } while (abs(epsilon) > 1e-6); - zeta[row][col] = new_zeta; + } while ( it_has_overexcavated); - // check for overexcavation - if(zeta[row][col] < zeta[receiver_row][receiver_col]) + // if the model didn't overexcavate at all, then increase the timestep. + //cout << "The timestep iterator is: " << timestep_iterator << endl; + if(let_timestep_increase) + { + if(timestep_iterator ==0) + { + timeStep = 2*timeStep; + if (timeStep > maxtimeStep) { - zeta[row][col] = zeta[receiver_row][receiver_col]+(0.00001)*dx; + timeStep = maxtimeStep; } } } - this->RasterData = zeta.copy(); + // we need to update the baselevel nodes + source_points_data.data_column_add_float(column_name, -bl_drop); + + this->RasterData = zeta.copy(); } -//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // This is the component of the model that is solved using the // FASTSCAPE algorithm of Willett and Braun (2013) // Uses Newton's method to solve incision if the slope exponent != 1 +// +// IMPORTANT: Convention is that the fall rate is positive for a dropping base level +// So positive fall rates will lower the base level nodes //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K_adaptive_timestep( LSDRaster& Urate_raster, LSDRaster& K_raster ) +void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K_adaptive_timestep_impose_baselevel( LSDRaster& Urate_raster, + LSDRaster& K_raster, LSDSpatialCSVReader& source_points_data, + vector bl_fall_rate, + bool let_timestep_increase,bool use_adaptive_timestep, + float minimum_slope, string column_name) { + //cout << endl << endl << "ENTERING incision with timestep " << timeStep << endl; + + // We need to impose the baselevel channels first + // This frequently has the effect of creating loads of baselevel nodes because the + // source points do not have enforced slopes + string bl_col_name = "baselevel_code"; + vector bl_code = source_points_data.data_column_to_int(bl_col_name); + + //Array2D zubu=RasterData.copy(); + //cout << "Y1111111 the value at crazy node is: " << zubu[617][761] << endl; + + // we need to fill the raster and then impose the channels + fill_raster(minimum_slope); + + //Array2D waba=RasterData.copy(); + //cout << "WAAAAAA the value at crazy node is: " << waba[617][761] << endl; + + //cout << "BL code of outlet is: " << bl_code[0] << endl; + //cout << "BL code of next pixel is: " << bl_code[1] << endl; + impose_channels(source_points_data, column_name, bl_code); + + // now we get the raster Array2D zeta=RasterData.copy(); + zeta_old = RasterData.copy(); + + //cout << "YoYoYo the value at crazy node is: " << zeta[617][761] << endl; // Step one, create donor "stack" etc. via FlowInfo LSDRaster temp(NRows, NCols, XMinimum, YMinimum, DataResolution, NoDataValue, zeta); //cout << "The nodatavalue is: " << NoDataValue << endl; LSDFlowInfo flow(boundary_conditions, temp); - //for(int i = 0; i<4; i++) - //{ - // cout << "bc["< bl_drop; + vector ni = source_points_data.get_nodeindices_from_lat_long(flow); + vector start_elev = source_points_data.data_column_to_float(column_name); + vector end_elev = start_elev; + + //cout << "The outlet elevation is: " << start_elev[0] << endl; + + string area_col_name = "area"; + //cout << "If I have a baselevel code for reading area, the area column needs to be: " << area_col_name << endl; + vector bl_area = source_points_data.data_column_to_float(area_col_name); + + //cout.precision(9); + //cout << "Areas of first three upstream pixels are: " << endl << bl_area[1] << endl << bl_area[2] << endl << bl_area[3] << endl; + + // A routine for bug checking. If you are satisfied it works set to false. + bool check_first_two_nodes = false; + if (check_first_two_nodes) + { + int r,c,r_o,c_o; + int outlet_ni = ni[0]; + flow.retrieve_current_row_and_col(outlet_ni, r_o, c_o); + float outlet_elev = zeta[r_o][c_o]; + + int upstream_ni = ni[1]; + flow.retrieve_current_row_and_col(upstream_ni, r, c); + float upstream_elev = zeta[r][c]; + + cout << "From the raster! The r,c of outlet is: " << r_o << "," << c_o << endl; + cout << "The outlet is at " << outlet_elev << " m and the upstream node is at " << upstream_elev << " m." << endl; + } + + + int N_BL_nodes = int(ni.size()); + int BL_fall_rate_nodes = int(bl_fall_rate.size()); + if (N_BL_nodes != BL_fall_rate_nodes) + { + cout << "Hey! Something is wrong. The number of fall rate nodes (" << BL_fall_rate_nodes << ")" << endl; + cout << "is not the same as the number of baselevel nodes (" << N_BL_nodes << endl; + cout << "I am exiting and you need to check your baselevel files" << endl; + exit(0); + } vector nodeList = flow.get_SVector(); int numNodes = nodeList.size(); @@ -6397,6 +8720,7 @@ void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K_adapti int timestep_iterator = 0; // this checks how many times you have reduced the // timestep bool it_has_overexcavated; + //cout << "TUBBO entering the adaptive timestep loop" << endl; do { // reset the overexcavation switch @@ -6406,168 +8730,371 @@ void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K_adapti // for the adaptive timestepping zeta=RasterData.copy(); + //bl_drop = timeStep*bl_fall_rate; + //cout << "Timestep is: " << timeStep << " and bl_drop is: " << bl_drop << endl; + //cout << "Elevation of node 10 is: " << start_elev[10] << endl; + + // Make a map! Map map map. This speeds the searching + // Also gets the final state of the imposed baselevel nodes + // We need to to this within the timestepping loop so that + // the adaptive timestep can work + map bl_map; + map bl_to_points_map; // this maps the node number to the index into the baselevel nodes + map::iterator it; + map::iterator it_bl_to_points; + //cout << "Setting the baselevel nodes based on the timestep" << endl; + for (int n = 0; n < int(ni.size()); n++) + { + // The following logic is for separating nodes where we use the baselevel fall + // rate from nodes where we just take the area + if (bl_code[n] == 0) // if the code is 0, we keep an elevation + { + bl_map[ ni[n] ] = start_elev[n] - timeStep*bl_fall_rate[n]; + end_elev[n] = start_elev[n] - timeStep*bl_fall_rate[n]; + } + else // if not, we keep a reference to the index + { + bl_to_points_map[ ni[n] ] = n; + + // some bug checking + //if (n == 1) + //{ + // cout << " n == 1, node is " << ni[n] << endl; + //} + } + } + // Calculate new heights for (int i=0; i 100) - { - cout << "There is an overexcavation that has not been fixed by a very small timestep." << endl; - cout << "I think there is a numerical instability and I am killing the computation." << endl; - cout << "This does not mean that you are a bad person." << endl; - cout << "zeta old is" << zeta_old << endl; - cout << "zeta_ reciever is: " << zeta[receiver_row][receiver_col] << endl; - cout << "Streampower factor: " << streamPowerFactor << endl; - cout << "denominator is: " << (1 + streamPowerFactor) << endl; - cout << "uplift term is: " << timeStep*U << endl; - cout << "reciever term is: " << zeta[receiver_row][receiver_col]*streamPowerFactor << endl; - exit(EXIT_FAILURE); - } + // get the uplift rate + U = Urate_raster.get_data_element(row,col); + // get the stream power factor + streamPowerFactor = K_raster.get_data_element(row,col) * pow(drainageArea, m) * (timeStep / dx); - } - } - } - else // this else loop is for when n is not close to one and you need an iterative solution - { - if (dx == -99) - { - continue; - } - float new_zeta = zeta[row][col]; - //float old_iter_zeta = zeta[row][col]; - float old_zeta = zeta[row][col]; + // calculate elevation + float zeta_old = zeta[row][col]; + zeta[row][col] = (zeta[row][col] + + zeta[receiver_row][receiver_col]*streamPowerFactor + + timeStep*U) / + (1 + streamPowerFactor); - // get the uplift rate - U = Urate_raster.get_data_element(row,col); + // ====================== SOME BUG CHECK LOGIC ================= + // logic for bug checking the bl_area nodes + bool check_area_bl_nodes = false; + if (check_area_bl_nodes) + { + if ( bl_to_points_map.find( node ) != bl_to_points_map.end()) + { + if ( bl_to_points_map.find( receiver ) == bl_to_points_map.end()) + { + if (receiver == ni[0]) + { + cout << "BL number " << bl_to_points_map[node] << " is draining to the outlet" << endl; + } + else + { + cout << "Warning the receiver node of bl number " << bl_to_points_map[node] << " is not in the bl list and is not draining to the outlet." << endl; + cout << "That means flow is being diverted from the imposed channel" << endl; + } + } + else + { + if (bl_to_points_map[node] - bl_to_points_map[receiver] != 1) + { + cout << "Something weird here, the bl node is " << bl_to_points_map[node] << " and r: " << bl_to_points_map[receiver] << endl; + } + } + + // This is an area baselevel node + int search_node = 1; + if (bl_to_points_map[node] == search_node) + { + //it_bl_to_points = bl_to_points_map.find(4); + cout << "This is the " << search_node << " channel pixel upstream of the outlet, its node is " << node << endl; + cout << "Area: " << drainageArea << " and the stream power factor is: " << streamPowerFactor << endl; + cout << "z_r: " << zeta[receiver_row][receiver_col] << " zold: " << zeta_old << " z_new: " << zeta[row][col] << endl; + cout << "r.c of this node is " << row << "," << col << endl; + } + } + } + //================= END BUG CHECK LOGIC ====================== + + // check for overexcavation + if(zeta[row][col] <= zeta[receiver_row][receiver_col]+dx*minimum_slope) + { + // See if the overexcavation is for a baselevel node + if (bl_map.find( receiver ) != bl_map.end()) + { + cout << "HEY HEY JABBA I found overexcavation and it is because of a baselevel node!" << endl; + } - float epsilon; // in newton's method, z_n+1 = z_n - f(z_n)/f'(z_n) - // and here epsilon = f(z_n)/f'(z_n) - // f(z_n) = -z_n + z_old - dt*K*A^m*( (z_n-z_r)/dx )^n - // We differentiate the above equation to get f'(z_n) - // the resulting equation f(z_n)/f'(z_n) is seen below - float streamPowerFactor = K_raster.get_data_element(row,col) * pow(drainageArea, m) * timeStep; - float slope; + //cout << "HEY HEY JABBA I found overexcavation!" << endl; + it_has_overexcavated = true; - // iterate until you converge on a solution. Uses Newton's method. - int iter_count = 0; - do - { - slope = (new_zeta - zeta[receiver_row][receiver_col]) / dx; + // If you are not using the adaptive timestep + // Fix the pixel to the minimum slope + if (use_adaptive_timestep == false) + { + it_has_overexcavated = false; + zeta[row][col] = zeta[receiver_row][receiver_col]+dx*minimum_slope; + } - if(slope < 0) - { - epsilon = 0; + if (timestep_iterator> 100) + { + cout << "There is an overexcavation that has not been fixed by a very small timestep." << endl; + cout << "I think there is a numerical instability and I am killing the computation." << endl; + cout << "This does not mean that you are a bad person." << endl; + cout << "zeta old is: " << zeta_old << endl; + cout << "zeta reciever is: " << zeta[receiver_row][receiver_col] << endl; + cout << "Streampower factor: " << streamPowerFactor << endl; + cout << "denominator is: " << (1 + streamPowerFactor) << endl; + cout << "uplift term is: " << timeStep*U << endl; + cout << "reciever term is: " << zeta[receiver_row][receiver_col]*streamPowerFactor << endl; + + if (bl_map.find( receiver ) != bl_map.end()) + { + cout << "The reciever of this problem node is in the imposed channel." << endl; + } + + exit(EXIT_FAILURE); + } + } } - else + } + else // this else loop is for when n is not close to one and you need an iterative solution + { + if (dx == -99) { - // Get epsilon based on f(z_n)/f'(z_n) - epsilon = (new_zeta - old_zeta - + streamPowerFactor * pow(slope, n) - timeStep*U) / - (1 + streamPowerFactor * (n/dx) * pow(slope, n-1)); + continue; } + float new_zeta = zeta[row][col]; + //float old_iter_zeta = zeta[row][col]; + float old_zeta = zeta[row][col]; - new_zeta -= epsilon; + // get the uplift rate + U = Urate_raster.get_data_element(row,col); - iter_count++; - if(iter_count > 100) + float epsilon; // in newton's method, z_n+1 = z_n - f(z_n)/f'(z_n) + // and here epsilon = f(z_n)/f'(z_n) + // f(z_n) = -z_n + z_old - dt*K*A^m*( (z_n-z_r)/dx )^n + // We differentiate the above equation to get f'(z_n) + // the resulting equation f(z_n)/f'(z_n) is seen below + float streamPowerFactor = K_raster.get_data_element(row,col) * pow(drainageArea, m) * timeStep; + float slope; + + // iterate until you converge on a solution. Uses Newton's method. + int iter_count = 0; + do { - epsilon = 0.5e-6; - } + slope = (new_zeta - zeta[receiver_row][receiver_col]) / dx; - } while (abs(epsilon) > 1e-6); - zeta[row][col] = new_zeta; + if(slope < 0) + { + epsilon = 0; + } + else + { + // Get epsilon based on f(z_n)/f'(z_n) + epsilon = (new_zeta - old_zeta + + streamPowerFactor * pow(slope, n) - timeStep*U) / + (1 + streamPowerFactor * (n/dx) * pow(slope, n-1)); + } - // check for overexcavation - if(zeta[row][col] <= zeta[receiver_row][receiver_col]) - { - it_has_overexcavated = true; + new_zeta -= epsilon; - // kill the program if the number of overexcavation steps get too small - if (timestep_iterator> 100) + iter_count++; + if(iter_count > 100) + { + epsilon = 0.5e-6; + } + + } while (abs(epsilon) > 1e-6); + zeta[row][col] = new_zeta; + + // check for overexcavation + if(zeta[row][col] <= zeta[receiver_row][receiver_col]) { - cout << "There is an overexcavation that has not been fixed by a very small timestep." << endl; - cout << "I think there is a numerical instability and I am killing the computation." << endl; - cout << "This does not mean that you are a bad person." << endl; - exit(EXIT_FAILURE); + it_has_overexcavated = true; + + // If you are not using the adaptive timestep let it overexcavate + if (use_adaptive_timestep == false) + { + it_has_overexcavated = false; + zeta[row][col] = zeta[receiver_row][receiver_col]+dx*minimum_slope; + } + + // kill the program if the number of overexcavation steps get too small + if (timestep_iterator> 100) + { + cout << "There is an overexcavation that has not been fixed by a very small timestep." << endl; + cout << "I think there is a numerical instability and I am killing the computation." << endl; + cout << "I am so sorry for your simulation." << endl; + cout << "This does not mean that you are a bad person." << endl; + exit(EXIT_FAILURE); + } } + } // end logic for n not equal to one + + if (it_has_overexcavated) + { + cout << "Whoops I had an overexcavation! " << endl; + cout << " The number of times this has happend this timestep is: " << timestep_iterator << endl; + timeStep = timeStep*0.25; + timestep_iterator++; + i = numNodes; } - } // end logic for n not equal to one + } // end logic for the if it is in the baselevel node list + } // end logic for node loop - if (it_has_overexcavated) + } while ( it_has_overexcavated); + + // if the model didn't overexcavate at all, then increase the timestep. + //cout << "The timestep iterator is: " << timestep_iterator << endl; + + // If you are not using the adaptive timestep let it overexcavate + if (use_adaptive_timestep == false) + { + let_timestep_increase = false; + } + + if(let_timestep_increase) + { + if(timestep_iterator ==0) + { + timeStep = 2*timeStep; + if (timeStep > maxtimeStep) { - //cout << "Whoops I had an overexcavation! " << endl; - //cout << " The number of times this has happend this timestep is: " << timestep_iterator << endl; - timeStep = timeStep*0.25; - timestep_iterator++; - i = numNodes; + timeStep = maxtimeStep; } + } + } - } // end logic for node loop + // +++++++++++++++++++++++++++++ CHECKING ZNO + //cout << "ZNO penultamite is: " << zeta[627][1] << endl; - } while ( it_has_overexcavated); - // if the model didn't overexcavate at all, then increase the timestep. - //cout << "The timestep iterator is: " << timestep_iterator << endl; - if(timestep_iterator ==0) + // we need to update the baselevel nodes + vector bl_drop; + vector bl_elevs; + //cout << "N BL Outlet" << N_BL_nodes << endl; + for(int n = 0; n< N_BL_nodes; n++) { - timeStep = 2*timeStep; - if (timeStep > maxtimeStep) + int this_ni = ni[n]; + int this_row,this_col; + flow.retrieve_current_row_and_col(this_ni, this_row, this_col); + bl_elevs.push_back(zeta[this_row][this_col]); + + //if(n == 1) + //{ + // cout << "ZNO BL replace" << zeta[627][1] << endl; + //} + + //cout << "Baselevel elev: "; + /* + if (bl_code[n] == 0) { - timeStep = maxtimeStep; + bl_drop.push_back( -bl_fall_rate[n]*timeStep); + cout << "dropping node: " << zeta[this_row][this_col] << endl; + bl_elevs.push_back(zeta[this_row][this_col]); + } + else + { + cout << "area node: " << zeta[this_row][this_col] << endl; + bl_drop.push_back(zeta_old[this_row][this_col]-zeta[this_row][this_col]); } + */ + } + // +++++++++++++++++++++++++++++ CHECKING ZNO + //cout << "ZNO penultamite (2) is: " << zeta[627][1] << endl; + + //source_points_data.data_column_add_float(column_name, bl_drop); + source_points_data.data_column_replace(column_name, bl_elevs); this->RasterData = zeta.copy(); } @@ -6576,6 +9103,22 @@ void LSDRasterModel::fluvial_incision_with_variable_uplift_and_variable_K_adapti + + + + + + + + + + + + + + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // This function is more or less identical to fluvial_incision above, but it @@ -7701,26 +10244,13 @@ void LSDRasterModel::print_rasters_and_csv( int frame ) cout << "Name of raster metadata file is: " << metadata_fname << endl; outfile.open(metadata_fname.c_str()); outfile << "Frame_num,"; - outfile << "Time,"; - outfile << "K,"; - outfile << "D,"; - outfile << "Erosion,"; - outfile << "Max_uplift"; + outfile << "Time"; outfile << endl; } outfile << frame << ","; - outfile << current_time << ","; - outfile << get_K() << ","; - outfile << get_D() << ","; - outfile << erosion << ","; - outfile << get_max_uplift() ; // The last comma was generating bug here - outfile << endl; - cout << "UPDATE_WARNING::I removed an extra comma from csv file here. It was creating a bug with pandas when reading csv (the python package, not the bamboo junkies). Let me know if it impacts your new outputs" << endl; + outfile << current_time << endl; map GRS = get_GeoReferencingStrings(); - //cout << "Printing, print elevation is " << print_elevation - // << " and erosion is " << print_erosion << endl; - stringstream ss; if (print_elevation) { diff --git a/src/LSDRasterModel.hpp b/src/LSDRasterModel.hpp index 2102d57..084b526 100644 --- a/src/LSDRasterModel.hpp +++ b/src/LSDRasterModel.hpp @@ -49,6 +49,7 @@ #include "LSDSpatialCSVReader.hpp" #include "LSDParticleColumn.hpp" #include "LSDCRNParameters.hpp" +#include "LSDLithoCube.hpp" using namespace std; using namespace TNT; @@ -176,6 +177,13 @@ class LSDRasterModel: public LSDRasterSpectral /// @date 01/01/2014 void random_surface_noise( float min, float max ); + /// @brief Adds random noise to each pixel using the noise data member + /// checks no data and you feed it a see + /// @param seed the seed to the random number (if you give the same seed you should get the same results) + /// @author SMM + /// @date 17/06/2014 + void random_surface_noise(long seed); + /// @brief Adds random noise to each pixel using the noise data member /// @author SMM /// @date 17/06/2014 @@ -243,6 +251,30 @@ class LSDRasterModel: public LSDRasterSpectral /// @date 25/08/2017 void raise_and_fill_raster(); void raise_and_fill_raster(float min_slope_for_fill); + void fill_raster(float min_slope_for_fill); + + /// @brief CHanges the elevation of all nodata nodes by adjustment + /// @param adjustment the elevation to change + /// @author SMM + /// @date 08/02/2021 + void add_fixed_elevation(float adjustment); + + /// @brief This takes the surface topography and subtracts from it the cumulative uplift, + /// in order to calculate the total exhumation and surface for the lithocube + /// @param Cumulative_uplift the raster containing the cumulative uplift of this step in m + /// @return a raster with the exhumation surface for the lithocube + /// @date 06/05/2021 + /// @author SMM + LSDRaster calculate_exhumation_surface_from_cumulative_uplft(LSDRaster& Cumulative_uplift); + + /// @brief This updates the cumulative uplift + /// @param Cumulative_uplift the raster containing the cumulative uplift of this step in m + /// passed by reference as this raster is updated in the function + /// @param Uplift_raster the uplift for the timestep + /// @param timestep The timestep in years + /// @date 07/05/2021 + /// @author SMM + void update_cumulative_uplift(LSDRaster& Cumulative_uplift, LSDRaster& Uplift_raster, float timestep); /// @brief Looks at another raster, checks to see if it the same dimensions as the /// model data, and then replaces any pixel in the model data with nodata @@ -307,6 +339,50 @@ class LSDRasterModel: public LSDRasterSpectral /// @date 18/06/2014 void add_path_to_names( string pathname); + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // @!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@ + // TOOLS FOR TRANSIENT RUNS + // @!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@ + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + /// @brief This takes some "phases" with rates and calculates the rate + /// for a given phase of incision + /// @param phase_start A vector or starting times for each phase + /// @param phase_rates A vector of the base level drop rates + /// @return the rate for a given time (the time is withing the data member current_time) + /// @author SMM + /// @date 08/02/2021 + float calculate_bl_drop_rate( vector phase_start, vector phase_rates); + + /// @brief This takes some "phases" with rates and calculates the rate + /// for a given phase of incision + /// @param phase_start A vector or starting times for each phase + /// @param phase_rates A vector of vectors with drop rates at specific pixels + /// @return a vector of rates for a given time (the time is within the data member current_time) + /// @author SMM + /// @date 23/04/2021 + vector calculate_bl_drop_rate( vector phase_start, vector< vector > phase_rates); + + /// @brief This takes some "phases" with elevations calculates the rate + /// for a given phase of incision + /// @param phase_start A vector or starting times for each phase + /// @param phase_elevations A vector of vectors with elevations of the base level + /// @return a rate for a given time (the time is within the data member current_time) + /// @author SMM + /// @date 07/08/2021 + float calculate_bl_drop_rate_from_elevations( vector phase_start, vector phase_elevations); + + /// @brief This takes some "phases" with elevations and calculates the rates between those phases + /// @param phase_start A vector or starting times for each phase + /// @param phase_rates A vector of vectors with elevations at specific pixels + /// @return a vector of vectors with rates that correspond to the phases + /// @date 23/04/2021 + vector< vector > calculate_phase_rates_from_elevations( vector phase_start, vector< vector > phase_elevations); + + + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // @!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@!@ // TOOLS FOR CHECKING STEADY STATE @@ -405,23 +481,35 @@ class LSDRasterModel: public LSDRasterSpectral /// @brief This fixes a channel, derived from source points data /// onto the model DEM /// @param source_points_data an LSDSpatialCSVReader object. It needs lat and long and elevation columns + /// @param column_name the name of the elevation column + /// @author SMM + /// @date 04/03/2020 + void impose_channels(LSDSpatialCSVReader& source_points_data, string column_name); + + /// @brief This fixes a channel, derived from source points data + /// onto the model DEM + /// @param source_points_data an LSDSpatialCSVReader object. It needs lat and long and elevation columns + /// @param column_name the name of the elevation column + /// @param bl_code a code for baselevel nodes. Usually there is a baselevel code that indicates the node is to be ignored /// @author SMM /// @date 04/03/2020 - void impose_channels(LSDSpatialCSVReader& source_points_data); + void impose_channels(LSDSpatialCSVReader& source_points_data, string column_name, vector bl_code); + /// @brief This fixes a channel, derived from source points data /// onto the model DEM. It also lifts the raster so no pixels in the raster are lower than /// @param source_points_data an LSDSpatialCSVReader object. It needs lat and long and elevation columns + /// @param tcolumn_name he name of the elevation column /// @author SMM /// @date 09//2020 - void impose_channels_and_lift_raster(LSDSpatialCSVReader& source_points_data); + void impose_channels_and_lift_raster(LSDSpatialCSVReader& source_points_data, string column_name); /// @brief Takes a model step and gets an LSDSpatialCSVReader object for later use /// @param contributing_pixels for the channel network /// @return source_points_data an LSDSpatialCSVReader object. It needs lat and long and elevation columns /// @author SMM /// @date 04/03/2020 - LSDSpatialCSVReader get_channels_for_burning(int contributing_pixels); + LSDSpatialCSVReader get_channels_for_burning(int contributing_pixels, string temp_channel_path, string temp_channel_fname); /// @brief Caps elevations using the initial raster /// WARNING no testing if the raster is the correct shape! @@ -444,6 +532,15 @@ class LSDRasterModel: public LSDRasterSpectral /// @date 01/01/2014 updated 01/07/2014 Array2D calculate_erosion_rates( void ); + /// @brief Simple function that creates an array with the erosion rates for a given + /// timestep. It doesn't do anything with NoData cells, + /// and uses an uplift raster + /// @param Uplift_raster a raster of uplift values in m/yr + /// @return an array with the erosion rates + /// @author SMM + /// @date 6/05/2021 + Array2D calculate_erosion_rates( LSDRaster& Uplift_raster ); + /// @brief This calculates the erosion rate for individual cells. /// Currently it assumes that the zeta_old data member is from the previous /// timestep @@ -649,6 +746,122 @@ class LSDRasterModel: public LSDRasterSpectral /// @date 03/09/2017 void run_components_combined( LSDRaster& URaster, LSDRaster& KRaster, bool use_adaptive_timestep ); + /// @brief This is a wrapper similar to run_components but sends the + /// fluvial and uplfit fields to the nonlinear solver. + /// This one allows you to put in a base level + /// And include transience + /// @detail Variable U and K rasters can be used. + /// @param URaster A raster of uplift rates + /// @param KRaster A raster of K values + /// @param use_adaptive_timestep If true, an adaptive timestep is used + /// @param source_points_data some points to serve as base level nodes + /// @param phase_time a vector of times for different base level fall rates + /// @param phase_rates a vector of base level fall rates + /// @param column_name the name of the elevation column + /// @author SMM + /// @date 08/02/2021 + void run_components_combined_imposed_baselevel( LSDRaster& URaster, LSDRaster& KRaster, + bool use_adaptive_timestep, LSDSpatialCSVReader& source_points_data, + vector phase_time, vector phase_rates, string column_name); + + + /// @brief This is a wrapper similar to run_components but sends the + /// fluvial and uplfit fields to the nonlinear solver. + /// This one allows you to put in a base level + /// And include transience. This one has transience at mulitiple pixels + /// @detail Variable U and K rasters can be used. + /// @param URaster A raster of uplift rates + /// @param KRaster A raster of K values + /// @param use_adaptive_timestep If true, an adaptive timestep is used + /// @param source_points_data some points to serve as base level nodes + /// @param phase_time a vector of times for different base level fall rates + /// @param phase_eleations a vec vec of base level elevations from which rates are calculated. + /// @param minimum_slope the minimum slope for elevation change between pixels. This replaces overexcavated nodes + /// @param column_name the name of the elevation column + /// @author SMM + /// @date 23/04/2021 + void run_components_combined_imposed_baselevel( LSDRaster& URaster, LSDRaster& KRaster, + bool use_adaptive_timestep, LSDSpatialCSVReader& source_points_data, + vector phase_time, vector< vector > phase_elevations, + float minimum_slope, string column_name); + + + /// @brief This is a wrapper similar to run_components but sends the + /// fluvial and uplfit fields to the nonlinear solver. + /// This one allows you to put in a base level + /// And include transience. This one has transience at mulitiple pixels + /// It also has the lithocube. + /// It is a beast, really + /// @detail Variable U and K rasters can be used. + /// @param URaster A raster of uplift rates + /// @param use_adaptive_timestep If true, an adaptive timestep is used + /// @param source_points_data some points to serve as base level nodes + /// @param phase_time a vector of times for different base level fall rates + /// @param phase_eleations a vec vec of base level elevations from which rates are calculated. + /// @oaram cumulative_uplift a raster of cumulative uplift for exhumation calculations + /// @param LSDLC a lithocube object + /// @param forbidden_lithocodes a list of lithocodes that will be set to the default value + /// @param print_lithocode_raster a bool when true print the lithocode raster at the printing timesteps + /// @param use_hillslope_hybrid if true, turns on the hybrid model that does linear diffusion but uses a critical + /// slope if too steep + /// @param threshold_contributing_pixels Number of pixels to designate as hillslopes for the hillslope model + /// @param minimum_slope the minimum slope for elevation change between pixels. This replaces overexcavated nodes + /// @param print_exhumation_and_cumulative_uplift if true prints the exhumation surface and cumulative uplift + /// @param column_name the name of the elevation column + /// @author SMM + /// @date 06/05/2021 + void run_components_combined_imposed_baselevel( LSDRaster& URaster, + bool use_adaptive_timestep, LSDSpatialCSVReader& source_points_data, + vector phase_time, vector< vector > phase_elevations, + LSDRaster& cumulative_uplift, + LSDLithoCube& LSDLC, list forbidden_lithocodes, + bool print_lithocode_raster, + bool use_hillslope_hybrid, + int threshold_contributing_pixels, + float minimum_slope, + bool print_exhumation_and_cumulative_uplift, + string column_name); + + + /// @brief This is a wrapper similar to run_components but sends the + /// fluvial and uplfit fields to the nonlinear solver. + /// This one allows you to put in a base level + /// And include transience. This one has transience at mulitiple pixels + /// It also has the lithocube. + /// It is a beast, really + /// @detail Variable U and K rasters can be used. + /// @param URaster A raster of uplift rates + /// @param use_adaptive_timestep If true, an adaptive timestep is used + /// @param source_points_data some points to serve as base level nodes + /// @param phase_time a vector of times for different base level fall rates + /// @param outlet_elevations is a vector of elevations at the outlet + /// @oaram cumulative_uplift a raster of cumulative uplift for exhumation calculations + /// @param LSDLC a lithocube object + /// @param forbidden_lithocodes a list of lithocodes that will be set to the default value + /// @param print_lithocode_raster a bool when true print the lithocode raster at the printing timesteps + /// @param use_hillslope_hybrid if true, turns on the hybrid model that does linear diffusion but uses a critical + /// slope if too steep + /// @param threshold_contributing_pixels Number of pixels to designate as hillslopes for the hillslope model + /// @param minimum_slope the minimum slope for elevation change between pixels. This replaces overexcavated nodes + /// @param print_exhumation_and_cumulative_uplift if true prints the exhumation surface and cumulative uplift + /// @param column_name the name of the elevation column + /// @author SMM + /// @date 07/08/2021 + void run_components_combined_imposed_baselevel( LSDRaster& URaster, + bool use_adaptive_timestep, LSDSpatialCSVReader& source_points_data, + vector phase_time, vector< float > outlet_elevations, + LSDRaster& cumulative_uplift, + LSDLithoCube& LSDLC, list forbidden_lithocodes, + bool print_lithocode_raster, + bool use_hillslope_hybrid, + int threshold_contributing_pixels, + float minimum_slope, + bool print_exhumation_and_cumulative_uplift, + string column_name); + + + + /// @brief This is a wrapper that runs the model but includes CRN columns /// fluvial and uplfit fields to the nonlinear solver @@ -732,10 +945,17 @@ class LSDRasterModel: public LSDRasterSpectral /// @brief This smooths the model DEM and returns this smoothed surface as a raster /// @param central_pixel_weighting A float that give the weighting of the central pixel. /// The higher this number, the less smoothing. 2 is probably a good starting value. + /// @return The smoothed elevation /// @author SMM /// @date 16/12/2019 LSDRaster basic_smooth(float central_pixel_weighting); + /// @brief Calucaltes the laplacian on the surface. Can be used in the hillslope module + /// @return The laplacian curvature as a raster + /// @author SMM + /// @date 07/05/2021 + LSDRaster basic_curvature(); + /// @brief This checks for rivers (using a drainage area threshold) and then any remaining pixels /// are popped to a critical slope. Creates a river network with striaght slopes in between /// @detail Very rudimentary: only uses slopes in the D8 direction so the slopes will @@ -762,20 +982,34 @@ class LSDRasterModel: public LSDRasterSpectral /// @param U_values a raster of uplift /// @param Source_points_data a spatialc csv reader with the appropriate file /// @param carve_before_fill if true, run the carving algorithm before the filling algorithm + /// @param column_name the name of the elevation column /// @author SMM /// @date 01/10/2019 - void fluvial_snap_to_steady_variable_K_variable_U(LSDRaster& K_values, LSDRaster& U_values, LSDSpatialCSVReader& Source_points_data, bool carve_before_fill); + void fluvial_snap_to_steady_variable_K_variable_U(LSDRaster& K_values, LSDRaster& U_values, + LSDSpatialCSVReader& Source_points_data, + bool carve_before_fill, string column_name); - /// @brief This method snaps to steady with spatially variable uplift and erodibility fields - /// It also allows fixed base level. - /// @detail In this version the base level is read from a csv file - /// @param K_values a raster of erodiblity + + /// @brief This is a hybrid model that calculates hillslope diffusion as well as a critical slope + /// so the diffused hillslopes cannot exceed a critical slope + /// @detail Must be used after the fluvial step + /// @param Sc_values a raster of critical slopes /// @param U_values a raster of uplift - /// @param csv_points_file the full path to a fiel with the elevation and node index data + /// @param threshold_pixels The number of contributing pixels must be less than this to snap the hillslope + /// @param Source_points_data a list of baselevel nodes to ignore + /// @author SMM + /// @date 11/05/2021 + void hillslope_hybrid_module(LSDRaster& Sc_values, LSDRaster& Uplift, + int threshold_pixels,LSDSpatialCSVReader& Source_points_data); + + + /// @brief A hillslope snapping routine based on the stack. + /// @param Sc_values a raster of critical slopes /// @param carve_before_fill if true, run the carving algorithm before the filling algorithm + /// @param threshold_pixels The number of contributing pixels must be less than this to snap the hillslope /// @author SMM - /// @date 03/10/2019 - void fluvial_snap_to_steady_variable_K_variable_U(LSDRaster& K_values, LSDRaster& U_values, string csv_of_fixed_channel, bool carve_before_fill); + /// @date 06/05/2021 + void hillslope_snap_to_steady_variable_Sc(LSDRaster& Sc_values, bool carve_before_fill, int threshold_pixels); /// @brief This method instantaneously tilts the landscape by a certain angle. /// @param angle the tilt angle in degrees @@ -843,6 +1077,86 @@ class LSDRasterModel: public LSDRasterSpectral /// @date 06/09/2017 void fluvial_incision_with_variable_uplift_and_variable_K_adaptive_timestep( LSDRaster& Uplift_rate, LSDRaster& K_raster ); + /// @brief This is used to process a transient base level file + /// @param source_points_data a spatial csv object that has the correct elevation input + /// @param timing_prefix the prefix of a time column in the csv + /// @param timing_multiplier how many years are in the each number of timestep (so, say t5 = 5000 year, then the + /// multiplier is 1000) + /// @param n_time_columns The number of time columns to attempt to read + /// @param phase_steps How many steps there are between phases. So if you have t5, then t10, then t15 the steps would be 5 + /// @author SMM + /// @date 22/04/2021 + void process_baselevel( LSDSpatialCSVReader& source_points_data, string timing_prefix, + float timing_multiplier, int n_time_columns, int phase_steps, + vector& phases_vec, vector< vector >& elevation_vecvec); + + + /// @brief This is used to process a transient base level file using only a single elevation value + /// @param transient_infile_name name of the transient input file including csv + /// @param phases Replaced in code these are the times + /// @param outlet_elevations these are the elevations at the fixed times of the outlet + /// @author SMM + /// @date 07/08/2021 + void process_transient_file(string transient_infile_name, vector& phases,vector& outlet_elevations); + + + /// @brief This is a little helper to tag specific nodes with a baselelvel switch + /// @detail creates a boolean column where 0 is an area node and 1 is a node that lowers at a fixed rate. + /// @param source_points_data a spatial csv object that has the correct elevation input + /// @param column_name The name of the column to test against a criteria. + /// @author SMM + /// @date 26/04/2021 + void baselevel_run_area_switch( LSDSpatialCSVReader& source_points_data, string column_name); + + + /// @brief Fastscape, implicit finite difference solver for stream power equations + /// O(n) + /// Method takes the K value from a raster fed to it + /// and also take a raster of the uplift rates + /// and solves the stream power equation at a future timestep in linear time + /// This version includes the current uplift, so you do not need to call + /// uplift after this has finished. Uses an adaptive timestep. + /// This version allows you to impose a base level + /// @param K_raster the raster of K values. + /// @param Uplift_rate a raster of uplift rates in m/yr + /// @param source_points_data a spatial csv object that has the correct elevation input + /// @param the rate the base level is falling. Need a rate rather than a fixed elevation because + /// of the adaptive timestep + /// @param let_timestep_increase a boolean that when true allows the timestep to increase. Make false + /// when you need to ensure the timestep lands on a specific time. + /// @param column_name the name of the elevation column + /// @author SMM + /// @date 08/02/2021 + void fluvial_incision_with_variable_uplift_and_variable_K_adaptive_timestep_impose_baselevel( LSDRaster& Urate_raster, + LSDRaster& K_raster, LSDSpatialCSVReader& source_points_data, + float bl_fall_rate, bool let_timestep_increase, string column_name); + + /// @brief Fastscape, implicit finite difference solver for stream power equations + /// O(n) + /// Method takes the K value from a raster fed to it + /// and also take a raster of the uplift rates + /// and solves the stream power equation at a future timestep in linear time + /// This version includes the current uplift, so you do not need to call + /// uplift after this has finished. Uses an adaptive timestep. + /// This version allows you to impose a base level + /// @param K_raster the raster of K values. + /// @param Uplift_rate a raster of uplift rates in m/yr + /// @param source_points_data a spatial csv object that has the correct elevation input + /// @param the rate the base level is falling. Need a rate rather than a fixed elevation because + /// of the adaptive timestep. This has baselevel rate at a vector of baselevel nodes. + /// @param let_timestep_increase a boolean that when true allows the timestep to increase. Make false + /// when you need to ensure the timestep lands on a specific time. + /// @param column_name the name of the elevation columns + /// @author SMM + /// @date 08/02/2021 + void fluvial_incision_with_variable_uplift_and_variable_K_adaptive_timestep_impose_baselevel( LSDRaster& Urate_raster, + LSDRaster& K_raster, LSDSpatialCSVReader& source_points_data, + vector bl_fall_rate, + bool let_timestep_increase, + bool use_adaptive_timestep, + float minimum_slope, + string column_name); + /// @brief This function is more or less identical to fluvial_incision above, but it /// Returns a raster with the erosion rate and takes arguments rather /// than reading from data members @@ -1135,10 +1449,10 @@ class LSDRasterModel: public LSDRasterSpectral void snap_periodicity( void ); /// set the print interval - void set_print_interval( int num_steps ) { print_interval = num_steps; } + void set_print_interval( int num_steps ) { print_interval = num_steps; float_print_interval = float(num_steps)*timeStep; } /// set the float print interval - void set_float_print_interval( float float_dt_print ) { float_print_interval = float_dt_print; } + //void set_float_print_interval( float float_dt_print ) { float_print_interval = float_dt_print; } /// set the float print interval void set_next_printing_time ( float next_float_dt_print ) { next_printing_time = next_float_dt_print; } @@ -1150,6 +1464,13 @@ class LSDRasterModel: public LSDRasterSpectral void set_raster_data(LSDRaster& Raster); + /// @brief makes a raster with a constant value + /// @detail a brute force way to make K and U rasters when those are needed + /// @param value the value that the raster pixels will take. + /// @author SMM + /// @date 24/06/2021 + LSDRaster make_constant_raster(float value); + /// @brief this sets the K mode /// @param mode The mode of calculating K @@ -1320,6 +1641,9 @@ class LSDRasterModel: public LSDRasterSpectral /// @brief Gets the timestep float get_timeStep( void) { return timeStep; } + /// @brief Gets the print interval in years + float get_float_print_interval( void) { return float_print_interval; } + /// @brief Gets the maximum timestep float get_maxtimeStep( void ) { return maxtimeStep; } diff --git a/src/LSDSpatialCSVReader.cpp b/src/LSDSpatialCSVReader.cpp index 9c6d16f..073fb21 100644 --- a/src/LSDSpatialCSVReader.cpp +++ b/src/LSDSpatialCSVReader.cpp @@ -50,6 +50,7 @@ //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include #include #include +#include #include "LSDStatsTools.hpp" #include "LSDShapeTools.hpp" #include "LSDCosmoData.hpp" @@ -229,7 +231,7 @@ void LSDSpatialCSVReader::load_csv_data(string filename) // create a stringstream stringstream ss(line_from_file); - ss.precision(9); + ss.precision(14); while( ss.good() ) { @@ -252,7 +254,7 @@ void LSDSpatialCSVReader::load_csv_data(string filename) int longitude_index = -9999; for (int i = 0; i column_data) +{ + + bool added_column_works = false; + int n_lat,n_col; + + n_lat = int(latitude.size()); + + n_col = int(column_data.size()); + + if(n_lat == n_col) + { + data_map[column_name] = column_data; + } + else + { + cout << "The data column is not the same size as the other columns. The addition of this column has failed" << endl; + } + + return added_column_works; + +} +//============================================================================== + + + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // // Checks if a column is in the csv @@ -426,6 +458,34 @@ bool LSDSpatialCSVReader::is_column_in_csv(string column_name) return is_in_csv; } +string LSDSpatialCSVReader::find_string_in_column_name(string column_name_fragment) +{ + + string column_name = "NULL"; + string this_key; + int column_count = 0; + for( map >::iterator it = data_map.begin(); it != data_map.end(); ++it) + { + this_key = it->first; + if (this_key.find(column_name_fragment) != std::string::npos) + { + column_name = this_key; + column_count++; + } + } + + if(column_count == 0) + { + cout << "I didn't find the string fragment. Returning a null column name." << endl; + cout << "Check if the fragment has a matching case." << endl; + } + if(column_count > 1) + { + cout << "I found more than one column names with that string fragment!" << endl; + } + return column_name; +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // @@ -558,7 +618,7 @@ vector LSDSpatialCSVReader::data_column_to_double(string column_name) // Adds a float to a data column void LSDSpatialCSVReader::data_column_add_float(string column_name, float add_value) { - cout << "Adding " << add_value << " to the column " << column_name << endl; + //cout << "Adding " << add_value << " to the column " << column_name << endl; vector string_vec = get_data_column(column_name); float this_value; vector new_string_vec; @@ -569,12 +629,89 @@ void LSDSpatialCSVReader::data_column_add_float(string column_name, float add_va } for(int i = 0; i add_value) +{ + //cout << "Adding " << add_value << " to the column " << column_name << endl; + vector string_vec = get_data_column(column_name); + float this_value; + vector new_string_vec; + int N_data_elements = string_vec.size(); + + if (N_data_elements != int( add_value.size())) + { + cout << "Can't add vectors of different size to csv object" << endl; + exit(0); + } + + if (N_data_elements == 0) + { + cout << "Couldn't read in the data column. Check the column name!" << endl; + } + for(int i = 0; i new_column) +{ + // cout << "Adding " << add_value << " to the column " << column_name << endl; + vector string_vec = get_data_column(column_name); + float this_value; + vector new_string_vec; + int N_data_elements = string_vec.size(); + if (N_data_elements == 0) + { + cout << "Couldn't read in the data column. Check the column name!" << endl; + } + + if (N_data_elements != int(new_column.size())) + { + cout << "You are trying to replace a data column using a column that is a different size. " << endl; + cout << "I'm afraid I can't do that." << endl; + cout << "Aborting replace." << endl; + } + else + { + for(int i = 0; i flow_distance = data_column_to_double(fd_column_name); vector elevation = data_column_to_double(elevation_column_name); - // the single channel starts from the top, so the last node is the base level and doesn't get modified. + // the single channel starts from the top, so the last node is the base level and doesn't get modified. int N_data_elements = int(flow_distance.size()); vector new_elevation = elevation; float dist, min_elev; @@ -613,18 +750,28 @@ void LSDSpatialCSVReader::enforce_slope(string fd_column_name, string elevation_ for (int i = N_data_elements-2; i>=0; i--) { dist = flow_distance[i]-flow_distance[i+1]; - + min_elev = dist*min_slope+new_elevation[i+1]; - //cout << "dist: " << dist << " z[i+1]: " << new_elevation[i+1] << " z[i]: " << elevation[i] << " min_elev: " << min_elev << endl; - if (elevation[i] < min_elev) + cout << "dist: " << dist << " z[i+1]: " << new_elevation[i+1] << " z[i]: " << elevation[i] << " min_elev: " << min_elev << endl; + if (dist < 0 || fabs(dist) >128) { - //cout << "Found something where I need to increase slope!" << endl; - new_elevation[i] = min_elev; + cout << "There seems to be a big changes in flow distance (greater than a diagonal pixel at 90m resolution" << endl; + cout << "I am considering this a new channel and resetting the elevation values to this pixel" << endl; + new_elevation[i] = elevation[i]; } else { - new_elevation[i] = elevation[i]; + if (elevation[i] < min_elev) + { + cout << "Found something where I need to increase slope!" << endl; + new_elevation[i] = min_elev; + } + else + { + new_elevation[i] = elevation[i]; + } } + } vector new_elev_string; @@ -632,7 +779,7 @@ void LSDSpatialCSVReader::enforce_slope(string fd_column_name, string elevation_ { new_elev_string.push_back(to_string(new_elevation[i])); } - + data_map[elevation_column_name] = new_elev_string; } @@ -1009,6 +1156,11 @@ void LSDSpatialCSVReader::check_if_points_are_in_raster() is_point_in_raster = temp_is_point_in_raster; } +vector LSDSpatialCSVReader::get_if_points_are_in_raster_vector() +{ + return is_point_in_raster; +} + //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // // Function to get vectors of x and y coordinates, and the node indices of these @@ -1040,6 +1192,7 @@ void LSDSpatialCSVReader::get_nodeindices_from_x_and_y_coords(LSDFlowInfo& FlowI } // get the node indices from lat-long coords in the csv file +// If the nodeindex is nodata it will return nodatavalue vector LSDSpatialCSVReader::get_nodeindices_from_lat_long(LSDFlowInfo& FlowInfo) { vector NIs; @@ -1048,18 +1201,38 @@ vector LSDSpatialCSVReader::get_nodeindices_from_lat_long(LSDFlowInfo& Flow for (int i = 0; i < int(X_coords.size()); i++) { int NodeIndex = FlowInfo.get_node_index_of_coordinate_point(X_coords[i], Y_coords[i]); - NIs.push_back(NodeIndex); + if (NodeIndex != NoDataValue) { + NIs.push_back(NodeIndex); + } } return NIs; } +void LSDSpatialCSVReader::add_nodeindex_vector_to_data_map_using_lat_long(LSDFlowInfo& FlowInfo) +{ + vector NIs; + vector NI_strings; + vector X_coords, Y_coords; + get_x_and_y_from_latlong(X_coords,Y_coords); + for (int i = 0; i < int(X_coords.size()); i++) + { + int NodeIndex = FlowInfo.get_node_index_of_coordinate_point(X_coords[i], Y_coords[i]); + NIs.push_back(NodeIndex); + NI_strings.push_back(itoa(NodeIndex)); + } + + string nistr1 = "nodeindex"; + add_data_column( nistr1, NI_strings); +} + + vector LSDSpatialCSVReader::get_nodeindex_vector() { vector ni_vec; - + bool is_nodeindex = false; bool is_id = false; bool is_node = false; @@ -1082,14 +1255,14 @@ vector LSDSpatialCSVReader::get_nodeindex_vector() is_node = is_column_in_csv(nistr2); is_id = is_column_in_csv(nistr3); cout << "Okay, done checking columns." << endl; - + if ( is_nodeindex == false && is_node == false && is_id == false) { cout << "I could not find a nodeindex column. Returning and empty map." << endl; } - else + else { - // This load of switches basically says that the order of preference if there are + // This load of switches basically says that the order of preference if there are // more than one liklely columns is nodeindex, node, id. if (is_nodeindex) { @@ -1105,12 +1278,12 @@ vector LSDSpatialCSVReader::get_nodeindex_vector() { cout << "I found the code 'id'" << endl; ni_column = nistr3; - } + } } // now get the data cout << "Grabbing the data from columns " << ni_column << endl; - ni_vec = data_column_to_int(ni_column); + ni_vec = data_column_to_int(ni_column); } return ni_vec; @@ -1120,7 +1293,7 @@ vector LSDSpatialCSVReader::get_nodeindex_vector() map LSDSpatialCSVReader::get_nodeindex_map_float(string column_name) { map nodeindex_map; - + bool is_nodeindex = false; bool is_id = false; bool is_node = false; @@ -1152,15 +1325,15 @@ map LSDSpatialCSVReader::get_nodeindex_map_float(string column_name) cout << "I can't find your data column" << endl; } else - { + { cout << "I found your data column." << endl; if (is_nodeindex == false && is_node == false && is_id == false) { cout << "I could not find a nodeindex column. Returning and empty map." << endl; } - else + else { - // This load of switches basically says that the order of preference if there are + // This load of switches basically says that the order of preference if there are // more than one liklely columns is nodeindex, node, id. if (is_nodeindex) { @@ -1176,7 +1349,7 @@ map LSDSpatialCSVReader::get_nodeindex_map_float(string column_name) { cout << "I found the code 'id'" << endl; ni_column = nistr3; - } + } } // now get the data @@ -1189,7 +1362,7 @@ map LSDSpatialCSVReader::get_nodeindex_map_float(string column_name) { nodeindex_map[ ni_vec[i] ] = data_vec[i]; } - + } } @@ -1342,11 +1515,11 @@ void LSDSpatialCSVReader::get_row_and_col_of_a_point(float X_coordinate,float Y_ //cout << "Getting row and col, " << row_point << " " << col_point << endl; - if(col_point > 0 && col_point < NCols-1) + if(col_point >= 0 && col_point <= NCols-1) { this_col = col_point; } - if(row_point > 0 && row_point < NRows -1) + if(row_point >= 0 && row_point <= NRows -1) { this_row = row_point; } @@ -1370,11 +1543,11 @@ void LSDSpatialCSVReader::get_row_and_col_of_a_point(double X_coordinate,double //cout << "Getting row and col, " << row_point << " " << col_point << endl; - if(col_point > 0 && col_point < NCols-1) + if(col_point >= 0 && col_point <= NCols-1) { this_col = col_point; } - if(row_point > 0 && row_point < NRows -1) + if(row_point >= 0 && row_point <= NRows -1) { this_row = row_point; } @@ -1441,6 +1614,15 @@ void LSDSpatialCSVReader::print_row_and_col_to_csv(string csv_outname) void LSDSpatialCSVReader::print_data_to_csv(string csv_outname) { ofstream outfile; + + // make sure the file has the csv extension + string ext_str = ".csv"; + if (csv_outname.find(ext_str) == std::string::npos) + { + csv_outname = csv_outname + ext_str; + } + + outfile.open(csv_outname.c_str()); outfile << "latitude,longitude"; @@ -1528,7 +1710,16 @@ void LSDSpatialCSVReader::print_data_to_geojson(string json_outname) } string fourth_bit = " }, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ "; //string fifth_bit = dtoa(this_longitude) +","+ dtoa(latitude[i]) +" ] } },"; - string fifth_bit = " ] } },"; + + string fifth_bit; + if (i == (n_nodes-1) ) + { + fifth_bit = " ] } }"; + } + else + { + fifth_bit = " ] } },"; + } outfile << first_bit << latitude[i] << second_bit << this_longitude << third_bit+fourth_bit << this_longitude << "," << latitude[i] << fifth_bit @@ -1549,5 +1740,288 @@ void LSDSpatialCSVReader::print_data_to_geojson(string json_outname) } +void LSDSpatialCSVReader::print_data_to_geojson_linestring(string json_outname) +{ + // the file will be projected in WGS84 so you need lat-long coordinates + if (check_if_latitude_and_longitude_exist()) + { + ofstream outfile; + outfile.precision(9); + outfile.open(json_outname.c_str()); + + outfile << "{" << endl; + outfile << "\"type\": \"FeatureCollection\"," << endl; + outfile << "\"crs\": { \"type\": \"name\", \"properties\": { \"name\": \"urn:ogc:def:crs:OGC:1.3:CRS84\" } }," << endl; + outfile << "\"features\": [" << endl; + + int n_nodes = int(latitude.size()); + for(int i = 0; i< n_nodes-1; i++) + { + double this_longitude, next_longitude; + if (longitude[i] > 180) + { + this_longitude = longitude[i]-360.0; + next_longitude = longitude[i+1]-360.0; + } + else if (longitude[i] < -180) + { + this_longitude = 360.0+longitude[i]; + next_longitude = 360.0+longitude[i+1]; + } + else + { + this_longitude = longitude[i]; + next_longitude = longitude[i+1]; + } + + + string first_bit = "{ \"type\": \"Feature\", \"properties\": { \"latitude\": "; + //string second_bit = dtoa(latitude[i])+", \"longitude\": "+ dtoa(this_longitude); + string second_bit = ", \"longitude\": "; + + string third_bit; + for( map >::iterator it = data_map.begin(); it != data_map.end(); ++it) + { + third_bit += ", \""+it->first+"\": "+(it->second)[i]; + } + string fourth_bit = " }, \"geometry\": { \"type\": \"Linestring\", \"coordinates\": [ ["; + //string fifth_bit = dtoa(this_longitude) +","+ dtoa(latitude[i]) +" ] } },"; + + string fifth_bit; + if (i == (n_nodes-2) ) + { + fifth_bit = " ] ] } }"; + } + else + { + fifth_bit = " ] ] } },"; + } + + outfile << first_bit << latitude[i] << second_bit << this_longitude + << third_bit+fourth_bit << this_longitude << "," << latitude[i] << "], [" << next_longitude << "," << latitude[i+1] << fifth_bit + << endl; + } + outfile << "]" << endl; + outfile << "}" << endl; + + outfile.close(); + } + else + { + cout << "LSDSpatialCSVReader::print_data_to_geojson error." << endl; + cout << "This dataset does not have lat-long information so I cannot print a geojson" << endl; + } + +} + +//============================================================================== +// This updates XY coordinates with the coordinates centred on a corresponding +// raster pixel. +//============================================================================== +void LSDSpatialCSVReader::centre_XY_by_row_col(LSDRaster& ThisRaster, string X_column_name, string Y_column_name) +{ + // Get the local coordinate as well as the elevations (elevation not really necessary) + vector UTME = data_column_to_float(X_column_name); + vector UTMN = data_column_to_float(Y_column_name); + // vector elev = channel_nodes.data_column_to_float(elevation_column_name); + + // Initialise vectors and variables to store XY data from the raster + vector new_X_vector; + vector new_Y_vector; + float new_X; + float new_Y; + + cout << "I am comparing your channel node coordinates with the centre coordinates of the corresponding raster pixel and collecting the raster coordinates." << endl; + int row,col; + for(int i = 0; i< int(UTME.size()); i++) + { + get_row_and_col_of_a_point(UTME[i],UTMN[i],row, col); + ThisRaster.get_x_and_y_locations(row, col, new_X, new_Y); + + // Some shenanigans to convert the coordinates into a string, letting us add them to the vector and then the csv data map + UTME[i] = new_X; + UTMN[i] = new_Y; + + stringstream new_X_string; + stringstream new_Y_string; + new_X_string.precision(9); + new_Y_string.precision(9); + new_X_string << UTME[i]; + new_Y_string << UTMN[i]; + // Let's add the new X and Y strings to our vectors + new_X_vector.push_back(new_X_string.str()); + new_Y_vector.push_back(new_Y_string.str()); + + } + cout << "New channel X coordinate vector stores " << int(new_X_vector.size()) << " X coordinates." << endl; + + cout << "I will try to add the new coordinates to your data now. \nWARNING: This will fail if you do not have a latitude column in your data set!" << endl; + cout << "We must all bow to our lord and saviour lat-long. \nThis will also overwrite your old XY coordinates." << endl; + add_data_column(X_column_name, new_X_vector); + add_data_column(Y_column_name, new_Y_vector); + + cout << "Updating lat-long from the new XY coordinates. This will overwrite any old lat-long coordinates." << endl; + get_latlong_from_x_and_y(X_column_name, Y_column_name); +} + +//============================================================================== +// This finds gaps in channel node data and fills the gap by interpolating +// across the gap and adding an extra node with a new value for all columns of +// the csv file. This is not a particularly elegant function but it does what it +// it is supposed to do. +// NOTE: This only adds one node per gap, so you would need multiple iterations +// of this function if you need to add more then one node. +//============================================================================== + +void LSDSpatialCSVReader::interpolate_across_gap(LSDRaster& ThisRaster, string X_column_name, string Y_column_name, string fd_column_name) +{ + cout << "Let's find some gaps!" << endl; + cout << "WARNING: This will only work if your data is SORTED BY FLOW DISTANCE. \nOtherwise your interpolated channel will be a pretzel." << endl; + + vector flow_distance = data_column_to_float(fd_column_name); + vector UTME = data_column_to_float(X_column_name); + vector UTMN = data_column_to_float(Y_column_name); + + int row, col; + float nw_x, nw_y, n_x, n_y, ne_x, ne_y, e_x, e_y, w_x, w_y, sw_x, sw_y, s_x, s_y, se_x, se_y; + + int gap_counter = 0; + vector guilty_X_vector, guilty_Y_vector; + float next_x, next_y; + + for(int i = 0; i< int(flow_distance.size()); i++) + { + // cout << "Flow distance is " << flow_distance[i] << endl; + if(i!=int(flow_distance.size()-1)) + { + // cout << "Next flow distance is " << flow_distance[i+1] << endl; + next_x = UTME[i+1]; + next_y = UTMN[i+1]; + // cout << "Next coordinates are " << next_x << next_y << endl; + get_row_and_col_of_a_point(UTME[i],UTMN[i],row, col); + // cout << "Row is " << row << " and col is " << col << endl; + ThisRaster.get_x_and_y_locations(row+1, col-1, nw_x, nw_y); + ThisRaster.get_x_and_y_locations(row+1, col, n_x, n_y); + ThisRaster.get_x_and_y_locations(row+1, col+1, ne_x, ne_y); + ThisRaster.get_x_and_y_locations(row, col-1, w_x, w_y); + ThisRaster.get_x_and_y_locations(row, col+1, e_x, e_y); + ThisRaster.get_x_and_y_locations(row-1, col-1, sw_x, sw_y); + ThisRaster.get_x_and_y_locations(row-1, col, s_x, s_y); + ThisRaster.get_x_and_y_locations(row-1, col+1, se_x, se_y); + + if((next_x == nw_x && next_y == nw_y) + || (next_x == n_x && next_y == n_y) + || (next_x == ne_x && next_y == ne_y) + || (next_x == e_x && next_y == e_y) + || (next_x == w_x && next_y == w_y) + || (next_x == sw_x && next_y == sw_y) + || (next_x == s_x && next_y == s_y) + || (next_x == se_x && next_y == se_y) + ) + { + // cout << "We've got the next node, all good" << endl; + } + else + { + cout.precision(12); + + cout << "There seems to be a gap." << endl; + gap_counter = gap_counter +1; + + float diff_flowdist; + cout << "Flow distance is " << flow_distance[i] << endl; + cout << "Next flow distance is " << flow_distance[i+1] << endl; + + diff_flowdist = flow_distance[i] - flow_distance[i+1]; + + cout << "Flow distance jump is: " << diff_flowdist << endl; + + if(abs(diff_flowdist) > 200) + { + cout << "I think we are at the head of a channel, let's ignore this point." << endl; + } + + else + { + + // Store coordinates of node before gap + guilty_X_vector.push_back(UTME[i]); + guilty_Y_vector.push_back(UTMN[i]); + + for (const auto& kv : get_data_map()) + { + // cout << "I AM NAME OF COLUMN:" << kv.first << endl; + vector this_string_vector = kv.second; + + // Converting the vectors to float, adapted from https://stackoverflow.com/questions/35419046/converting-from-vectorstring-to-vectordouble-without-stdstod + vector this_float_vector; + // iterate over vector and convert each object to float, then add to new float vector + for (vector::const_iterator iter = this_string_vector.begin(); iter != this_string_vector.end(); ++iter) + { + string const& element = *iter; + // use a stringstream to get a float value: + // istringstream is(element); + // float result; + // is >> result; + std::ostringstream out; + out << std::setprecision(12) << std::stof(element); + float precise = std::stof(out.str()); + + + + // add the float value to the result vector: + this_float_vector.push_back(precise); + } + // cout << "Downstream of gap, " << kv.first << " is " << this_float_vector[i] << endl; + // cout << "Upstream of gap, " << kv.first << " is " << this_float_vector[i+1] << endl; + // cout << "Let's do a super basic interpolation!" << endl; + + float interpolated_value; + interpolated_value = this_float_vector[i] + ((this_float_vector[i+1] - this_float_vector[i])/2); + // cout << "Interpolated value of " << kv.first << " is " << interpolated_value << endl; + stringstream interpolated_string; + interpolated_string.precision(12); + interpolated_string << interpolated_value; + append_to_col(kv.first, to_string(interpolated_value) ); // previous, outdated version: append_to_col(kv.first, interpolated_string.str()); + } + } + } + } + else + { + cout << "That's the last node, I don't need to check for gaps." << endl; + } + } + + // We should probably also centre all the coordinates again to make sure that the new ones are good + cout << "There were " << gap_counter << " gaps, captain. I have filled them." << endl; + // Let's check that each column is actually increasing in size + for (const auto& kv : get_data_map()) + { + vector new_string_vector = kv.second; + cout << "Column " << kv.first << " stores " << int(new_string_vector.size()) << " values." << endl; + } + // let's print the coordinates before each gap to csv to check them + print_UTM_coords_to_csv(guilty_X_vector, guilty_Y_vector, "bad_channel_points.csv"); + + cout << "Getting lat-long from the new XY coordinates. This will overwrite any old lat-long coordinates." << endl; + get_latlong_from_x_and_y(X_column_name, Y_column_name); + +} + +//============================================================================== +// This returns the data map +//============================================================================== +map >& LSDSpatialCSVReader::get_data_map() {return this->data_map;} + +//============================================================================== +// This appends a value to a column +//============================================================================== +void LSDSpatialCSVReader::append_to_col(string colname, string val){this->data_map[colname].push_back(val);} + + + + + #endif diff --git a/src/LSDSpatialCSVReader.hpp b/src/LSDSpatialCSVReader.hpp index 856215c..84ab1a0 100644 --- a/src/LSDSpatialCSVReader.hpp +++ b/src/LSDSpatialCSVReader.hpp @@ -213,6 +213,25 @@ class LSDSpatialCSVReader /// @date 03/10/2019 bool is_column_in_csv(string column_name); + /// This looks for a string fragment in the column names so that + /// if you are a little off with the column name you can still try to find the correct one. + /// @param column_name_fragment A string fragment you will search for in the column names + /// @return the full name of the column that contains the fragment + /// @author SMM + /// @date 19/03/2021 + string find_string_in_column_name(string column_name_fragment); + + /// @brief Adds a data column to the map. + /// @detail Note this assumes you have the node ordering correct + /// @param column_name the column name, duh + /// @param column_data the data as a vector of strings. You need to convert + /// other kinds of data to string if you want it in the data map + /// @return a boolean that is true if the column was added + /// @author SMM + /// @date 09/02/2021 + bool add_data_column(string column_name, vector column_data); + + /// @brief This gets a data column from the csv file /// @param column_name a string that holds the column name /// @return a vector of strings: this holds the data. @@ -253,6 +272,15 @@ class LSDSpatialCSVReader /// @date 28/09/2020 void data_column_add_float(string column_name, float add_value); + /// @brief This takes the values in the data column and adds + /// a float value. to them. It will not check if the column is actually floats + /// so caution is needed! + /// @param column_name a string that holds the column name + /// @float add_value The value to be added. If you want to subtract use the negative value + /// @author SMM + /// @date 28/09/2020 + void data_column_add_float(string column_name, vector add_value); + /// @brief This takes the values in the data column and multiplies /// a float value. to them. It will not check if the column is actually floats /// so caution is needed! @@ -262,6 +290,13 @@ class LSDSpatialCSVReader /// @date 28/09/2020 void data_column_multiply_float(string column_name, float multiply_value); + /// @brief Replaces a data column with a new float vector + /// @param column_name a string that holds the column name + /// @float new_column The new float column + /// @author SMM + /// @date 21/04/2021 + void data_column_replace(string column_name, vector new_column); + /// @brief This is a very specific function used only to impose a minimum gradient on the single /// channel /// @param fd_column_name a string that holds the flow distance column name @@ -279,6 +314,12 @@ class LSDSpatialCSVReader /// @date 13/11/2014 void check_if_points_are_in_raster(); + /// @brief Returns the points_in_raster_vector + /// @return is_in_raster a boolean telling if the point is in the raster + /// @author SMM + /// @date 14/02/2021 + vector get_if_points_are_in_raster_vector(); + /// @brief This function gets vectors of x and y coordinates and node indices /// from these points /// @details This DOES NOT use latitude and longitude, instead @@ -302,10 +343,19 @@ class LSDSpatialCSVReader /// @brief Function to extract the nodeindex /// The nodeindex needs to be in the object, will take "node", "id", and "nodeindex" as columns /// @author SMM - /// @return A vectoe of the nodeindices + /// @return A vector of the nodeindices /// @date 08/10/2019 vector get_nodeindex_vector(); + /// @brief Uses the lat-long in the csv information to get the nodeindex for a given flowinfo object + /// Creates a new "nodeindex" column in the object + /// @param FlowInfo a flowinfo object + /// @author SMM + /// @return none, but updates the data_map + /// @date 09/02/2021 + void add_nodeindex_vector_to_data_map_using_lat_long(LSDFlowInfo& FlowInfo); + + /// @brief Function to create a map with nodeindex as the key /// The nodeindex needs to be in the object /// @param column name @@ -333,7 +383,7 @@ class LSDSpatialCSVReader /// @param row /// @param col /// @author SMM - /// @date 30/09/2020 + /// @date 30/09/2020 void get_row_and_col_of_a_point(float X_coordinate,float Y_coordinate,int& row, int& col); void get_row_and_col_of_a_point(double X_coordinate,double Y_coordinate,int& row, int& col); @@ -375,6 +425,35 @@ class LSDSpatialCSVReader /// @date 14/03/17 void print_data_to_geojson(string json_outname); + void print_data_to_geojson_linestring(string json_outname); + + /// @brief Centres XY coordinates on row col of a topography raster + /// @param ThisRaster the raster + /// @param X_column_name the name of the X coordinate column + /// @param Y_column_name the name of the Y coordinate column + /// @author ELSG + /// @date 04/03/21 + void centre_XY_by_row_col(LSDRaster& ThisRaster, string X_column_name, string Y_column_name); + + /// @brief Finds gaps in channel data and fills them with one extra node. + /// @param ThisRaster the raster + /// @param X_column_name the name of the X coordinate column + /// @param Y_column_name the name of the Y coordinate column + /// @param fd_column_name the name of the flow distance column + /// @author ELSG + /// @date 04/03/21 + void interpolate_across_gap(LSDRaster& ThisRaster, string X_column_name, string Y_column_name, string fd_column_name); + + // @brief Returns the data map + /// @author BG/ELSG + /// @date 01/03/21 + map >& get_data_map(); + + // @brief Appends a value to a column + /// @author BG/ELSG + /// @date 02/03/21 + void append_to_col(string colname, string val); + /// Gets the various data members vector get_latitude() const {return latitude;} vector get_longitude() const {return longitude;} diff --git a/src/LSDStatsTools.cpp b/src/LSDStatsTools.cpp index 5ed9566..3580c77 100644 --- a/src/LSDStatsTools.cpp +++ b/src/LSDStatsTools.cpp @@ -5819,6 +5819,239 @@ vector linspace(float min, float max, int n){ } + +///=========================================================================== +/// +/// .#####....####...######..##..##..######...####.. +/// .##..##..##..##....##....###.##....##....##..... +/// .#####...##..##....##....##.###....##.....####.. +/// .##......##..##....##....##..##....##........##. +/// .##.......####...######..##..##....##.....####.. +/// +///=========================================================================== +float distance_between_two_points(float x1, float y1, float x2, float y2) +{ + float x_dif = x2-x1; + float y_dif = y2-y1; + + float dist = sqrt(x_dif*x_dif+y_dif*y_dif); + return dist; +} + +vector distance_between_point_and_set_of_points(float x1, float y1, vector x2, vector y2) +{ + int n_points = int(x2.size()); + vector dists; + for (int i = 0; i< n_points; i++) + { + dists.push_back( distance_between_two_points(x1,y1,x2[i],y2[i])); + } + + return dists; +} + +vector point_along_lines_at_distance_from_start(float xs, float ys, float xe, float ye, float distance) +{ + float dist_between_points = distance_between_two_points(xs,ys,xe,ye); + + float dist_frac = distance/dist_between_points; + //cout << "Dist: " << dist_between_points << " distance_along: " << distance << " dist_frac "<< dist_frac << endl; + //cout << "new x: " << (1-dist_frac)*xs+dist_frac*xe << " new y: " << (1-dist_frac)*ys+dist_frac*ye << endl; + vector x_and_y; + x_and_y.push_back((1-dist_frac)*xs+dist_frac*xe); + x_and_y.push_back((1-dist_frac)*ys+dist_frac*ye); + + return x_and_y; + +} + + +// Does what it says on the tin +vector< pair > evenly_spaced_points_along_polyline(vector x1, vector y1, float spacing) +{ + cout << "Brace yourself, I am about to do some super segmentation!" << endl; + + vector< pair > points_along_line; + vector location_vec; + float current_x, current_y; + int n_points = int(x1.size()); + float sxs, sys,sxe,sye; + float this_segment_distance,current_distance_along_segment,next_distance_along_segment; + float leftover_distance; + if(n_points > 1) + { + current_x = x1[0]; + current_y = y1[0]; + pair start_pair(current_x,current_y); + points_along_line.push_back(start_pair); + leftover_distance = spacing; + for(int i = 0; i< n_points-1; i++) + { + cout << "I am on segment number " << i+1 << " of " << n_points-1 << endl; + // get the starting and ending points of this segment, and its distance + sxs = x1[i]; + sys = y1[i]; + sxe = x1[i+1]; + sye = y1[i+1]; + this_segment_distance = distance_between_two_points(sxs,sys,sxe,sye); + + cout << "Segment distance is: " << this_segment_distance << " and leftover is " << leftover_distance << endl; + + // if the leftover distance is greater than the length of te segment, we just move on + // to the next segment + if(leftover_distance > this_segment_distance) + { + leftover_distance = leftover_distance-this_segment_distance; + } + else + { + next_distance_along_segment = leftover_distance; + + do + { + // first get the current point based on the next distance along segment + location_vec = point_along_lines_at_distance_from_start(sxs, sys, sxe, sye, next_distance_along_segment); + cout << "New point: " << location_vec[0] << "," << location_vec[1] << endl; + pair current_location_pair(location_vec[0],location_vec[1]); + points_along_line.push_back(current_location_pair); + + // now increment the next distance along the segment + next_distance_along_segment+=spacing; + cout << "The next distance will be: " << next_distance_along_segment << endl; + + } while (next_distance_along_segment <= this_segment_distance); + leftover_distance = next_distance_along_segment-this_segment_distance; + + } + } + } + return points_along_line; +} + +// Overloaded version that includes the distance along the polyline +// Vectors are overwritten +void evenly_spaced_points_along_polyline(vector x1, vector y1, float spacing, + vector& spaced_eastings, vector& spaced_northings, + vector& spaced_distances) +{ + cout << "Brace yourself, I am about to do some super segmentation!" << endl; + + + vector SD, SE, SN; + + + vector< pair > points_along_line; + vector location_vec; + float current_x, current_y, current_distance; + int n_points = int(x1.size()); + float sxs, sys,sxe,sye; + float this_segment_distance,current_distance_along_segment,next_distance_along_segment; + float leftover_distance; + if(n_points > 1) + { + + + current_x = x1[0]; + current_y = y1[0]; + current_distance = 0; + + SE.push_back(current_x); + SN.push_back(current_y); + SD.push_back(0); + + leftover_distance = spacing; + for(int i = 0; i< n_points-1; i++) + { + cout << "I am on segment number " << i+1 << " of " << n_points-1 << endl; + // get the starting and ending points of this segment, and its distance + sxs = x1[i]; + sys = y1[i]; + sxe = x1[i+1]; + sye = y1[i+1]; + this_segment_distance = distance_between_two_points(sxs,sys,sxe,sye); + + cout << "Segment distance is: " << this_segment_distance << " and leftover is " << leftover_distance << endl; + + // if the leftover distance is greater than the length of te segment, we just move on + // to the next segment + if(leftover_distance > this_segment_distance) + { + leftover_distance = leftover_distance-this_segment_distance; + } + else + { + next_distance_along_segment = leftover_distance; + + do + { + // first get the current point based on the next distance along segment + location_vec = point_along_lines_at_distance_from_start(sxs, sys, sxe, sye, next_distance_along_segment); + cout << "New point: " << location_vec[0] << "," << location_vec[1] << endl; + current_distance = current_distance+spacing; + + SE.push_back(location_vec[0]); + SN.push_back(location_vec[1]); + SD.push_back(current_distance); + + // now increment the next distance along the segment + next_distance_along_segment+=spacing; + cout << "The next distance will be: " << next_distance_along_segment << endl; + + } while (next_distance_along_segment <= this_segment_distance); + leftover_distance = next_distance_along_segment-this_segment_distance; + + } + } + } + spaced_eastings = SE; + spaced_northings = SN; + spaced_distances = SD; + +} + + +vector< pair > convert_x_and_y_vecs_to_pairs(vector x_vec, vector y_vec) +{ + int n_pairs = int(x_vec.size()); + vector< pair > pairs; + + for (int i = 0; i this_pair(x_vec[i],y_vec[i]); + pairs.push_back(this_pair); + } + + return pairs; +} + +void convert_pairs_to_x_and_y_vecs(vector< pair > pairs, vector& x_vec, vector& y_vec) +{ + int n_pairs = int(pairs.size()); + vector new_x; + vector new_y; + + for (int i = 0; i get_directional_vector_coords_from_dataset(vector x_data, v } + + +// Some utility functions +// This creates a template array for a line of a given bearing +// The bearing is in degrees +Array2D make_template_for_vector_bearing(float bearing, int scale) +{ + // Open the data file. + // This is for debugging. + bool print_file_for_debug = false; + ofstream out_file; + + if(print_file_for_debug) + { + out_file.open("data_out.csv"); + } + + + int dim = 2*scale+1; + Array2D bearing_template(dim,dim,0); + + float max_extent = float(scale)*0.5+float(scale); + + //Declare parameters + int r,c; + int dir; + + // This is the index of the starting node + int a = 0; + int b = 0; + float degs, degs_new, theta; + float xo, yo, xi, yi, temp_yo1, temp_yo2, temp_xo1, temp_xo2; + + degs = bearing; + float rads = BearingToRad(degs); + float tan_of_bearing = tan(rads); + float cos_of_bearing = cos(rads); + + vector empty_f_vec; + vector east_vec; + vector north_vec; + + vector empty_i_vec; + vector a_vec; + vector b_vec; + + // We do the four edge cases first + // Remember that the y coordinate increases with decreasing + // row number + int shifted_i; + if(degs == 45) + { + shifted_i = scale; + bearing_template[shifted_i][shifted_i] = 1; + for(int i = 0; i<= scale; i++) + { + bearing_template[-i+scale][i+scale] = 1; + bearing_template[i+scale][-i+scale] = 2; + } + } + else if (degs==135) + { + shifted_i = scale; + bearing_template[shifted_i][shifted_i] = 1; + for(int i = 0; i<= scale; i++) + { + shifted_i = i+scale; + bearing_template[i+scale][i+scale] = 1; + bearing_template[-i+scale][-i+scale] = 2; + } + } + else if (degs==225) + { + shifted_i = scale; + bearing_template[shifted_i][shifted_i] = 1; + for(int i = 0; i<= scale; i++) + { + shifted_i = i+scale; + bearing_template[i+scale][-i+scale] = 1; + bearing_template[-i+scale][i+scale] = 2; + } + } + else if (degs==315) + { + shifted_i = scale; + bearing_template[shifted_i][shifted_i] = 1; + for(int i = 0; i<= scale; i++) + { + shifted_i = i+scale; + bearing_template[-i+scale][-i+scale] = 1; + bearing_template[i+scale][i+scale] = 2; + } + } + else + { + // Do each side of the bearing + // This code comes from the flow routing routines by stuart + // This is for the first bearing + xo = 0; + yo = 0; + + east_vec.push_back(0); + north_vec.push_back(0); + + a_vec.push_back(a); + b_vec.push_back(b); + + //test direction, calculate outlet coordinates and update indicies + // easterly + if (degs >= 45 && degs < 135) + { + //cout << "\neasterly" << endl; + xo = 1, yo = (1+tan_of_bearing)*0.5; + xi = 0, yi = yo; + dir = 1; + east_vec.push_back(0.5); + north_vec.push_back(yo - 0.5); + ++b; + if (yi == 0) yi = 0.00001; + else if (yi == 1) yi = 1 - 0.00001; + } + //southerly + else if (degs >= 135 && degs < 225) + { + //cout << "\nsoutherly" << endl; + xo = (1-(1/tan_of_bearing))*0.5, yo = 0; + xi = xo, yi = 1; + dir = 2; + east_vec.push_back(xo - 0.5); + north_vec.push_back(-0.5); + ++a; + if (xi == 0) xi = 0.00001; + else if (xi == 1) xi = 1 - 0.00001; + } + // westerly + else if (degs >= 225 && degs < 315) + { + //cout << "\nwesterly" << endl; + xo = 0, yo = (1-tan_of_bearing)*0.5; + xi = 1, yi = yo; + dir = 3; + east_vec.push_back(-0.5); + north_vec.push_back((yo - 0.5)); + //cout << "LSDStatstools Line 6456" << endl; + --b; + if (yi == 0) yi = 0.00001; + else if (yi == 1) yi = 1 - 0.00001; + } + //northerly + else if (degs >= 315 || degs < 45) + { + //cout << "\nnortherly" << endl; + xo = (1+(1/tan_of_bearing))*0.5, yo = 1; + xi = xo, yi = 0; + dir = 4; + east_vec.push_back(xo - 0.5); + north_vec.push_back(0.5); + --a; + if (xi == 0) xi = 0.00001; + else if (xi == 1) xi = 1 - 0.00001; + } + a_vec.push_back(a); + b_vec.push_back(b); + + + // now loop through the next + //continue trace until you get to the edge + while (a > -scale-1 && a < scale && b > -scale-1 && b < scale) + { //added boudary checking to catch cells which flow off the edge of the DEM tile. + + degs_new = degs; + + //DO NORMAL FLOW PATH + //set xo, yo to 0 and 1 in turn and test for true outlet (xi || yi == 0 || 1) + temp_yo1 = yi + (1-xi)*tan_of_bearing; // xo = 1 + temp_xo1 = xi + (1-yi)*(1/tan_of_bearing); // yo = 1 + temp_yo2 = yi - xi*tan_of_bearing; // xo = 0 + temp_xo2 = xi - yi*(1/tan_of_bearing); // yo = 0 + + //cout << "temp yo1 " << temp_yo1 << " temp xo1 " << temp_xo1 << " temp_yo2 " << temp_yo2 << " temp_xo2 " << temp_xo2 << " dir " << dir << endl; + + // can't outlet at same point as inlet + if (dir == 1) temp_yo2 = -1; + else if (dir == 2) temp_xo1 = -1; + else if (dir == 3) temp_yo1 = -1; + else if (dir == 4) temp_xo2 = -1; + + if (temp_yo1 <= 1 && temp_yo1 > 0) + { + xo = 1, yo = temp_yo1; + xi = 0, yi = yo, + dir = 1; + east_vec.push_back(float(b) + 0.5); + north_vec.push_back(float(-a) + (yo - 0.5)); + ++b; + //cout << "b " << b << endl; + if (xi== 0 && yi == 0) yi = 0.00001; + else if (xi== 0 && yi == 1) yi = 1 - 0.00001; + } + else if (temp_xo2 <= 1 && temp_xo2 > 0) + { + xo = temp_xo2, yo = 0; + xi = xo, yi = 1, + dir = 2; + east_vec.push_back(float(b) + (xo - 0.5)); + north_vec.push_back(float(-a) - 0.5); + ++a; + if (xi== 0 && yi == 1) xi = 0.00001; + else if (xi== 1 && yi == 1) xi = 1 - 0.00001; + } + else if (temp_yo2 <= 1 && temp_yo2 > 0) + { + xo = 0, yo = temp_yo2; + xi = 1, yi = yo, + dir = 3; + east_vec.push_back(float(b) -0.5); + north_vec.push_back(float(-a) + (yo - 0.5)); + --b; + if (xi== 1 && yi == 0) yi = 0.00001; + else if (xi== 1 && yi == 1) yi = 1 - 0.00001; + } + else if (temp_xo1 <= 1 && temp_xo1 > 0) + { + xo = temp_xo1, yo = 1; + xi = xo, yi = 0, + dir = 4; + east_vec.push_back(float(b) + (xo - 0.5)); + north_vec.push_back(float(-a) + 0.5); + --a; + if (xi == 0 && yi == 0) xi = 0.00001; + else if (xi== 1 && yi == 0) xi = 1 - 0.00001; + } + else { a = scale ; } // FJC - added 28/07/21 to stop infinite loop bug where a and b do not change. Does this work? + //cout << "a " << a << " b " << b << endl; + a_vec.push_back(a); + b_vec.push_back(b); + } + + // now tag the raster + int n_cells = int( a_vec.size()); + for(int i = 0; i= 0 && r < 2*scale+1 && c >= 0 && c < 2*scale+1) + { + bearing_template[r][c] = 1; + } + } + + if(print_file_for_debug) + { + for(int i = 0; i 360) + { + degs = degs-360; + } + rads = BearingToRad(degs); + tan_of_bearing = tan(rads); + cos_of_bearing = cos(rads); + // Now the reverse direction + //test direction, calculate outlet coordinates and update indicies + // easterly + if (degs >= 45 && degs < 135) + { + //cout << "\neasterly" << endl; + xo = 1, yo = (1+tan_of_bearing)*0.5; + xi = 0, yi = yo; + dir = 1; + east_vec.push_back(0.5); + north_vec.push_back(yo - 0.5); + ++b; + if (yi == 0) yi = 0.00001; + else if (yi == 1) yi = 1 - 0.00001; + } + //southerly + else if (degs >= 135 && degs < 225) + { + //cout << "\nsoutherly" << endl; + xo = (1-(1/tan_of_bearing))*0.5, yo = 0; + xi = xo, yi = 1; + dir = 2; + east_vec.push_back(xo - 0.5); + north_vec.push_back(-0.5); + ++a; + if (xi == 0) xi = 0.00001; + else if (xi == 1) xi = 1 - 0.00001; + } + // westerly + else if (degs >= 225 && degs < 315) + { + //cout << "\nwesterly" << endl; + xo = 0, yo = (1-tan_of_bearing)*0.5; + xi = 1, yi = yo; + dir = 3; + east_vec.push_back(-0.5); + north_vec.push_back((yo - 0.5)); + --b; + if (yi == 0) yi = 0.00001; + else if (yi == 1) yi = 1 - 0.00001; + } + //northerly + else if (degs >= 315 || degs < 45) + { + //cout << "\nnortherly" << endl; + xo = (1+(1/tan_of_bearing))*0.5, yo = 1; + xi = xo, yi = 0; + dir = 4; + east_vec.push_back(xo - 0.5); + north_vec.push_back(0.5); + --a; + if (xi == 0) xi = 0.00001; + else if (xi == 1) xi = 1 - 0.00001; + } + a_vec.push_back(a); + b_vec.push_back(b); + + //cout << "Got the first step" << endl; + + // now loop through the next + //continue trace until you get to the edge + while (a > -scale-1 && a < scale && b > -scale-1 && b < scale) + { //added boundary checking to catch cells which flow off the edge of the DEM tile. + + degs_new = degs; + + //cout << "x: " << xi << " y: " << yi << endl; + + //DO NORMAL FLOW PATH + //set xo, yo to 0 and 1 in turn and test for true outlet (xi || yi == 0 || 1) + temp_yo1 = yi + (1-xi)*tan_of_bearing; // xo = 1 + temp_xo1 = xi + (1-yi)*(1/tan_of_bearing); // yo = 1 + temp_yo2 = yi - xi*tan_of_bearing; // xo = 0 + temp_xo2 = xi - yi*(1/tan_of_bearing); // yo = 0 + + // can't have outlet at same point as inlet + if (dir == 1) temp_yo2 = -1; + else if (dir == 2) temp_xo1 = -1; + else if (dir == 3) temp_yo1 = -1; + else if (dir == 4) temp_xo2 = -1; + + //cout << "o1: " << temp_xo1 << "," << temp_yo1 << " o2: " << temp_xo2 << "," << temp_yo2 << endl; + + if (temp_yo1 <= 1 && temp_yo1 > 0) + { + //cout << "case 1" << endl; + xo = 1, yo = temp_yo1; + xi = 0, yi = yo, + dir = 1; + east_vec.push_back(float(b) + 0.5); + north_vec.push_back(float(-a) + (yo - 0.5)); + ++b; + if (xi== 0 && yi == 0) yi = 0.00001; + else if (xi== 0 && yi == 1) yi = 1 - 0.00001; + } + else if (temp_xo2 <= 1 && temp_xo2 > 0) + { + //cout << "case 2" << endl; + xo = temp_xo2, yo = 0; + xi = xo, yi = 1, + dir = 2; + east_vec.push_back(float(b) + (xo - 0.5)); + north_vec.push_back(float(-a) - 0.5); + ++a; + if (xi== 0 && yi == 1) xi = 0.00001; + else if (xi== 1 && yi == 1) xi = 1 - 0.00001; + } + else if (temp_yo2 <= 1 && temp_yo2 > 0) + { + //cout << "case 3" << endl; + xo = 0, yo = temp_yo2; + xi = 1, yi = yo, + dir = 3; + east_vec.push_back(float(b) -0.5); + north_vec.push_back(float(-a) + (yo - 0.5)); + --b; + if (xi== 1 && yi == 0) yi = 0.00001; + else if (xi== 1 && yi == 1) yi = 1 - 0.00001; + } + else if (temp_xo1 <= 1 && temp_xo1 > 0) + { + //cout << "case 4" << endl; + xo = temp_xo1, yo = 1; + xi = xo, yi = 0, + dir = 4; + east_vec.push_back(float(b) + (xo - 0.5)); + north_vec.push_back(float(-a) + 0.5); + --a; + if (xi == 0 && yi == 0) xi = 0.00001; + else if (xi== 1 && yi == 0) xi = 1 - 0.00001; + } + a_vec.push_back(a); + b_vec.push_back(b); + } + + // now tag the raster + n_cells = int( a_vec.size()); + for(int i = 0; i= 0 && r < 2*scale+1 && c >= 0 && c < 2*scale+1) + { + bearing_template[r][c] = 2; + } + } + + if(print_file_for_debug) + { + for(int i = 0; i duplicates(vector data); // Generate vector of evenly spaced numbers between two points vector linspace(float min, float max, int n); + +///=========================================================================== +/// +/// .#####....####...######..##..##..######...####.. +/// .##..##..##..##....##....###.##....##....##..... +/// .#####...##..##....##....##.###....##.....####.. +/// .##......##..##....##....##..##....##........##. +/// .##.......####...######..##..##....##.....####.. +/// +///=========================================================================== +float distance_between_two_points(float x1, float y1, float x2, float y2); +vector distance_between_point_and_set_of_points(float x1, float y1, vector x2, vector y2); +float points_along_lines_at_distance_from_start(float x1, float y1, float x2, float y2, float distance); +vector< pair > evenly_spaced_points_along_polyline(vector x1, vector y1, float spacing); +void evenly_spaced_points_along_polyline(vector x1, vector y1, float spacing, + vector& spaced_eastings, vector& spaced_northings, + vector& spaced_distances); +vector< pair > convert_x_and_y_vecs_to_pairs(vector x_vec, vector y_vec); +void convert_pairs_to_x_and_y_vecs(vector< pair > pairs, vector& x_vec, vector& y_vec); + + + +///=========================================================================== +/// +/// ...####...##..##...####...##......######...####.. +/// ..##..##..###.##..##......##......##......##..... +/// ..######..##.###..##.###..##......####.....####.. +/// ..##..##..##..##..##..##..##......##..........##. +/// ..##..##..##..##...####...######..######...####.. +/// +///=========================================================================== // convert degree bearing from north to radians from east float BearingToRad(float Bearing); @@ -520,7 +551,9 @@ double deg(double radians); // Get the angle between two vectors float angle_between_vectors(float x1, float y1, float x2, float y2); -// Get the clockwise angle between two vectors +// Get the clockwise angle a vector and north +// this gets the bearing +// returns the value in radians float clockwise_angle_between_vector_and_north(float x1, float y1, float x2, float y2); // get clockwise angle between two vectors specifying the origin @@ -542,6 +575,15 @@ float angle_between_two_vector_datasets(vector& x1_data, vector& y vector get_directional_vector_coords_from_dataset(vector x1_data, vector& y_data, bool vectors_point_downstream); + +// This creates a template array for a line of a given bearing +Array2D make_template_for_vector_bearing(float bearing, int scale); + +// This is a template with empty values on the inside and the bearing as values along the outside +Array2D make_template_for_line_angles(int scale); + + + // Get the data for a boxplot from an unsorted vector of floats, which does not // contain any NDV values. // diff --git a/src/driver_functions/MuddPILEdriver.cpp b/src/driver_functions/MuddPILEdriver.cpp index b2327e4..d3404a8 100644 --- a/src/driver_functions/MuddPILEdriver.cpp +++ b/src/driver_functions/MuddPILEdriver.cpp @@ -61,6 +61,10 @@ using namespace std; int main (int nNumberofArgs,char *argv[]) { + string version_number = "0.08d"; + string citation = "https://www.doi.org/10.5281/zenodo.997388"; + + cout << "================================================================" << endl; cout << "|| Welcome to the MuddPILEdirver! ||" << endl; cout << "|| This program drives the MuddPILE lanscape evolution model. ||" << endl; @@ -77,14 +81,47 @@ int main (int nNumberofArgs,char *argv[]) cout << "|| Documentation can be found at: ||" << endl; cout << "|| https://lsdtopotools.github.io/LSDTT_documentation/ ||" << endl; cout << "================================================================" << endl; + cout << "|| This is MuddPILE version ||" << endl; + cout << "|| " << version_number << endl; + cout << "|| If the version number has a d at the end it is a ||" << endl; + cout << "|| development version. ||" << endl; + cout << "================================================================" << endl; + // Get the arguments vector path_and_file = DriverIngestor(nNumberofArgs,argv); - - string path_name = path_and_file[0]; string f_name = path_and_file[1]; + // Check if we are doing the version or the citation + if(f_name == "lsdtt_citation.txt") + { + + cout << endl << endl << endl << "==============================================" << endl; + cout << "To cite this code, please use this citation: " << endl; + cout << citation << endl; + cout << "Copy this url to find the full citation." << endl; + cout << "also see above for more detailed citation information." << endl; + cout << "=========================================================" << endl; + + ofstream ofs; + ofs.open("./muddpiledriver-citation.txt"); + ofs << citation << endl; + ofs.close(); + + exit(0); + } + + if(f_name == "lsdtt_version.txt") + { + ofstream ofs; + ofs.open("./muddpiledriver-version.txt"); + ofs << version_number << endl; + ofs.close(); + + exit(0); + } + // load parameter parser object LSDParameterParser LSDPP(path_name,f_name); @@ -97,251 +134,660 @@ int main (int nNumberofArgs,char *argv[]) map bool_default_map; map string_default_map; + // this will contain the help file + map< string, vector > help_map; + + + //================================================================================== + // + // .#####....####...#####....####...##...##..######..######..######..#####....####.. + // .##..##..##..##..##..##..##..##..###.###..##........##....##......##..##..##..... + // .#####...######..#####...######..##.#.##..####......##....####....#####....####.. + // .##......##..##..##..##..##..##..##...##..##........##....##......##..##......##. + // .##......##..##..##..##..##..##..##...##..######....##....######..##..##...####.. + // + //================================================================================= // Parameters for initiating the model domain int_default_map["NRows"] = 200; + help_map["NRows"] = { "int","200","Number of rows in a model where you have not loaded a DEM.","This is overwritten if you load an initial DEM."}; + int_default_map["NCols"] = 400; + help_map["NCols"] = { "int","400","Number of cols in a model where you have not loaded a DEM.","This is overwritten if you load an initial DEM."}; + float_default_map["DataResolution"] = 30; + help_map["DataResolution"] = { "float","30","Pixel resolution of a model where you have not loaded a DEM.","This is overwritten if you load an initial DEM."}; // Parameters that are used if you load a raster bool_default_map["read_initial_raster"] = false; + help_map["read_initial_raster"] = { "bool","false","Reads a raster from a DEM using the read path and read fname.","Make sure your raster in in ENVI bi format and in UTM coordinate system EPSG:326XX or EPSG:327XX."}; + float_default_map["minimum_elevation"] = 0.0; + help_map["minimum_elevation"] = { "float","0.0","All elevation values below this become nodata if remove_seas is true.","Ususally 0."}; + float_default_map["maximum_elevation"] = 30000; + help_map["maximum_elevation"] = { "float","0.0","All elevation values above this become nodata if remove_seas is true.","Pick a big number."}; + float_default_map["min_slope_for_fill"] = 0.0001; + help_map["min_slope_for_fill"] = { "float","0.0001","Minimum slope between pixels for the filling algorithm.","Best not to change the default."}; + bool_default_map["remove_seas"] = true; // elevations above minimum and maximum will be changed to nodata + help_map["remove_seas"] = { "bool","true","Slightly misleading name; it replaces both high and low DEM values with nodata.","This gets rid of low lying areas but also is handy when the nodata is not translated from the raw DEM and it is full of funny large numbers."}; + bool_default_map["print_raster_without_seas"] = false; + help_map["print_raster_without_seas"] = { "bool","false","Overwrites the raster without seas.","DANGER this will replace your existing raster with the high and low points replaced by nodata. See the remove_seas flag"}; - bool_default_map["convert_intitial_raster_to_diamond_square_fractal"] = false; + bool_default_map["convert_initial_raster_to_diamond_square_fractal"] = false; + help_map["convert_initial_raster_to_diamond_square_fractal"] = { "bool","false","This takes the initial raster (with its nodata footprint) and creates a random diamond square raster on to original footprint.","The spinup of the diamond square surface will follow the diamond square spinup parameters elsewhere in this program."}; - // Some logi for filling rasters that are being loaded + bool_default_map["test_single_basin_outlet"] = false; + help_map["test_single_basin_outlet"] = { "bool","false","This tests the buffering routines around the outside of the DEM.","Most useful for the diamond square conversion."}; + + bool_default_map["raise_raster_over_base_level"] = true; + help_map["raise_raster_over_base_level"] = { "bool","false","This looks at the baselevel file and if the lowest point is above the lowest point in the model raster then the raster is raised above the baselevel.","If pixels are above the base level channel then the raster will not incise."}; + + + + // Some logic for filling rasters that are being loaded bool_default_map["carve_before_fill"] = false; // Implements a carving algorithm + help_map["carve_before_fill"] = { "bool","false","This implements a breaching algorithm before filling.","Good for landscapes with DEM obstructions (like roads) across the channels."}; + bool_default_map["raster_is_filled"] = false; + help_map["raster_is_filled"] = { "bool","false","This reads a pre-existing fill raster to save time.","You need to have printed the fill raster if you set this to true."}; + bool_default_map["print_fill_raster"] = false; + help_map["print_fill_raster"] = { "bool","false","Prints the fill raster.","Filename includes _FILL"}; + // Read a channel file + bool_default_map["extract_single_channel"] = false; + help_map["extract_single_channel"] = { "bool","false","Extracts a single channel from the DEM. The channel is a flow path that starts from a point designated by channel_source_fname.","Doing this in lsdtt-basic-metrics provides more options."}; + string_default_map["channel_source_fname"] = "channel_source_fname"; + help_map["channel_source_fname"] = { "string","channel_source_fname","A csv with latitude and longitude as headers that is the source point of a flow path that will be printed if extract_single_channel is true.","Only reads the first point so far."}; - // Read a channel file - bool_default_map["extract_single_channel"] = false; - string_default_map["channel_source_fname"] = "single_channel_source"; string_default_map["single_channel_print_fname"] = "single_channel"; + help_map["single_channel_print_fname"] = { "bool","single_channel","The file prefix of the single channel csv. This has data from a single flow path.","Columns include latitude longitude flow distance, area, elevation and a few other items"}; // paramters for controlling model output int_default_map["print_interval"] = 10; + help_map["print_interval"] = { "int","10","The number of timesteps required for printing.","This is reactive to the adaptive timestep so will give a fixed printing interval."}; + bool_default_map["write_hillshade"] = true; + help_map["write_hillshade"] = { "bool","false","Write the hillshade raster.","You need this for a lot of our plotting routines. Filename includes _HS"}; + // control of m and n, and paramters for chi float_default_map["A_0"] = 1; + help_map["A_0"] = { "float","1.0","The A_0 parameter for chi computation.","Usually set to 1 so that the slope in chi-elevation space is the same as k_sn"}; + float_default_map["m"] = 0.5; + help_map["m"] = { "float","0.5","The area exponent in the stream power law.","Defaults lead to m/n = 0.5"}; + float_default_map["n"] = 1; + help_map["n"] = { "float","1","The slope exponent in the stream power law.","Model slows down a lot if n does not equal 1 but there is much evidence that n is greater than 1 for example Harel et al 2016 Geomorphology."}; + int_default_map["uplift_mode"] = 0; + help_map["uplift_mode"] = { "int","0","Model can run with specific uplift fields. This is a bit old and we now use uplift rasters.","default == block; uplift 1 == tilt block; 2 == gaussian; 3 == quadratic; 4 == periodic."}; + float_default_map["dt"] = 250; + help_map["dt"] = { "float","250","The starting timestep in years.","This can change if you use adaptive timesteps."}; + float_default_map["D"] = 0.002; + help_map["D"] = { "float","0.002","Default sediment transport coefficient.","In m^2 per year"}; + float_default_map["S_c"] = 1.0; + help_map["S_c"] = { "float","1.0","Default critical slope parameter for hillslopes.","Dimensionless"}; + float_default_map["background_K"] = 0.0005; + help_map["background_K"] = { "float","0.0005","Default channel erodibility.","Dimensions depend on m and n"}; // Parameters for the initial surface bool_default_map["use_diamond_square_initial"] = true; + help_map["use_diamond_square_initial"] = { "bool","true","Uses a diamond square algorithm to produce a pseudo fractal surface as the initial surface in a rectangular model run.","You get better branching behaviour using this algorithm in the channel network than if you use other initial surfaces. You can now use diamond_square_spinup and this is preferred"}; + float_default_map["diamond_square_relief"] = 16; + help_map["diamond_square_relief"] = { "float","16","The total releif of the original diamond square surface after initiation.","Usually you snap to stead after this to control the final relief but this controls how oriented the initial drainage patterns are."}; + int_default_map["diamond_square_feature_order"] = 8; + help_map["diamond_square_feature_order"] = { "int","8","Diamond square has fractal features that are of order 2^n and this sets the maximum value of n.","You will want 2^n to be less than the number of rows or columns (whichever is less) in the raster. Sets the repeating scale of the big features."}; + int_default_map["ds_minimum_feature_order"] = 4; + help_map["ds_minimum_feature_order"] = { "int","4","Sets the minimum scale of the diamon square features. This is 2^n where n is the feature order.","I wouldn't change this."}; + bool_default_map["taper_edges"] = true; + help_map["taper_edges"] = { "bool","true","This reduces the relief along the edges so you don't get weird negative elevations below base level along the edge.","Setting to false might lead to some weird behaviour so don't change."}; + int_default_map["taper_rows"] = 10; + help_map["taper_rows"] = { "int","10","Number of rows over which the model tapers the initial surface to 0 at the edge of the DEM","Larger numbers mean smoother edges."}; + + bool_default_map["superimpose_parabola"] = true; + help_map["superimpose_parabola"] = { "bool","true","After initial construction of the diamond square surface this adds a parabolic surface to get channel to drain away from the centre","If this is false you will get a lot of lakes in the middle of the initial DEM."}; + float_default_map["parabola_relief"] = 6; + help_map["parabola_relief"] = { "float","6","Relief of the parabolic surface added to the DEM","Too small and you get a lot of lakes. Too big and the channels start to straighten our toward the edge."}; + bool_default_map["roughen_surface"] = true; + help_map["roughen_surface"] = { "bool","true","Adds some noise after the initial DEM", "This tries to add some roughness to fill steps"}; + bool_default_map["fill_then_roughen_surface"] = true; + help_map["fill_then_roughen_surface"] = { "bool","true","The initial diamond square surface will produce some low topography that will get filled. Adter filling you will get very straight channels. So this roughens the surface before filling to get a more random network in the filled lakes", "Used to deal with filled depressions after inital diamond square step."}; + float_default_map["roughness_relief"] = 0.25; + help_map["roughness_relief"] = { "float","0.25","Amplitude of diamond square roughening elements. Uses a unifrom distribution", "Used to deal with filled depressions after inital diamond square step."}; + bool_default_map["diffuse_initial_surface"] = false; + help_map["diffuse_initial_surface"] = { "bool","false","Smooths initial surface", "Not sure why I had this--SMM"}; + int_default_map["diffuse_steps"] = 5; + help_map["diffuse_steps"] = { "int","5","Number of smoothing cycles", "More means smoother"}; + bool_default_map["print_initial_surface"] = true; + help_map["print_initial_surface"] = { "bool","true","Does what is says on the tin", "Self explanitory"}; // Parameters for spinning up the simulation bool_default_map["spinup"] = false; + help_map["spinup"] = { "bool","false","For spinning up a rectangular model.","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + float_default_map["spinup_K"] = 0.001; + help_map["spinup_K"] = { "float","0.001","For spinning up a rectangular model, the K value.","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + float_default_map["spinup_U"] = 0.001; + help_map["spinup_U"] = { "float","0.001","For spinning up a rectangular model, the U value.","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + float_default_map["spinup_dt"] = 250; + help_map["spinup_dt"] = { "float","250","For spinning up a rectangular model, the dt value.","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + float_default_map["spinup_time"] = 20000; + help_map["spinup_time"] = { "float","20000","For spinning up the time to spend spinning up the model.","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + bool_default_map["staged_spinup"] = true; + help_map["spinup"] = { "bool","true","For spinning up a rectangular model. I forgot what this does but I don't use it anymore-SMM","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + // This spinup routine tries to combine snapping and hillslope diffusion bool_default_map["cyclic_spinup"] = false; + help_map["cyclic_spinup"] = { "bool","false","For spinning up a rectangular model. Turns hillslopes on and off sequentially","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + int_default_map["spinup_cycles"] = 5; + help_map["spinup_cycles"] = { "int","5","For spinning up a rectangular model. Turns hillslopes on and off sequentially. This is the number of cycles","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + bool_default_map["cycle_K"] = true; + help_map["cycle_K"] = { "bool","true","For spinning up a rectangular model. This varies K in the cycles","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + bool_default_map["cycle_U"] = false; + help_map["cycle_U"] = { "bool","false","For spinning up a rectangular model. This varies U in the cycles","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + float_default_map["cycle_K_factor"] = 2; + help_map["cycle_K_factor"] = { "float","2","For spinning up a rectangular model. This factor by which K varies in the cycles","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + float_default_map["cycle_U_factor"] = 2; + help_map["cycle_U_factor"] = { "float","2","For spinning up a rectangular model. This factor by which U varies in the cycles","More useful to use diamond_square_spinup and snapping so not advised to use this"}; + + // The diamond square spinup routine // This generates the nicest initial surfaces. bool_default_map["diamond_square_spinup"] = false; + help_map["diamond_square_spinup"] = { "bool","false","For spinup of a rectangular model run. Uses the diamond square algorithm as an initial surface.","This is done so the channel network is dendritic"}; // control of snapping to steady state bool_default_map["snap_to_steady"] = false; + help_map["snap_to_steady"] = { "bool","false","This calculates the relief in the fluvial nework by solving stream power for a given uplift and K.","Used to start model runs with a steady landscape"}; + float_default_map["snapped_to_steady_uplift"] = 0.0001; + help_map["snapped_to_steady_uplift"] = { "float","0.0001","The uplift rate used for the snap_to_steady routine.","In m/yr"}; + float_default_map["snapped_to_steady_relief"] = 400; + help_map["snapped_to_steady_relief"] = { "float","400","For the snap_to_steady option this back calculates K to get this relief in the landscape.","In m"}; + bool_default_map["print_snapped_to_steady_frame"] = false; + help_map["print_snapped_to_steady_frame"] = { "bool","false","Prints the snapped DEM.","works with snap_to_steady"}; // More complex snapping bool_default_map["snap_to_steady_variable_variable_K_variable_U"] = false; - string_default_map["variable_K_name"] = "variable_K"; + help_map["snap_to_steady_variable_variable_K_variable_U"] = { "bool","false","A snapping routine with spatially variable U and K.","Assign K and U with rasters using variable_K_name and variable_U_name"}; + + string_default_map["variable_K_name"] = "variable_K"; + help_map["variable_K_name"] = { "string","variable_K","For snap_to_steady_variable_variable_K_variable_U this is the name of the K raster.","There are some flags to do this with a constant K as well. Use make_constant_K_raster"}; + string_default_map["variable_U_name"] = "variable_U"; - string_default_map["fixed_channel_csv_name"] = "single_channel_nodes"; // This contains the fixed channel data + help_map["variable_U_name"] = { "string","variable_U","For snap_to_steady_variable_variable_K_variable_U this is the name of the U raster.","There are some flags to do this with a constant U as well. Use make_constant_U_raster"}; + + // Even more complex snapping + bool_default_map["snap_to_steady_variable_variable_K_variable_U_use_LithoCube"] = false; + help_map["snap_to_steady_variable_variable_K_variable_U_use_LithoCube"] = { "bool","false","A snapping routine with spatially variable U and K and uses a lithocube.","U raster is assigned and K raster is determined with lithocube parameters"}; + + string_default_map["fixed_channel_csv_name"] = "NULL"; // This contains the fixed channel data + help_map["fixed_channel_csv_name"] = { "string","NULL","This is the name of a csv file with a fixed channel.","Channel needs column headers latitude longitude elevation and has csv in the name."}; bool_default_map["only_test_single_channel"] = false; + help_map["only_test_single_channel"] = { "bool","false","This checks the format of the fixed channel and exits.","For bug checking."}; + + bool_default_map["only_test_impose_single_channel"] = false; + help_map["only_test_impose_single_channel"] = { "bool","false","Imposes single channel prints DEM and exits.","For bug checking."}; + + bool_default_map["buffer_single_channel"] = true; + help_map["buffer_single_channel"] = { "bool","true","Adds data to nodata pixels next to the single channel so that after flow routing the channel does not drain off the edge of the DEM.","Useful for when the single channel runs along the edge of the DEM."}; + bool_default_map["fixed_channel_dig_and_slope"] = false; + help_map["fixed_channel_dig_and_slope"] = { "bool","false","For the single channel this enforces the slope and digs the channel in a bit to ensure flow is routed through it","The digging is used to enforce flow routing"}; + float_default_map["single_channel_drop"] = 0; - float_default_map["single_channel_dig"] = 0.01; - string_default_map["single_channel_fd_string"] = "flow distance(m)"; + help_map["single_channel_drop"] = { "float","0","For the single channel this drops the elevation","Use if you want a big drop in base level"}; + + string_default_map["single_channel_fd_string"] = "flowdistance(m)"; + help_map["single_channel_fd_string"] = { "string","flow distance(m)","The name of the flow distance column in the single channel csv.","Case sensitive"}; + string_default_map["single_channel_elev_string"] = "elevation(m)"; + help_map["single_channel_elev_string"] = { "string","elevation(m)","The name of the elevation column in the single channel csv.","Case sensitive"}; + string_default_map["test_single_channel_name"] = "test_single_channel"; + help_map["test_single_channel_name"] = { "string","test_single_channel","This is the filename of the single channel that prints after the slope and dig are imposed","File is used for bug checking."}; + + bool_default_map["lower_raster_before_channel_buffer"] = false; + help_map["lower_raster_before_channel_buffer"] = { "bool","false","Lowers the raster before the single channel buffering happens","Used in case raster is dropped."}; + + float_default_map["lower_raster_before_buffer_amount"] = 0; + help_map["lower_raster_before_buffer_amount"] = { "float","0","Lowers the raster before the single channel by this amount","Used in case raster is dropped."}; + + bool_default_map["print_updated_fixed_channel"] = false; + help_map["print_updated_fixed_channel"] = { "bool","false","Prints the single channel after imposition of the fixed slope and diggin.","For bug checking."}; - // Even more complex snapping - bool_default_map["snap_to_steady_variable_variable_K_variable_U_use_LithoCube"] = false; // Yet more complex snapping that attempts to migrate divides by cycling snapping bool_default_map["cycle_fluvial_snapping"] = false; + help_map["cycle_fluvial_snapping"] = { "bool","false","This cycles the snapping so the divides can migrate in the event of spatially heterogeneuous lithology.","Allow divides to move."}; + int_default_map["snapping_cycles"] = 30; + help_map["cycle_fluvial_snapping"] = { "int","30","Number of fluvial snapping cycles.","Turn on with cycle_fluvial_snapping."}; + int_default_map["snap_print_rate"] = 10; // how frequently the snapping is printed in terms of snap cycles + help_map["snap_print_rate"] = { "int","10","This prints the DEM evey n cycles set by this parameters.","Turn on with cycle_fluvial_snapping."}; + // Yet more complex snapping that attempts to migrate divides by cycling snapping of the critical slope component bool_default_map["cycle_hillslope_snapping"] = false; + help_map["cycle_hillslope_snapping"] = { "bool","false","This includes the hillslopes in the cyclic snapping.","Turn on with cycle_fluvial_snapping."}; + int_default_map["snapping_hillslope_cycles"] = 1; + help_map["snapping_hillslope_cycles"] = { "int","1","Not used in the lithosnap but in only hillslope snapping this is the number of snaps.","More for bug checking has no affect on lithosnap."}; + int_default_map["snap_hillslope_print_rate"] = 10; // how frequently the snapping is printed in terms of snap cycles + help_map["snap_hillslope_print_rate"] = { "int","10","Not used in the lithosnap but in only hillslope snapping this is printing for hillslope snaps.","More for bug checking has no affect on lithosnap."}; + // Snapping if you use the initial raster as a lid so that // the snapped landscape cannot go above the current surface bool_default_map["use_initial_topography_as_max_elevation"] = false; + help_map["use_initial_topography_as_max_elevation"] = { "bool","false","For snapping this caps the elevation at the elevation of the input DEM.","Used to make sure you don't snap above present elevations."}; // This is for printing of the channel network bool_default_map["print_initial_channel_network"] = false; + help_map["use_initial_topography_as_max_elevation"] = { "bool","false","Prints the initial channel network after the base level has been imposed.","Used to bug check the base level snapping."}; + bool_default_map["print_final_channel_network"] = false; + help_map["print_final_channel_network"] = { "bool","false","Prints the final channel network after the base level has been imposed.","Used to bug check the base level snapping."}; + + + bool_default_map["impose_single_channel_printing"] = false; + help_map["impose_single_channel_printing"] = { "bool","false","Prints the imposed channel to csv. This is after it has been snapped to pixels and slope has been enforced.","Used to bug check the base level snapping."}; - bool_default_map["impose_single_channel_printing"] = true; bool_default_map["nodata_single_channel_printing"] = false; + help_map["nodata_single_channel_printing"] = { "bool","false","Prints the imposed channel to csv. Includes nodata to identify problem pixels.","Used to bug check the base level snapping."}; bool_default_map["convert_csv_to_geojson"] = false; + help_map["convert_csv_to_geojson"] = { "bool","false","Converts csv files to geojson files","Makes csv output easier to read with a GIS. Warning: these files are much bigger than csv files."}; // This is for the critical slope float_default_map["rudimentary_critical_slope"] = 0.8; + help_map["rudimentary_critical_slope"] = { "float","0.8","If you don't want to use the lithocube for critical slopes this just sets S_c the same everywhere to this value.","Normally you would use the lithocube but this is for testing routines without needing the lithocube which takes a long time to load."}; + int_default_map["threshold_contributing_pixels"] = 1000; + help_map["threshold_contributing_pixels"] = { "int","1000","Threshold number of accumulated pixels before you get a hillslope.","Pixels not area"}; + bool_default_map["snap_to_critical_slopes"] = false; + help_map["snap_to_critical_slopes"] = { "bool","false","A bug testing routine that snaps to critical slopes but does not fluvial snapping.","For bug testing"}; + string_default_map["Sc_raster_fname"] = "S_c"; + help_map["Sc_raster_fname"] = { "string","S_c","If you load the critical slope raster from file this is its prefix (without bil extension).","You can combine this with creating an S_c raster"}; + bool_default_map["use_Sc_raster"] = false; + help_map["use_Sc_raster"] = { "bool","false","Loads an S_c raster for critical slopes rather than computing from the rudimentary value or from the lithocube","You can combine this with creating an S_c raster"}; + bool_default_map["snap_to_steady_critical_slopes_use_LithoCube"] = false; - bool_default_map["print_Sc_raster"] = false; + help_map["snap_to_steady_critical_slopes_use_LithoCube"] = { "bool","false","A bug testing routine that snaps to critical slopes but does not fluvial snapping. THis one tests the lithocube","For bug testing"}; + bool_default_map["print_Sc_raster"] = false; + help_map["use_Sc_raster"] = { "bool","false","Prints S_c raster used in a run","For checking your S_c values"}; + + string_default_map["temp_chan_name_for_crit_slopes"] = "temp_channels"; + help_map["temp_chan_name_for_crit_slopes"] = { "string","temp_channels","Due to some nuances of the code you must write the channel to file and then read the file for the hillslope snapping. If you are batch processing you will need to have different names for the channel files","Batch processing scripts should make sure this name is different for each model implementation."}; + // Smoothing parameters - float_default_map["smoothing_window_radius"] =50; + float_default_map["smoothing_window_radius"] = 50; + help_map["smoothing_window_radius"] = { "float","50","For snapping this smooths the raster at the end of the snapping cycles over a window of this radius.","In metres"}; + int_default_map["smoothing_sweeps"] = 1; + help_map["smoothing_sweeps"] = { "int","1","The number of smoothing iterations if you smooth after snapping.","More diffused landscape if this number is higher"}; // forcing of dissection bool_default_map["force_dissect"] = true; + help_map["force_dissect"] = { "bool","true","For spinup options and diamond square this forces channel dissection.","Used to ensure channel connectivity"}; + int_default_map["force_dissect_steps"] = 10000; + help_map["force_dissect_steps"] = { "int","10000","For spinup options and diamond square this forces the number channel dissection iterations. The higher the number the better your chance of getting a fully dissected landscape.","Used to ensure channel connectivity"}; // Some parameters for very rudimentary steady forcing bool_default_map["make_constant_K_raster"] = false; + help_map["make_constant_K_raster"] = { "bool","false","Makes a K raster that can be read by model runs.","The value of K is set by rudimentary_steady_forcing_K"}; + bool_default_map["make_constant_U_raster"] = false; + help_map["make_constant_U_raster"] = { "bool","false","Makes a U raster that can be read by model runs.","The value of K is set by rudimentary_steady_forcing_uplift"}; + + bool_default_map["make_constant_Sc_raster"] = false; + help_map["make_constant_Sc_raster"] = { "bool","false","Makes a Sc raster that can be read by model runs.","The value of Sc is set by rudimentary_critical_slope"}; + bool_default_map["rudimentary_steady_forcing"] = false; + help_map["rudimentary_steady_forcing"] = { "bool","false","This just runs a single forcing for a fixed time.","See other default parameters with rudimentary in the name."}; + float_default_map["rudimentary_steady_forcing_time"] = 100000; + help_map["rudimentary_steady_forcing_time"] = { "float","100000","The time of a rudimentary forced run.","See other default parameters with rudimentary in the name."}; + float_default_map["rudimentary_steady_forcing_uplift"] = 0.0005; - float_default_map["rudimentary_steady_forcing_K"] = 0.0005; + help_map["rudimentary_steady_forcing_uplift"] = { "float","0.0005","Uplift rate in the rudimentary forced runs and also used as a fixed uplift field.","Units are m/yr."}; + + float_default_map["rudimentary_steady_forcing_K"] = 0.000005; + help_map["rudimentary_steady_forcing_K"] = { "float","0.000005","The K value used in the constant K raster.","Units depend on m and n."}; // Parameters for hillslopes bool_default_map["hillslopes_on"] = false; + help_map["hillslopes_on"] = { "bool","false","Turn the hillslope module on.","This is computationally intensive and doesn't work on irregular boundaries."}; + bool_default_map["nonlinear"] = false; + help_map["nonlinear"] = { "bool","false","Use the nonlinear hillslope module.","This is the roering et al model."}; + bool_default_map["use_hillslope_hybrid"] = false; + help_map["use_hillslope_hybrid"] = { "bool","false","If true the model will use a hybrid hillslope model.","This is linear diffusion combined with a critical slope."}; + + + + // BELOW ARE SETTINGS FOR DIFFERENT RECTANGULAR MODEL SCENARIOS // Some parameters for cyclic forcing // these also inherit parameters from the cyclic spinup bool_default_map["run_cyclic_forcing"] = false; + help_map["run_cyclic_forcing"] = { "bool","false","For rectangular model runs. Cycles the forcing (either U or K) through time.","Forcings follow a sine wave. Examples in Mudd 2017 ESPL."}; + float_default_map["cyclic_forcing_time"] = 10000; + help_map["cyclic_forcing_time"] = { "float","1000","The temporal wavelength of cyclic forcing.","Forcings follow a sine wave. Examples in Mudd 2017 ESPL."}; + float_default_map["baseline_K_for_cyclic"] = 0.0001; + help_map["baseline_K_for_cyclic"] = { "float","0.0001","The lowest K value in the cyclic K forcing. Maximum determined by cycle_K_factor.","Forcings follow a sine wave. Examples in Mudd 2017 ESPL."}; + float_default_map["baseline_U_for_cyclic"] = 0.0005; + help_map["baseline_U_for_cyclic"] = { "float","0.0005","The lowest U value in the cyclic U forcing. Maximum determined by cycle_UY_factor.","Forcings follow a sine wave. Examples in Mudd 2017 ESPL."}; + int_default_map["cyclic_cycles"] = 3; + help_map["cyclic_cycles"] = { "int","3","Number of cycles in the cyclic forcing.","Forcings follow a sine wave. Examples in Mudd 2017 ESPL."}; + float_default_map["cyclic_dt"] = 250; + help_map["cyclic_dt"] = { "float","250","Timestep during cyclic forcing.","Forcings follow a sine wave. Examples in Mudd 2017 ESPL."}; // some parameters for setting K to a fixed uplift and relief bool_default_map["set_fixed_relief"] = false; + help_map["set_fixed_relief"] = { "bool","false","Back calculates K to force a value of relief in the initial model.","Used to make sure different model domains have similar relief."}; + + float_default_map["fixed_relief"] = 500; + help_map["fixed_relief"] = { "float","500","Set the relief of the steady state run.","Once relief if set this back calculates K."}; + // some parameters for having a random uplift through time bool_default_map["run_random_forcing"] = false; + help_map["run_random_forcing"] = { "bool","false","Runs a transient scenario with random periods of uplift","For running models with random uplift histories"}; + float_default_map["maximum_time_for_random_cycle"] = 20000; + help_map["maximum_time_for_random_cycle"] = { "float","20000","For random uplift this is the maximum time of a step","For running models with random uplift histories"}; + float_default_map["minimum_time_for_random_cycle"] = 5000; + help_map["minimum_time_for_random_cycle"] = { "float","5000","For random uplift this is the minimum time of a step","For running models with random uplift histories"}; + float_default_map["maximum_U_for_random_cycle"] = 0.001; + help_map["maxcimum_U_for_random_cycle"] = { "float","0.001","For transient run with random uplift this is the maximum uplift.","For running models with random uplift histories."}; + float_default_map["minimum_U_for_random_cycle"] = 0.0001; + help_map["minimum_U_for_random_cycle"] = { "float","0.0001","Set the relief of the steady state run.","For running models with random uplift histories"}; + float_default_map["random_dt"] = 10; + help_map["random_dt"] = { "float","10","Timestep during random uplift forcing.","Used to perturb the landscape."}; + int_default_map["random_cycles"] = 4; + help_map["random_cycles"] = { "int","3","Number of different uplift rates in the random uplift forcing.","Used to perturb the landscape."}; // some parameters for a spatially varying K and U bool_default_map["make_spatially_varying_K"] = false; // This just prints a raster + help_map["make_spatially_varying_K"] = { "bool","false","This uses rastermaker to create a K raster and prints it","For running models with spatially variable K without a lithocube"}; + float_default_map["spatially_varying_max_K"] = 0.0001; + help_map["spatially_varying_max_K"] = { "float","0.0001","For making a random K field this is the maximum K value.","For running models with spatially variable K without a lithocube."}; + float_default_map["spatially_varying_min_K"] = 0.000001; + help_map["spatially_varying_min_K"] = { "float","0.000001","For making a random K field this is the minimum K value.","For running models with spatially variable K without a lithocube."}; + int_default_map["min_blob_size"] = 50; + help_map["spatially_varying_min_K"] = { "int","50","For making a random K field this is the minimum width of a random K blob in pixels (they are square).","For running models with spatially variable K without a lithocube."}; + int_default_map["max_blob_size"] = 100; + help_map["max_blob_size"] = { "int","100","For making a random K field this is the mmaximum width of a random K blob in pixels (they are square).","For running models with spatially variable K without a lithocube."}; + int_default_map["n_blobs"] = 10; + help_map["n_blobs"] = { "int","10","For making a random K field this is the number of blobs in the raster.","For running models with spatially variable K without a lithocube."}; bool_default_map["spatially_varying_forcing"] = false; + help_map["spatially_varying_forcing"] = { "bool","false","Turn on one of spatially varying U or K","For running models with spatially variable U or K without a lithocube"}; + bool_default_map["spatially_varying_K"] = false; + help_map["spatially_varying_K"] = { "bool","false","The tells the model to read a K raster","For running models with spatially variable K without a lithocube"}; + bool_default_map["spatially_varying_U"] = false; + help_map["spatially_varying_U"] = { "bool","false","The tells the model to read a U raster","For running models with spatially variable U"}; + bool_default_map["calculate_K_from_relief"] = false; + help_map["calculate_K_from_relief"] = { "bool","false","This takes a DEM and an uplift rate and back calculates the K value for each pixel","For constraining the K values in a real DEM"}; + int_default_map["spatial_K_method"] = 0; + help_map["spatial_K_method"] = { "int","0","Method for making spatially variable K options are 0 for blobs 1 dinsnae work and 2 has half the raster at higher K","For variable K values"}; + int_default_map["spatial_U_method"] = 1; // 0 doesn't work + help_map["spatial_U_method"] = { "int","1","Method for making spatially variable U options are 0 dinsnae work 1 sine wave and 2 has half the raster at higher U","For variable U values"}; + bool_default_map["load_K_raster"] = false; + help_map["load_K_raster"] = { "bool","false","Loads the K raster from file","For imposing spatially variable K"}; + bool_default_map["load_U_raster"] = false; + help_map["load_U_raster"] = { "bool","false","Loads the U raster from file","For imposing spatially variable U"}; float_default_map["spatial_K_factor"] = 3; + help_map["spatial_K_factor"] = { "float","3","For K variation this the the factor over which K varies","For variable K values"}; + float_default_map["spatial_variation_time"] = 20000; + help_map["spatial_variation_time"] = { "float","20000","I don't understand what this is for and I wrote it--SMM","For variable K values"}; + bool_default_map["make_spatially_varying_U"] = false; // This just prints a raster + help_map["make_spatially_varying_U"] = { "bool","false","This uses rastermaker to create a U raster and prints it","For running models with spatially variable U"}; + float_default_map["min_U_for_spatial_var"] = 0.0001; + help_map["min_U_for_spatial_var"] = { "float","0.0001","For spatially varied U this is the minimum value.","For running models with spatially variable U."}; + float_default_map["max_U_for_spatial_var"] = 0.0005; + help_map["max_U_for_spatial_var"] = { "float","0.0005","For spatially varied U this is the maximum value.","For running models with spatially variable U."}; + int_default_map["K_smoothing_steps"] = 2; + help_map["K_smoothing_steps"] = { "int","2","For spatially varied K this diffuses K around the edges of the blobs.","For running models with spatially variable K. ensures you don't get discontinuities at the boundaries."}; + float_default_map["spatial_dt"] = 100; + help_map["spatial_dt"] = { "float","100","Timestep when running spatially varied K or U.","For running models with spatially variable K. ensures you don't get discontinuities at the boundaries."}; + int_default_map["spatial_cycles"] = 5; + help_map["spatial_cycles"] = { "int","5","Number of cycles for varied K or U.","For running models with spatially variable K. Why did I do this and not just set the end time?--SMM."}; + bool_default_map["use_adaptive_timestep"] = true; + help_map["use_adaptive_timestep"] = { "bool","true","This enables the variable timestep.","With this enabled you can speed up the model or avoid numerical instability but in some cases it really slows it down."}; + float_default_map["maximum_timestep"] = 500; - float_default_map["float_print_interval"] = 2000; + help_map["maximum_timestep"] = { "float","500","For adaptive timestepping this caps the timestep.","Only used with use_adaptive_timestep."}; + bool_default_map["snap_to_steep_for_spatial_uplift"] = false; + help_map["snap_to_steep_for_spatial_uplift"] = { "bool","false","If you are using variable upflift this will snap either to minimum or maximum uplift.","Whether min or max depends on nap_to_minimum_uplift."}; + bool_default_map["snap_to_minimum_uplift"] = true; + help_map["snap_to_minimum_uplift"] = { "bool","true","If you are using variable upflift this will snap to minimum uplift but if false will snap to maximum uplift.","snap_to_steep_for_spatial_uplift needs to be true."}; + int_default_map["increase_amt"] = 10; + help_map["increase_amt"] = { "int","10","For variable K or U that have half the forcing field at a different value this is the multipler for the high value.","For say 10 the north will have 10x the uplift of the south."}; + bool_default_map["finish_with_steady_forcing"] = false; + help_map["finish_with_steady_forcing"] = { "bool","false","If you are using variable upflift this will finish the run with a period of steady uplift.","To smooth out the raster at the end. Although I never really used this--SMM"}; + + + // Some faults or base level falls bool_default_map["simple_fault_uplift"] = false; + help_map["simple_fault_uplift"] = { "bool","false","This just raises the north half of the raster by a fixed amount","Some basic transient forcing for toy models."}; + int_default_map["uplift_amt"] = 10; + help_map["simple_fault_uplift"] = { "int","10","For simple fault uplift this is the instantaneous uplift of the fault","Some basic transient forcing for toy models."}; + int_default_map["uplift_time"] = 50000; + help_map["uplift_time"] = { "int","50000","For simple fault uplift this is the time when the fault goes","Some basic transient forcing for toy models."}; + float_default_map["uplift_rate"] = 0.0002; + help_map["uplift_rate"] = { "float","0.0002","Uplift rate for various transient scenarios and snapping","Some basic transient forcing for toy models."}; + bool_default_map["base_level_fall"] = false; + help_map["base_level_fall"] = { "bool","false","This just raises drops one side of the raster by a fixed amoount","Some basic transient forcing for toy models."}; + int_default_map["transient_print_interval"] = 2; + help_map["transient_print_interval"] = { "int","2","Print interval for transient runs","This is controled by dt."}; + + + // An imposed transient scenario + bool_default_map["run_transient_base_level"] = false; + help_map["run_transient_base_level"] = { "bool","true","Tests the transient component","Still under construction."}; + + // These are paramaters for the transient base level file + string_default_map["transient_channel_csv_name"] = "NULL"; // This contains the fixed channel data + help_map["transient_channel_csv_name"] = { "string","NULL","This is the name of a csv file with a fixed channel.","Channel needs column headers latitude longitude elevation and has csv in the name. Also has timesteps in columns"}; + + bool_default_map["test_transient_channel"] = false; + help_map["test_transient_channel"] = { "bool","false","This checks the format of the transient channel and exits.","For bug checking."}; + + bool_default_map["use_transient_outlet"] = false; + help_map["use_transient_outlet"] = { "bool","false","If true you read the transient_outlet_fname and and drive the transient model with baselevel fall.","This runs the transient model with outlet elevations only."}; + + string_default_map["transient_outlet_fname"] = "outlet_elevations.csv"; + help_map["transient_outlet_fname"] = { "string","outlet_elevations.csv","This contains the filename for the outlet elevations through time if you are running the model with the outlet only.","Only used if use_transient_outlet is true."}; + + string_default_map["baselevel_switch_column"] = "MEAS_Aare"; + help_map["baselevel_switch_column"] = { "string","MEAS_Aare","This is the name of a column used to popuate the baselevel switch. If this colum has a zero the baselevel switch is 0. If it is not zero the switch is 1.","If the switch is 1 it means that the pixel will have a forced drainage area but elevation will evolve transiently. A 0 means it is a baselevel node with a fixed elevation."}; + + string_default_map["transient_channel_timing_prefix"] = "t"; // This contains the fixed channel data + help_map["transient_channel_timing_prefix"] = { "string","t","The prefix of the timing column headers.","If timing columns are t5 t10 etc then the prefix would be t"}; + + float_default_map["transient_channel_timing_multiplier"] = 1000; // This contains the fixed channel data + help_map["transient_channel_timing_multiplier"] = { "float","1000","How many years each column integer represents.","I t5 is 5000 years then the multiplyer is 1000"}; + + int_default_map["transient_channel_n_time_columns"] = 10; // This contains the fixed channel data + help_map["transient_channel_n_time_columns"] = { "int","10","Number of transient time steps you will attempt to read.","This looks for column names so should be resilient to crashing."}; + + int_default_map["transient_channel_phase_steps"] = 5; // This contains the fixed channel data + help_map["transient_channel_phase_steps"] = { "int","5","How many timing multipliers each phase step you will attempt.","If timing columns are t5 t10 etc then the steps will be 5."}; + + float_default_map["transient_maximum_time"] = 10500; + help_map["transient_maximum_time"] = { "float","10500","Sets the maximum time for the transient run.","To get the full baselevel record set this longer than the baselelvel steps"}; + + bool_default_map["use_lithocube_for_transient"] = false; + help_map["use_lithocube_for_transient"] = { "bool","false","Uses the lithocube in the transient runs if true.","You need to vo file for this to work."}; // tilt scenarios bool_default_map["run_instantaneous_tilt"] = false; + help_map["run_instantaneous_tilt"] = { "bool","false","A tilting scenario where you tilt the whole raster instantaneously.","Set tilt with tilt_angle and tilt_boundary."}; + + bool_default_map["run_progressive_tilt"] = false; + help_map["run_progressive_tilt"] = { "bool","false","A tilting scenario where you tilt the raster progressively.","Set tilt with tilt_angle and tilt_boundary."}; + int_default_map["tilt_time"] = 100000; + help_map["tilt_time"] = { "int","100000","For run_progressive_tilt scenario this is the time over which the tile will occur.","Set tilt with tilt_angle and tilt_boundary."}; + float_default_map["tilt_angle"] = 10; + help_map["tilt_angle"] = { "float","10","For tilt scenarios this is the final angle of the tilt.","Enable with one of run_progressive_tilt or run_instantaneous_tilt."}; + string_default_map["tilt_boundary"] = "S"; + help_map["tilt_boundary"] = { "string","S","For tilt scenarios this is the side of the raster that will serve as the hinge.","Enable with one of run_progressive_tilt or run_instantaneous_tilt."}; + int_default_map["tilt_print_interval"] = 150; + help_map["tilt_print_interval"] = { "int","150","For tilt scenarios this is the printing interval.","Enable with one of run_progressive_tilt or run_instantaneous_tilt."}; + // Various options to load and process lithologic data bool_default_map["only_test_lithology"] = false; + help_map["only_test_lithology"] = { "bool","false","Loads the lithology data and stops there if this is true.","Used to check lithocube files."}; + string_default_map["vo_filename"] = "test.vo"; + help_map["vo_filename"] = { "string","test.vo","This is the file that contains metadata for the lithocube.","Lithocubes are made of a small .vo file and a massive .asc file"}; + string_default_map["vo_asc_filename"] = "test.asc"; + help_map["vo_asc_filename"] = { "string","test.asc","This is the name of the lithocube file that has the actual three dimensional lithology types in it.","You need to also give it the .vo file that contains the metadata about this file."}; + bool_default_map["print_K_raster"] = false; + help_map["print_K_raster"] = { "bool","false","Prints the K values in the lithocube testing.","Generates a raster with the lithocube derived K values."}; + bool_default_map["print_lithocode_raster"] = false; + help_map["print_lithocode_raster"] = { "bool","false","Prints the lithocodes in the lithocube testing.","Generates a raster with the lithocube derived lithocodes."}; + + bool_default_map["print_exhumation_and_cumulative_uplift"] = false; + help_map["print_exhumation_and_cumulative_uplift"] = { "bool","false","Prints the cumulative uplift and exhumation rasters for use with transient runs and the lithocube.","Generates rasters for checking exhumation and cumulative uplift."}; + // options for loading lookup tables bool_default_map["load_lithocode_to_K_csv"] = false; + help_map["load_lithocode_to_K_csv"] = { "bool","false","Reads the lookup table converting lithocode to K if true.","Filename designated by lookup_filename_K."}; + bool_default_map["load_lithocode_to_Sc_csv"] = false; + help_map["load_lithocode_to_Sc_csv"] = { "bool","false","Reads the lookup table converting lithocode to Sc if true.","Filename designated by lookup_filename_Sc."}; + string_default_map["lookup_filename_K"] = "lookup_table_strati_K.csv"; + help_map["lookup_filename_K"] = { "string","lookup_table_strati_K.csv","The name of the lithocode to K csv file.","Column names are Strati and K."}; + string_default_map["lookup_filename_Sc"] = "lookup_table_strati_Sc.csv"; + help_map["lookup_filename_Sc"] = { "string","lookup_table_strati_Sc.csv","The name of the lithocode to Sc csv file.","Column names are Strati and Sc."}; + // option to have forbidden lithocodes string_default_map["forbidden_lithocodes"] = "NULL"; - + help_map["forbidden_lithocodes"] = { "string","NULL","If a lithocube value takes one of these then it is set to a default value.","Used to override lithologic unit K and Sc values with a default."}; + //Option to adjust elevation of raster before lithocube snapping - int_default_map["elevation_change"] = 0; - - + float_default_map["elevation_change"] = 0; + help_map["elevation_change"] = { "float","0","Adjusts the elevation of the base raster by this amount.","For when you need to lift or drop the base raster."}; + + //========================================================================= + // + //.#####....####...#####....####...##...##..######..######..######..#####.. + //.##..##..##..##..##..##..##..##..###.###..##........##....##......##..##. + //.#####...######..#####...######..##.#.##..####......##....####....#####.. + //.##......##..##..##..##..##..##..##...##..##........##....##......##..##. + //.##......##..##..##..##..##..##..##...##..######....##....######..##..##. + // + //..####...##..##..######...####...##..##...####.. + //.##..##..##..##..##......##..##..##.##...##..... + //.##......######..####....##......####.....####.. + //.##..##..##..##..##......##..##..##.##.......##. + //..####...##..##..######...####...##..##...####.. + //============================================================================ // Use the parameter parser to get the maps of the parameters required for the analysis LSDPP.parse_all_parameters(float_default_map, int_default_map, bool_default_map,string_default_map); @@ -350,6 +796,18 @@ int main (int nNumberofArgs,char *argv[]) map this_bool_map = LSDPP.get_bool_parameters(); map this_string_map = LSDPP.get_string_parameters(); + if(f_name == "cry_for_help.txt") + { + cout << "I am going to print the help and exit." << endl; + cout << "You can find the help in the file:" << endl; + cout << "./MuddPILEDriver-README.csv" << endl; + string help_prefix = "MuddPILEDriver-README"; + LSDPP.print_help(help_map, help_prefix, version_number, citation); + exit(0); + } + + + list forbidden_lithocodes; if (this_string_map["forbidden_lithocodes"] != "NULL") { @@ -367,7 +825,7 @@ int main (int nNumberofArgs,char *argv[]) // Now print the parameters for bug checking - LSDPP.print_parameters(); + //LSDPP.print_parameters(); // location of the files string DATA_DIR = LSDPP.get_read_path(); @@ -397,17 +855,25 @@ int main (int nNumberofArgs,char *argv[]) mod.set_K( this_float_map["background_K"]); // print parameters to screen - mod.print_parameters(); + //mod.print_parameters(); // need this to keep track of the end time float current_end_time = 0; + //============================================================================ + //.##......######..######..##..##...####...........##.......####....####...#####.. + //.##........##......##....##..##..##..##..........##......##..##..##..##..##..##. + //.##........##......##....######..##..##..........##......##..##..######..##..##. + //.##........##......##....##..##..##..##..........##......##..##..##..##..##..##. + //.######..######....##....##..##...####...........######...####...##..##..#####.. //============================================================================ // Logic for loading lithology //============================================================================ if(this_bool_map["only_test_lithology"]) { - cout << "Let's load the lithology!"; + cout << endl << endl << endl << "============================================" << endl; + cout << "Let's load the lithology! I am doing this as a test so this routine" << endl; + cout << "Will exit once the lithology is loaded." << endl; string this_vo_filename = DATA_DIR+this_string_map["vo_filename"]; cout << "I am going to load lithology information" << endl; cout << "The filename is: " << this_vo_filename << endl; @@ -500,6 +966,18 @@ int main (int nNumberofArgs,char *argv[]) } + //============================================================================ + //.######..##..##..######..######..######...####...##..... + //...##....###.##....##......##......##....##..##..##..... + //...##....##.###....##......##......##....######..##..... + //...##....##..##....##......##......##....##..##..##..... + //.######..##..##..######....##....######..##..##..######. + //........................................................ + //.#####....####....####...######..######..#####.. + //.##..##..##..##..##........##....##......##..##. + //.#####...######...####.....##....####....#####.. + //.##..##..##..##......##....##....##......##..##. + //.##..##..##..##...####.....##....######..##..##. //============================================================================ // Logic for reading an initial surface // It will look for a raster but if it doesn't find one it will default back to @@ -507,9 +985,11 @@ int main (int nNumberofArgs,char *argv[]) // instructions about NRows, NCols, and DataResolution //============================================================================ LSDRaster InitialRaster; + LSDRaster filled_topography; bool create_initial_surface = false; if(this_bool_map["read_initial_raster"]) { + cout << endl << endl << endl << "====================================" << endl; cout << "I am going to try to read an intial raster for you." << endl; cout << "The read filename is: " << DATA_DIR+DEM_ID << endl; cout << "I am going to IGNORE initial surface instructions!" << endl; @@ -543,7 +1023,7 @@ int main (int nNumberofArgs,char *argv[]) map GRS = temp_raster.get_GeoReferencingStrings(); string CS = GRS["ENVI_coordinate_system"]; string MI = GRS["ENVI_map_info"]; - cout << "I am laoding a raster with Cooridinate string: " << endl; + cout << "I am loading a raster with Cooridinate string: " << endl; cout << CS << endl; cout << "and MI is: " << endl; cout << MI << endl; @@ -556,7 +1036,7 @@ int main (int nNumberofArgs,char *argv[]) //========================================================================== // Fill the raster //========================================================================== - LSDRaster filled_topography,carved_topography; + LSDRaster carved_topography; // filled topography is declared outside this logic if ( this_bool_map["raster_is_filled"] ) { cout << "You have chosen to use a filled raster." << endl; @@ -577,10 +1057,14 @@ int main (int nNumberofArgs,char *argv[]) } } - // This is a testing routine for the single channel - if(this_bool_map["only_test_single_channel"]) - { - cout << "Let me test the single channel modification routines." << endl; + + // some tests for the single channel + if( this_bool_map["impose_single_channel_printing"] || + this_bool_map["only_test_single_channel"] || + this_string_map["fixed_channel_csv_name"] != "NULL") + { + cout << "You seem to have some logic for a single channel in this model run." << endl; + cout << "I need to check if your flow distance and elevation columns are in the file " << endl; string fd_column_name = this_string_map["single_channel_fd_string"]; string elevation_column_name = this_string_map["single_channel_elev_string"]; @@ -588,134 +1072,136 @@ int main (int nNumberofArgs,char *argv[]) // Get the latitude and longitude cout << "I am reading points from the file: "+ this_string_map["fixed_channel_csv_name"] << endl; LSDSpatialCSVReader single_channel_data( RI, (DATA_DIR+this_string_map["fixed_channel_csv_name"]) ); - single_channel_data.print_data_map_keys_to_screen(); - - single_channel_data.data_column_add_float(elevation_column_name, -this_float_map["single_channel_drop"]); - single_channel_data.enforce_slope(fd_column_name, elevation_column_name, this_float_map["min_slope_for_fill"]); - single_channel_data.print_data_to_csv(this_string_map["test_single_channel_name"]); - - single_channel_data.print_row_and_col_to_csv("check_r_and_c.csv"); - - + single_channel_data.print_data_map_keys_to_screen(); + if (not single_channel_data.is_column_in_csv(elevation_column_name)) + { + string elevation_fragment = "elev"; + elevation_column_name = single_channel_data.find_string_in_column_name(elevation_fragment); + if (elevation_column_name == "NULL") + { + cout << "Fatal error. I cannot find the elevation column name. " << endl; + cout << "Check your single channel data file and the single_channel_elev_string. " << endl; + exit(0); + } - exit(0); + } + if (not single_channel_data.is_column_in_csv(fd_column_name)) + { + string elevation_fragment = "flow"; + elevation_column_name = single_channel_data.find_string_in_column_name(elevation_fragment); + if (elevation_column_name == "NULL") + { + cout << "Fatal error. I cannot find the flow distance column name. " << endl; + cout << "Check your single channel data file and the single_channel_fd_string. " << endl; + exit(0); + } + } + cout << "You flow distance column name is " << fd_column_name << " and elev is: " << elevation_column_name << endl; + cout << "If these are not in the column names this routine will fail!" << endl; + cout << "WARNING: whitespace is removed from column names!!" << endl; } - // Print the fill raster if you want it - if (this_bool_map["print_fill_raster"]) + // .#####...##..##..######..######..######..#####.. + // .##..##..##..##..##......##......##......##..##. + // .#####...##..##..####....####....####....#####.. + // .##..##..##..##..##......##......##......##..##. + // .#####....####...##......##......######..##..##. + // ................................................ + // ..####...##..##...####...##..##..##..##..######..##..... + // .##..##..##..##..##..##..###.##..###.##..##......##..... + // .##......######..######..##.###..##.###..####....##..... + // .##..##..##..##..##..##..##..##..##..##..##......##..... + // ..####...##..##..##..##..##..##..##..##..######..######. + // This buffers and implements the single channel + if (this_bool_map["buffer_single_channel"] && this_string_map["fixed_channel_csv_name"] != "NULL") { - cout << "Let me print the fill raster for you." << endl; - string filled_raster_name = OUT_DIR+OUT_ID+"_Fill"; - filled_topography.write_raster(filled_raster_name,raster_ext); - } - - // This prints the channel network. Uses the chi tool so that we have the source pixels - // As a column in the extraction - if (this_bool_map["print_initial_channel_network"]) - { - if (this_bool_map["write_hillshade"]) - { - cout << "Let me print the hillshade of the initial map for you. " << endl; - float hs_azimuth = 315; - float hs_altitude = 45; - float hs_z_factor = 1; - LSDRaster hs_raster = filled_topography.hillshade(hs_altitude,hs_azimuth,hs_z_factor); + cout << endl << endl << endl <<"=================================================" << endl; + cout << "Let me buffer the single channel." << endl; + cout << "I am doing this to stop the channel from segmenting after snapping." << endl; + cout << "Don't worry, it is only getting buffered by one pixel." << endl; + string fd_column_name = this_string_map["single_channel_fd_string"]; + string elevation_column_name = this_string_map["single_channel_elev_string"]; - string hs_fname = OUT_DIR+OUT_ID+"_hs"; - hs_raster.write_raster(hs_fname,raster_ext); - } - - cout << "\t Flow routing..." << endl; - // get a flow info object - LSDFlowInfo FlowInfo(boundary_conditions,filled_topography); + LSDRasterInfo RI(filled_topography); + // Get the latitude and longitude + cout << "I am reading points from the file: "+ this_string_map["fixed_channel_csv_name"] << endl; + LSDSpatialCSVReader single_channel_data( RI, (DATA_DIR+this_string_map["fixed_channel_csv_name"]) ); + single_channel_data.print_data_map_keys_to_screen(); - // calculate the flow accumulation - cout << "\t Calculating flow accumulation (in pixels)..." << endl; - LSDIndexRaster FlowAcc = FlowInfo.write_NContributingNodes_to_LSDIndexRaster(); + cout << "Subtracting " << this_float_map["single_channel_drop"] << " m elevation from the single channel" << endl; + single_channel_data.data_column_add_float(elevation_column_name, -this_float_map["single_channel_drop"]); + single_channel_data.enforce_slope(fd_column_name, elevation_column_name, this_float_map["min_slope_for_fill"]); + // single_channel_data.print_data_to_csv(this_string_map["test_single_channel_name"]); - cout << "\t Converting to flow area..." << endl; - LSDRaster DrainageArea = FlowInfo.write_DrainageArea_to_LSDRaster(); + LSDRasterMaker RM(filled_topography); - if (this_bool_map["print_DrainageArea_raster"]) + if(this_bool_map["lower_raster_before_channel_buffer"]) { - string DA_raster_name = OUT_DIR+OUT_ID+"_DArea"; - DrainageArea.write_raster(DA_raster_name,raster_ext); + // We subtract some elevation because the imposed channel has been dropped by 100m prior to saving as csv + RM.add_value(-this_float_map["lower_raster_before_buffer_amount"]); } - // calculate the distance from outlet - cout << "\t Calculating flow distance..." << endl; - LSDRaster DistanceFromOutlet = FlowInfo.distance_from_outlet(); + RM.impose_channels_with_buffer(single_channel_data, this_float_map["min_slope_for_fill"]*2, this_string_map["single_channel_elev_string"]); - cout << "\t Loading Sources..." << endl; - cout << "\t Source file is... " << CHeads_file << endl; + filled_topography = RM.return_as_raster(); - // load the sources - vector sources; - if (CHeads_file == "NULL" || CHeads_file == "Null" || CHeads_file == "null") - { - cout << endl << endl << endl << "==================================" << endl; - cout << "The channel head file is null. " << endl; - cout << "Getting sources from a threshold of "<< this_int_map["threshold_contributing_pixels"] << " pixels." < BaseLevelJunctions = JunctionNetwork.get_BaseLevelJunctions(); - cout << endl << endl << endl << "=================" << endl; - for (int i = 0; i< int(BaseLevelJunctions.size()); i++) - { - cout << "bl["< source_nodes; - vector outlet_nodes; - vector baselevel_node_of_each_basin; - int n_nodes_to_visit = 10; - JunctionNetwork.get_overlapping_channels_to_downstream_outlets(FlowInfo, BaseLevelJunctions, DistanceFromOutlet, - source_nodes,outlet_nodes,baselevel_node_of_each_basin,n_nodes_to_visit); + string initial_elev_column = "initial_elevation(m)"; + single_channel_data.burn_raster_data_to_csv(InitialRaster,initial_elev_column); - LSDChiTools ChiTool_chi_checker(FlowInfo); - ChiTool_chi_checker.chi_map_automator_chi_only(FlowInfo, source_nodes, outlet_nodes, baselevel_node_of_each_basin, - filled_topography, DistanceFromOutlet, - DrainageArea, chi_coordinate); + single_channel_data.print_data_to_csv(this_string_map["test_single_channel_name"]); - string chi_data_maps_string = OUT_DIR+OUT_ID+"_initial_chi_data_map.csv"; - ChiTool_chi_checker.print_chi_data_map_to_csv(FlowInfo, chi_data_maps_string); + single_channel_data.print_row_and_col_to_csv("check_r_and_c.csv"); - if ( this_bool_map["convert_csv_to_geojson"]) - { - string gjson_name = OUT_DIR+OUT_ID+"_initial_chi_data_map.geojson"; - LSDSpatialCSVReader thiscsv(chi_data_maps_string); - thiscsv.print_data_to_geojson(gjson_name); - } + exit(0); + } + // Print the fill raster if you want it + if (this_bool_map["print_fill_raster"]) + { + cout << "Let me print the fill raster for you." << endl; + string filled_raster_name = OUT_DIR+OUT_ID+"_Fill"; + filled_topography.write_raster(filled_raster_name,raster_ext); } + + // This gets a single channel from your loaded raster for use later if(this_bool_map["extract_single_channel"]) { - + cout << "Let me extract the single channel for you." << endl; LSDFlowInfo FlowInfo(boundary_conditions,filled_topography); LSDRasterInfo RI(filled_topography); @@ -763,18 +1249,68 @@ int main (int nNumberofArgs,char *argv[]) LSDRasterModel temp_mod(temp_raster); mod = temp_mod; // taper the edges of the input raster to 0 at N and S boundaries - mod.initialise_taper_edges_and_raise_raster(1); + //mod.initialise_taper_edges_and_raise_raster(1); create_initial_surface = false; this_bool_map["force_dissect"] = false; + if( this_bool_map["test_single_basin_outlet"]) + { + cout << "I am going to test routing to a single outlet." << endl; + LSDRasterInfo RI(filled_topography); + LSDRasterMaker Test_basin_1(temp_raster); + LSDRasterMaker Test_basin_2(temp_raster); + LSDSpatialCSVReader single_channel_data( RI, (DATA_DIR+this_string_map["fixed_channel_csv_name"]) ); + + cout << "Buffering the long way" << endl; + Test_basin_1.buffer_basin_to_single_outlet(single_channel_data, this_float_map["min_slope_for_fill"]); + + cout << "Buffering the short way" << endl; + Test_basin_2.buffer_basin_to_single_outlet(this_float_map["min_slope_for_fill"]); + + LSDRaster Test1 = Test_basin_1.return_as_raster(); + //bool belowthresholdisnodata = true; + //Test1 = Test1.mask_to_nodata_using_threshold(-100,belowthresholdisnodata); + LSDRaster Test2 = Test_basin_2.return_as_raster(); + //Test2 = Test2.mask_to_nodata_using_threshold(-100,belowthresholdisnodata); + + string T1_fname = OUT_DIR+OUT_ID+"_basintest1"; + Test1.write_raster(T1_fname,raster_ext); + + string T2_fname = OUT_DIR+OUT_ID+"_basintest2"; + Test2.write_raster(T2_fname,raster_ext); + + cout << "Done with the test! Exiting" << endl; + exit(0); + + } + + + // .#####...######...####...##...##...####...##..##..#####.. + // .##..##....##....##..##..###.###..##..##..###.##..##..##. + // .##..##....##....######..##.#.##..##..##..##.###..##..##. + // .##..##....##....##..##..##...##..##..##..##..##..##..##. + // .#####...######..##..##..##...##...####...##..##..#####.. + // ......................................................... + // ..####....####...##..##...####...#####...######......... + // .##......##..##..##..##..##..##..##..##..##............. + // ..####...##.###..##..##..######..#####...####........... + // .....##..##..##..##..##..##..##..##..##..##............. + // ..####....#####...####...##..##..##..##..######......... + // ........................................................ + // .#####...######..#####...##.......####....####...######. + // .##..##..##......##..##..##......##..##..##..##..##..... + // .#####...####....#####...##......######..##......####... + // .##..##..##......##......##......##..##..##..##..##..... + // .##..##..######..##......######..##..##...####...######. + // ........................................................ // now for logic if you want a diamond square replacement - if (this_bool_map["convert_intitial_raster_to_diamond_square_fractal"]) + if (this_bool_map["convert_initial_raster_to_diamond_square_fractal"]) { int this_frame; - + cout << endl << endl << endl << "=<>[]<>[]<>[]<>[]<>[]<>[]<>[]<>[]<>[]<>[]<>[]===" << endl; cout << "I going to replace your raster with a diamond square fractal surface." << endl; - cout <<"then dissects the landscape for you." << endl; + cout <<"then dissect the landscape for you." << endl; int smallest_dimension; if( mod.get_NRows() <= mod.get_NCols()) @@ -795,19 +1331,19 @@ int main (int nNumberofArgs,char *argv[]) { this_int_map["diamond_square_feature_order"] = largest_possible_scale; } - cout << " The largest dimnesion is: " << smallest_dimension << " pixels." << endl; + cout << " The largest dimension is: " << smallest_dimension << " pixels." << endl; cout << " So the biggest scale is: " << pow(2,largest_possible_scale) << " pixels or 2 to the " << largest_possible_scale << " power" << endl; cout << " I am setting the scale to: " << this_int_map["diamond_square_feature_order"] << endl; // Now actually build the fractal surface mod.intialise_diamond_square_fractal_surface(this_int_map["diamond_square_feature_order"], this_float_map["diamond_square_relief"]); - cout << " Let me taper the edges of this fractal surface for you so that everything drains to the edge." << endl; - cout << " I am tapering along the " << this_int_map["taper_rows"] << " row closes to the N and S boundaries" << endl; - mod.initialise_taper_edges_and_raise_raster(this_int_map["taper_rows"]); + //cout << " Let me taper the edges of this fractal surface for you so that everything drains to the edge." << endl; + //cout << " I am tapering along the " << this_int_map["taper_rows"] << " row closes to the N and S boundaries" << endl; + //mod.initialise_taper_edges_and_raise_raster(this_int_map["taper_rows"]); - cout << " I am superimposing a parabola with a relief of " << this_float_map["parabola_relief"] << " metres" << endl; - mod.superimpose_parabolic_surface(this_float_map["parabola_relief"]); + //cout << " I am superimposing a parabola with a relief of " << this_float_map["parabola_relief"] << " metres" << endl; + //mod.superimpose_parabolic_surface(this_float_map["parabola_relief"]); cout << " I am going to fill and then roughen the surface for you. Roughness elements will " << endl; cout << " have a maximum amplitude of " << this_float_map["roughness_relief"]<< " metres." << endl; @@ -819,10 +1355,54 @@ int main (int nNumberofArgs,char *argv[]) this_frame = 9999; mod.print_rasters_and_csv( this_frame ); + // Now impose nodata + cout << "Imposing nodata around the edges of your DS surface" << endl; + mod.mask_to_nodata_impose_nodata(temp_raster); + this_frame = 9998; + mod.print_rasters_and_csv( this_frame ); + + // Now tilt the edge up + cout << "Tilting up the edge of your DS surface, and getting it to drain to a single outlet." << endl; + LSDRasterInfo RI(filled_topography); + LSDRaster this_raster = mod.return_as_raster(); + LSDRasterMaker new_basin(this_raster); + LSDSpatialCSVReader single_channel_data( RI, (DATA_DIR+this_string_map["fixed_channel_csv_name"]) ); + + cout << "I am enforcing the slope on the single channel" << endl; + string fd_column_name = this_string_map["single_channel_fd_string"]; + string elevation_column_name = this_string_map["single_channel_elev_string"]; + single_channel_data.enforce_slope(fd_column_name, elevation_column_name, this_float_map["min_slope_for_fill"]); + + // get the maximum elevation in this collection of points + vector elevs = single_channel_data.data_column_to_float(this_string_map["single_channel_elev_string"]); + float max_elev = -99999; + for (int i = 0; i< int(elevs.size()); i++) + { + if (elevs[i] > max_elev) + { + max_elev = elevs[i]; + } + } + cout << "The maximum elevation in the single channel is: " << max_elev << endl; + + new_basin.buffer_basin_to_single_outlet(single_channel_data, this_float_map["min_slope_for_fill"]); + this_raster = new_basin.return_as_raster(); + mod.set_raster_data(this_raster); + + // uplift the raster to the maximum elevation + cout << "I'm adding a fixed elevation to your DEM" << endl; + mod.add_fixed_elevation(max_elev); + + this_frame = 9997; + mod.print_rasters_and_csv( this_frame ); + + // impose the channels then raise and fill + mod.impose_channels(single_channel_data, this_string_map["single_channel_elev_string"]); + mod.raise_and_fill_raster(); + + this_frame = 9996; + mod.print_rasters_and_csv( this_frame ); - cout << "Now I am going to run some fluvial incision at a small timestep" << endl; - cout <<"I am going to use a moderate K value and uplift, since I don't" << endl; - cout << "want to overexcavate the surface" << endl; mod.set_hillslope(false); //mod.set_timeStep( 1 ); int this_uplift_mode = 0; // block uplift @@ -830,11 +1410,40 @@ int main (int nNumberofArgs,char *argv[]) float this_U = 0.0005; mod.set_K(this_K); + // we run a snap just to see what happens + cout << "Finished with the initial DS surface, snapping to 500m relief. " << endl; + float desired_relief = 500; + mod.fluvial_snap_to_steady_state_tune_K_for_relief(this_U, desired_relief); + this_frame = 9995; + mod.print_rasters_and_csv( this_frame ); + + // now do some repeat snapping + int n_snaps = 20; + long seed = time(NULL); + mod.set_noise(2); + for (int snap = 0; snap sources; + if (CHeads_file == "NULL" || CHeads_file == "Null" || CHeads_file == "null") + { + cout << endl << endl << endl << "==================================" << endl; + cout << "The channel head file is null. " << endl; + cout << "Getting sources from a threshold of "<< this_int_map["threshold_contributing_pixels"] << " pixels." < BaseLevelJunctions = JunctionNetwork.get_BaseLevelJunctions(); + + cout << endl << endl << endl << "=================" << endl; + for (int i = 0; i< int(BaseLevelJunctions.size()); i++) + { + cout << "bl["< source_nodes; + vector outlet_nodes; + vector baselevel_node_of_each_basin; + int n_nodes_to_visit = 10; + JunctionNetwork.get_overlapping_channels_to_downstream_outlets(FlowInfo, BaseLevelJunctions, DistanceFromOutlet, + source_nodes,outlet_nodes,baselevel_node_of_each_basin,n_nodes_to_visit); + + LSDChiTools ChiTool_chi_checker(FlowInfo); + ChiTool_chi_checker.chi_map_automator_chi_only(FlowInfo, source_nodes, outlet_nodes, baselevel_node_of_each_basin, + filled_topography, DistanceFromOutlet, + DrainageArea, chi_coordinate); + + string chi_data_maps_string = OUT_DIR+OUT_ID+"_initial_chi_data_map.csv"; + ChiTool_chi_checker.print_chi_data_map_to_csv(FlowInfo, chi_data_maps_string); + + if ( this_bool_map["convert_csv_to_geojson"]) + { + string gjson_name = OUT_DIR+OUT_ID+"_initial_chi_data_map.geojson"; + LSDSpatialCSVReader thiscsv(chi_data_maps_string); + thiscsv.print_data_to_geojson(gjson_name); + } + + } // Finished routine for printing the initial channel network } else @@ -873,7 +1585,7 @@ int main (int nNumberofArgs,char *argv[]) } - } + } // This is the end of the logic for reading the inital raster else { cout << "You have chosen not to read an initial raster so I will create an initial surface for you." << endl; @@ -886,7 +1598,7 @@ int main (int nNumberofArgs,char *argv[]) // a bit of logic to override spinup logic if you choose the diamond square spinup if(this_bool_map["diamond_square_spinup"]) { - cout << "You have chosen the diamond square spinup. This orverrides all other spinup otions" << endl; + cout << "You have chosen the diamond square spinup. This overrides all other spinup otions" << endl; cout << "It also overrides initial surface options" << endl; create_initial_surface = false; this_bool_map["cyclic_spinup"] = false; @@ -894,6 +1606,23 @@ int main (int nNumberofArgs,char *argv[]) } + //.######..##..##..######..######..######...####...##..... + //...##....###.##....##......##......##....##..##..##..... + //...##....##.###....##......##......##....######..##..... + //...##....##..##....##......##......##....##..##..##..... + //.######..##..##..######....##....######..##..##..######. + //........................................................ + //..####...##..##..#####...######...####....####...######......... + //.##......##..##..##..##..##......##..##..##..##..##............. + //..####...##..##..#####...####....######..##......####........... + //.....##..##..##..##..##..##......##..##..##..##..##............. + //..####....####...##..##..##......##..##...####...######......... + //................................................................ + //..####...#####...######..##..##..##..##..#####.. + //.##......##..##....##....###.##..##..##..##..##. + //..####...#####.....##....##.###..##..##..#####.. + //.....##..##........##....##..##..##..##..##..... + //..####...##......######..##..##...####...##..... //============================================================================ // Logic for creating an initial surface // There are a number of options here, but the defaults are the ones we have @@ -1039,6 +1768,21 @@ int main (int nNumberofArgs,char *argv[]) KRaster.write_raster(K_fname,bil_name); } + if(this_bool_map["make_constant_Sc_raster"]) + { + LSDRaster Temp_raster = mod.return_as_raster(); + cout << "I am going to make a contant raster for the Sc parameter." << endl; + cout << "Setting K to " << this_float_map["rudimentary_critical_slope"] << endl; + LSDRasterMaker KRaster(Temp_raster); + KRaster.set_to_constant_value(this_float_map["rudimentary_critical_slope"]); + + // write the raster + string K_fname = OUT_DIR+OUT_ID+"_ConstScRaster"; + string bil_name = "bil"; + + KRaster.write_raster(K_fname,bil_name); + } + if(this_bool_map["make_spatially_varying_U"]) { // now do a sine version. @@ -1064,19 +1808,21 @@ int main (int nNumberofArgs,char *argv[]) URaster.write_raster(U_fname,bil_name); } + + if(this_bool_map["make_constant_U_raster"]) { LSDRaster Temp_raster = mod.return_as_raster(); cout << "I am going to make a contant raster for the U parameter." << endl; - LSDRasterMaker KRaster(Temp_raster); - KRaster.set_to_constant_value(this_float_map["rudimentary_steady_forcing_uplift"]); + LSDRasterMaker URaster(Temp_raster); + URaster.set_to_constant_value(this_float_map["rudimentary_steady_forcing_uplift"]); // write the raster - string K_fname = OUT_DIR+OUT_ID+"_ConstURaster"; + string U_fname = OUT_DIR+OUT_ID+"_ConstURaster"; string bil_name = "bil"; - KRaster.write_raster(K_fname,bil_name); + URaster.write_raster(U_fname,bil_name); } @@ -1391,6 +2137,58 @@ int main (int nNumberofArgs,char *argv[]) cout << "I am reading points from the file: "+ this_string_map["fixed_channel_csv_name"] << endl; LSDSpatialCSVReader source_points_data( RI, (DATA_DIR+this_string_map["fixed_channel_csv_name"]) ); + if (this_float_map["single_channel_drop"] != 0) + { + cout << endl << endl << endl << "==================================" << endl; + cout << "I am dropping and adjusting the slope of the single channel." << endl; + cout << "The data keys are: " << endl; + source_points_data.print_data_map_keys_to_screen(); + cout << "==================================" << endl << endl << endl < bc(4, "n"); // Initialise boundaries to No flow - if (this_string_map["tilt_boundary"] == "N") - { - bc[0] = "b"; - bc[1] = "p"; - bc[2] = "n"; - bc[3] = "p"; - } - else if (this_string_map["tilt_boundary"] == "E") - { - bc[0] = "p"; - bc[1] = "b"; - bc[2] = "p"; - bc[3] = "n"; - } - else if (this_string_map["tilt_boundary"] == "S") - { - bc[0] = "n"; - bc[1] = "p"; - bc[2] = "b"; - bc[3] = "p"; - } - else if (this_string_map["tilt_boundary"] == "W") - { - bc[0] = "p"; - bc[1] = "n"; - bc[2] = "p"; - bc[3] = "b"; - } - mod.set_boundary_conditions( bc ); // Set these as default boundary conditions + // set boundary conditions based on the tilt boundary. + vector bc(4, "n"); // Initialise boundaries to No flow + if (this_string_map["tilt_boundary"] == "N") + { + bc[0] = "b"; + bc[1] = "p"; + bc[2] = "n"; + bc[3] = "p"; + } + else if (this_string_map["tilt_boundary"] == "E") + { + bc[0] = "p"; + bc[1] = "b"; + bc[2] = "p"; + bc[3] = "n"; + } + else if (this_string_map["tilt_boundary"] == "S") + { + bc[0] = "n"; + bc[1] = "p"; + bc[2] = "b"; + bc[3] = "p"; + } + else if (this_string_map["tilt_boundary"] == "W") + { + bc[0] = "p"; + bc[1] = "n"; + bc[2] = "p"; + bc[3] = "b"; + } + mod.set_boundary_conditions( bc ); // Set these as default boundary conditions - // now snap! - float new_K = mod.fluvial_snap_to_steady_state_tune_K_for_relief(this_float_map["snapped_to_steady_uplift"], this_float_map["snapped_to_steady_relief"]); - cout << "Getting a steady solution for a landscape with relief of " << this_float_map["snapped_to_steady_relief"] - << " metres and uplift of " << this_float_map["snapped_to_steady_uplift"]*1000 << " mm per year." << endl; - cout << "The new K is: " << new_K << endl; - mod.set_K(new_K); + // now snap! + float new_K = mod.fluvial_snap_to_steady_state_tune_K_for_relief(this_float_map["snapped_to_steady_uplift"], this_float_map["snapped_to_steady_relief"]); + cout << "Getting a steady solution for a landscape with relief of " << this_float_map["snapped_to_steady_relief"] + << " metres and uplift of " << this_float_map["snapped_to_steady_uplift"]*1000 << " mm per year." << endl; + cout << "The new K is: " << new_K << endl; + mod.set_K(new_K); - if(this_bool_map["print_snapped_to_steady_frame"]) - { - cout << "I am printing the snapped surface, increasing the frame count by 1." << endl; - int this_frame = mod.get_current_frame(); - mod.print_rasters_and_csv( this_frame ); - this_frame++; - mod.set_current_frame(this_frame); + if(this_bool_map["print_snapped_to_steady_frame"]) + { + cout << "I am printing the snapped surface, increasing the frame count by 1." << endl; + int this_frame = mod.get_current_frame(); + mod.print_rasters_and_csv( this_frame ); + this_frame++; + mod.set_current_frame(this_frame); - } + } - // run progressive tilting for a defined period of time (set uplift field to tilted block) - cout << "Running progressive tilting of " << this_float_map["tilt_angle"] << " deg, for " << this_int_map["tilt_time"] << " years" << endl; - - // make a U raster based on a tilted block - LSDRasterMaker URaster(this_int_map["NRows"],this_int_map["NCols"]); - URaster.resize_and_reset(this_int_map["NRows"],this_int_map["NCols"],this_float_map["DataResolution"],this_float_map["snapped_to_steady_uplift"]); - URaster.tilted_block(this_float_map["tilt_angle"], this_string_map["tilt_boundary"]); - LSDRaster this_U_raster = URaster.return_as_raster(); - // write the raster - string U_fname = OUT_DIR+OUT_ID+"_URaster"; - string bil_name = "bil"; - this_U_raster.write_raster(U_fname,bil_name); - - // make a spatially invariant K raster. - LSDRasterMaker KRaster(this_int_map["NRows"],this_int_map["NCols"]); - KRaster.resize_and_reset(this_int_map["NRows"],this_int_map["NCols"],this_float_map["DataResolution"],new_K); - LSDRaster this_K_raster = KRaster.return_as_raster(); - - // reduce the print interval - mod.set_print_interval( this_int_map["tilt_print_interval"] ); - - current_end_time = current_end_time+this_int_map["tilt_time"]; - mod.set_endTime(current_end_time); - - mod.run_components_combined(this_U_raster, this_K_raster, this_bool_map["use_adaptive_timestep"]); - - // now run with a spatially invariant uplift block until steady state - current_end_time = current_end_time+this_int_map["uplift_time"]; - mod.set_endTime(current_end_time); - mod.set_uplift(0, this_float_map["snapped_to_steady_uplift"]); - mod.run_components_combined(); -} + // run progressive tilting for a defined period of time (set uplift field to tilted block) + cout << "Running progressive tilting of " << this_float_map["tilt_angle"] << " deg, for " << this_int_map["tilt_time"] << " years" << endl; -if( this_bool_map["run_instantaneous_tilt"] ) -{ - // set the end time and other model parameters - if( not this_bool_map["hillslopes_on"] ) - { - cout << "I'm turning hillslope diffusion off." << endl; - mod.set_hillslope(false); + // make a U raster based on a tilted block + LSDRasterMaker URaster(this_int_map["NRows"],this_int_map["NCols"]); + URaster.resize_and_reset(this_int_map["NRows"],this_int_map["NCols"],this_float_map["DataResolution"],this_float_map["snapped_to_steady_uplift"]); + URaster.tilted_block(this_float_map["tilt_angle"], this_string_map["tilt_boundary"]); + LSDRaster this_U_raster = URaster.return_as_raster(); + // write the raster + string U_fname = OUT_DIR+OUT_ID+"_URaster"; + string bil_name = "bil"; + this_U_raster.write_raster(U_fname,bil_name); + + // make a spatially invariant K raster. + LSDRasterMaker KRaster(this_int_map["NRows"],this_int_map["NCols"]); + KRaster.resize_and_reset(this_int_map["NRows"],this_int_map["NCols"],this_float_map["DataResolution"],new_K); + LSDRaster this_K_raster = KRaster.return_as_raster(); + + // reduce the print interval + mod.set_print_interval( this_int_map["tilt_print_interval"] ); + + current_end_time = current_end_time+this_int_map["tilt_time"]; + mod.set_endTime(current_end_time); + + mod.run_components_combined(this_U_raster, this_K_raster, this_bool_map["use_adaptive_timestep"]); + + // now run with a spatially invariant uplift block until steady state + current_end_time = current_end_time+this_int_map["uplift_time"]; + mod.set_endTime(current_end_time); + mod.set_uplift(0, this_float_map["snapped_to_steady_uplift"]); + mod.run_components_combined(); } - else + + if( this_bool_map["run_instantaneous_tilt"] ) { - cout << "This forcing includes hillslope diffusion." << endl; - mod.set_hillslope(true); - if (this_bool_map["nonlinear"]) + // set the end time and other model parameters + if( not this_bool_map["hillslopes_on"] ) { - mod.set_nonlinear(true); + cout << "I'm turning hillslope diffusion off." << endl; + mod.set_hillslope(false); + } + else + { + cout << "This forcing includes hillslope diffusion." << endl; + mod.set_hillslope(true); + if (this_bool_map["nonlinear"]) + { + mod.set_nonlinear(true); + } } - } - // set boundary conditions based on the tilt boundary. - vector bc(4, "n"); // Initialise boundaries to No flow - if (this_string_map["tilt_boundary"] == "N") - { - bc[0] = "b"; - bc[1] = "p"; - bc[2] = "n"; - bc[3] = "p"; - } - else if (this_string_map["tilt_boundary"] == "E") - { - bc[0] = "p"; - bc[1] = "b"; - bc[2] = "p"; - bc[3] = "n"; - } - else if (this_string_map["tilt_boundary"] == "S") - { - bc[0] = "n"; - bc[1] = "p"; - bc[2] = "b"; - bc[3] = "p"; + // set boundary conditions based on the tilt boundary. + vector bc(4, "n"); // Initialise boundaries to No flow + if (this_string_map["tilt_boundary"] == "N") + { + bc[0] = "b"; + bc[1] = "p"; + bc[2] = "n"; + bc[3] = "p"; + } + else if (this_string_map["tilt_boundary"] == "E") + { + bc[0] = "p"; + bc[1] = "b"; + bc[2] = "p"; + bc[3] = "n"; + } + else if (this_string_map["tilt_boundary"] == "S") + { + bc[0] = "n"; + bc[1] = "p"; + bc[2] = "b"; + bc[3] = "p"; + } + else if (this_string_map["tilt_boundary"] == "W") + { + bc[0] = "p"; + bc[1] = "n"; + bc[2] = "p"; + bc[3] = "b"; + } + mod.set_boundary_conditions( bc ); // Set these as default boundary conditions + + // now snap! + float new_K = mod.fluvial_snap_to_steady_state_tune_K_for_relief(this_float_map["snapped_to_steady_uplift"], this_float_map["snapped_to_steady_relief"]); + cout << "Getting a steady solution for a landscape with relief of " << this_float_map["snapped_to_steady_relief"] + << " metres and uplift of " << this_float_map["snapped_to_steady_uplift"]*1000 << " mm per year." << endl; + cout << "The new K is: " << new_K << endl; + mod.set_K(new_K); + + if(this_bool_map["print_snapped_to_steady_frame"]) + { + cout << "I am printing the snapped surface, increasing the frame count by 1." << endl; + int this_frame = mod.get_current_frame(); + mod.print_rasters_and_csv( this_frame ); + this_frame++; + mod.set_current_frame(this_frame); + + } + + cout << "Now I'm tilting your landscape by " << this_float_map["tilt_angle"] << " deg" << endl; + + // reduce the print interval + mod.set_print_interval( this_int_map["tilt_print_interval"] ); + + current_end_time = current_end_time+this_int_map["uplift_time"]; + mod.set_endTime(current_end_time); + //mod.raise_and_fill_raster(this_float_map["min_slope_for_fill"]); + + mod.instantaneous_tilt(this_float_map["tilt_angle"], this_string_map["tilt_boundary"]); + mod.run_components_combined(); } - else if (this_string_map["tilt_boundary"] == "W") + + if( this_bool_map["test_transient_channel"]) { - bc[0] = "p"; - bc[1] = "n"; - bc[2] = "p"; - bc[3] = "b"; + // get the single channel + cout << "Let me get the single channel..."; + LSDRasterInfo RI(InitialRaster); + LSDSpatialCSVReader transient_channel_data( RI, (DATA_DIR+this_string_map["transient_channel_csv_name"]) ); + cout << "Got it!" << endl; + + string fd_column_name = this_string_map["single_channel_fd_string"]; + string elevation_column_name = this_string_map["single_channel_elev_string"]; + + + vector phases; + vector< vector > elevations_vecvec; + mod.process_baselevel( transient_channel_data, this_string_map["transient_channel_timing_prefix"], + this_float_map["transient_channel_timing_multiplier"], this_int_map["transient_channel_n_time_columns"], + this_int_map["transient_channel_phase_steps"], + phases,elevations_vecvec); + + exit(0); } - mod.set_boundary_conditions( bc ); // Set these as default boundary conditions - // now snap! - float new_K = mod.fluvial_snap_to_steady_state_tune_K_for_relief(this_float_map["snapped_to_steady_uplift"], this_float_map["snapped_to_steady_relief"]); - cout << "Getting a steady solution for a landscape with relief of " << this_float_map["snapped_to_steady_relief"] - << " metres and uplift of " << this_float_map["snapped_to_steady_uplift"]*1000 << " mm per year." << endl; - cout << "The new K is: " << new_K << endl; - mod.set_K(new_K); - if(this_bool_map["print_snapped_to_steady_frame"]) + //======================================================================================================================== + // + //.######..#####....####...##..##...####...######..######..##..##..######......##......######..######..##..##...####.. + //...##....##..##..##..##..###.##..##........##....##......###.##....##........##........##......##....##..##..##..##. + //...##....#####...######..##.###...####.....##....####....##.###....##........##........##......##....######..##..##. + //...##....##..##..##..##..##..##......##....##....##......##..##....##........##........##......##....##..##..##..##. + //...##....##..##..##..##..##..##...####...######..######..##..##....##........######..######....##....##..##...####.. + // + //======================================================================================================================== + if( this_bool_map["run_transient_base_level"] ) { - cout << "I am printing the snapped surface, increasing the frame count by 1." << endl; - int this_frame = mod.get_current_frame(); - mod.print_rasters_and_csv( this_frame ); - this_frame++; - mod.set_current_frame(this_frame); + cout << "Hi there. Let me test some transient runs for you. " << endl; - } + cout << endl << endl << endl << endl << "Before I start, I need to make sure the raster is filled. " << endl; + LSDRaster temp_raster = mod.return_as_raster(); + LSDRaster carved_topography; + LSDRaster filled_topography; + + if(this_bool_map["carve_before_fill"]) + { + carved_topography = temp_raster.Breaching_Lindsay2016(); + filled_topography = carved_topography.fill(this_float_map["min_slope_for_fill"]); + } + else + { + filled_topography = temp_raster.fill(this_float_map["min_slope_for_fill"]); + } + LSDRasterModel temp_mod2(filled_topography); + mod = temp_mod2; + cout << "Done filling raster" << endl; + + cout << "Let me check the baselevel situation" << endl; + LSDFlowInfo FI(filled_topography); + cout << "N baselevel nodes is " << FI.get_NBaseLevelNodes() << endl << endl << endl; + + + + // set the print interval + mod.set_print_interval( this_int_map["print_interval"] ); + + // set the soil transport coefficient + mod.set_D( this_float_map["D"]); + + cout << "The printing interval is: " << mod.get_float_print_interval() << " years." << endl; + + // set the end time + current_end_time = this_float_map["transient_maximum_time"]; + mod.set_endTime(current_end_time); + + // Set the timestep + cout << "The timestep is " << this_float_map["dt"] << endl; + mod.set_timeStep( this_float_map["dt"] ); + + + LSDLithoCube LSDLC; + LSDRasterInfo RI(InitialRaster); + + cout <<"Let me get some K, Sc and U rasters for you." << endl; + cout << "If you are using the lithocube the K and Sc rasters will be ignored." << endl; + string U_name = OUT_DIR+OUT_ID+"_ConstURaster"; + string rext = "bil"; + LSDRaster KRaster; + LSDRaster ScRaster; + + cout << "Getting the constant uplift raster..."; + LSDRaster URaster(U_name,rext);; + cout << "got it" << endl; - cout << "Now I'm tilting your landscape by " << this_float_map["tilt_angle"] << " deg" << endl; - // reduce the print interval - mod.set_print_interval( this_int_map["tilt_print_interval"] ); + // First check to see if you are using the lithocube + if (this_bool_map["use_lithocube_for_transient"]) + { + cout << " I am going to use a lithocube in your transient runs. " << endl; + + // Then load the vo and asc files containing the 3D litho data + string this_vo_filename = DATA_DIR+this_string_map["vo_filename"]; + string this_vo_asc = DATA_DIR+this_string_map["vo_asc_filename"]; + + // Set names for K raster and lithocode raster to be printed + string K_name = DATA_DIR+OUT_ID+"_KRasterLSDLC"; + string Sc_name = DATA_DIR+OUT_ID+"_ScRasterLSDLC"; + string lithocodes_raster_name = DATA_DIR+OUT_ID+"_LithoCodes"; + + + // Initialise the lithocube and ingest the lithology data + cout << "I am going to load lithology information" << endl; + cout << "The filename is: " << this_vo_filename << endl; + LSDLithoCube LSDLC_ingest(this_vo_filename); + LSDLC_ingest.ingest_litho_data(this_vo_asc); + + // Deal with the georeferencing + int UTM_zone; + bool is_North = true; + string NorS = "N"; + RI.get_UTM_information(UTM_zone,is_North); + if (is_North == false) + { + NorS = "S"; + } + LSDLC_ingest.impose_georeferencing_UTM(UTM_zone, NorS); + + // Let's create the maps of stratigraphy codes and K values, and Sc values + if( this_bool_map["load_lithocode_to_K_csv"]) + { + string this_lookup_table_K = DATA_DIR+this_string_map["lookup_filename_K"]; + cout << "I am going to load a lookup table for K" << endl; + cout << "The filename is: " << this_lookup_table_K << endl; + LSDLC_ingest.read_strati_to_K_csv(this_lookup_table_K); + } + else + { + LSDLC_ingest.hardcode_fill_strati_to_K_map(); + } + // Let's create the map of stratigraphy codes and K values + if( this_bool_map["load_lithocode_to_Sc_csv"]) + { + string this_lookup_table_Sc = DATA_DIR+this_string_map["lookup_filename_Sc"]; + cout << "I am going to load a lookup table for Sc" << endl; + cout << "The filename is: " << this_lookup_table_Sc << endl; + LSDLC_ingest.read_strati_to_Sc_csv(this_lookup_table_Sc); + } + else + { + LSDLC_ingest.hardcode_fill_strati_to_Sc_map(); + } + + // Now we'll get the lithology codes, convert them to K values and make a raster of K values for the model to use + cout << "Getting the lithology codes" << endl; + LSDIndexRaster lithocodes_index_raster = LSDLC_ingest.get_lithology_codes_from_raster(InitialRaster,forbidden_lithocodes); + cout << "Converting the litho codes to K values" << endl; + LSDRaster K_values = LSDLC_ingest.index_to_K(lithocodes_index_raster, 0.000003); + cout << "Converting the litho codes to Sc values" << endl; + LSDRaster Sc_values = LSDLC_ingest.index_to_Sc(lithocodes_index_raster, 0.21); + + KRaster = K_values; + ScRaster = Sc_values; + LSDLC = LSDLC_ingest; + } + else + { + cout << " This transient run will use a static K raster (not a lithocube that will change K values as it is exhumed)." << endl; + cout << " Let me read the rasters for you" << endl; + string K_name = OUT_DIR+OUT_ID+"_ConstKRaster"; + string Sc_name = OUT_DIR+OUT_ID+"_ConstScRaster"; + LSDRaster K_values(K_name,rext); + LSDRaster Sc_values(Sc_name,rext); + KRaster = K_values; + ScRaster = Sc_values; + + cout << " Got the constant rasters. " << endl; + } + + + // get the single channel + cout << "Let me get the single channel..."; + LSDSpatialCSVReader transient_channel_data( RI, (DATA_DIR+this_string_map["transient_channel_csv_name"]) ); + cout << "Got it!" << endl; + + string fd_column_name = this_string_map["single_channel_fd_string"]; + string elevation_column_name = this_string_map["single_channel_elev_string"]; + + // This step takes the data that has elevations through timesteps and + // extracts the elevations as a function of times (which we call "phases") + cout << "Now I need to process the baselevel information." << endl; + cout << "WARNING: If I have a baselevel code for reading area, the area column needs to be: area" << endl; + cout << "At the moment you can only change this in the source code (LSDRasterModel line 7978 or thereabouts" << endl; + vector phases; + vector< vector > elevations_vecvec; + vector outlet_elevations; + + if (this_bool_map["use_transient_outlet"]) + { + cout << "I am processing a transient run with only a single outlet." << endl; + cout << "You need a transient csv for this. " << endl; + string transient_infile_name = DATA_DIR+this_string_map["transient_outlet_fname"]; + cout << "The name of the transient infile is: " << transient_infile_name << endl; + mod.process_transient_file(transient_infile_name, phases,outlet_elevations); + } + else + { + cout << "I am processing baselevel with a full transient channel" << endl; + cout << "This has different elevations at pixels throughout" << endl; + cout << "one or more channels." << endl; + mod.process_baselevel( transient_channel_data, this_string_map["transient_channel_timing_prefix"], + this_float_map["transient_channel_timing_multiplier"], this_int_map["transient_channel_n_time_columns"], + this_int_map["transient_channel_phase_steps"], + phases,elevations_vecvec); + } + cout << "Finished with the baselevel" << endl; + + + + // Make sure the baselevel channel does not sit above the rest of the raster + if (this_bool_map["raise_raster_over_base_level"]) + { + cout << "Min an max elevations are: " << mod.min_elevation() << " " << mod.max_elevation() << endl; + float min_elevation = mod.min_elevation(); + + vector this_elev = transient_channel_data.data_column_to_float(this_string_map["single_channel_elev_string"]); + + float outlet = this_elev[0]; + cout << "Outlet elevation is: " << outlet << endl; + if (outlet > min_elevation) + { + mod.add_fixed_elevation( outlet-min_elevation+2); + } + } + + // Add the baselevel switch to the baselevel channel + mod.baselevel_run_area_switch( transient_channel_data, this_string_map["baselevel_switch_column"]); + + cout << "I am going to turn off nonlinear diffusion." << endl; + cout << "In this model the hillslopes are critical slopes near channel." << endl; + cout << "And linear diffusion on the ridgetops." << endl; + mod.set_hillslope(false); + + // Make a cumulative uplift raster + // We get the right shape and then populate with a single value of 0 + cout << "Making the cumulative uplift raster." << endl; + LSDRaster Cumulative_uplift = mod.return_as_raster(); + Cumulative_uplift = Cumulative_uplift.PopulateRasterSingleValue(0.0); + Cumulative_uplift.write_raster(OUT_DIR+OUT_ID+"_test_CU_","bil"); + + cout << "Let me print the rasters before I do any simulation." << endl; + int this_frame = 9999; + mod.print_rasters( this_frame ); + + // reset the frame + mod.set_current_frame(0); - current_end_time = current_end_time+this_int_map["uplift_time"]; - mod.set_endTime(current_end_time); - //mod.raise_and_fill_raster(this_float_map["min_slope_for_fill"]); + // Set the timestep + cout << "I am repeating the timestep enforcement: " << this_float_map["dt"] << endl; + mod.set_timeStep( this_float_map["dt"] ); - mod.instantaneous_tilt(this_float_map["tilt_angle"], this_string_map["tilt_boundary"]); - mod.run_components_combined(); + cout << "Okay, now into the simulation loop..."; + if (this_bool_map["use_adaptive_timestep"]) + { + cout << "Using an adaptive timestep. This might slow down your model a lot (but it ensures it is stable)! "<< endl; + } + + if (this_bool_map["use_lithocube_for_transient"]) + { + cout << endl << endl << "=============================================" << endl; + cout << "I repeat, I am running the transient model using the lithocube" << endl; + + if (this_bool_map["use_transient_outlet"]) + { + cout << "I am running the model with a lowering outlet node" << endl; + mod.run_components_combined_imposed_baselevel( URaster, this_bool_map["use_adaptive_timestep"], transient_channel_data, + phases,outlet_elevations, Cumulative_uplift, LSDLC, forbidden_lithocodes, + this_bool_map["print_lithocode_raster"], this_bool_map["use_hillslope_hybrid"], + this_int_map["threshold_contributing_pixels"], + this_float_map["min_slope_for_fill"], + this_bool_map["print_exhumation_and_cumulative_uplift"], this_string_map["single_channel_elev_string"]); + } + else + { + cout << "I am running the model with an imposed channel" << endl; + mod.run_components_combined_imposed_baselevel( URaster, this_bool_map["use_adaptive_timestep"], transient_channel_data, + phases,elevations_vecvec, Cumulative_uplift, LSDLC, forbidden_lithocodes, + this_bool_map["print_lithocode_raster"], this_bool_map["use_hillslope_hybrid"], + this_int_map["threshold_contributing_pixels"], + this_float_map["min_slope_for_fill"], + this_bool_map["print_exhumation_and_cumulative_uplift"], this_string_map["single_channel_elev_string"]); + } + + } + else + { + cout << endl << endl << "=============================================" << endl; + cout << "I repeat, I am running the transient model with a fixed K raster" << endl; + mod.run_components_combined_imposed_baselevel( URaster, KRaster, this_bool_map["use_adaptive_timestep"], transient_channel_data, + phases,elevations_vecvec, this_float_map["min_slope_for_fill"], this_string_map["single_channel_elev_string"]); + + } + + cout << "Done!" << endl; + } + }