Mapping an Image onto Elevation Data

The following Object Graphics example maps a satellite image from the Los Angeles, California vicinity onto a DEM (Digital Elevation Model) containing the area's topographical features. The realism resulting from mapping the image onto the corresponding elevation data provides a more informative view of the area's topography. The process is segmented into the following three sections:

Note
Data can be either regularly gridded (defined by a 2D array) or irregularly gridded (defined by irregular x, y, z points). Both the image and elevation data used in this example are regularly gridded. If you are dealing with irregularly gridded data, use GRIDDATA to map the data to a regular grid.

Complete the following steps for a detailed description of the process.

Example Code
See elevation_object.pro in the examples/doc/image subdirectory of the IDL installation directory for code that duplicates this example. Run the example procedure by entering elevation_object at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT elevation_object.pro.

Opening Image and Geometry Files

The following steps read in the satellite image and DEM files and display the elevation data.

  1. Select the satellite image:
  2. imageFile = FILEPATH('elev_t.jpg', $ 
       SUBDIRECTORY = ['examples', 'data']) 
    
  3. Import the JPEG file:
  4. READ_JPEG, imageFile, image 
    
  5. Select the DEM file:
  6. demFile = FILEPATH('elevbin.dat', $ 
       SUBDIRECTORY = ['examples', 'data']) 
    
  7. Define an array for the elevation data, open the file, read in the data and close the file:
  8. dem = READ_BINARY(demFile, DATA_DIMS = [64, 64]) 
    
  9. Enlarge the size of the elevation array for display purposes:
  10. dem = CONGRID(dem, 128, 128, /INTERP) 
    
  11. To quickly visualize the elevation data before continuing on to the Object Graphics section, initialize the display, create a window and display the elevation data using the SHADE_SURF command:
  12. DEVICE, DECOMPOSED = 0 
    WINDOW, 0, TITLE = 'Elevation Data' 
    SHADE_SURF, dem  
    

Figure 3-1: Visual Display of the Elevation Data

imgtex01.gif

After reading in the satellite image and DEM data, continue with the next section to create the objects necessary to map the satellite image onto the elevation surface.

Initializing the IDL Display Objects

After reading in the image and surface data in the previous steps, you will need to create objects containing the data. When creating an IDL Object Graphics display, it is necessary to create a window object (oWindow), a view object (oView) and a model object (oModel). These display objects, shown in the conceptual representation in the following figure, will contain a geometric surface object (the DEM data) and an image object (the satellite image). These user-defined objects are instances of existing IDL object classes and provide access to the properties and methods associated with each object class.

Figure 3-2: Conceptualization of Object Graphics Display Example

imgtex02.jpg

Note
The XOBJVIEW utility (described in Mapping an Image Object onto a Sphere (Object Programming)) automatically creates window and view objects.

Complete the following steps to initialize the necessary IDL objects.

  1. Initialize the window, view and model display objects. For detailed syntax, arguments and keywords available with each object initialization, see IDLgrWindow::Init, IDLgrView::Init and IDLgrModel::Init. The following three lines use the basic syntax oNewObject = OBJ_NEW('Class_Name') to create these objects:
  2. oWindow = OBJ_NEW('IDLgrWindow', RETAIN = 2, COLOR_MODEL = 0) 
    oView = OBJ_NEW('IDLgrView') 
    oModel = OBJ_NEW('IDLgrModel') 
    
  3. Assign the elevation surface data, dem, to an IDLgrSurface object. The IDLgrSurface::Init keyword, STYLE = 2, draws the elevation data using a filled line style:
  4. oSurface = OBJ_NEW('IDLgrSurface', dem, STYLE = 2) 
    
  5. Assign the satellite image to a user-defined IDLgrImage object using IDLgrImage::Init:
  6. oImage = OBJ_NEW('IDLgrImage', image, INTERLEAVE = 0, $ 
       /INTERPOLATE) 
     

    INTERLEAVE = 0 indicates that the satellite image is organized using pixel interleaving, and therefore has the dimensions (3, m, n). The INTERPOLATE keyword forces bilinear interpolation instead of using the default nearest-neighbor interpolation method.

Displaying the Image and Geometric Surface Objects

This section displays the objects created in the previous steps. The image and surface objects will first be displayed in an IDL Object Graphics window and then with the interactive XOBJVIEW utility.

  1. Center the elevation surface object in the display window. The default object graphics coordinate system is [–1,–1], [1,1]. To center the object in the window, position the lower left corner of the surface data at [–0.5,–0.5, –0.5] for the x, y and z dimensions:
  2. oSurface -> GETPROPERTY, XRANGE = xr, YRANGE = yr, $ 
       ZRANGE = zr 
    xs = NORM_COORD(xr) 
    xs[0] = xs[0] - 0.5 
    ys = NORM_COORD(yr) 
    ys[0] = ys[0] - 0.5 
    zs = NORM_COORD(zr) 
    zs[0] = zs[0] - 0.5 
    oSurface -> SETPROPERTY, XCOORD_CONV = xs, $ 
       YCOORD_CONV = ys, ZCOORD = zs 
    
  3. Map the satellite image onto the geometric elevation surface using the IDLgrSurface::Init TEXTURE_MAP keyword:
  4. oSurface -> SetProperty, TEXTURE_MAP = oImage, $ 
       COLOR = [255, 255, 255] 
     

    For clearest display of the texture map, set COLOR = [255, 255, 255]. If the image does not have dimensions that are exact powers of 2, IDL resamples the image into a larger size that has dimensions which are the next powers of two greater than the original dimensions. This resampling may cause unwanted sampling artifacts. In this example, the image does have dimensions that are exact powers of two, so no resampling occurs.

    Note
    If your texture does not have dimensions that are exact powers of 2 and you do not want to introduce resampling artifacts, you can pad the texture with unused data to a power of two and tell IDL to map only a subset of the texture onto the surface.

    For example, if your image is 40 by 40, create a 64 by 64 image and fill part of it with the image data:

    textureImage = BYTARR(64, 64, /NOZERO)
    textureImage[0:39, 0:39] = image ; image is 40 by 40
    oImage = OBJ_NEW('IDLgrImage', textureImage)

    Then, construct texture coordinates that map the active part of the texture to a surface (oSurface):

    textureCoords = [[], [], [], []]
    oSurface -> SetProperty, TEXTURE_COORD = textureCoords

    The surface object in IDL 5.6 is has been enhanced to automatically perform the above calculation. In the above example, just use the image data (the 40 by 40 array) to create the image texture and do not supply texture coordinates. IDL computes the appropriate texture coordinates to correctly use the 40 by 40 image.

    Note
    Some graphic devices have a limit for the maximum texture size. If your texture is larger than the maximum size, IDL scales it down into dimensions that work on the device. This rescaling may introduce resampling artifacts and loss of detail in the texture. To avoid this, use the TEXTURE_HIGHRES keyword to tell IDL to draw the surface in smaller pieces that can be texture mapped without loss of detail.

  5. Add the surface object, covered by the satellite image, to the model object. Then add the model to the view object:
  6. oModel -> Add, oSurface 
    oView -> Add, oModel 
    
  7. Rotate the model for better display in the object window. Without rotating the model, the surface is displayed at a 90° elevation angle, containing no depth information. The following lines rotate the model 90° away from the viewer along the x-axis and 30° clockwise along the y-axis and the x-axis:
  8. oModel -> ROTATE, [1, 0, 0], -90 
    oModel -> ROTATE, [0, 1, 0], 30 
    oModel -> ROTATE, [1, 0, 0], 30 
    
  9. Display the result in the Object Graphics window:
  10. oWindow -> Draw, oView  
    

Figure 3-3: Image Mapped onto a Surface in an Object Graphics Window

imgtex03.gif

  1. Display the results using XOBJVIEW, setting the SCALE = 1 (instead of the default value of 1/SQRT3) to increase the size of the initial display:
  2. XOBJVIEW, oModel, /BLOCK, SCALE = 1 
     

    This results in the following display.

    Figure 3-4: Displaying the Image Mapped onto the Surface in XOBJVIEW

    imgtex04.gif

    After displaying the model, you can rotate it by clicking in the application window and dragging your mouse. Select the magnify button, then click near the middle of the image. Drag your mouse away from the center of the display to magnify the image or toward the center of the display to shrink the image. Select the left-most button on the XOBJVIEW toolbar to reset the display.

  3. Destroy unneeded object references after closing the display windows:
  4. OBJ_DESTROY, [oView, oImage] 
     

    The oModel and oSurface objects are automatically destroyed when oView is destroyed.

For an example of mapping an image onto a regular surface using both Direct and Object Graphics displays, see Mapping an Image onto a Sphere.