diff --git a/src/Plugins/SimplnxCore/docs/Images/Write_Asci_1.png b/src/Plugins/SimplnxCore/docs/Images/Write_Asci_1.png new file mode 100644 index 0000000000..b2a3e7b936 Binary files /dev/null and b/src/Plugins/SimplnxCore/docs/Images/Write_Asci_1.png differ diff --git a/src/Plugins/SimplnxCore/docs/Images/Write_Asci_2.png b/src/Plugins/SimplnxCore/docs/Images/Write_Asci_2.png new file mode 100644 index 0000000000..cca3636b3c Binary files /dev/null and b/src/Plugins/SimplnxCore/docs/Images/Write_Asci_2.png differ diff --git a/src/Plugins/SimplnxCore/docs/WriteASCIIDataFilter.md b/src/Plugins/SimplnxCore/docs/WriteASCIIDataFilter.md index ca78fac1d0..af071a39b8 100644 --- a/src/Plugins/SimplnxCore/docs/WriteASCIIDataFilter.md +++ b/src/Plugins/SimplnxCore/docs/WriteASCIIDataFilter.md @@ -6,7 +6,28 @@ IO (Output) (Write) (Export) (Text) (CSV) (ASCII) ## Description -This **Filter** accepts DataArray(s) as input, extracts the data, creates the file(s), and writes it out according to parameter choices +This filter will write the selected DataArrays to either individual files or as a single CSV style of file. + +## String Data Array Caveats + +- The "Maximum Tuples per Line" will not have any effect for that specific array. +- If the output is for a single file, then each String value will be enclosed in a set of Single Quotes (') characters. + +### Multiple Files + +Each input data array will be written to its own output file. The name of the file will be the name of the Data Array + the extension from the parameters. + +![Example of multiple output files](Images/Write_Asci_1.png) + +### Single File + +The output data file will be a column oriented CSV file. The optional header of each column will be the name of the Data Array. If the Data Array has multiple components then the zero based index will also be appended to the data array name. For example Euler Angles have 3 components, the header would look like: + +```console +Euler_0,Euler_1,Euler_2 +``` + +![Example of single output file](Images/Write_Asci_2.png) % Auto generated parameter table will be inserted here diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteASCIIDataFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteASCIIDataFilter.cpp index 461bedc891..06153ddb7b 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteASCIIDataFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteASCIIDataFilter.cpp @@ -65,23 +65,23 @@ Parameters WriteASCIIDataFilter::parameters() const // Create the parameter descriptors that are needed for this filter params.insertSeparator(Parameters::Separator{"Input Parameters"}); - params.insertLinkableParameter(std::make_unique(k_OutputStyle_Key, "Output Type", "Whether to output a folder of files or a single file with all the data in column form", - to_underlying(OutputStyle::MultipleFiles), + params.insertLinkableParameter(std::make_unique(k_OutputStyle_Key, "Output File Generation", + "Whether to output a folder of files or a single file with all the data in column form", to_underlying(OutputStyle::SingleFile), ChoicesParameter::Choices{"Multiple Files", "Single File"})); // sequence dependent DO NOT REORDER params.insert(std::make_unique(k_OutputPath_Key, "Output Path", "The output file path", fs::path(""), FileSystemPathParameter::ExtensionsType{}, FileSystemPathParameter::PathType::OutputFile, true)); params.insert(std::make_unique(k_OutputDir_Key, "Output Directory", "The output file path", fs::path(""), FileSystemPathParameter::ExtensionsType{}, FileSystemPathParameter::PathType::OutputDir, true)); - params.insert(std::make_unique(k_FileExtension_Key, "File Extension", "The file extension for the output file(s)", ".txt")); - params.insert(std::make_unique(k_MaxValPerLine_Key, "Maximum Elements Per Line", "Number of tuples to print on each line", 0)); + params.insert(std::make_unique(k_FileExtension_Key, "File Extension", "The file extension for the output file(s)", ".csv")); + params.insert(std::make_unique(k_MaxValPerLine_Key, "Maximum Tuples Per Line", "Number of tuples to print on each line. Does not apply to string arrays", 1)); params.insert(std::make_unique(k_Delimiter_Key, "Delimiter", "The delimiter separating the data", to_underlying(OStreamUtilities::Delimiter::Comma), ChoicesParameter::Choices{"Space", "Semicolon", "Comma", "Colon", "Tab"})); // sequence dependent DO NOT REORDER params.insert(std::make_unique(k_Includes_Key, "Header and Index Options", "Default Include is Headers only", to_underlying(Includes::Headers), ChoicesParameter::Choices{"Neither", "Headers", "Index", "Both"})); // sequence dependent DO NOT REORDER params.insertSeparator(Parameters::Separator{"Required Data Objects"}); - params.insert(std::make_unique(k_SelectedDataArrayPaths_Key, "Attribute Arrays to Export", "Output arrays to be written as ASCII representations", - MultiArraySelectionParameter::ValueType{}, MultiArraySelectionParameter::AllowedTypes{IArray::ArrayType::DataArray}, - nx::core::GetAllDataTypes())); + params.insert(std::make_unique(k_SelectedDataArrayPaths_Key, "Attribute Arrays to Export", "Data Arrays to be written to disk", + MultiArraySelectionParameter::ValueType{}, + MultiArraySelectionParameter::AllowedTypes{IArray::ArrayType::DataArray, IArray::ArrayType::StringArray}, nx::core::GetAllDataTypes())); // Associate the Linkable Parameter(s) to the children parameters that they control params.linkParameters(k_OutputStyle_Key, k_MaxValPerLine_Key, std::make_any(to_underlying(OutputStyle::MultipleFiles))); diff --git a/src/simplnx/Utilities/DataArrayUtilities.cpp b/src/simplnx/Utilities/DataArrayUtilities.cpp index a4ca8c8aef..645fc8f2fc 100644 --- a/src/simplnx/Utilities/DataArrayUtilities.cpp +++ b/src/simplnx/Utilities/DataArrayUtilities.cpp @@ -136,8 +136,8 @@ bool CheckArraysHaveSameTupleCount(const DataStructure& dataStructure, const std std::set types; for(const auto& dataPath : dataArrayPaths) { - const auto* dataArray = dataStructure.getDataAs(dataPath); - types.insert(dataArray->getNumberOfTuples()); + const auto* iArrayPtr = dataStructure.getDataAs(dataPath); + types.insert(iArrayPtr->getNumberOfTuples()); } return types.size() == 1; } diff --git a/src/simplnx/Utilities/OStreamUtilities.cpp b/src/simplnx/Utilities/OStreamUtilities.cpp index 90f11a482e..e159687db5 100644 --- a/src/simplnx/Utilities/OStreamUtilities.cpp +++ b/src/simplnx/Utilities/OStreamUtilities.cpp @@ -200,18 +200,13 @@ struct PrintDataArray * @param mesgHandler The message handler to dump progress updates to * // default parameters * @param delimiter The delimiter to insert between values - * @param componentsPerLine The number of components per line + * @return A result object with any errors or warnings */ Result<> PrintStringArray(std::ostream& outputStrm, const StringArray& inputStringArray, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, - const std::string& delimiter = ",", int32 componentsPerLine = 0) + const std::string& delimiter = ",") { auto start = std::chrono::steady_clock::now(); auto numTuples = inputStringArray.getNumberOfTuples(); - auto maxLine = static_cast(componentsPerLine); - if(componentsPerLine == 0) - { - maxLine = static_cast(inputStringArray.getNumberOfComponents()); - } for(size_t tuple = 0; tuple < numTuples; tuple++) { @@ -226,19 +221,7 @@ Result<> PrintStringArray(std::ostream& outputStrm, const StringArray& inputStri return {}; } } - - for(size_t index = 0; index < inputStringArray.getNumberOfComponents(); index++) - { - outputStrm << inputStringArray[index]; - if(index != maxLine - 1) - { - outputStrm << delimiter; - } - else - { - outputStrm << "\n"; - } - } + outputStrm << inputStringArray[tuple] << "\n"; } return {}; @@ -253,6 +236,39 @@ class ITupleWriter virtual void writeHeader(std::ostream& outputStrm) const = 0; }; +class StringTupleWriter : public ITupleWriter +{ + using DataArrayType = StringArray; + +public: + StringTupleWriter(const StringArray& iDataArray, const std::string& delimiter) + : m_DataArray(dynamic_cast(iDataArray)) + , m_Delimiter(delimiter) + { + } + ~StringTupleWriter() override = default; + + StringTupleWriter(const StringTupleWriter&) = delete; + StringTupleWriter(StringTupleWriter&&) noexcept = delete; + + StringTupleWriter& operator=(const StringTupleWriter&) = delete; + StringTupleWriter& operator=(StringTupleWriter&&) noexcept = delete; + + void write(std::ostream& outputStrm, usize tupleIndex) const override + { + outputStrm << m_Delimiter << m_DataArray[tupleIndex] << m_Delimiter; + } + + void writeHeader(std::ostream& outputStrm) const override + { + outputStrm << m_DataArray.getName(); + } + +private: + const DataArrayType& m_DataArray; + const std::string m_Delimiter = "'"; +}; + template class TupleWriter : public ITupleWriter { @@ -396,11 +412,7 @@ void PrintDataSetsToMultipleFiles(const std::vector& objectPaths, Data auto* stringArray = dataStructure.getDataAs(dataPath); if(stringArray != nullptr) { - if(exportToBinary) - { - throw std::runtime_error(fmt::format("{}({}): Function {}: Error. Cannot print a StringArray to binary: '{}'", "PrintDataSetsToMultipleFiles", __FILE__, __LINE__, dataPath.getTargetName())); - } - PrintStringArray(outStrm, *stringArray, mesgHandler, shouldCancel, delimiter, componentsPerLine); + PrintStringArray(outStrm, *stringArray, mesgHandler, shouldCancel, delimiter); } auto* neighborList = dataStructure.getDataAs(dataPath); if(neighborList != nullptr) @@ -449,7 +461,7 @@ void PrintSingleDataObject(std::ostream& outputStrm, const DataPath& objectPath, auto* stringArray = dataStructure.getDataAs(objectPath); if(stringArray != nullptr) { - PrintStringArray(outputStrm, *stringArray, mesgHandler, shouldCancel, delimiter, componentsPerLine); + PrintStringArray(outputStrm, *stringArray, mesgHandler, shouldCancel, delimiter); } auto* neighborList = dataStructure.getDataAs(objectPath); if(neighborList != nullptr) @@ -476,7 +488,7 @@ void PrintDataSetsToSingleFile(std::ostream& outputStrm, const std::vector& neighborLists, bool writeNumOfFeatures) { - const auto& firstDataArray = dataStructure.getDataRefAs(objectPaths[0]); + const auto& firstDataArray = dataStructure.getDataRefAs(objectPaths[0]); usize numTuples = firstDataArray.getNumberOfTuples(); auto start = std::chrono::steady_clock::now(); @@ -484,8 +496,18 @@ void PrintDataSetsToSingleFile(std::ostream& outputStrm, const std::vector> writers; for(const auto& selectedArrayPath : objectPaths) { - const auto& iDataArray = dataStructure.getDataRefAs(selectedArrayPath); - ExecuteDataFunction(AddTupleWriter{}, iDataArray.getDataType(), writers, iDataArray, delimiter); + auto* dataArrayPtr = dataStructure.getDataAs(selectedArrayPath); + if(nullptr != dataArrayPtr) + { + const auto& iDataArrayRef = dataStructure.getDataRefAs(selectedArrayPath); + ExecuteDataFunction(AddTupleWriter{}, iDataArrayRef.getDataType(), writers, iDataArrayRef, delimiter); + } + auto* stringArrayPtr = dataStructure.getDataAs(selectedArrayPath); + if(nullptr != stringArrayPtr) + { + const auto& iDataArrayRef = dataStructure.getDataRefAs(selectedArrayPath); + writers.push_back(std::make_shared(iDataArrayRef, "'")); + } } size_t writersCount = writers.size();