Home Tour Gallery Demo Order Plug-ins Testimonials Sitemap Help Dev Dev Dev
Heightfield Clipboard Format
Last updated: August 5, 2006

 

Introduction
Design Goals
Tag Structure
Minimal Tag Set
Tag Details
Clipboard Parser Pseudocode
Current Issues
Schedule of Operations
Contributors



Introduction

The following is the current specification for the public clipboard format used by Leveller to exchange elevation data. Such formats allow applications to easily exchange data via standard Cut/Copy/Paste operations, and help developers avoid having to write or otherwise implement file translators.

Documentation on experimental/upcoming features appears in a gray typeface, like this paragraph.

Leveller 2.2 adds public clipboard support using (under Windows) the CF_TEXT format. However, this is a very simple tab-delimited text representation of elevation data. Not only is the interpretation of the data subjective, but since each elevation is expressed using ASCII digits in floating-point form, siginficant memory is used. A binary format, however, requires careful design in order to be portable and scalable (Leveller's own private clipboard format is unsuitable since it is closely tied to the way Leveller represents heightfield data).

Related data types such as bumpmaps are not covered by this proposal (although elevation data can be used to convey a bumpmap by having the receiver use the elevations to compute surface normals).

 
Design Goals

  • Use a tagged, hierarchical, order-insensitive block system to store all information. Data of interest can be easily located by name instead of field offsets, item storage order is unimportant, and new items can be easily added (and existing items expanded). XML was considered but a) typical XML parsers are complicated, and b) XML is normally text-only. Ideally, the format parser should be lightweight and simple, something one could write in a day if needed. Both the reference parser and generator currently take a single C++ class whose code measures less than 500 lines.
     
  • The elevation data is primarily stored in a regular row/column structure of values sharing one described heixel format. One assumption is that the heixels are organized like a top-down bitmap. Terrain data, of course, can be mapped to any arbitrary set of realworld coordinates through the coordsys tag.
     
  • Define a set of base tags to support the minimum functionality, which all applications could assume to be present. This functionality would provide the signature, elevation values, their heixel (sample) extents, and the heixel format. The tags are described below.
     
  • Avoid defining a comprehensive set of tags up front. Otherwise, complexity creeps in until the format becomes more trouble to use than it's worth. People wanting to simply exchange elevations should be able to do so simply. Complexity should be reserved for those wanting to do complex things (e.g., GIS data).
     
  • The format would first be used in a preliminary test phase between willing parties, to work out design errors, add mandatory items, and so on. The format would not be treated as truly public, would not use the intended public format name, and no one would be obliged to support it or "fix" their products to meet compliance with it.
     
  • Once officially released, the test parties wishing to use the public version would modify and rebuild their applications to switch over to the latest design and public format name.
     
  • After release, all subsequent changes to the format must not cause data in older-format blocks to be dependant on data in newer blocks. This lets a) older applications safely ignore any unknown tags and b) lets older applications, if they are so programmed, to store such blocks and write them back, thus leaving newer information unharmed.
     
  • Newer applications must always write out the minimum mandatory set of tags. This way, old applications can always paste something, even if it's just raw elevations.
     
  • Daylon Graphics is providing and maintaining the initial read/write sample source code for ANSI C++ compilers, as part of the Leveller SDK.
     

 
Tag Structure

The tag structure is encapsulated by the provided read/write code, for those who don't need or want to worry about the details. Once you have a pointer to the clipboard data and its length, you can just provide them to the code and it will take it from there.

The data block on the clipboard is an unpadded array of variable-sized tag blocks. Every tag starts with the following structure. The tag's value data is stored immediately after the structure.


  {
    char               szName[16];    // Tag's name, including null terminator.
    unsigned int32   valueSize;     // Size of value, in bytes.
    unsigned int32   reserved;      // In case we need blobs over 4GB.
                                      // For now, must be zero.

    unsigned int32   tagSize;       // Size of tag, in bytes.
    unsigned int32   reserved;      // In case we need tags over 4GB.
                                      // For now, must be zero.

    __int16            relationFlags; // Relationship of next tag
                                      // to this one.

    __int16            valueKind;     // Type of data.
  }

  relationFlags is a combination of the bitflags
  TAGRELATION_CHILD and TAGRELATION_SIBLING. If both are 
  set, it means the next tag is a child, but after all 
  the children, the next tag is a sibling.

  If valueKind is a scalar, and valueSize is greater than 
  sizeof(valueKind), then the value is a scalar array 
  of valueSize / sizeof(valueKind) elements.

  tagSize is not currently used by the clipboard format, 
  but is available to improve tag skipping performance for 
  file-based implementations. After writing all the tags, 
  one iterates through them and computes their aggregate sizes 
  (i.e., the size of a tag and all of its children) and 
  updates the tagSize members.

 
Minimal Tag Set


    Types are: c (char), i16, i32, i64, f (float), d (double), b (binary).
    A "u" prefix means type is unsigned.
    A "[]" suffix denotes an array.

    When type is not specified, tag is a parent or empty.
    
    Tag names are limited to fifteen singlebyte characters, 
    ASCII codepoints 32 through 127. Case-sensitive.

    Endianness of all tag values is the same as the tag structure itself, 
    i.e., matching the host platform.


    Tag                  Type  Description
    -------------------  ----- ----------------------------------------------------
    hf01                       Format and endianness identifier, when equal to host.
      OR  
    hf10                       Format and endianness identifier, when unequal to host.

    [header]
      [version]          ui32  Version number; useful for apps that want to 
                               save time by not looking for secondary tags
                               that don't exist in certain versions.
    
    body
      heixels
        extents
          width          ui32  Number of columns in heightfield.
          breadth        ui32  Number of rows in heightfield.
          [lowest]       b     Lowest elevation, in heixelformat.
          [highest]      b     Highest elevation, in heixelformat

          (Note: tags below marked with '*' should be omitted since 
          they are superceded by body/coordsys).
           
          [scale]*             Relative scaling along 3D axes.
            [x]*         d     West-east direction.
            [y]*         d     Up-down.
            [z]*         d     North-south.
          [size]*              Absolute size of extents, in meters.
            width*       d
            breadth*     d
            span*        d

        format
          depth          ui32  Bits per heixel.
          [fp]           i32   TRUE if heixels are floating-point (IEEE 754).
          [unsigned]     i32   FALSE if topmost bit is sign bit.
          [fracsize]     ui32  If not floating-point, number of bits in fractional 
                               part of possible fixed-point representation.

        [readoffsets]
          [first]        ui32  Bit offset from data start to first heixel.
          [column]       ui32  Bit offset between adjacent heixels.
          [row]          ui32  Bit offset between adjacent scanlines.

        [specialelevs]
          [void]         b     Value of "void" heixel, in heixel format.
                               If not present, no heixels are void.

        data             b     Elevation data, taking
                               depth * width * breadth / 8,
                               readoffsets/column * width * breadth / 8, or
                               readoffsets/row * breadth / 8 bytes (rounded up).


      [alpha]                  Alpha channel. If ommitted, all heixels are pasteable.
                               100% transparency = maximum alpha value.
        [extents]
          [width]        ui32  Number of columns in alpha mask.
          [breadth]      ui32  Number of rows in alpha mask.
          [offsets]            Where mask overlays heightfield. Assume (0, 0).
            [column]     ui32  Which heightfield column first mask column maps to.
            [row]        ui32  Which heightfield row first mask row maps to.

        [format]
          [depth]        ui32  Bits per pixel (8 is assumed).
          [fp]           i32   TRUE if pixels are floating-point (IEEE 754).

        [readoffsets]
          [first]        ui32  Bit offset from data start to first pixel.
          [column]       ui32  Bit offset between adjacent pixels.
          [row]          ui32  Bit offset between adjacent scanlines.


        data             b     Alpha mask pixel data.


      [coordsys]               Coordinate system.
        [geometry]       ui32    Base shape (0=flat, 1=planet Earth).
        [projection]             Shape/projection mapping.
          [format]       ui32      Projection format (0=WKT if geometry=1).
          data           b         Projection string.
        [pixelmapping]           Raster-to-projection mapping.
          units          ui32      Daylon UOM code (refer to daylon_mcodes.h)
          [transform]              Affine transform matrix.
            [origin]
              [x]        d
              [z]        d
            [scale]
              [x]        d
              [z]        d
        [altitude]               Elevation scaling.
          units          ui32      Daylon UOM code (refer to daylon_mcodes.h)
          [scale]        d         Raw-to-units scale.
          [offset]       d         Post-scaling offset.

      [privateuse]             Private use scope. Tags here should be scoped 
                               by organization's name.

    [trailer]                  Reserved tagname.
      [checksum]               Reserved tagname.

 
Tag Details

The following is an explanation of each tag, in the order listed above.

Tag: "hf01"
Type: void
Purpose: Tagset signature and endianness specifier

This is the only tag whose physical placement matters; since its name forms the format signature, it must be at the start of the memory block (or file) holding the tagset. It is also the only tag that can be guaranteed to be readable without a normal tag search, since its position (zero) is known in advance.

The first two bytes of the tag's name must be "hf". The next two are "01" written by assigning the 16-bit value 0x3031 into the tagname, e.g.:


    *((__int16*)&szTagname[2]) = 0x3031;

On Intel (little-endian) platforms, this will create a tagname of "hf10". If, when reading the 16-bit value back by assigning the tagname's third and fourth bytes to an __int16, and you get 0x3130 instead, then the rest of the data stream needs to be byteswapped.

 
Tag: "header/version"
Type: unsigned int32
Purpose: Ordinal format version number

This optional field is provided for tag writers who want to provide a version hint to other readers. If one or more tags were added under a specific version, then testing for that version is usually easier (and always faster) than searching for those tags and discovering they are not present.

The value, if set, is currently zero. The maximum version number possible is incremented by one whenever a new set of tags is defined in the format. Tag definitions are currently managed by Daylon Graphics.

 
Tag: "body"
Type: parent
Purpose: Analogous to HTML <BODY> tag; holds main information.

 
Tag: "body/heixels"
Type: parent
Purpose: Holds tags pertaining to heixel (heightfield element) information.

 
Tag: "body/heixels/extents"
Type: parent
Purpose: Holds tags pertaining to heightfield size.

Advanced sizing information such as geospatial coordinate systems and projections, because it is not heixel-centric, is stored elsewhere.

 
Tag: "body/heixels/extents/width"
Type: unsigned int32
Purpose: Heightfield width, in heixels (i.e., columns).

The number of heixels in each heightfield scanline. Since each heixel maps to a mesh vertex when rendering, the number of mesh patches between vertices is width less one. For the same reason, although a heightfield having a width of one heixel is meaningful informationally, it is not visible when rendered (except perhaps as a line).

 
Tag: "body/heixels/extents/scale"
Type: parent
Purpose: Relative size of heightfield extents.

In the absence of size information, Leveller assumes that heixels and their elevations occupy unit voxel space, where equal values imply the same distance along all three spatial dimensions. A 100 x 100 heightfield with a span of 99, for example, occupies a perfect cube. However, since elevation values are not always in the same space as heixel counts, providing other measurement info is necessary to avoid slope distortion, or vertical under- or overexaggeration.

The "scale" tag provides a 3D scaling transform. If the children are <1.0, 1.0, 1.0>, then the size implied by the width/breadth heixel counts with unit cube voxels is used. For example, a 200 x 100 heightfield would be twice as wide as its breadth. If a scale tag of <0.5, 1.0, 1.0> or <1.0, 1.0, 2.0> were provided, however, the heightfield would be square. Omitted child tags are assumed to be 1.0. When pasting, Leveller resamples scaled heightfields upwards to avoid data loss. For either of the above child tag values, Leveller would scale the pasted heightfield to 200 x 200.

The alpha channel, if present, is scaled also.

 
Tag: "body/heixels/extents/size"
Type: parent
Purpose: Absolute size of heightfield extents, in meters.

This optional tag specifies a real-world size for the heightfield. All three extents must be specified. Although you can also provide a "scale" tag, it will be overridden by "size" by applications that use it.

If you only know how far apart two adjacent heixels are, then the extent would be that distance multiplied by all the heixels along that axis less one. Or, in forumla notation:


    extent = distance × (heixels - 1)

To a program only interested in heixels, real-world size has no meaning. Since world units in Leveller 2.2 can have measurement labels attached, however, Leveller will use the size info to make pasted heixels fit the real-world size of the receiving heightfield. This may require resampling of the heixels.

A situation where resampling would not be needed, for example, is if the Leveller document's world unit label is "metres", and the pasted heightfield happens to have a heixel distance that matches the document's world scale. To know in advance if resampling would occur, you need to determine heixel distance, which is the inverse of the previous formula:


    distance = extent ÷ (heixels - 1)

The upward resampling Leveller uses to avoid data loss with scaled heightfields is overridden if a "size" tag is used, because the whole idea of a real-world size is to force only one interpretation of the heightfield's extents. An extreme example would be if a pasted heightfield measured 100 meters, but the Leveller document had a world spacing of 100 meters: the pasted heightfield would be resampled into a single heixel.

Since Leveller works with heixels, a slight loss of precision will occur if the scaled heightfield does not fit evenly on a heixel boundary.

The alpha channel, if present, is scaled also.

 
Tag: "body/heixels/format/depth"
Type: unsigned int32
Purpose: Number of bits per heixel (mandatory).

This value specifies how much precision each heixel encodes, including the sign bit, if the unsigned tag is false.

Leveller currently handles bit depths of 8, 16, 32, and 64. 64-bit heixels must be floating-point since they are interpreted as double-precision numbers. Bit depths of 16, 32, and 64 are treated as endian-sensitive.

 
Tag: "body/heixels/format/fp"
Type: unsigned int32
Purpose: Floating-point heixel indication.

If the value is one, heixels of 32/64 bits are treated as floating-point numbers, and the unsigned tag is ignored. Otherwise, they are interpreted as integers.

 
Tag: "body/heixels/format/unsigned"
Type: unsigned int32
Purpose: Unsigned heixel indication.

If the value is one, integer heixels are composed of value bits only. Otherwise, the most significant bit is the sign bit.

 
Tag: "body/heixels/format/fracsize"
Type: unsigned int32
Purpose: Fractional bitdepth of fixed-point heixels.

If integer heixels are fixed-point, this tag indicates how many of the least significant bits represent the fractional portion. The remaining bits represent the whole number portion.

 


Tag: "body/coordsys"
Type: parent
Purpose: Coordinate system data.

This tag contains several subtags that define how the heightfield maps to a geometric shape of possibly realworld size. It can be used to supercede the information in the body/heixels/extents/scale and body/heixels/extents/size tags.

 
Tag: "body/coordsys/geometry"
Type: parent
Purpose: Coordinate system base shape descriptor.

This tag defines onto which base shape the heightfield is mapped onto (e.g., flat plane, sphere, cone, planetary body, etc.). For most shapes, projections are simple since the parameters are few (such as sphere radius) and are handled as straightforward UV mappings. For code 1 (planet Earth), projections require more definition.

 
Tag: "body/coordsys/projection"
Type: parent
Purpose: Maps from the base shape to a linear 2D projected space.

This tag contains the parameters on how coordinates on the base shape are represented in projection space. This can be omitted for geometry code zero (flat plane). For geometry code 1 (planet Earth), the "format" subtag indicates what format the "data" subtag uses (e.g., 0 for Well Known Text in 8-bit ASCII).

 
Tag: "body/coordsys/pixelmapping"
Type: parent
Purpose: Maps from pixel (raster) space to projection space.

For geometry code 1 (planet Earth), projection space is often latitude/longitude or a UTM zone. The subtags are basically an affine transform matrix that convert pixel coordinates (whose origin is at the northwest corner) to projection coordinates. The units subtag indicates the realworld measure for ground coordinates (which can differ from elevation units).

 
Tag: "body/coordsys/pixelmapping/units"
Type: unsigned int32
Purpose: Defines the measurement units for the groundplane.

Daylon Graphics programs use an OEM-defined list of measurement units defined in the file daylon_mcodes.h. Earlier versions of the clipboard spec used EPSG codes, but these do not cover all the units used by Leveller.

 
Tag: "body/coordsys/altitude"
Type: parent
Purpose: Maps elevation values to projection space.

Elevation data is provided in whatever range the application prefers. The "units" subtag indicates in which units the elevations are ultimately expressed, after being scaled and offset.

For example, one may prefer to provide elevations in raw heightfield grid units such that an elevation delta of 1.0 is as tall as a pixel is wide. Such values, however, have little utility for geographic datasets and require a mapping to a realworld measure. The "scale" subtag lets one scale the raw elevations to the desired unit space (e.g., meters) and the "offset" subtag provides a realworld base elevation to be added after the scaling. One would also set the "units" subtag to 9001, which is the EPSG code for meters.

If providing elevations directly in the specified space, then the scale and offset can be ommitted or set to 1.0 and 0.0, respectively.

 
Tag: "body/coordsys/altitude/units"
Type: unsigned int32
Purpose: Defines unit of measure for elevations.

Same encoding as pixelmapping units; refer to daylon_mcodes.h for specifics.

 
 
 
Parser Pseudocode

Sometimes it's easier to understand a spec by reading code, so here's a sample parser in C-ish pseudocode:


    width   = clip.val("/body/heixels/extents/width");
    breadth = clip.val("/body/heixels/extents/width");
    vscale  = 1.0 OR clip.val("/body/heixels/extents/scale/y");

    depth   = clip.val("body/heixels/format/depth");
    datastart = clip.val("body/heixels/format/readoffsets/first");

    isFP    = FALSE OR clip.val("body/heixels/format/fp");
    isSigned= FALSE OR not clip.val("body/heixels/format/unsigned");
    fracsize= 0 OR clip.val("/body/heixels/format/fracsize");

    if(isFP and depth != 32 and depth != 64)
        throw unknownFormatException;

    coloff  = depth OR clip.val("/body/heixels/readoffsets/column");
    rowoff  = depth * width OR clip.val("/body/heixels/readoffsets/column");
    data    = clip.val("/body/heixels/data");


    data.forwardbits(datastart);

    for(z = 0; z < breadth; z++)
    {
        bitsPerScanline = coloff * width;

        for(x = 0; x < width; x++)
        {
            heixel = data.readbits(depth);

            data.forwardbits(coloff - depth);

            double dElev;

            if(bigendian and host isn't)
                byteswap(heixel, (depth + 7) / 8);

            if(isFP)
            {
                if(depth == 32)
                    dElev = (double)(float)heixel;
                else if(depth == 64)
                    dElev = (double)heixel;
            }
            else
            {
                isNeg = FALSE;

                if(isSigned)
                {
                    isNeg = heixel.bit(depth-1);
                    heixel.bit(depth-1) = 0;
                }
                dElev = (double)heixel / (2 ** fracsize);
                
                if(isNeg)
                    dElev *= -1;
            }

            dElev *= vscale;

            // Store dElev in our heightfield.

        } // next heixel on scanline

        data.forwardbits(rowoff - bitsPerScanline);

    } // next scanline
    
The above is a generalized parser that will handle all heixelformat possibilities. An application that prefers supporting specific formats, however, needs to check for them. For example, if Leveller 2.4 insisted on only pasting its own internal heightfield format, it would do this:


    width   = clip.val("/body/heixels/extents/width");
    breadth = clip.val("/body/heixels/extents/width");
    vscale  = 1.0 OR clip.val("/body/heixels/extents/scale/y");

    depth   = clip.val("body/heixels/format/depth");
    datastart = clip.val("body/heixels/format/readoffsets/first");

    isFP    = FALSE OR clip.val("body/heixels/format/fp");
    isSigned= FALSE OR not clip.val("body/heixels/format/unsigned");
    fracsize= 0 OR clip.val("/body/heixels/format/fracsize");

    coloff  = depth OR clip.val("/body/heixels/readoffsets/column");
    rowoff  = depth * width OR clip.val("/body/heixels/readoffsets/column");
    data    = clip.val("/body/heixels/data");

    // Check for 16.16 fixed point heixelformat.

    if(isFP            or 
       depth != 32     or 
       datastart != 0  or
       vscale != 1.0   or
       !isSigned       or 
       fracsize != 16  or
       coloff != depth or 
       rowoff != depth * width)
        throw unknownFormatException;


    LEV_HEIGHTVAL* pData = (LEV_HEIGHTVAL*)data.GetAddress();

    for(z = 0; z < breadth; z++)
    {
        for(x = 0; x < width; x++)
        {
            LEV_HEIGHTVAL heixel = pData[z * width + x];

            if(bigendian and host isn't)
                byteswap(heixel, sizeof(LEV_HEIGHTVAL));

            // Store heixel in our heightfield.

        } // next heixel on scanline

    } // next scanline
    

Tags in [] indicate optional items, to make writing the most common output easy. If omitted, appropriate defaults are assumed. extents/lowest and extents/highest can be computed by scanning the elevations. The default heixelformat (aside from bpp) is signed little-endian integers with no fixed-point interpretation. If readoffsets is missing, the pixels are assumed to be bytealigned with no scanline padding (bpp is rounded up to the nearest multiple of eight).

The tag names in the pixelformat block were chosen so that apps exporting data could initialize internal structures by zero-filling them. This way, the most common options for the fp, unsigned, bigendian, and fracsize members tend to be immediately set (i.e., pixels are integers, signed, little endian, and not fixed-point).

pixelformat/mask is used for apps that want to just copy their internal pixel buffers, which may include non-elevation data in each pixel such as transparency, surface normal references, material flags, etc. Simply logical-AND'ing the pixel bits with the mask and shifting down obtains the elevation. The default mask is the same bit length as the pixel and is all one's.

If readoffsets/column equals depth, it means pixels are tightly packed on each scanline. If readoffsets/row equals depth * width, it means scanlines are unpadded even on a bit basis. This supports applications using fractional byte pixel depths (e.g., 4, 11, etc. bpp) seeking optimal (but uncompressed) storage.

The scale and size tags provide optional data on what shape and size the heightfield has. The scale data is primarily of interest to apps that need to know how the span relates to the width/breadth (e.g., when making surface normals for bumpmaps). If scale.y is 1.0, for example, it means that one unit of elevation equals the distance between adjacent pixels. The size tag is for apps that need to know the heightfield's "real" size. A 1 x 1 heightfield has no extent.

 
Current Issues

Pixelformats using bit depths greater than eight but which are multiples of eight (e.g., 16/24/32) may present endian issues. For irregular bit depths, the usual solution is to enforce big-endian byte order since the streaming is on a bit basis, i.e., regardless of the platform, the bytes storing a bitstream are encoded identically. But for regular depths, the assumption is that 16-bit and 32-bit values are being assigned using 16-bit/32-bit datatypes, which are endian-dependant. Hence, using a bit parser as a general solution is not possible unless we perform byteswapping for regular depths. For those depths, a "byteorder" tag should be added, and if nonzero, it means the bytes in each pixel are in the reverse order of the platform's endianness.

Although the reference code does not optimize tag reading (it always starts from the top of the tree and iterates through every tag), tagSize members were added to the tag struct for future enhancement. If one computes a tag's full size and sets its tagSize field with it, then skipping unmatched tags can be done in a single operation. For now, it's not a problem, since the clipboard is in memory, and using the tag system for file storage is likely not around the corner.

Moved alpha/offsets into alpha/extents, since offsets are never used if extents are unspecified. i.e., the mask is implicitly the same size as the heightfield, and therefore must be at (0,0).

Dropped the endianness tags from the pixelformats. All tag values simply share the endianness of the clipboard. There is a global endianness indicator in the third and fourth bytes of the first tag's name. Using the first tag as a signature works because tags have their names as their first member.

Dropped fracsize from alpha/pixelformat, since it's unnecessary. Whatever the bit pattern in an alpha pixel is, it's simply normalized to 0...1 using the max value given by its bit depth.

The requirement that all non-binary tags have their values expressed as text was removed. Instead, everything is stored in binary form. This made the requirement for a lightweight parser much easier.

Added the "alpha" tag since Leveller cannot copy/paste its heightfields without knowing what the selection is shaped like. Otherwise, all pastings would result in rectangular floating selections.

Removed the "mask" tag since the depth and column offset take care of it. Added the "readoffsets/first" tag since we still need to know how many bits into the data block the first pixel starts at.

Renamed "pixel" to "heixel" in the tag names. The advantage is less confusion with future tags that refer to true pixels, such as picture textures, alpha masks, etc. The "bpp" tag was renamed "depth" since bpp means bits per pixel, and "depth" sounded better than "bph".

Changed readoffsets/row to use bits instead of bytes, since it simplifies reading past scanline padding.

Applications such as 3D modelers and renderers tend to apply local 4 x 4 transform matrices to scene objects including heightfields. This is a simple type of linear CS mapping and supports basic 3D space transforms such as translation, rotation, shearing, and proportional/non-proportional scaling. Projections are unused because heightfields are not interpreted as being part of a planetary surface.

GIS applications and geo-specific modelers/renderers (such as WCS) support nonlinear CS and projections. The heightfield can be curved, and user coordinates depend not only on the CS (which may follow a UV mapping such as latitude/longitude), but also on projections (Mercator, Polar, etc.) and even datums (the particular ellipsoid used to model a planet's almost-spherical shape). The experimental "body/coordsys" tag will contain data to ease supporting GIS applications.

The "body/heixels/extents/scale" and "body/heixels/extents/size" tags have been dropped for Leveller 2.6 and replaced with the more comprehensive "body/coordsys" tag.

 
Schedule of Operations

A small SDK has been developed containing


    - A reference C++ class that reads/writes the 
      format's tag structure. Preliminary documentation also.

    - C sample pasting code derived from Leveller's 
      own paste event handler, demonstrating how to 
      build a format tag structure for elevations and 
      optimized selection masks.

    - A Win32 test app that copies Leveller TER files 
      to the clipboard with options for size, scale, 
      and heixelformat tags.

Daylon has implemented the minimum tagset above in Leveller 2.2 and has provided working code to participants for testing and evaluation purposes. Leveller 2.2 can currently copy the format and paste formats using byte-multiple bit depths.

Daylon has implemented georeferencing support via the "body/coordsys" tag for Leveller 2.6. Testing is available via the Leveller 2.6 alpha.

 
Contributors

    Frank Barchard, Electronic Arts Canada
    Ben Discoe, Virtual Terrain Project
    Ray Gardener, Daylon Graphics Ltd.
    Stephen Schmitt, author of World Machine