126 if(input->IsA(
"vtkDataSet")) {
128 vtkXMLDataObjectWriter::NewWriter(input->GetDataObjectType()));
129 }
else if(input->IsA(
"vtkMultiBlockDataSet")) {
135 xmlWriter->SetDataModeToAppended();
136 xmlWriter->SetCompressorTypeToZLib();
137 const auto compressor
138 = vtkZLibDataCompressor::SafeDownCast(xmlWriter->GetCompressor());
139 if(compressor !=
nullptr)
140 compressor->SetCompressionLevel(this->CompressionLevel);
142 std::string
const productExtension = this->Format ==
FORMAT::VTK
143 ? xmlWriter->GetDefaultFileExtension()
151 inputFD->ShallowCopy(input->GetFieldData());
152 size_t nFields = inputFD->GetNumberOfArrays();
158 std::vector<std::string> toIgnore;
161 toIgnore.emplace_back(
"FILE");
164 for(
size_t i = 0; i < nFields; i++) {
165 std::string
const name(inputFD->GetArrayName(i));
166 if(name.substr(0, 4).compare(
"_ttk") == 0)
167 toIgnore.emplace_back(name);
171 for(
const auto &name : toIgnore)
172 inputFD->RemoveArray(name.data());
174 nFields = inputFD->GetNumberOfArrays();
180 std::string productId;
181 std::string rDataProductPath;
182 std::vector<std::string> fields;
183 std::vector<std::string> values;
189 for(
size_t i = 0; i < nFields; i++) {
190 auto array = inputFD->GetAbstractArray(i);
191 auto n = array->GetNumberOfTuples();
192 auto m = array->GetNumberOfComponents();
198 for(
int j = 0; j < n; j++) {
199 for(
int k = 0; k < m; k++) {
200 value += array->GetVariantValue(j * m + k).ToString() +
"|";
202 value = value.substr(0, value.size() - 1);
205 value = value.substr(0, value.size() - 1);
208 fields.emplace_back(array->GetName());
209 values.emplace_back(value);
212 productId = values[0];
213 for(
size_t i = 1; i < nFields; i++)
214 productId +=
"_" + values[i];
217 rDataProductPath =
"data/" + productId +
"." + productExtension;
222 std::vector<std::vector<std::string>> rows(nFields + 1);
223 for(
size_t i = 0; i < nFields; i++)
224 rows[i] = {fields[i], values[i]};
225 rows[nFields] = {
"Key", productId};
235 std::string lockFilePath;
239 boost::interprocess::file_lock flock;
241 flock = boost::interprocess::file_lock(lockFilePath.data());
243 }
catch(boost::interprocess::interprocess_exception &) {
246 std::string
const csvPath = this->DatabasePath +
"/data.csv";
252 if(stat(csvPath.data(), &info) != 0) {
257 std::ofstream csvFile;
258 csvFile.open(csvPath.data());
259 if(!csvFile.is_open()) {
260 this->
printErr(
"Unable to create 'data.csv' file.");
265 std::string firstRow;
266 for(
size_t i = 0; i < nFields; i++) {
267 header += fields[i] +
",";
268 firstRow += values[i] +
",";
271 firstRow += rDataProductPath;
273 csvFile << header << endl << firstRow << endl;
294 reader->SetFileName(csvPath.data());
295 reader->DetectNumericColumnsOff();
296 reader->SetHaveHeaders(
true);
297 reader->SetFieldDelimiterCharacters(
",");
299 auto readerOutput = vtkTable::SafeDownCast(reader->GetOutput());
301 this->
printErr(
"Unable to read 'data.csv' file.");
305 csvTable->ShallowCopy(readerOutput);
312 std::vector<vtkStringArray *> fieldToCSVColumnMap(nFields);
313 size_t const nRows = csvTable->GetNumberOfRows();
314 size_t const nColumns = csvTable->GetNumberOfColumns();
319 "Empty 'data.csv' file (vtkDelimitedTextReader limitation).");
324 for(
size_t i = 0; i < nColumns; i++) {
325 std::string
const columnName = csvTable->GetColumnName(i);
328 if(columnName.compare(
"FILE") == 0)
332 for(
size_t j = 0; j < nFields; j++) {
333 if(fields[j].compare(columnName) == 0)
337 this->
printErr(
"'data.csv' file contains column '" + columnName
338 +
"' not present in field data.");
339 this->
printErr(
"Unable to insert data product into cinema database.");
345 for(
size_t i = 0; i < nFields; i++) {
346 auto column = vtkStringArray::SafeDownCast(
347 csvTable->GetColumnByName(fields[i].data()));
349 this->
printErr(
"Data product has field data array '" + fields[i]
350 +
"' no recorded in the data.csv file.");
353 fieldToCSVColumnMap[i] = column;
359 = vtkStringArray::SafeDownCast(csvTable->GetColumnByName(
"FILE"));
361 this->
printErr(
"'data.csv' file has no 'FILE' column");
369 std::vector<int> rowsToDelete;
370 for(
size_t i = 0; i < nRows; i++) {
372 for(
size_t j = 0; j < nFields; j++)
373 if(values[j].compare(fieldToCSVColumnMap[j]->GetValue(i)) != 0)
376 rowsToDelete.emplace_back(i);
379 if(rowsToDelete.size() > 0) {
381 this->
printMsg(
"Deleting products with same keys", 0,
385 for(
int i = rowsToDelete.size() - 1; i >= 0; i--) {
386 auto path = fileColumn->GetValue(rowsToDelete[i]);
389 remove((this->DatabasePath +
"/" + path).data());
392 csvTable->RemoveRow(rowsToDelete[i]);
395 this->
printMsg(
"Deleting products with same keys", 1,
409 size_t const rowIndex = csvTable->GetNumberOfRows();
410 csvTable->InsertNextBlankRow();
412 for(
size_t j = 0; j < nFields; j++)
413 fieldToCSVColumnMap[j]->SetValue(rowIndex, values[j]);
415 fileColumn->SetValue(rowIndex, rDataProductPath);
419 csvWriter->SetUseStringDelimiter(
false);
420 csvWriter->SetFileName((this->DatabasePath +
"/data.csv").data());
421 csvWriter->SetInputData(csvTable);
435 this->
printMsg(
"Writing data product to disk", 0,
438 switch(this->Format) {
441 xmlWriter->SetFileName(
442 (this->DatabasePath +
"/" + rDataProductPath).data());
443 xmlWriter->SetInputData(input);
448 auto inputAsID = vtkImageData::SafeDownCast(input);
450 this->
printErr(
"PNG format requires input of type 'vtkImageData'.");
457 auto inputPD = inputAsID->GetPointData();
458 for(
int i = 0; i < inputPD->GetNumberOfArrays(); i++) {
459 auto array = inputPD->GetAbstractArray(i);
460 if(array->IsA(
"vtkUnsignedCharArray")) {
461 inputPD->SetActiveScalars(inputPD->GetArrayName(i));
468 this->
printErr(
"Input image does not have any color array.");
474 imageWriter->SetCompressionLevel(this->CompressionLevel);
475 imageWriter->SetFileName(
476 (this->DatabasePath +
"/" + rDataProductPath).data());
477 imageWriter->SetInputData(inputAsID);
478 imageWriter->Write();
483 if(!input->IsA(
"vtkImageData")) {
485 "Cannot use Topological Compression without a vtkImageData");
489 const auto inputData = vtkImageData::SafeDownCast(input);
490 const auto sf = this->GetInputArrayToProcess(0, inputData);
492 vtkNew<ttkTopologicalCompressionWriter> topologicalCompressionWriter{};
493 topologicalCompressionWriter->SetInputArrayToProcess(
494 0, 0, 0, 0, sf->GetName());
496 topologicalCompressionWriter->SetTolerance(this->Tolerance);
497 topologicalCompressionWriter->SetMaximumError(this->MaximumError);
498 topologicalCompressionWriter->SetZFPTolerance(this->ZFPTolerance);
499 topologicalCompressionWriter->SetCompressionType(this->CompressionType);
500 topologicalCompressionWriter->SetSQMethodPV(this->SQMethodPV);
501 topologicalCompressionWriter->SetZFPOnly(this->ZFPOnly);
502 topologicalCompressionWriter->SetSubdivide(this->Subdivide);
503 topologicalCompressionWriter->SetUseTopologicalSimplification(
504 this->UseTopologicalSimplification);
507 if(sf->GetNumberOfComponents() != 1) {
508 vtkErrorMacro(
"Input scalar field should have only 1 component");
511 topologicalCompressionWriter->SetDebugLevel(this->
debugLevel_);
512 topologicalCompressionWriter->SetFileName(
513 (this->DatabasePath +
"/" + rDataProductPath).data());
514 topologicalCompressionWriter->SetInputData(inputData);
515 topologicalCompressionWriter->Write();
519 this->
printErr(
"Unsupported Format");
526 this->
printMsg(
"Wrote " + productId +
"." + productExtension);
532 vtkInformationVector **inputVector,
533 vtkInformationVector *outputVector) {
538 std::string format = this->Format ==
FORMAT::VTK ?
"VTK"
541 this->
printMsg({{
"Database", this->DatabasePath},
542 {
"C. Level", std::to_string(this->CompressionLevel)},
544 {
"Iterate", this->IterateMultiBlock ?
"Yes" :
"No"}});
551 auto input = vtkDataObject::GetData(inputVector[0]);
552 auto output = vtkDataObject::GetData(outputVector);
553 if(this->ForwardInput)
554 output->ShallowCopy(input);
561 std::string lockFilePath;
565 boost::interprocess::file_lock flock;
567 flock = boost::interprocess::file_lock(lockFilePath.data());
569 }
catch(boost::interprocess::interprocess_exception &) {
575 if(ensureFolder(this->DatabasePath) == 0) {
576 this->
printErr(
"Unable to open/create cinema database.");
580 if(ensureFolder(this->DatabasePath +
"/data") == 0) {
581 this->
printErr(
"Unable to open/create cinema database.");
586 auto inputAsMB = vtkMultiBlockDataSet::SafeDownCast(input);
587 if(this->IterateMultiBlock && inputAsMB) {
588 size_t const n = inputAsMB->GetNumberOfBlocks();
589 for(
size_t i = 0; i < n; i++)
597 std::string resultString =
"Complete (#products: ";
598 resultString += !this->IterateMultiBlock || !inputAsMB
600 : std::to_string(inputAsMB->GetNumberOfBlocks());