/*========================================================================= Program: Visualization Toolkit Module: OMFProject.cxx Language: C++ Copyright (c) 1993-2002 Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "OMFProject.h" #include "OMFElement.h" #include "OMFFile.h" #include "OMFHelpers.h" #include "vtkDataArraySelection.h" #include "vtkDataAssembly.h" #include "vtkInformation.h" #include "vtkPartitionedDataSet.h" #include "vtkPartitionedDataSetCollection.h" #include "vtkSmartPointer.h" #include "vtk_jsoncpp.h" #include "vtksys/SystemTools.hxx" #include #include namespace omf { namespace { enum class ElementTypes { PointSetElement, LineSetElement, SurfaceElement, VolumeElement, InvalidElement }; ElementTypes getElementTypeFromSubtype(const Json::Value& json) { std::string subtype; helper::GetStringValue(json, subtype); static const std::unordered_map subtypeMap{ { "point", ElementTypes::PointSetElement }, { "collar", ElementTypes::PointSetElement }, { "blasthole", ElementTypes::PointSetElement }, { "line", ElementTypes::LineSetElement }, { "borehole", ElementTypes::LineSetElement }, { "surface", ElementTypes::SurfaceElement }, { "volume", ElementTypes::VolumeElement } }; auto it = subtypeMap.find(subtype); return it == subtypeMap.end() ? ElementTypes::InvalidElement : it->second; } } // end anon namespace struct OMFProject::ProjectImpl { std::string UID; std::shared_ptr ProjectFile; std::unordered_map> Elements; using ElementMapType = std::unordered_map>; void ProcessElement(const std::string& elementUID, vtkPartitionedDataSetCollection* output, vtkDataArraySelection* selection, bool writeOutTextures, bool columnMajorOrdering) { const auto& element = this->ProjectFile->JSONRoot()[elementUID]; if (element.isNull() || !element.isObject()) { vtkGenericWarningMacro(<< "element is null or not an object"); return; } std::string name; helper::GetStringValue(element["name"], name); if (!selection->ArrayIsEnabled(name.c_str())) { return; } std::pair elementResult; double globalOrigin[3]; helper::GetPointFromJSON(this->ProjectFile->JSONRoot()[this->UID]["origin"], globalOrigin); if (element.isMember("subtype") && element["subtype"].isString()) { auto subtype = getElementTypeFromSubtype(element["subtype"]); switch (subtype) { case ElementTypes::PointSetElement: elementResult = this->Elements.insert( std::make_pair(name, std::make_shared(elementUID, globalOrigin))); break; case ElementTypes::LineSetElement: elementResult = this->Elements.insert( std::make_pair(name, std::make_shared(elementUID, globalOrigin))); break; case ElementTypes::SurfaceElement: elementResult = this->Elements.insert( std::make_pair(name, std::make_shared(elementUID, globalOrigin))); break; case ElementTypes::VolumeElement: elementResult = this->Elements.insert( std::make_pair(name, std::make_shared(elementUID, globalOrigin))); break; default: vtkGenericWarningMacro(<< "subtype " << element["subtype"].asString() << " is not a valid type"); return; } vtkSmartPointer partitionedDS = vtkSmartPointer::New(); elementResult.first->second->ProcessJSON( this->ProjectFile, element, partitionedDS, writeOutTextures, columnMajorOrdering); // names in vtkDataAssembly CANNOT contain spaces or parenthesis vtksys::SystemTools::ReplaceString(name, " ", "_"); vtksys::SystemTools::ReplaceString(name, "(", "_"); vtksys::SystemTools::ReplaceString(name, ")", "_"); // also dashes are supposed to be fine to use in names, but there's a bug, // so replace dashes with underscores as well // see vtk issue #18128 vtksys::SystemTools::ReplaceString(name, "-", "_"); // names can only start with the following (see vtkDataAssembly::IsNodeNameValid) if ((name[0] < 'a' || name[0] > 'z') && (name[0] < 'A' || name[0] > 'Z') && name[0] != '_') { name = "_" + name; } auto assembly = output->GetDataAssembly(); auto node = assembly->AddNode(name.c_str()); vtkIdType pdsIdx = output->GetNumberOfPartitionedDataSets(); assembly->AddDataSetIndex(node, pdsIdx); output->SetPartitionedDataSet(pdsIdx, partitionedDS); output->GetMetaData(pdsIdx)->Set(vtkCompositeDataSet::NAME(), name.c_str()); } } bool GetDataElements(vtkDataArraySelection* selection) { const Json::Value& elements = this->ProjectFile->JSONRoot()[this->UID]["elements"]; if (elements.isNull() || !elements.isArray()) { vtkGenericWarningMacro(<< "Missing elements node under project node"); return false; } for (Json::Value::ArrayIndex i = 0; i < elements.size(); ++i) { if (elements[i].isNull()) { continue; } if (!elements[i].isString()) { vtkGenericWarningMacro(<< "element " << i << " is not a string. It should be a UID."); continue; } std::string uid; helper::GetStringValue(elements[i], uid); const auto& element = this->ProjectFile->JSONRoot()[uid]; std::string name; helper::GetStringValue(element["name"], name); selection->AddArray(name.c_str()); } return true; } }; //------------------------------------------------------------------------------ OMFProject::OMFProject() : Impl(new ProjectImpl()) { } //------------------------------------------------------------------------------ OMFProject::~OMFProject() = default; //------------------------------------------------------------------------------ bool OMFProject::CanParseFile(const char* filename, vtkDataArraySelection* selection) { if (!filename) { vtkGenericWarningMacro(<< "Input filename not specified"); return false; } if (!this->Impl->ProjectFile) { this->Impl->ProjectFile = std::make_shared(); } if (!this->Impl->ProjectFile->OpenStream(filename)) { vtkGenericWarningMacro(<< "Unable to open file " << filename); return false; } // read header to get location where JSON actually starts in file if (!this->Impl->ProjectFile->ReadHeader(this->Impl->UID)) { return false; } if (!this->Impl->ProjectFile->ParseJSON()) { return false; } if (this->Impl->ProjectFile->JSONRoot().empty()) { vtkGenericWarningMacro(<< "root JSON object is empty"); return false; } // all elements are stored in the json with a uid const Json::Value& projRoot = this->Impl->ProjectFile->JSONRoot()[this->Impl->UID]; if (projRoot.isNull()) { vtkGenericWarningMacro(<< "Missing project root node for UID " << this->Impl->UID); return false; } this->Impl->GetDataElements(selection); return true; } //------------------------------------------------------------------------------ bool OMFProject::ProcessJSON(vtkPartitionedDataSetCollection* output, vtkDataArraySelection* selection, bool writeOutTextures, bool columnMajorOrder) { // loop thru elements and add processed elements to output // here we have the higher level elements of the data set // eg line set, point set, surfaces, etc // projRoot["elements"] only contains the uid of these elements const Json::Value& projRoot = this->Impl->ProjectFile->JSONRoot()[this->Impl->UID]; if (projRoot.isNull()) { vtkGenericWarningMacro(<< "Missing project root node for UID " << this->Impl->UID); return false; } std::string name; helper::GetStringValue(projRoot["name"], name); if (name.empty()) { name = "OMF"; } output->GetDataAssembly()->SetRootNodeName(name.c_str()); const Json::Value& elements = projRoot["elements"]; if (elements.isNull() || !elements.isArray()) { vtkGenericWarningMacro(<< "ProcessJSON: Missing elements node under project node"); return false; } for (Json::Value::ArrayIndex i = 0; i < elements.size(); ++i) { if (elements[i].isNull()) { continue; } if (!elements[i].isString()) { vtkGenericWarningMacro(<< "element " << i << " is not a string. It should be a UID."); continue; } std::string uid; helper::GetStringValue(elements[i], uid); this->Impl->ProcessElement(uid, output, selection, writeOutTextures, columnMajorOrder); } return true; } } // end namespace omf