Select to view content in your preferred language

Arc 9.3 method to write SHORT INT to pixelblock fails on Arc 10

1960
2
02-04-2011 11:21 AM
ErikaDade
Occasional Contributor
Hello-

I have C++ code to write a raster that has been working fine on Arc 9.2/9.3, but only works partially on Arc 10.  The migrated code to write a raster in chunks works on Arc 10 for floating point datatypes, but not for short integer.  The pixel values in the output raster are large negative numbers but the least significant digits vary correctly.  The output raster ends up getting written out as long integer because the values are outside the range of short int.  It seems like the the lower two bytes might be correct, but garbage is getting written into the upper two bytes.  The raster was created with pixel type PT_SHORT.  Why is it getting interpreted as long int?  Can anyone help identify what might have changed in Arc 10 that would make this not work properly anymore?

Thanks for your help.


// ==============================================
// WRITE_GRID_BLOCK
// ==============================================
HRESULT WriteGRIDBlock(SPcontainer* SP, int iTLRow, int iTLCol,
   int PBHeight, int PBWidth, CString sDatatype, float* pDataBlock)
{
// Create a Pixelblock of size Width x Height
IPntPtr ipPntSize(CLSID_DblPnt);
ipPntSize->SetCoords(PBWidth, PBHeight);

IPixelBlockPtr ipPixelBlock;
HRESULT hr = SP->ipRawPixels->CreatePixelBlock(ipPntSize, &ipPixelBlock);
if (FAILED(hr)) { return hr; }

// MUST READ pixelblock before writing to complete initialization
IPntPtr ipOrigin(CLSID_DblPnt);
ipOrigin->put_X(iTLCol);
ipOrigin->put_Y(iTLRow);
hr = SP->ipRawPixels->Read(ipOrigin, ipPixelBlock);
if (FAILED(hr)) { return hr; }

// Read a SafeArray from the PixelBlock
IPixelBlock3Ptr ipPixelBlock3;
hr = ipPixelBlock->QueryInterface(IID_IPixelBlock3, (void**)&ipPixelBlock3);
if (FAILED(hr)) { return hr; }

// =========== From ESRI Support ==============
//VARIANT varArray;
CComVariant varArray;
::VariantInit(&varArray);

hr = ipPixelBlock3->get_PixelDataByRef(0, &varArray);
if (FAILED(hr)) { return hr; }
SAFEARRAY *saArray = *(varArray.pparray);
// ========================================

hr = SafeArrayLock(saArray);
if (FAILED(hr)) { return hr; }

// Input array is row-major (ie row by row, column-order)
// Output Arc array is column-major (upper left 0,0, read down and to right)
if ((sDatatype == "FLOAT") || (sDatatype == "PT_FLOAT")) {
  for(int i = 0; i < PBHeight; i++) {
   for(int j = 0; j < PBWidth; j++)  {
    long ind[2];
    ind[0]=j;
    ind[1]=i;
    float val = pDataBlock[i*PBWidth + j];
    hr = ::SafeArrayPutElement(saArray, ind, (void*)&val);
   }
  }
}
else if ((sDatatype == "SHORT") || (sDatatype == "PT_SHORT")) {
  for(int i = 0; i < PBHeight; i++) {
   for(int j = 0; j < PBWidth; j++)  {
    long ind[2];
    ind[0]=j;
    ind[1]=i;
    short val = (short)pDataBlock[i*PBWidth + j];
    hr = ::SafeArrayPutElement(saArray, ind, (void*)&val);
   }
  }
}

hr = SafeArrayUnlock(saArray);
if (FAILED(hr)) { return hr; }

hr = SP->ipRawPixels->Write(ipOrigin, ipPixelBlock);
if (FAILED(hr)) { return hr; }

::VariantClear(&varArray);

ipOrigin = 0;
ipPntSize = 0;
ipPixelBlock3 = 0;

return hr;
}
0 Kudos
2 Replies
ErikaDade
Occasional Contributor
Looking into this more...

If I change the datatype of the elements written to the safearray to long, it works fine.  The problem appears to be that IRasterWorkspace2->CreateRasterDataset is not creating a PT_SHORT file-based raster.  If I check the pixel type of the raster immediately after creating a short int raster, it returns as PT_LONG. 

Anyone else having difficulty creating short int rasters ?   I am working with VS2010, Win7 x64, ArcGIS 10.  The method worked fine on VS2005, Win XP, ArcGIS 9.3

Thanks for any ideas!
0 Kudos
TimDieterich
Deactivated User
I too, have no small degree of problems writing out SHORT rasterdatasets on ArcGIS 10 (it worked for me also in ArcGIS 9.3).  The best I've been able to do so far is to create a rasterdataset, then copy the file with properties set using the SaveAs functionality.  This is a kludge, as the functionality should work as the interface indicates.  The other wrinkle is that their documentation talks about autocorrecting the pixel type based on the data present.  I've noticed this is often incorrect, but this is why I write out a few sample pixels first.

This is in my open method:
         // This first creation of a raster dataset will result in a 32-bit dataset.  According to the
         // documentation, the resolution is automatically recomputed when adding data to the raster dataset.
         IRasterDataset tempDataset = workspace.createRasterDataset(tempFileName,
                                                            "GRID",
                                                            originPoint,
                                                            width,
                                                            height,
                                                            cellSizeX,
                                                            cellSizeY,
                                                            noOfBands,
                                                            rstPixelType.PT_SHORT,
                                                            geographicCoordinateSystem,
                                                            false);

         //Get the raster band.
         IRasterBandCollection rasterBands = (IRasterBandCollection)tempDataset;
//       IRasterBandCollection rasterBands = new IRasterBandCollectionProxy(tempDataset)
//       ;
         IRasterBand rasterBand = rasterBands.item(0);
         IRasterProps rasterProps = new IRasterPropsProxy(rasterBand);

         //Set NoData if necessary. For a multiband image, NoData value needs to be set for each band.
         if (noDataValue != null)
         {
          rasterProps.setNoDataValue(noDataValue.shortValue());
         }
         else
         {
          rasterProps.setNoDataValue(-500);
         }  // end if

         IRaster raster = tempDataset.createDefaultRaster();

         IRasterProps rasterProps2 = new IRasterPropsProxy(raster);
         rasterProps2.setPixelType(com.esri.arcgis.geodatabase.rstPixelType.PT_SHORT);
         rasterProps2.setSpatialReference(geographicCoordinateSystem);
         if (noDataValue != null)
         {
          rasterProps2.setNoDataValue(noDataValue.shortValue());
         }
         else
         {
          rasterProps2.setNoDataValue(-500);
         }  // end if
         rasterProps2.setHeight(height);
         rasterProps2.setWidth(width);

         // Here we're writing out pixels to the tempDataset to keep the attribution as we want it
         // when copied in the subsequent save as command
         writeOutTokenPixels(raster);

         IWorkspace tempWorkspace = new IWorkspaceProxy(rwf.openFromFile(this.workspaceName,
                 0));
         ISaveAs saveAs = new ISaveAsProxy(rasterProps2);
//         ISaveAs saveAs = new ISaveAsProxy(rasterBands);
         saveAs.saveAs(gridFileName, tempWorkspace, "GRID");

         // We need to open the newly copied raster to write a few pixels out to it...
        this.rasterDataset = workspace.openRasterDataset(gridFileName);
//         raster = this.rasterDataset.createDefaultRaster();
        IRasterDataset2 rasterDataset2 = (IRasterDataset2)this.rasterDataset;
        IRaster2 rasterF = new IRaster2Proxy(rasterDataset2.createFullRaster());
        raster = new IRasterProxy(rasterF);
        
         writeOutTokenPixels(raster);

         EsriCleanerWrapper.releaseObject(this.rasterDataset);
         this.rasterDataset = null;


This the logic in my write function:
       double computedX = GridFileReader.determineXOffsetFromExtents(chunkExtent, this.cellSizeX, this.fileBounds);
       double computedY = GridFileReader.determineYOffsetFromExtents(chunkExtent, this.cellSizeY, this.fileBounds);

       //Create a raster.
       IRasterDataset2 rasterDataset2 = (IRasterDataset2)rasterDataset;
       IRaster2 raster2 = new IRaster2Proxy(rasterDataset2.createFullRaster());
//       if (this.raster2 == null)
//       {
//           this.raster2 = new IRaster2Proxy(rasterDataset2.createFullRaster());
//       }  // end if raster2
          //Create a pixel block.
          IPnt blocksize = new DblPnt();
          blocksize.setCoords(width, height);

          // Allocate the rasterCursor if not already allocated
          if (this.rasterCursor == null)
          {
             this.rasterCursor = raster2.createCursorEx(blocksize);
          } // end if rasterCursor

          // Move the cursor to the desired location
          boolean foundChunk = GridFileReader.moveCursorToChunk(this.rasterCursor, computedX,
                                                 computedY);

          if (foundChunk)
          {
           //Use the IRasterEdit interface to update the raster
           IRasterEdit rasterEdit = new IRasterEditProxy(raster2);

           IPnt tlc = this.rasterCursor.getTopLeft();
        double tlcX = tlc.getX();
        double tlcY = tlc.getY();

              logWriter.debug("For computed=" + computedX + "," + computedY +
                         ",tlc=" + tlcX + "," + tlcY);

           IPixelBlock3 pixelblock3 = new IPixelBlock3Proxy(this.rasterCursor.getPixelBlock());
           int blockwidth = pixelblock3.getWidth();
           int blockheight = pixelblock3.getHeight();
           pixelblock3.setPixelType(0, com.esri.arcgis.geodatabase.rstPixelType.PT_SHORT);

           //pixelblock3.mask(255);
           //Get the pixel array.
           short[][] pixels = (short[][])pixelblock3.getPixelData(0);
           for (int i = 0; i < blockheight; i++){
            for (int j = 0; j < blockwidth; j++)
            {
             //Get the pixel value from the Object pixels.
             pixels = rasterData;
            }  // for j
           }  // for i

           //Set the pixel array to the pixel block.
           pixelblock3.setPixelData(0, pixels);

           IPixelBlock pblock = new IPixelBlockProxy(pixelblock3);
           pblock.setPixelType(0, com.esri.arcgis.geodatabase.rstPixelType.PT_SHORT);

           //Write back to the raster.
           rasterEdit.write(tlc, pblock);
           rasterEdit.refresh();

           EsriCleanerWrapper.releaseObject(rasterEdit);
           rasterEdit = null;

          }
          else
          {
             throw new CoreSystemException("LOGIC ERROR:  Could not find chunk within GRID file");
          }


Hope this helps someone...
0 Kudos