diff --git a/ApplicationCode/Application/Tools/RiaStdStringTools.cpp b/ApplicationCode/Application/Tools/RiaStdStringTools.cpp index 44b9451a62..90ca5e4a2b 100644 --- a/ApplicationCode/Application/Tools/RiaStdStringTools.cpp +++ b/ApplicationCode/Application/Tools/RiaStdStringTools.cpp @@ -34,7 +34,13 @@ std::string RiaStdStringTools::trimString(const std::string& s) //-------------------------------------------------------------------------------------------------- bool RiaStdStringTools::isNumber(const std::string& s) { - return (s.find_first_not_of("0123456789.eE-") != std::string::npos); + if (s.size() == 0) return false; + if (findCharMatchCount(s, '.') > 1) return false; + if (findCharMatchCount(s, '-') > 1) return false; + if (findCharMatchCount(s, 'e') > 1) return false; + if (findCharMatchCount(s, 'E') > 1) return false; + + return (s.find_first_not_of("0123456789.eE-") == std::string::npos); } //-------------------------------------------------------------------------------------------------- @@ -79,3 +85,17 @@ std::vector RiaStdStringTools::splitStringBySpace(const std::string return words; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +size_t RiaStdStringTools::findCharMatchCount(const std::string& s, char c) +{ + size_t count = 0; + size_t pos = 0; + while ((pos = s.find_first_of(c, pos + 1)) != std::string::npos) + { + count++; + } + return count; +} diff --git a/ApplicationCode/Application/Tools/RiaStdStringTools.h b/ApplicationCode/Application/Tools/RiaStdStringTools.h index a3b5921205..a68ace5917 100644 --- a/ApplicationCode/Application/Tools/RiaStdStringTools.h +++ b/ApplicationCode/Application/Tools/RiaStdStringTools.h @@ -52,5 +52,7 @@ private: } } } + + static size_t findCharMatchCount(const std::string& s, char c); }; diff --git a/ApplicationCode/Commands/RicImportObservedDataFeature.cpp b/ApplicationCode/Commands/RicImportObservedDataFeature.cpp index 77a5b1fcf5..0b95b98933 100644 --- a/ApplicationCode/Commands/RicImportObservedDataFeature.cpp +++ b/ApplicationCode/Commands/RicImportObservedDataFeature.cpp @@ -51,7 +51,7 @@ void RicImportObservedDataFeature::selectObservedDataFileInDialog() { RiaApplication* app = RiaApplication::instance(); QString defaultDir = app->lastUsedDialogDirectory("INPUT_FILES"); - QStringList fileNames = QFileDialog::getOpenFileNames(nullptr, "Import Observed Time History Data", defaultDir, "Observed Data (*.RSM *.txt *.inc);;All Files (*.*)"); + QStringList fileNames = QFileDialog::getOpenFileNames(nullptr, "Import Observed Time History Data", defaultDir, "Observed Data (*.RSM *.txt *.csv *.inc);;All Files (*.*)"); if (fileNames.isEmpty()) return; diff --git a/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeature.cpp b/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeature.cpp index 1ac9479fe2..efe71d8c43 100644 --- a/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeature.cpp +++ b/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeature.cpp @@ -161,8 +161,8 @@ bool RicPasteAsciiDataToSummaryPlotFeature::hasPastedText() std::vector RicPasteAsciiDataToSummaryPlotFeature::parseCurves(QString& data, const RicPasteAsciiDataToSummaryPlotFeatureUi& settings) { std::vector curves; - - RifColumnBasedAsciiParser parser = RifColumnBasedAsciiParser(data, settings.dateFormat(), settings.decimalLocale(), settings.cellSeparator()); + const AsciiDataParseOptions& parseOptions = settings.parseOptions(); + RifColumnBasedAsciiParser parser = RifColumnBasedAsciiParser(data, parseOptions.dateTimeFormat(), parseOptions.decimalSeparator, parseOptions.cellSeparator); if (parser.headers().empty()) { @@ -171,7 +171,7 @@ std::vector RicPasteAsciiDataToSummaryPlotFeature::parseCurv std::map< CurveType, std::vector > curveToTypeMap; - QString curvePrefix = settings.curvePrefix(); + QString curvePrefix = parseOptions.curvePrefix; for (size_t i = 0; i < parser.headers().size(); i++) { @@ -187,9 +187,9 @@ std::vector RicPasteAsciiDataToSummaryPlotFeature::parseCurv curve->setTitle(QString("%1: %2").arg(curvePrefix).arg(parser.headers()[i])); } // Appearance - curve->setSymbol(settings.pointSymbol()); - curve->setLineStyle(settings.lineStyle()); - curve->setSymbolSkipDinstance(settings.symbolSkipDinstance()); + curve->setSymbol(parseOptions.curveSymbol); + curve->setLineStyle(parseOptions.curveLineStyle); + curve->setSymbolSkipDinstance(parseOptions.curveSymbolSkipDistance); curveToTypeMap[guessCurveType(parser.headers()[i])].push_back(curve); curves.push_back(curve); } diff --git a/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.cpp b/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.cpp index 9b83e85ce9..17f07481c6 100644 --- a/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.cpp +++ b/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.cpp @@ -60,6 +60,7 @@ namespace caf { CAF_PDM_SOURCE_INIT(RicPasteAsciiDataToSummaryPlotFeatureUi, "RicPasteAsciiDataToSummaryPlotFeatureUi"); + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -92,91 +93,71 @@ RicPasteAsciiDataToSummaryPlotFeatureUi::RicPasteAsciiDataToSummaryPlotFeatureUi //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -QString RicPasteAsciiDataToSummaryPlotFeatureUi::dateFormat() const +const AsciiDataParseOptions RicPasteAsciiDataToSummaryPlotFeatureUi::parseOptions() const { - if (m_useCustomDateFormat()) + AsciiDataParseOptions parseOptions; + + parseOptions.plotTitle = m_plotTitle(); + parseOptions.curvePrefix = m_curvePrefix(); + { - return m_customDateFormat(); - } - else - { - QString format = m_dateFormat().text(); - if (m_timeFormat() != TIME_NONE) + QString separator; + switch (m_decimalSeparator()) { - format += " " + m_timeFormat().text(); + case DECIMAL_COMMA: + separator = ","; + break; + case DECIMAL_DOT: + default: + separator = "."; + break; } - return format; - } -} -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QLocale RicPasteAsciiDataToSummaryPlotFeatureUi::decimalLocale() const -{ - switch (m_decimalSeparator()) + parseOptions.decimalSeparator = separator; + } + { - case DECIMAL_COMMA: - return QLocale::Norwegian; - case DECIMAL_DOT: - default: - return QLocale::c(); - } -} + QString dateFormat; + + if (m_useCustomDateFormat()) + { + dateFormat = m_customDateFormat; + } + else + { + dateFormat = m_dateFormat().text(); + } + + parseOptions.dateFormat_ = dateFormat; + } + + parseOptions.timeFormat = m_timeFormat() != TIME_NONE ? m_timeFormat().text() : QString(""); -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QString RicPasteAsciiDataToSummaryPlotFeatureUi::cellSeparator() const -{ - switch (m_cellSeparator()) { - case CELL_COMMA: - return ","; - case CELL_TAB: - default: - return "\t"; + QString separator; + + switch (m_cellSeparator()) + { + case CELL_COMMA: + separator = ","; + break; + case CELL_SEMICOLON: + separator = ";"; + break; + case CELL_TAB: + default: + separator = "\t"; + break; + } + + parseOptions.cellSeparator = separator; } -} -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QString RicPasteAsciiDataToSummaryPlotFeatureUi::plotTitle() const -{ - return m_plotTitle(); -} + parseOptions.curveLineStyle = m_curveLineStyle(); + parseOptions.curveSymbol = m_curveSymbol(); + parseOptions.curveSymbolSkipDistance = m_curveSymbolSkipDistance(); -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -QString RicPasteAsciiDataToSummaryPlotFeatureUi::curvePrefix() const -{ - return m_curvePrefix(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RimPlotCurve::LineStyleEnum RicPasteAsciiDataToSummaryPlotFeatureUi::lineStyle() const -{ - return m_curveLineStyle(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -RimPlotCurve::PointSymbolEnum RicPasteAsciiDataToSummaryPlotFeatureUi::pointSymbol() const -{ - return m_curveSymbol(); -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -float RicPasteAsciiDataToSummaryPlotFeatureUi::symbolSkipDinstance() const -{ - return m_curveSymbolSkipDistance(); + return parseOptions; } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.h b/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.h index f47b550866..033b4e299c 100644 --- a/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.h +++ b/ApplicationCode/Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.h @@ -27,6 +27,27 @@ #include #include +//================================================================================================== +/// +//================================================================================================== +class AsciiDataParseOptions +{ +public: + QString plotTitle; + QString curvePrefix; + QString decimalSeparator; + QString dateFormat_; + QString timeFormat; + QString cellSeparator; + + RimPlotCurve::LineStyleEnum curveLineStyle; + RimPlotCurve::PointSymbolEnum curveSymbol; + float curveSymbolSkipDistance; + + QString dateTimeFormat() const { return dateFormat_ + " " + timeFormat; } +}; + + //================================================================================================== /// //================================================================================================== @@ -72,15 +93,7 @@ public: public: RicPasteAsciiDataToSummaryPlotFeatureUi(); - QString dateFormat() const; - QLocale decimalLocale() const; - QString cellSeparator() const; - QString plotTitle() const; - QString curvePrefix() const; - RimPlotCurve::LineStyleEnum lineStyle() const; - RimPlotCurve::PointSymbolEnum pointSymbol() const; - float symbolSkipDinstance() const; - + const AsciiDataParseOptions parseOptions() const; void createNewPlot(); void setPreviewText(const QString& text); @@ -89,20 +102,19 @@ protected: virtual void defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute) override; private: - caf::PdmField m_plotTitle; - caf::PdmField m_curvePrefix; - caf::PdmField m_decimalSeparator; - caf::PdmField m_dateFormat; - caf::PdmField m_timeFormat; - caf::PdmField m_useCustomDateFormat; - caf::PdmField m_customDateFormat; - caf::PdmField m_cellSeparator; + caf::PdmField m_plotTitle; + caf::PdmField m_curvePrefix; + caf::PdmField m_decimalSeparator; + caf::PdmField m_dateFormat; + caf::PdmField m_timeFormat; + caf::PdmField m_useCustomDateFormat; + caf::PdmField m_customDateFormat; + caf::PdmField m_cellSeparator; - caf::PdmField> m_curveLineStyle; - caf::PdmField> m_curveSymbol; - caf::PdmField m_curveSymbolSkipDistance; + caf::PdmField> m_curveLineStyle; + caf::PdmField> m_curveSymbol; + caf::PdmField m_curveSymbolSkipDistance; - bool m_createNewPlot; - - caf::PdmField m_previewText; + bool m_createNewPlot; + caf::PdmField m_previewText; }; diff --git a/ApplicationCode/FileInterface/CMakeLists_files.cmake b/ApplicationCode/FileInterface/CMakeLists_files.cmake index 2c04ab9eff..89743f11fc 100644 --- a/ApplicationCode/FileInterface/CMakeLists_files.cmake +++ b/ApplicationCode/FileInterface/CMakeLists_files.cmake @@ -37,6 +37,8 @@ ${CEE_CURRENT_LIST_DIR}RifKeywordVectorUserData.h ${CEE_CURRENT_LIST_DIR}RifDataSourceForRftPlt.h ${CEE_CURRENT_LIST_DIR}RifDataSourceForRftPltQMetaType.h ${CEE_CURRENT_LIST_DIR}RifEclipseUserDataKeywordTools.h +${CEE_CURRENT_LIST_DIR}RifCsvUserData.h +${CEE_CURRENT_LIST_DIR}RifCsvUserDataParser.h # HDF5 file reader is directly included in ResInsight main CmakeList.txt #${CEE_CURRENT_LIST_DIR}RifHdf5Reader.h @@ -79,7 +81,8 @@ ${CEE_CURRENT_LIST_DIR}RifColumnBasedUserData.cpp ${CEE_CURRENT_LIST_DIR}RifKeywordVectorUserData.cpp ${CEE_CURRENT_LIST_DIR}RifDataSourceForRftPlt.cpp ${CEE_CURRENT_LIST_DIR}RifEclipseUserDataKeywordTools.cpp - +${CEE_CURRENT_LIST_DIR}RifCsvUserData.cpp +${CEE_CURRENT_LIST_DIR}RifCsvUserDataParser.cpp # HDF5 file reader is directly included in ResInsight main CmakeList.txt #${CEE_CURRENT_LIST_DIR}RifHdf5Reader.cpp diff --git a/ApplicationCode/FileInterface/RifColumnBasedUserData.cpp b/ApplicationCode/FileInterface/RifColumnBasedUserData.cpp index a2757e3bf2..b157fa2cf1 100644 --- a/ApplicationCode/FileInterface/RifColumnBasedUserData.cpp +++ b/ApplicationCode/FileInterface/RifColumnBasedUserData.cpp @@ -159,7 +159,7 @@ void RifColumnBasedUserData::buildTimeStepsAndMappings() for (size_t columIndex = 0; columIndex < tableData.columnInfos().size(); columIndex++) { const ColumnInfo& ci = tableData.columnInfos()[columIndex]; - if (!ci.isStringData) + if (ci.dataType == ColumnInfo::NUMERIC) { RifEclipseSummaryAddress sumAddress = ci.summaryAddress; @@ -264,7 +264,7 @@ std::vector RifColumnBasedUserData::createTimeSteps(const TableData& tab const ColumnInfo& ci = tableData.columnInfos()[dateColumnIndex]; QString dateFormat; - for (auto s : ci.stringValues) + for (auto s : ci.textValues) { QDateTime dt = RiaDateStringParser::parseDateString(s); diff --git a/ApplicationCode/FileInterface/RifColumnBasedUserDataParser.cpp b/ApplicationCode/FileInterface/RifColumnBasedUserDataParser.cpp index 28ad86a09d..cb77b9b0dd 100644 --- a/ApplicationCode/FileInterface/RifColumnBasedUserDataParser.cpp +++ b/ApplicationCode/FileInterface/RifColumnBasedUserDataParser.cpp @@ -84,7 +84,7 @@ void RifColumnBasedUserDataParser::parseTableData(const QString& data) if (isFixedWidth) { auto columnInfos = RifEclipseUserDataParserTools::columnInfoForFixedColumnWidth(streamData); - table = TableData("", "", "", columnInfos); + table = TableData("", "", columnInfos); } else { @@ -131,9 +131,9 @@ void RifColumnBasedUserDataParser::parseTableData(const QString& data) for (int i = 0; i < columnCount; i++) { - if (columnInfos[i].isStringData) + if (columnInfos[i].dataType == ColumnInfo::TEXT) { - columnInfos[i].stringValues.push_back(entries[i].toStdString()); + columnInfos[i].textValues.push_back(entries[i].toStdString()); } else { diff --git a/ApplicationCode/FileInterface/RifCsvUserData.cpp b/ApplicationCode/FileInterface/RifCsvUserData.cpp new file mode 100644 index 0000000000..ca90a92609 --- /dev/null +++ b/ApplicationCode/FileInterface/RifCsvUserData.cpp @@ -0,0 +1,187 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017- Statoil ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RifCsvUserData.h" + +#include "RiaDateStringParser.h" +#include "RiaLogging.h" +#include "RiaQDateTimeTools.h" + +#include "RifCsvUserDataParser.h" +#include "RifEclipseUserDataKeywordTools.h" +#include "RifEclipseUserDataParserTools.h" + +#include "cafUtils.h" + +#include +#include +#include + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifCsvUserData::RifCsvUserData() +{ + +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifCsvUserData::~RifCsvUserData() +{ + +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifCsvUserData::parse(const QString& data, const AsciiDataParseOptions& parseOptions, QString* errorText) +{ + m_allResultAddresses.clear(); + m_timeSteps.clear(); + m_mapFromAddressToTimeStepIndex.clear(); + m_mapFromAddressToResultIndex.clear(); + + m_parser = std::unique_ptr(new RifCsvUserDataParser(errorText)); + m_parser->parse(data, parseOptions); + if (!m_parser) + { + RiaLogging::error(QString("Failed to parse file")); + + return false; + } + + buildTimeStepsAndMappings(); + + return true; + +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifCsvUserData::values(const RifEclipseSummaryAddress& resultAddress, std::vector* values) const +{ + auto search = m_mapFromAddressToResultIndex.find(resultAddress); + if (search != m_mapFromAddressToResultIndex.end()) + { + size_t columnIndex = search->second; + + const ColumnInfo* ci = m_parser->columnInfo(columnIndex); + if (!ci) return false; + + values->clear(); + values->reserve(ci->values.size()); + for (double val : ci->values) + { + values->push_back(val); + } + } + + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const std::vector& RifCsvUserData::timeSteps(const RifEclipseSummaryAddress& resultAddress) const +{ + auto search = m_mapFromAddressToTimeStepIndex.find(resultAddress); + if (search != m_mapFromAddressToTimeStepIndex.end()) + { + return m_timeSteps; + } + + static std::vector emptyVector; + + return emptyVector; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::string RifCsvUserData::unitName(const RifEclipseSummaryAddress& resultAddress) const +{ + auto search = m_mapFromAddressToResultIndex.find(resultAddress); + if (search != m_mapFromAddressToResultIndex.end()) + { + size_t columnIndex = search->second; + + const ColumnInfo* ci = m_parser->columnInfo(columnIndex); + if (ci) + { + return ci->unitName; + } + } + + return ""; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RifCsvUserData::buildTimeStepsAndMappings() +{ + auto tableData = m_parser->tableData(); + + std::vector timeStepsForTable = createTimeSteps(tableData); + + if (timeStepsForTable.empty()) + { + RiaLogging::warning(QString("Failed to find time data for table in file")); + RiaLogging::warning(QString("No data for this table is imported")); + + return; + } + + m_timeSteps = timeStepsForTable; + + + for (size_t columnIndex = 0; columnIndex < tableData.columnInfos().size(); columnIndex++) + { + const ColumnInfo& ci = tableData.columnInfos()[columnIndex]; + if (ci.dataType == ColumnInfo::NUMERIC) + { + RifEclipseSummaryAddress sumAddress = ci.summaryAddress; + + m_allResultAddresses.push_back(sumAddress); + + m_mapFromAddressToTimeStepIndex[sumAddress] = m_timeSteps.size() - 1; + m_mapFromAddressToResultIndex[sumAddress] = columnIndex; + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RifCsvUserData::createTimeSteps(const TableData& tableData) +{ + std::vector tsVector; + + const ColumnInfo& col = tableData.columnInfos()[0]; + + tsVector.reserve(col.dateTimeValues.size()); + for (const QDateTime& qdt : col.dateTimeValues) + { + tsVector.push_back(qdt.toTime_t()); + } + + return tsVector; +} diff --git a/ApplicationCode/FileInterface/RifCsvUserData.h b/ApplicationCode/FileInterface/RifCsvUserData.h new file mode 100644 index 0000000000..4a14e9b487 --- /dev/null +++ b/ApplicationCode/FileInterface/RifCsvUserData.h @@ -0,0 +1,65 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017- Statoil ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + + +#pragma once + +#include "RifSummaryReaderInterface.h" + +#include "../Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.h" + +#include +#include +#include + +class QString; + +class RifCsvUserDataParser; +class RifEclipseSummaryAddress; +class TableData; + +//================================================================================================== +// +// +//================================================================================================== +class RifCsvUserData : public RifSummaryReaderInterface +{ +public: + RifCsvUserData(); + ~RifCsvUserData(); + + bool parse(const QString& data, const AsciiDataParseOptions& parseOptions, QString* errorText = nullptr); + + virtual const std::vector& timeSteps(const RifEclipseSummaryAddress& resultAddress) const override; + + virtual bool values(const RifEclipseSummaryAddress& resultAddress, + std::vector* values) const override; + + std::string unitName(const RifEclipseSummaryAddress& resultAddress) const override; + +private: + void buildTimeStepsAndMappings(); + static std::vector createTimeSteps(const TableData& table); + +private: + std::unique_ptr m_parser; + std::vector m_timeSteps; + + std::map m_mapFromAddressToTimeStepIndex; + std::map m_mapFromAddressToResultIndex; +}; diff --git a/ApplicationCode/FileInterface/RifCsvUserDataParser.cpp b/ApplicationCode/FileInterface/RifCsvUserDataParser.cpp new file mode 100644 index 0000000000..c49687fb3e --- /dev/null +++ b/ApplicationCode/FileInterface/RifCsvUserDataParser.cpp @@ -0,0 +1,276 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017- Statoil ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RifCsvUserDataParser.h" + +#include "RifEclipseUserDataKeywordTools.h" +#include "RifEclipseUserDataParserTools.h" + +#include "RiaDateStringParser.h" +#include "RiaLogging.h" +#include "RiaStdStringTools.h" +#include "RiaQDateTimeTools.h" + +#include "cvfAssert.h" + +#include +#include + + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifCsvUserDataParser::RifCsvUserDataParser(QString* errorText) + : m_errorText(errorText) +{ +} + + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifCsvUserDataParser::parse(const QString& data, const AsciiDataParseOptions& parseOptions) +{ + return parseData(data, parseOptions); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const TableData& RifCsvUserDataParser::tableData() const +{ + return m_tableData; +} + + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +const ColumnInfo* RifCsvUserDataParser::columnInfo(size_t columnIndex) const +{ + if (columnIndex >= m_tableData.columnInfos().size()) return nullptr; + + return &(m_tableData.columnInfos()[columnIndex]); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifCsvUserDataParser::parseData(const QString& data, const AsciiDataParseOptions& parseOptions) +{ + enum { HEADER_ROW, FIRST_DATA_ROW, DATA_ROW } parseState = HEADER_ROW; + int colCount = -1; + std::vector cols; + + QTextStream dataStream(const_cast(&data)); + while (!dataStream.atEnd()) + { + QString line = dataStream.readLine(); + if(line.trimmed().isEmpty()) continue; + + QStringList lineColumns = splitLineAndTrim(line, parseOptions.cellSeparator); + + if (parseState == HEADER_ROW) + { + colCount = lineColumns.size(); + + for (int iCol = 0; iCol < colCount; iCol++) + { + std::string colName = lineColumns[iCol].toStdString(); + RifEclipseSummaryAddress addr = RifEclipseSummaryAddress::importedAddress(colName); + ColumnInfo col = ColumnInfo::createColumnInfoFromCsvData(addr, ""); + cols.push_back(col); + } + + parseState = FIRST_DATA_ROW; + } + else if(lineColumns.size() != colCount) + { + m_errorText->append("CSV file has invalid content (Column count mismatch)"); + return false; + } + else if(parseState == FIRST_DATA_ROW) + { + for (int iCol = 0; iCol < colCount; iCol++) + { + std::string colData = lineColumns[iCol].toStdString(); + ColumnInfo& col = cols[iCol]; + + // Check if text column + if (RiaStdStringTools::isNumber(colData)) + { + col.dataType = ColumnInfo::NUMERIC; + } + else if (tryParseDateTime(colData, parseOptions.dateTimeFormat()).isValid() || + tryParseDateTime(colData, parseOptions.dateFormat_).isValid()) + { + col.dataType = ColumnInfo::DATETIME; + } + else + { + col.dataType = ColumnInfo::TEXT; + } + } + + parseState = DATA_ROW; + } + + if (parseState == DATA_ROW) + { + for (int iCol = 0; iCol < colCount; iCol++) + { + std::string colData = lineColumns[iCol].toStdString(); + ColumnInfo& col = cols[iCol]; + + try + { + if (col.dataType == ColumnInfo::NUMERIC) + { + col.values.push_back(RiaStdStringTools::toDouble(colData)); + } + else if (col.dataType == ColumnInfo::TEXT) + { + col.textValues.push_back(colData); + } + else if (col.dataType == ColumnInfo::DATETIME) + { + QDateTime dt = tryParseDateTime(colData, parseOptions.dateTimeFormat()); + if (!dt.isValid()) + { + dt = tryParseDateTime(colData, parseOptions.dateFormat_); + } + if (!dt.isValid()) throw 0; + col.dateTimeValues.push_back(dt); + } + } + catch (...) + { + m_errorText->append("CSV file has invalid content (Column type mismatch)"); + return false; + } + } + } + } + + TableData td("", "", cols); + m_tableData = td; + return true; + + //std::string stdData = data.toStdString(); + //bool isFixedWidth = RifEclipseUserDataParserTools::isFixedWidthHeader(stdData); + + //std::stringstream streamData; + //streamData.str(stdData); + + //std::vector rawTables; + + //do + //{ + // std::vector errorStrings; + + // TableData table; + + // if (isFixedWidth) + // { + // auto columnInfos = RifEclipseUserDataParserTools::columnInfoForFixedColumnWidth(streamData); + // table = TableData("", "", "", columnInfos); + // } + // else + // { + // table = RifEclipseUserDataParserTools::tableDataFromText(streamData, &errorStrings); + // } + + // if (m_errorText) + // { + // for (auto s : errorStrings) + // { + // QString errorText = QString("\n%1").arg(QString::fromStdString(s)); + // m_errorText->append(errorText); + // } + // } + + // std::vector& columnInfos = table.columnInfos(); + // int columnCount = static_cast(columnInfos.size()); + // if (columnCount == 0) break; + + // int stepTypeIndex = -1; + // for (size_t i = 0; i < columnInfos.size(); i++) + // { + // if (RifEclipseUserDataKeywordTools::isStepType(columnInfos[i].summaryAddress.quantityName())) + // { + // stepTypeIndex = static_cast(i); + // } + // } + + // std::string line; + // std::getline(streamData, line); + + // do + // { + // QString qLine = QString::fromStdString(line); + // QStringList entries = qLine.split(" ", QString::SkipEmptyParts); + + // if (stepTypeIndex > -1 && + // (unsigned int)entries.size() < columnInfos.size()) + // { + // entries.insert(stepTypeIndex, " "); + // } + + // if (entries.size() < columnCount) break; + + // for (int i = 0; i < columnCount; i++) + // { + // if (columnInfos[i].dataType == ColumnInfo::NUMERIC) + // { + // double entry = entries[i].toDouble(); + // columnInfos[i].values.push_back(entry); + // } + // else + // { + // columnInfos[i].textValues.push_back(entries[i].toStdString()); + // } + // } + // } while (std::getline(streamData, line)); + + // rawTables.push_back(table); + + //} while (streamData.good()); + + //m_tableDatas = RifEclipseUserDataParserTools::mergeEqualTimeSteps(rawTables); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QStringList RifCsvUserDataParser::splitLineAndTrim(const QString& line, const QString& separator) +{ + QStringList cols = line.split(separator); + for (QString& col : cols) + { + col = col.trimmed(); + } + return cols; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QDateTime RifCsvUserDataParser::tryParseDateTime(const std::string& colData, const QString& format) +{ + return RiaQDateTimeTools::fromString(QString::fromStdString(colData), format); +} diff --git a/ApplicationCode/FileInterface/RifCsvUserDataParser.h b/ApplicationCode/FileInterface/RifCsvUserDataParser.h new file mode 100644 index 0000000000..fc4516d6bb --- /dev/null +++ b/ApplicationCode/FileInterface/RifCsvUserDataParser.h @@ -0,0 +1,57 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017- Statoil ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "RifEclipseSummaryAddress.h" +#include "RifEclipseUserDataParserTools.h" + +#include "../Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.h" + +#include +#include +#include +#include + +#include + +class ColumnInfo; + +//================================================================================================== +/// +//================================================================================================== +class RifCsvUserDataParser +{ +public: + RifCsvUserDataParser(QString* errorText = nullptr); + + + bool parse(const QString& data, const AsciiDataParseOptions& parseOptions); + const TableData& tableData() const; + + const ColumnInfo* columnInfo(size_t columnIndex) const; + +private: + bool parseData(const QString& data, const AsciiDataParseOptions& parseOptions); + QStringList splitLineAndTrim(const QString& list, const QString& separator); + QDateTime tryParseDateTime(const std::string& colData, const QString& format); + +private: + TableData m_tableData; + QString* m_errorText; +}; diff --git a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp index aa15367a95..c946d767e1 100644 --- a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp +++ b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.cpp @@ -121,10 +121,10 @@ RifEclipseSummaryAddress RifEclipseSummaryAddress::calculatedCurveAddress(const //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -RifEclipseSummaryAddress RifEclipseSummaryAddress::miscAddress(const std::string& quantityName) +RifEclipseSummaryAddress RifEclipseSummaryAddress::importedAddress(const std::string& quantityName) { RifEclipseSummaryAddress fieldAddr; - fieldAddr.m_variableCategory = SUMMARY_MISC; + fieldAddr.m_variableCategory = SUMMARY_IMPORTED; fieldAddr.m_quantityName = quantityName; return fieldAddr; diff --git a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.h b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.h index b0f1a076bd..03b50a9248 100644 --- a/ApplicationCode/FileInterface/RifEclipseSummaryAddress.h +++ b/ApplicationCode/FileInterface/RifEclipseSummaryAddress.h @@ -46,7 +46,8 @@ public: SUMMARY_WELL_SEGMENT, SUMMARY_BLOCK, SUMMARY_BLOCK_LGR, - SUMMARY_CALCULATED + SUMMARY_CALCULATED, + SUMMARY_IMPORTED }; enum SummaryIdentifierType @@ -106,7 +107,7 @@ public: static RifEclipseSummaryAddress fieldVarAddress(const std::string& fieldVarName); static RifEclipseSummaryAddress calculatedCurveAddress(const std::string& curveName); - static RifEclipseSummaryAddress miscAddress(const std::string& quantityName); + static RifEclipseSummaryAddress importedAddress(const std::string& quantityName); // Access methods diff --git a/ApplicationCode/FileInterface/RifEclipseUserDataKeywordTools.cpp b/ApplicationCode/FileInterface/RifEclipseUserDataKeywordTools.cpp index 85d3244f73..e319a3fe4b 100644 --- a/ApplicationCode/FileInterface/RifEclipseUserDataKeywordTools.cpp +++ b/ApplicationCode/FileInterface/RifEclipseUserDataKeywordTools.cpp @@ -155,7 +155,7 @@ RifEclipseSummaryAddress RifEclipseUserDataKeywordTools::makeAndFillAddress(cons if (category == RifEclipseSummaryAddress::SUMMARY_INVALID) { - return RifEclipseSummaryAddress::miscAddress(quantityName); + return RifEclipseSummaryAddress::importedAddress(quantityName); } int regionNumber = -1; diff --git a/ApplicationCode/FileInterface/RifEclipseUserDataParserTools.cpp b/ApplicationCode/FileInterface/RifEclipseUserDataParserTools.cpp index e2be0b1a19..6c2209b3c0 100644 --- a/ApplicationCode/FileInterface/RifEclipseUserDataParserTools.cpp +++ b/ApplicationCode/FileInterface/RifEclipseUserDataParserTools.cpp @@ -494,13 +494,13 @@ TableData RifEclipseUserDataParserTools::tableDataFromText(std::stringstream& st RifEclipseSummaryAddress adr = RifEclipseUserDataKeywordTools::makeAndFillAddress(quantity, columnHeader); - ColumnInfo ci = ColumnInfo::createColumnInfo(quantity, unit, adr); + ColumnInfo ci = ColumnInfo::createColumnInfoFromRsmData(quantity, unit, adr); columnInfos.push_back(ci); } } - return TableData(origin, dateFormat, startDate, columnInfos); + return TableData(origin, startDate, columnInfos); } //-------------------------------------------------------------------------------------------------- @@ -774,7 +774,7 @@ std::vector RifEclipseUserDataParserTools::columnInfoFromColumnHeade RifEclipseSummaryAddress adr = RifEclipseUserDataKeywordTools::makeAndFillAddress(quantity, restOfHeader); - ColumnInfo ci = ColumnInfo::createColumnInfo(quantity, unit, adr); + ColumnInfo ci = ColumnInfo::createColumnInfoFromRsmData(quantity, unit, adr); table.push_back(ci); } @@ -818,9 +818,9 @@ std::vector RifEclipseUserDataParserTools::mergeEqualTimeSteps(const { if (c.summaryAddress.quantityName() == "DATE") { - if (c.stringValues.size() > 0) + if (c.itemCount() > 0) { - firstTableStartTime = RiaDateStringParser::parseDateString(c.stringValues[0]); + firstTableStartTime = RiaDateStringParser::parseDateString(c.textValues[0]); } } } @@ -848,9 +848,9 @@ std::vector RifEclipseUserDataParserTools::mergeEqualTimeSteps(const { if (c.summaryAddress.quantityName() == "DATE") { - if (c.stringValues.size() > 0) + if (c.itemCount() > 0) { - tableFirstTime = RiaDateStringParser::parseDateString(c.stringValues[0]); + tableFirstTime = RiaDateStringParser::parseDateString(c.textValues[0]); } } } @@ -918,38 +918,67 @@ bool RifEclipseUserDataParserTools::isScalingText(const std::string& word) return word.find_first_of('*') != std::string::npos; } +////-------------------------------------------------------------------------------------------------- +///// +////-------------------------------------------------------------------------------------------------- +//ColumnInfo::~ColumnInfo() +//{ +// if (values) +// { +// delete values; +// } +// if (textValues) +// { +// delete textValues; +// } +// if (dateTimeValues) +// { +// delete dateTimeValues; +// } +//} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t ColumnInfo::itemCount() const { - if (isStringData) + switch (dataType) { - return stringValues.size(); + case NUMERIC: return values.size(); + case TEXT: return textValues.size(); + case DATETIME: return dateTimeValues.size(); + default: return 0; } - else - return values.size(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -ColumnInfo ColumnInfo::createColumnInfo(const std::string& quantity, const std::string& unit, const RifEclipseSummaryAddress& adr) +ColumnInfo ColumnInfo::createColumnInfoFromRsmData(const std::string& quantity, const std::string& unit, const RifEclipseSummaryAddress& adr) { ColumnInfo ci(adr, unit); if (RifEclipseUserDataKeywordTools::isDate(quantity)) { - ci.isStringData = true; + ci.dataType = TEXT; } else if (RifEclipseUserDataKeywordTools::isStepType(quantity)) { - ci.isStringData = true; + ci.dataType = TEXT; } return ci; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +ColumnInfo ColumnInfo::createColumnInfoFromCsvData(const RifEclipseSummaryAddress& addr, const std::string& unit) +{ + ColumnInfo col(addr, unit); + return col; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -961,9 +990,9 @@ QDateTime TableData::findFirstDate() const { if (RifEclipseUserDataKeywordTools::isDate(ci.summaryAddress.quantityName())) { - if (ci.stringValues.size() > 0) + if (ci.itemCount() > 0) { - std::string firstDateString = ci.stringValues[0]; + std::string firstDateString = ci.textValues[0]; QDateTime candidate = RiaDateStringParser::parseDateString(firstDateString); if (candidate.isValid()) diff --git a/ApplicationCode/FileInterface/RifEclipseUserDataParserTools.h b/ApplicationCode/FileInterface/RifEclipseUserDataParserTools.h index 4f5765955a..c287186350 100644 --- a/ApplicationCode/FileInterface/RifEclipseUserDataParserTools.h +++ b/ApplicationCode/FileInterface/RifEclipseUserDataParserTools.h @@ -34,9 +34,17 @@ class ColumnInfo { public: + enum DataType + { + NONE, + NUMERIC, + TEXT, + DATETIME + }; + ColumnInfo() : scaleFactor(1.0), - isStringData(false) + dataType(NONE) { } @@ -44,23 +52,28 @@ public: : summaryAddress(adr), scaleFactor(1.0), unitName(unit), - isStringData(false) + dataType(NONE) { } - size_t itemCount() const; + size_t itemCount() const; public: - static ColumnInfo createColumnInfo(const std::string& quantity, const std::string& unit, const RifEclipseSummaryAddress& adr); + static ColumnInfo createColumnInfoFromRsmData(const std::string& quantity, const std::string& unit, const RifEclipseSummaryAddress& adr); + static ColumnInfo createColumnInfoFromCsvData(const RifEclipseSummaryAddress& addr, const std::string& unit); RifEclipseSummaryAddress summaryAddress; std::string unitName; double scaleFactor; + DataType dataType; + + // Data containers std::vector values; - bool isStringData; - std::vector stringValues; + std::vector textValues; + std::vector dateTimeValues; }; + //================================================================================================== /// //================================================================================================== @@ -71,11 +84,9 @@ public: {} TableData(const std::string& origin, - const std::string& dateFormat, const std::string& startDate, const std::vector& columnInfos) : m_origin(origin), - m_dateFormat(dateFormat), m_startDate(startDate), m_columnInfos(columnInfos) { @@ -91,11 +102,6 @@ public: return m_startDate; } - std::string dateFormat() const - { - return m_dateFormat; - } - std::vector& columnInfos() { return m_columnInfos; @@ -110,7 +116,6 @@ public: private: std::string m_origin; - std::string m_dateFormat; std::string m_startDate; std::vector m_columnInfos; diff --git a/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake b/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake index 9bfcbe738c..4790e3d1a8 100644 --- a/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake +++ b/ApplicationCode/ProjectDataModel/Summary/CMakeLists_files.cmake @@ -31,6 +31,7 @@ ${CEE_CURRENT_LIST_DIR}RimCalculatedSummaryCase.h ${CEE_CURRENT_LIST_DIR}RimCalculatedSummaryCurveReader.h ${CEE_CURRENT_LIST_DIR}RimSummaryAddress.h ${CEE_CURRENT_LIST_DIR}RimSummaryCrossPlot.h +${CEE_CURRENT_LIST_DIR}RimCsvUserData.h ) set (SOURCE_GROUP_SOURCE_FILES @@ -60,6 +61,7 @@ ${CEE_CURRENT_LIST_DIR}RimCalculatedSummaryCase.cpp ${CEE_CURRENT_LIST_DIR}RimCalculatedSummaryCurveReader.cpp ${CEE_CURRENT_LIST_DIR}RimSummaryAddress.cpp ${CEE_CURRENT_LIST_DIR}RimSummaryCrossPlot.cpp +${CEE_CURRENT_LIST_DIR}RimCsvUserData.cpp ) list(APPEND CODE_HEADER_FILES diff --git a/ApplicationCode/ProjectDataModel/Summary/RimCsvUserData.cpp b/ApplicationCode/ProjectDataModel/Summary/RimCsvUserData.cpp new file mode 100644 index 0000000000..d2a5bcfa87 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimCsvUserData.cpp @@ -0,0 +1,105 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017- Statoil ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimCsvUserData.h" + +#include "RiaLogging.h" + +#include "RifCsvUserData.h" +#include "RifColumnBasedUserDataParser.h" +#include "RifKeywordVectorUserData.h" +#include "RifSummaryReaderInterface.h" + +#include "cafUtils.h" + +#include + +CAF_PDM_SOURCE_INIT(RimCsvUserData, "RimCsvUserData"); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimCsvUserData::RimCsvUserData() +{ + CAF_PDM_InitObject("Observed CSV Data File", ":/Default.png", "", ""); + m_summaryHeaderFilename.uiCapability()->setUiName("File"); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimCsvUserData::~RimCsvUserData() +{ + +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimCsvUserData::createSummaryReaderInterface() +{ + m_summaryReader = nullptr; + + if (caf::Utils::fileExists(this->summaryHeaderFilename())) + { + QFile file(this->summaryHeaderFilename()); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + RiaLogging::error(QString("Failed to open %1").arg(this->summaryHeaderFilename())); + + return; + } + + QTextStream in(&file); + QString fileContents = in.readAll(); + + RifCsvUserData* csvUserData = new RifCsvUserData(); + if (csvUserData->parse(fileContents, m_parseOptions, &m_errorText)) + { + m_summaryReader = csvUserData; + } + } + else + { + m_summaryReader = nullptr; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifSummaryReaderInterface* RimCsvUserData::summaryReader() +{ + return m_summaryReader.p(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimCsvUserData::errorMessagesFromReader() +{ + return m_errorText; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimCsvUserData::setParseOptions(const AsciiDataParseOptions &parseOptions) +{ + m_parseOptions = parseOptions; +} diff --git a/ApplicationCode/ProjectDataModel/Summary/RimCsvUserData.h b/ApplicationCode/ProjectDataModel/Summary/RimCsvUserData.h new file mode 100644 index 0000000000..4bf3920687 --- /dev/null +++ b/ApplicationCode/ProjectDataModel/Summary/RimCsvUserData.h @@ -0,0 +1,53 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017- Statoil ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "RimObservedData.h" + +#include "../../Commands/SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.h" + +#include "cafPdmObject.h" +#include "cafPdmField.h" +#include "cvfObject.h" + +class RifSummaryReaderInterface; + +//================================================================================================== +// +//================================================================================================== +class RimCsvUserData : public RimObservedData +{ + CAF_PDM_HEADER_INIT; +public: + RimCsvUserData(); + virtual ~RimCsvUserData(); + + virtual void createSummaryReaderInterface() override; + + virtual RifSummaryReaderInterface* summaryReader() override; + + virtual QString errorMessagesFromReader() override; + + void setParseOptions(const AsciiDataParseOptions &parseOptions); + +private: + cvf::ref m_summaryReader; + QString m_errorText; + AsciiDataParseOptions m_parseOptions; +}; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimObservedDataCollection.cpp b/ApplicationCode/ProjectDataModel/Summary/RimObservedDataCollection.cpp index 08646f5ea9..22fe69a2f8 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimObservedDataCollection.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimObservedDataCollection.cpp @@ -22,15 +22,20 @@ #include "RiaApplication.h" #include "RiaLogging.h" +#include "SummaryPlotCommands/RicPasteAsciiDataToSummaryPlotFeatureUi.h" + #include "RifKeywordVectorParser.h" #include "RimObservedData.h" +#include "RimCsvUserData.h" #include "RimObservedEclipseUserData.h" #include "RimSummaryObservedDataFile.h" #include "RiuMainPlotWindow.h" #include "cafUtils.h" +#include "cafPdmSettings.h" +#include "cafPdmUiPropertyViewDialog.h" #include @@ -92,59 +97,24 @@ RimObservedData* RimObservedDataCollection::createAndAddObservedDataFromFileName return nullptr; } - QTextStream in(&file); - QString fileContents = in.readAll(); - - bool eclipseUserData = false; if (fileName.endsWith(".rsm", Qt::CaseInsensitive)) { - eclipseUserData = true; + return createAndAddRsmObservedDataFromFile(file, errorText); } - - if (RifKeywordVectorParser::canBeParsed(fileContents)) + else if (fileName.endsWith(".txt", Qt::CaseInsensitive) || fileName.endsWith(".csv", Qt::CaseInsensitive)) { - eclipseUserData = true; - } - - if (eclipseUserData) - { - RimObservedEclipseUserData* columnBasedUserData = new RimObservedEclipseUserData(); - - observedData = columnBasedUserData; - } - - if (observedData) - { - this->m_observedDataArray.push_back(observedData); - observedData->setSummaryHeaderFileName(fileName); - observedData->createSummaryReaderInterface(); - observedData->updateMetaData(); - observedData->updateOptionSensitivity(); - - if (errorText && !observedData->errorMessagesFromReader().isEmpty()) - { - errorText->append(observedData->errorMessagesFromReader()); - } - - RiuMainPlotWindow* mainPlotWindow = RiaApplication::instance()->getOrCreateAndShowMainPlotWindow(); - if (mainPlotWindow) - { - mainPlotWindow->selectAsCurrentItem(observedData); - mainPlotWindow->setExpanded(observedData); - } - - this->updateConnectedEditors(); + return createAndAddCvsObservedDataFromFile(file, errorText); } else { if (errorText) { - errorText->append("Not able to import file. Make sure '*.rsm' is used as extension if data is in RMS format."); + errorText->append("Not able to import file. Make sure '*.rsm' is used as extension if data is in RMS format or '*.txt' or '*.csv' if data is in CSV format."); } } } - - return observedData; + + return nullptr; } //-------------------------------------------------------------------------------------------------- @@ -158,3 +128,88 @@ std::vector RimObservedDataCollection::allObservedData() return allObservedData; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimObservedData* RimObservedDataCollection::createAndAddRsmObservedDataFromFile(QFile& file, QString* errorText /*= nullptr*/) +{ + RimObservedData* observedData = nullptr; + + if (!file.isOpen()) return nullptr; + + QTextStream in(&file); + QString fileContents = in.readAll(); + + RimObservedEclipseUserData* columnBasedUserData = new RimObservedEclipseUserData(); + observedData = columnBasedUserData; + + this->m_observedDataArray.push_back(observedData); + observedData->setSummaryHeaderFileName(file.fileName()); + observedData->createSummaryReaderInterface(); + observedData->updateMetaData(); + observedData->updateOptionSensitivity(); + + if (errorText && !observedData->errorMessagesFromReader().isEmpty()) + { + errorText->append(observedData->errorMessagesFromReader()); + } + + RiuMainPlotWindow* mainPlotWindow = RiaApplication::instance()->getOrCreateAndShowMainPlotWindow(); + if (mainPlotWindow) + { + mainPlotWindow->selectAsCurrentItem(observedData); + mainPlotWindow->setExpanded(observedData); + } + + this->updateConnectedEditors(); + return observedData; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimObservedData* RimObservedDataCollection::createAndAddCvsObservedDataFromFile(QFile& file, QString* errorText /*= nullptr*/) +{ + RimObservedData* observedData = nullptr; + + if (!file.isOpen()) return nullptr; + + QTextStream in(&file); + QString fileContents = in.readAll(); + + RicPasteAsciiDataToSummaryPlotFeatureUi parseOptionsUi; + parseOptionsUi.setPreviewText(fileContents); + caf::PdmSettings::readFieldsFromApplicationStore(&parseOptionsUi); + + caf::PdmUiPropertyViewDialog propertyDialog(NULL, &parseOptionsUi, "CSV Import Options", ""); + if (propertyDialog.exec() != QDialog::Accepted) + { + return nullptr; + } + + RimCsvUserData* columnBasedUserData = new RimCsvUserData(); + columnBasedUserData->setParseOptions(parseOptionsUi.parseOptions()); + observedData = columnBasedUserData; + + this->m_observedDataArray.push_back(observedData); + observedData->setSummaryHeaderFileName(file.fileName()); + observedData->createSummaryReaderInterface(); + observedData->updateMetaData(); + observedData->updateOptionSensitivity(); + + if (errorText && !observedData->errorMessagesFromReader().isEmpty()) + { + errorText->append(observedData->errorMessagesFromReader()); + } + + RiuMainPlotWindow* mainPlotWindow = RiaApplication::instance()->getOrCreateAndShowMainPlotWindow(); + if (mainPlotWindow) + { + mainPlotWindow->selectAsCurrentItem(observedData); + mainPlotWindow->setExpanded(observedData); + } + + this->updateConnectedEditors(); + return observedData; +} diff --git a/ApplicationCode/ProjectDataModel/Summary/RimObservedDataCollection.h b/ApplicationCode/ProjectDataModel/Summary/RimObservedDataCollection.h index a01f45f156..282ae43138 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimObservedDataCollection.h +++ b/ApplicationCode/ProjectDataModel/Summary/RimObservedDataCollection.h @@ -23,6 +23,7 @@ class RimSummaryCase; class RimObservedData; +class QFile; //-------------------------------------------------------------------------------------------------- /// @@ -40,6 +41,10 @@ public: RimObservedData* createAndAddObservedDataFromFileName(const QString& fileName, QString* errorText = nullptr); std::vector allObservedData(); +private: + RimObservedData* createAndAddRsmObservedDataFromFile(QFile& file, QString* errorText = nullptr); + RimObservedData* createAndAddCvsObservedDataFromFile(QFile& file, QString* errorText = nullptr); + private: caf::PdmChildArrayField m_observedDataArray; }; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryAddress.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryAddress.cpp index b41857cab2..817b2ec90b 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryAddress.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryAddress.cpp @@ -39,6 +39,7 @@ namespace caf addItem(RifEclipseSummaryAddress::SUMMARY_BLOCK, "SUMMARY_BLOCK", "Block"); addItem(RifEclipseSummaryAddress::SUMMARY_BLOCK_LGR, "SUMMARY_BLOCK_LGR", "Lgr-Block"); addItem(RifEclipseSummaryAddress::SUMMARY_CALCULATED, "SUMMARY_CALCULATED", "Calculated"); + addItem(RifEclipseSummaryAddress::SUMMARY_IMPORTED, "SUMMARY_IMPORTED", "Imported"); setDefault(RifEclipseSummaryAddress::SUMMARY_FIELD); } diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.cpp index 42cb6cb51b..a25ab7a2d1 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.cpp @@ -135,6 +135,7 @@ void RimSummaryTimeAxisProperties::setVisibleRangeMin(double value) m_visibleTimeRangeMin = value; m_visibleDateRangeMin = fromDisplayTimeToDate(value); } + auto s = m_visibleDateRangeMin().toString(); } //-------------------------------------------------------------------------------------------------- @@ -153,6 +154,7 @@ void RimSummaryTimeAxisProperties::setVisibleRangeMax(double value) m_visibleTimeRangeMax = value; m_visibleDateRangeMax = fromDisplayTimeToDate(value); } + auto s = m_visibleDateRangeMax().toString(); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.cpp b/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.cpp index 0bfdef9445..fe20ae5cfe 100644 --- a/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.cpp +++ b/ApplicationCode/UserInterface/RiuSummaryCurveDefSelection.cpp @@ -140,7 +140,10 @@ RiuSummaryCurveDefSelection::RiuSummaryCurveDefSelection() : m_identifierFieldsM } }, { RifEclipseSummaryAddress::SUMMARY_CALCULATED, { { new SummaryIdentifierAndField(RifEclipseSummaryAddress::INPUT_VECTOR_NAME) } - } } + } }, + { RifEclipseSummaryAddress::SUMMARY_IMPORTED,{ + { new SummaryIdentifierAndField(RifEclipseSummaryAddress::INPUT_VECTOR_NAME) } + } }, }) { CAF_PDM_InitFieldNoDefault(&m_selectedCases, "SummaryCases", "Cases", "", "", ""); @@ -196,6 +199,8 @@ RiuSummaryCurveDefSelection::RiuSummaryCurveDefSelection() : m_identifierFieldsM CAF_PDM_InitFieldNoDefault(m_identifierFieldsMap[RifEclipseSummaryAddress::SUMMARY_CALCULATED][0]->pdmField(), "CalculatedVectors", "Calculated vectors", "", "", ""); + CAF_PDM_InitFieldNoDefault(m_identifierFieldsMap[RifEclipseSummaryAddress::SUMMARY_IMPORTED][0]->pdmField(), "ImportedVectors", "Imported vectors", "", "", ""); + for (const auto& itemTypes : m_identifierFieldsMap) { for (const auto& itemInputType : itemTypes.second) @@ -656,6 +661,10 @@ void RiuSummaryCurveDefSelection::defineUiOrdering(QString uiConfigName, caf::Pd { summaryiesField = m_identifierFieldsMap[RifEclipseSummaryAddress::SUMMARY_CALCULATED][0]->pdmField(); } + else if (sumCategory == RifEclipseSummaryAddress::SUMMARY_IMPORTED) + { + summaryiesField = m_identifierFieldsMap[RifEclipseSummaryAddress::SUMMARY_IMPORTED][0]->pdmField(); + } caf::PdmUiGroup* summariesGroup = uiOrdering.addNewGroupWithKeyword("Summaries", RiuSummaryCurveDefinitionKeywords::summaries()); if (summaryiesField)