/*============================================================================= Program: Visualization Toolkit Module: vtkGeoImageNode.cxx Copyright (c) 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. =============================================================================*/ /*------------------------------------------------------------------------- Copyright 2008 Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. -------------------------------------------------------------------------*/ #include "vtkObjectFactory.h" #include "vtkGeoImageNode.h" #include "vtkJPEGWriter.h" #include "vtkTexture.h" #include "vtkXMLImageDataReader.h" #include "vtkXMLImageDataWriter.h" vtkStandardNewMacro(vtkGeoImageNode); //---------------------------------------------------------------------------- vtkGeoImageNode::vtkGeoImageNode() { this->Image = vtkSmartPointer::New(); this->Texture = vtkSmartPointer::New(); } //----------------------------------------------------------------------------- vtkGeoImageNode::~vtkGeoImageNode() { } //----------------------------------------------------------------------------- void vtkGeoImageNode::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); } //----------------------------------------------------------------------------- void vtkGeoImageNode::SetImage(vtkImageData* image) { this->Image = image; } //----------------------------------------------------------------------------- vtkImageData* vtkGeoImageNode::GetImage() { return this->Image; } //----------------------------------------------------------------------------- void vtkGeoImageNode::SetTexture(vtkTexture* texture) { this->Texture = texture; } //----------------------------------------------------------------------------- vtkTexture* vtkGeoImageNode::GetTexture() { return this->Texture; } //----------------------------------------------------------------------------- vtkGeoImageNode* vtkGeoImageNode::GetChild(int idx) { if (idx < 0 || idx > 3) { vtkErrorMacro("Index out of range."); return nullptr; } return vtkGeoImageNode::SafeDownCast(this->Children[idx]); } //----------------------------------------------------------------------------- vtkGeoImageNode* vtkGeoImageNode::GetParent() { return vtkGeoImageNode::SafeDownCast(this->Parent); } //----------------------------------------------------------------------------- // We have to get a power of 2 for dimensions of the image. VTK // resamples every time a tile is selected anr changed otherwise. // // We have two choises for dealing with images. // // 1: Treat pixels like cell data. // This makes subsampling easy. Simply use vtkImageShrink3D. // Tile images do not overlap. // Difficult: // Texture mapping is point data. TCoords have to be extended half a pixel. // vtkImageData is point data. Have to handle meta data extenal to object. // Interpolated texture map will have seams between tiles. // 2: Treat pixels like point data. // We would need a new shrink filter that uses a 3x3 kernel. // Tiles would have to duplicate a row of xels with neighbors. // This would make dividing image more difficult with natural pixel boundaries // every 255 pixels (instead of 256). void vtkGeoImageNode::CropImageForTile( vtkImageData* image, double* imageLonLatExt, const char* prefix) { int ext[6]; int wholeExt[6]; // I am keeping this all external to the imageData object because // I consider pixels are cells not points. image->GetExtent(ext); image->GetExtent(wholeExt); double origin[2]; double spacing[2]; spacing[0] = (imageLonLatExt[1]-imageLonLatExt[0])/(ext[1]-ext[0]+1); spacing[1] = (imageLonLatExt[3]-imageLonLatExt[2])/(ext[3]-ext[2]+1); origin[0] = imageLonLatExt[0] - ext[0]*spacing[0]; origin[1] = imageLonLatExt[2] - ext[2]*spacing[1]; // Compute the minimum extent that covers the terrain patch. ext[0] = static_cast(floor((this->LongitudeRange[0]-origin[0])/spacing[0])); ext[1] = static_cast(ceil((this->LongitudeRange[1]-origin[0])/spacing[0])); ext[2] = static_cast(floor((this->LatitudeRange[0]-origin[1])/spacing[1])); ext[3] = static_cast(ceil((this->LatitudeRange[1]-origin[1])/spacing[1])); int dims[2]; dims[0] = this->PowerOfTwo(ext[1]-ext[0]+1); dims[1] = this->PowerOfTwo(ext[3]-ext[2]+1); ext[1] = ext[0] + dims[0] - 1; ext[3] = ext[2] + dims[1] - 1; if (ext[1] > wholeExt[1]) { ext[1] = wholeExt[1]; } if (ext[3] > wholeExt[3]) { ext[3] = wholeExt[3]; } ext[0] = ext[1] - dims[0] + 1; ext[2] = ext[3] - dims[1] + 1; if (ext[0] < wholeExt[0]) { ext[0] = wholeExt[0]; } if (ext[2] < wholeExt[2]) { ext[2] = wholeExt[2]; } if (this->Image == nullptr) { this->Image = vtkSmartPointer::New(); } this->Image->ShallowCopy(image); this->Image->Crop(ext); // Now set the longitude and latitude range based on the actual image size. this->LongitudeRange[0] = origin[0] + ext[0]*spacing[0]; this->LongitudeRange[1] = origin[0] + (ext[1]+1)*spacing[0]; this->LatitudeRange[0] = origin[1] + ext[2]*spacing[1]; this->LatitudeRange[1] = origin[1] + (ext[3]+1)*spacing[1]; // Save out the image to verify we are processing properly if (prefix) { vtkImageData* storedImage = vtkImageData::New(); storedImage->ShallowCopy(this->Image); storedImage->SetOrigin(this->LongitudeRange[0], this->LatitudeRange[0], 0); storedImage->SetSpacing(this->LongitudeRange[1], this->LatitudeRange[1], 0); vtkXMLImageDataWriter* writer = vtkXMLImageDataWriter::New(); char fn[512]; snprintf(fn, sizeof(fn), "%s/tile_%d_%lu.vti", prefix, this->Level, this->Id); writer->SetFileName(fn); writer->SetInputData(storedImage); writer->Write(); writer->Delete(); storedImage->Delete(); } } //----------------------------------------------------------------------------- void vtkGeoImageNode::LoadAnImage(const char* prefix) { vtkXMLImageDataReader* reader = vtkXMLImageDataReader::New(); char fn[512]; snprintf(fn, sizeof(fn), "%s/tile_%d_%lu.vti", prefix, this->Level, this->Id); reader->SetFileName(fn); reader->Update(); vtkImageData* image = reader->GetOutput(); this->Image = image; this->LongitudeRange[0] = this->Image->GetOrigin()[0]; this->LatitudeRange[0] = this->Image->GetOrigin()[1]; this->LongitudeRange[1] = this->Image->GetSpacing()[0]; this->LatitudeRange[1] = this->Image->GetSpacing()[1]; reader->Delete(); } //----------------------------------------------------------------------------- int vtkGeoImageNode::PowerOfTwo(int val) { // Pick the next highest power of two. int tmp; bool nextHigherFlag = false; tmp = 1; while (val) { if ((val & 1) && val > 1) { nextHigherFlag = true; } val = val >> 1; tmp = tmp << 1; } if ( ! nextHigherFlag) { tmp = tmp >> 1; } return tmp; } //----------------------------------------------------------------------------- void vtkGeoImageNode::ShallowCopy(vtkGeoTreeNode *src) { vtkGeoImageNode *imageNode = vtkGeoImageNode::SafeDownCast(src); if(imageNode != nullptr) { this->Image = imageNode->Image; this->Texture = imageNode->Texture; } this->Superclass::ShallowCopy(src); } //----------------------------------------------------------------------------- void vtkGeoImageNode::DeepCopy(vtkGeoTreeNode *src) { vtkGeoImageNode *imageNode = vtkGeoImageNode::SafeDownCast(src); if(imageNode != nullptr) { vtkImageData * image = vtkImageData::New(); image->DeepCopy(imageNode->Image); this->SetImage(image); image->Delete(); image = nullptr; this->Texture = imageNode->Texture; } this->Superclass::DeepCopy(src); } //----------------------------------------------------------------------------- bool vtkGeoImageNode::HasData() { return (this->Image != nullptr); } //----------------------------------------------------------------------------- void vtkGeoImageNode::DeleteData() { this->Image = nullptr; this->Texture = nullptr; }