Index: /geovis/trunk/ColorMap.cpp
===================================================================
--- /geovis/trunk/ColorMap.cpp	(revision 5105)
+++ /geovis/trunk/ColorMap.cpp	(revision 5105)
@@ -0,0 +1,77 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2015  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+#include "ColorMap.h"
+
+using namespace GeoVis;
+
+void
+ColorMap::renderColorMap(osg::TransferFunction1D *map, int width, int height,
+                         osg::Image *imgData,
+                         bool opaque, float bgColor[3],
+                         bool bgr,
+                         int bytesPerPixel)
+{
+    int size = bytesPerPixel * width * height;
+    if (imgData->getTotalSizeInBytes() != (unsigned int)size) {
+        GLenum pixelFormat;
+        if (bytesPerPixel == 3) {
+            pixelFormat = bgr ? GL_BGR : GL_RGB;
+        } else {
+            pixelFormat = bgr ? GL_BGRA: GL_RGBA;
+        }; 
+        imgData->allocateImage(width, height, 1, pixelFormat, GL_UNSIGNED_BYTE, 1);
+    }
+    unsigned char *dst = imgData->data();
+    //unsigned char *src = map->data();
+    if (height > width) {
+        for (int i = 0; i < height; i++) {
+            float x = (float)i/(height-1);
+            osg::Vec4f rgb = map->getColor(x);
+            unsigned char color[3];
+            if (opaque) {
+                color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
+                color[1] = (unsigned char)(255. * rgb[1]);
+                color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
+            } else {
+                float opacity = rgb[3];
+                color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
+                color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
+                color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
+            }
+            for (int j = 0; j < width; j++) {
+                memcpy(dst, color, 3);
+                dst += 3;
+                if (bytesPerPixel == 4) {
+                    *(dst++) = (unsigned char)255.;
+                }
+            }
+        }
+    } else {
+        for (int i = 0; i < height; i++) {
+            for (int j = 0; j < width; j++) {
+                float x = (float)j/(width-1);
+                osg::Vec4f rgb = map->getColor(x);
+                unsigned char color[3];
+                if (opaque) {
+                    color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
+                    color[1] = (unsigned char)(255. * rgb[1]);
+                    color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
+                } else {
+                    double opacity = rgb[3];
+                    color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
+                    color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
+                    color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
+                }
+                memcpy(dst, color, 3);
+                dst += 3;
+                if (bytesPerPixel == 4) {
+                    *(dst++) = (unsigned char)255.;
+                }
+            }
+        }
+    }
+}
Index: /geovis/trunk/ColorMap.h
===================================================================
--- /geovis/trunk/ColorMap.h	(revision 5105)
+++ /geovis/trunk/ColorMap.h	(revision 5105)
@@ -0,0 +1,25 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2015  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+#ifndef GEOVIS_COLORMAP_H
+#define GEOVIS_COLORMAP_H
+
+#include <osg/TransferFunction>
+
+namespace GeoVis {
+
+class ColorMap {
+public:
+    static void renderColorMap(osg::TransferFunction1D *map, int width, int height,
+                               osg::Image *imgData,
+                               bool opaque, float bgColor[3],
+                               bool bgr = false,
+                               int bytesPerPixel = 3);
+};
+
+}
+
+#endif
Index: /geovis/trunk/Makefile.in
===================================================================
--- /geovis/trunk/Makefile.in	(revision 5104)
+++ /geovis/trunk/Makefile.in	(revision 5105)
@@ -121,4 +121,5 @@
 SERVER_SRCS	= \
 		CmdProc.cpp \
+		ColorMap.cpp \
 		FileUtil.cpp \
 		PPMWriter.cpp \
@@ -188,4 +189,5 @@
 
 CmdProc.o: CmdProc.h
+ColorMap.o: ColorMap.h
 CommandQueue.o: CommandQueue.h Trace.h
 md5.o: md5.h
Index: /geovis/trunk/RenderServer.h
===================================================================
--- /geovis/trunk/RenderServer.h	(revision 5104)
+++ /geovis/trunk/RenderServer.h	(revision 5105)
@@ -21,5 +21,5 @@
 class Stats;
 
-#define GEOVIS_VERSION_STRING "0.6.1"
+#define GEOVIS_VERSION_STRING "0.7.0"
 
 #define MSECS_ELAPSED(t1, t2) \
Index: /geovis/trunk/Renderer.cpp
===================================================================
--- /geovis/trunk/Renderer.cpp	(revision 5104)
+++ /geovis/trunk/Renderer.cpp	(revision 5105)
@@ -80,4 +80,5 @@
 
 #include "Renderer.h"
+#include "ColorMap.h"
 #if 0
 #include "SingleWindow.h"
@@ -179,5 +180,5 @@
     if (!_colorMaps.empty())
         return;
-
+#if 0
     osg::TransferFunction1D *defaultColorMap = new osg::TransferFunction1D;
     defaultColorMap->allocate(256);
@@ -195,4 +196,40 @@
     defaultGrayColorMap->updateImage();
     addColorMap("grayDefault", defaultGrayColorMap);
+#endif
+}
+
+void Renderer::saveCLRFile(const std::string& path, osg::TransferFunction1D *xfer)
+{
+    std::ofstream out(path.c_str());
+    float value;
+    unsigned int r, g, b, a;
+    for (osg::TransferFunction1D::ColorMap::const_iterator itr = xfer->getColorMap().begin();
+         itr != xfer->getColorMap().end(); ++itr) {
+        value = itr->first;
+        r = (unsigned int)(255.0 * itr->second.r());
+        g = (unsigned int)(255.0 * itr->second.g());
+        b = (unsigned int)(255.0 * itr->second.b());
+        a = (unsigned int)(255.0 * itr->second.a());
+        out << value << " " << r << " " << g << " " << b << " " << a << std::endl;
+    }
+}
+
+void Renderer::getColorMapRange(const ColorMapId& id, float *min, float *max) const
+{
+    ColorMapHashmap::const_iterator itr = _colorMaps.find(id);
+    if (itr == _colorMaps.end()) {
+        ERROR("Unknown ColorMap %s", id.c_str());
+        return;
+    }
+
+    *min = itr->second->getMinimum();
+    *max = itr->second->getMaximum();
+}
+
+std::string Renderer::getColorMapFilePath(const ColorMapId& id) const
+{
+    std::ostringstream path;
+    path << "/tmp/" << id << "_" << getpid() << ".clr";
+    return path.str();
 }
 
@@ -200,4 +237,5 @@
 {
     _colorMaps[id] = xfer;
+    saveCLRFile(getColorMapFilePath(id), xfer);
 }
 
@@ -253,6 +291,21 @@
     do {
         itr->second->allocate(numEntries);
-        itr->second->updateImage();
     } while (doAll && ++itr != _colorMaps.end());
+}
+
+bool
+Renderer::renderColorMap(const ColorMapId& id, int width, int height,
+                         osg::Image *imgData,
+                         bool opaque, float bgColor[3],
+                         bool bgr, int bytesPerPixel) const
+{
+    ColorMapHashmap::const_iterator itr = _colorMaps.find(id);
+    if (itr == _colorMaps.end()) {
+        ERROR("Unknown ColorMap %s", id.c_str());
+        return false;
+    }
+
+    ColorMap::renderColorMap(itr->second, width, height, imgData, opaque, bgColor, bgr, bytesPerPixel);
+    return true;
 }
 
@@ -671,4 +724,5 @@
 
 void Renderer::resetMap(osgEarth::MapOptions::CoordinateSystemType type,
+                        const osg::Vec4f& bgColor,
                         const char *profile,
                         double bounds[4])
@@ -724,5 +778,5 @@
 #endif
     // Set background layer color
-    mpOpt.color() = osg::Vec4(1, 1, 1, 1);
+    mpOpt.color() = bgColor;
     //mpOpt.minLOD() = 1;
     // Sets shader uniform for terrain renderer (config var defaults to false)
@@ -843,4 +897,10 @@
 }
 
+void Renderer::setTerrainColor(const osg::Vec4f& color)
+{
+    
+    _needsRedraw = true;
+}
+
 void Renderer::setTerrainLighting(bool state)
 {
@@ -1153,5 +1213,6 @@
                              bool enableCache,
                              bool makeShared,
-                             bool visible)
+                             bool visible,
+                             int minLOD, int maxLOD)
 {
     if (!_map.valid()) {
@@ -1177,4 +1238,6 @@
         layerOpts.visible() = false;
     }
+    layerOpts.minLevel() = minLOD;
+    layerOpts.maxLevel() = maxLOD;
     osg::ref_ptr<osgEarth::ImageLayer> layer = new osgEarth::ImageLayer(layerOpts);
     _map->addImageLayer(layer.get());
@@ -1273,4 +1336,20 @@
 }
 
+void Renderer::setImageLayerVisibleRange(const char *name, float min, float max)
+{
+    if (!_map.valid()) {
+        ERROR("No map");
+        return;
+    }
+    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
+    if (layer != NULL) {
+        layer->setMinVisibleRange(min);
+        layer->setMaxVisibleRange(max);
+        _needsRedraw = true;
+    } else {
+        TRACE("Image layer not found: %s", name);
+    }
+}
+
 void Renderer::setImageLayerVisibility(const char *name, bool state)
 {
@@ -1291,5 +1370,6 @@
                                  osgEarth::TileSourceOptions& opts,
                                  bool enableCache,
-                                 bool visible)
+                                 bool visible,
+                                 int minLOD, int maxLOD)
 {
     if (!_map.valid()) {
@@ -1299,5 +1379,5 @@
     TRACE("layer: %s", name);
     if (!opts.tileSize().isSet()) {
-        opts.tileSize() = 15;
+        opts.tileSize() = 17;
     }
     osgEarth::ElevationLayerOptions layerOpts(name, opts);
@@ -1308,4 +1388,6 @@
         layerOpts.visible() = false;
     }
+    layerOpts.minLevel() = minLOD;
+    layerOpts.maxLevel() = maxLOD;
     // XXX: GDAL does not report vertical datum, it should be specified here
     // Common options: geodetic (default), egm96, egm84, egm2008
@@ -1602,5 +1684,5 @@
 {
     if (!_placeNodes.valid()) {
-        ERROR("No place nodes");
+        TRACE("No place nodes");
         return;
     }
@@ -1649,5 +1731,5 @@
 {
     if (!_placeNodes.valid()) {
-        ERROR("No place nodes");
+        TRACE("No place nodes");
         return;
     }
@@ -1746,5 +1828,21 @@
 #endif
 }
-
+#if 0
+void Renderer::setModelLayerVisibleRange(const char *name, float min, float max)
+{
+    if (!_map.valid()) {
+        ERROR("No map");
+        return;
+    }
+    osgEarth::ModelLayer *layer = _map->getModelLayerByName(name);
+    if (layer != NULL) {
+        layer->minVisibleRange(min);
+        layer->maxVisibleRange(max);
+        _needsRedraw = true;
+    } else {
+        TRACE("Model layer not found: %s", name);
+    }
+}
+#endif
 void Renderer::setModelLayerVisibility(const char *name, bool state)
 {
@@ -2136,5 +2234,5 @@
     _lastFrameTime = osg::Timer::instance()->delta_s(_startFrameTime, endFrameTick);
     if (_lastFrameTime > _minFrameTime) {
-        ERROR("BROKE FRAME by %.2f msec", (_lastFrameTime - _minFrameTime)*1000.0f);
+        TRACE("BROKE FRAME by %.2f msec", (_lastFrameTime - _minFrameTime)*1000.0f);
     } else {
         TRACE("Frame time: %.2f msec", _lastFrameTime*1000.0f);
Index: /geovis/trunk/Renderer.h
===================================================================
--- /geovis/trunk/Renderer.h	(revision 5104)
+++ /geovis/trunk/Renderer.h	(revision 5105)
@@ -168,4 +168,16 @@
     void setColorMapNumberOfTableEntries(const ColorMapId& id, int numEntries);
 
+    bool renderColorMap(const ColorMapId& id, int width, int height,
+                        osg::Image *imgData,
+                        bool opaque, float bgColor[3],
+                        bool bgr = false,
+                        int bytesPerPixel = 3) const;
+
+    void getColorMapRange(const ColorMapId& id, float *min, float *max) const;
+
+    std::string getColorMapFilePath(const ColorMapId& id) const;
+
+    void saveCLRFile(const std::string& path, osg::TransferFunction1D *xfer);
+
     // Scene
 
@@ -173,4 +185,5 @@
 
     void resetMap(osgEarth::MapOptions::CoordinateSystemType type,
+                  const osg::Vec4f& bgColor = osg::Vec4f(1,1,1,1),
                   const char *profile = NULL,
                   double bounds[4] = NULL);
@@ -197,4 +210,12 @@
 
     void setLighting(bool state);
+
+    void setTerrainColor(const osg::Vec4f& color);
+
+    void setTerrainEdges(bool state) {}
+
+    void setTerrainLineColor(const osg::Vec4f& color) {}
+
+    void setTerrainLineWidth(float width) {}
 
     void setTerrainLighting(bool state);
@@ -230,9 +251,14 @@
     bool addImageLayer(const char *name, osgEarth::TileSourceOptions& opts,
                        bool enableCache = true, bool makeShared = false,
-                       bool visible = true);
+                       bool visible = true,
+                       int minLOD = 0, int maxLOD = 23);
 
     void removeImageLayer(const char *name);
 
     void moveImageLayer(const char *name, unsigned int pos);
+
+    void setImageLayerVisibleRange(const char *name, float min, float max);
+
+    void setImageLayerLODRange(const char *name, int min, int max);
 
     void setImageLayerOpacity(const char *name, double opacity);
@@ -265,9 +291,12 @@
 
     void addElevationLayer(const char *name, osgEarth::TileSourceOptions& opts,
-                           bool enableCache = true, bool visible = true);
+                           bool enableCache = true, bool visible = true,
+                           int minLOD = 0, int maxLOD = 23);
 
     void removeElevationLayer(const char *name);
 
     void moveElevationLayer(const char *name, unsigned int pos);
+
+    void setElevationLayerVisibleRange(const char *name, float min, float max);
 
     void setElevationLayerVisibility(const char *name, bool state);
@@ -298,4 +327,6 @@
 
     void moveModelLayer(const char *name, unsigned int pos);
+
+    //void setModelLayerVisibleRange(const char *name, float min, float max);
 
     void setModelLayerOpacity(const char *name, double opacity);
Index: /geovis/trunk/RendererCmd.cpp
===================================================================
--- /geovis/trunk/RendererCmd.cpp	(revision 5104)
+++ /geovis/trunk/RendererCmd.cpp	(revision 5105)
@@ -23,4 +23,5 @@
 #include <osgDB/FileNameUtils>
 
+#include <osgEarth/Version>
 #include <osgEarth/Registry>
 #include <osgEarthFeatures/FeatureModelSource>
@@ -32,4 +33,7 @@
 #include <osgEarthSymbology/RenderSymbol>
 
+#if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1)
+#include <osgEarthDrivers/osg/ColorRampOptions>
+#endif
 #include <osgEarthDrivers/debug/DebugOptions>
 #include <osgEarthDrivers/gdal/GDALOptions>
@@ -528,14 +532,13 @@
 {
     const char *name = Tcl_GetString(objv[2]);
-    int cmapc, omapc;
+    int cmapc;
     Tcl_Obj **cmapv = NULL;
-    Tcl_Obj **omapv = NULL;
 
     if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) {
         return TCL_ERROR;
     }
-    if ((cmapc % 4) != 0) {
+    if ((cmapc % 5) != 0) {
         Tcl_AppendResult(interp, "wrong # elements in colormap: should be ",
-                         "{ value r g b ... }", (char*)NULL);
+                         "{ value r g b a... }", (char*)NULL);
         return TCL_ERROR;
     }
@@ -544,12 +547,13 @@
     colorMap->allocate(256);
 
-    for (int i = 0; i < cmapc; i += 4) {
-        double val[4];
-        for (int j = 0; j < 4; j++) {
+    for (int i = 0; i < cmapc; i += 5) {
+        double val[5];
+        for (int j = 0; j < 5; j++) {
             if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &val[j]) != TCL_OK) {
                 delete colorMap;
                 return TCL_ERROR;
             }
-            if ((val[j] < 0.0) || (val[j] > 1.0)) {
+            // Need to permit un-normalized values
+            if (j > 0 && (val[j] < 0.0 || val[j] > 1.0)) {
                 Tcl_AppendResult(interp, "bad colormap value \"",
                                  Tcl_GetString(cmapv[i+j]),
@@ -559,43 +563,9 @@
             }
         }
-        colorMap->setColor(val[0], osg::Vec4f(val[1], val[2], val[3], 1.0), false);
+        colorMap->setColor(val[0], osg::Vec4f(val[1], val[2], val[3], val[4]), false);
     }
 
     colorMap->updateImage();
 
-    if (Tcl_ListObjGetElements(interp, objv[4], &omapc, &omapv) != TCL_OK) {
-        delete colorMap;
-        return TCL_ERROR;
-    }
-    if ((omapc % 2) != 0) {
-        Tcl_AppendResult(interp, "wrong # elements in opacitymap: should be ",
-                         "{ value alpha ... }", (char*)NULL);
-        delete colorMap;
-        return TCL_ERROR;
-    }
-    for (int i = 0; i < omapc; i += 2) {
-        double val[2];
-        for (int j = 0; j < 2; j++) {
-            if (Tcl_GetDoubleFromObj(interp, omapv[i+j], &val[j]) != TCL_OK) {
-                delete colorMap;
-                return TCL_ERROR;
-            }
-            if ((val[j] < 0.0) || (val[j] > 1.0)) {
-                Tcl_AppendResult(interp, "bad opacitymap value \"",
-                                 Tcl_GetString(omapv[i+j]),
-                                 "\": should be in the range [0,1]", (char*)NULL);
-                delete colorMap;
-                return TCL_ERROR;
-            }
-        }
-#if 0
-        ColorMap::OpacityControlPoint ocp;
-        ocp.value = val[0];
-        ocp.alpha = val[1];
-        colorMap->addOpacityControlPoint(ocp);
-#endif
-    }
-
-    //colorMap->build();
     g_renderer->addColorMap(name, colorMap);
     return TCL_OK;
@@ -646,6 +616,6 @@
 
 static CmdSpec colorMapOps[] = {
-    {"add",    1, ColorMapAddOp,             5, 5, "colorMapName colormap alphamap"},
-    {"define", 3, ColorMapAddOp,             5, 5, "colorMapName colormap alphamap"},
+    {"add",    1, ColorMapAddOp,             4, 4, "colorMapName colormap"},
+    {"define", 3, ColorMapAddOp,             4, 4, "colorMapName colormap"},
     {"delete", 3, ColorMapDeleteOp,          2, 3, "?colorMapName?"},
     {"res",    1, ColorMapNumTableEntriesOp, 3, 4, "numTableEntries ?colorMapName?"}
@@ -767,4 +737,76 @@
     }
     return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+LegendCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    if (objc != 4) {
+        Tcl_AppendResult(interp, "wrong # args: should be \"",
+                Tcl_GetString(objv[0]), " colormapName width height\"", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *colorMapName = Tcl_GetString(objv[1]);
+
+    int width, height;
+    if (Tcl_GetIntFromObj(interp, objv[2], &width) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[3], &height) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    bool opaque = true;
+    float bgColor[3];
+    memset(bgColor, 0, sizeof(float)*3);
+    osg::ref_ptr<osg::Image> imgData = new osg::Image();
+
+#if defined(RENDER_TARGA) || defined(DEBUG)
+    if (!g_renderer->renderColorMap(colorMapName, width, height, imgData, opaque,
+                                    bgColor, true)) {
+        Tcl_AppendResult(interp, "Color map \"",
+                         colorMapName, "\" was not found", (char*)NULL);
+        return TCL_ERROR;
+    }
+#else
+    if (!g_renderer->renderColorMap(colorMapName, width, height, imgData, opaque,
+                                    bgColor)) {
+        Tcl_AppendResult(interp, "Color map \"",
+                         colorMapName, "\" was not found", (char*)NULL);
+        return TCL_ERROR;
+    }
+#endif
+
+#ifdef DEBUG
+# ifdef RENDER_TARGA
+    writeTGAFile("/tmp/legend.tga", imgData->data(), width, height,
+                 TARGA_BYTES_PER_PIXEL);
+# else
+    writeTGAFile("/tmp/legend.tga", imgData->data(), width, height,
+                 TARGA_BYTES_PER_PIXEL, true);
+# endif
+#else
+    char cmd[256];
+    float min, max;
+    g_renderer->getColorMapRange(colorMapName, &min, &max);
+    snprintf(cmd, sizeof(cmd), "nv>legend {%s} %g %g", colorMapName, min, max);
+
+# ifdef USE_THREADS
+#  ifdef RENDER_TARGA
+    queueTGA(g_outQueue, cmd, imgData->data(), width, height,
+             TARGA_BYTES_PER_PIXEL);
+#  else
+    queuePPM(g_outQueue, cmd, imgData->data(), width, height);
+#  endif
+# else
+#  ifdef RENDER_TARGA
+    writeTGA(g_fdOut, cmd, imgData->data(), width, height,
+             TARGA_BYTES_PER_PIXEL);
+#  else
+    writePPM(g_fdOut, cmd, imgData->data(), width, height);
+#  endif
+# endif // USE_THREADS
+#endif // DEBUG
+
+    return TCL_OK;
 }
 
@@ -947,12 +989,12 @@
               Tcl_Obj *const *objv)
 {
-    char *type = Tcl_GetString(objv[3]);
+    char *name = Tcl_GetString(objv[3]);
+    char *type = Tcl_GetString(objv[4]);
     if (type[0] == 'i' && strcmp(type, "image") == 0) {
         bool ret;
-        char *name;
-        char *driver = Tcl_GetString(objv[4]);
+        char *driver = Tcl_GetString(objv[5]);
         std::string url;
         if (objc > 6) {
-            char *urlIn = Tcl_GetString(objv[5]);
+            char *urlIn = Tcl_GetString(objv[6]);
             url = g_renderer->getCanonicalPath(std::string(urlIn));
             if (url.empty()) {
@@ -964,32 +1006,57 @@
         bool cache = true;
         if (objc > 7) {
-            if (GetBooleanFromObj(interp, objv[6], &cache) != TCL_OK) {
+            if (GetBooleanFromObj(interp, objv[7], &cache) != TCL_OK) {
                 return TCL_ERROR;
             }
         }
-        if (driver[0] == 'd' && strcmp(driver, "debug") == 0) {
+        bool shared = false;
+        bool visible = true;
+        int minLOD = 0;
+        int maxLOD = 23;
+        float minRange = 0.f;
+        float maxRange = FLT_MAX;
+#if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1)
+        if (driver[0] == 'c' && strcmp(driver, "colorramp") == 0) {
+            osgEarth::Drivers::ColorRampOptions colorRampOpts;
+            osgEarth::Drivers::GDALOptions opts;
+            opts.url() = url;
+            char *profile = Tcl_GetString(objv[8]);
+            char *colormap = Tcl_GetString(objv[9]);
+            osgEarth::ElevationLayerOptions elevOpts(name, opts);
+            if (!cache) {
+                elevOpts.cachePolicy() = osgEarth::CachePolicy(osgEarth::CachePolicy::USAGE_NO_CACHE);
+            }
+            if (profile != NULL) {
+                //elevOpts.driver()->profile()->srsString() = "epsg:4326";
+                elevOpts.driver()->profile() = osgEarth::ProfileOptions(profile);
+            }
+            colorRampOpts.elevationLayer() = elevOpts;
+            colorRampOpts.ramp() = g_renderer->getColorMapFilePath(colormap);
+            ret = g_renderer->addImageLayer(name, colorRampOpts, cache, shared, visible, minLOD, maxLOD);
+        } else
+#endif
+            if (driver[0] == 'd' && strcmp(driver, "debug") == 0) {
             osgEarth::Drivers::DebugOptions opts;
-            name = Tcl_GetString(objv[5]);
-            ret = g_renderer->addImageLayer(name, opts, cache);
+            ret = g_renderer->addImageLayer(name, opts, cache, shared, visible, minLOD, maxLOD);
         } else if (driver[0] == 'g' && strcmp(driver, "gdal") == 0) {
             osgEarth::Drivers::GDALOptions opts;
             opts.url() = url;
-            name = Tcl_GetString(objv[7]);
-            ret = g_renderer->addImageLayer(name, opts, cache);
+            ret = g_renderer->addImageLayer(name, opts, cache, shared, visible, minLOD, maxLOD);
+            g_renderer->setImageLayerVisibleRange(name, minRange, maxRange);
         } else if (driver[0] == 't' && strcmp(driver, "tms") == 0) {
             osgEarth::Drivers::TMSOptions opts;
-            //char *tmsType = Tcl_GetString(objv[5]);
-            //char *format = Tcl_GetString(objv[6]);
+            //char *tmsType = Tcl_GetString(objv[8]);
+            //char *format = Tcl_GetString(objv[9]);
             opts.url() = url;
             //opts.tmsType() = tmsType;
             //opts.format() = format;
-            name = Tcl_GetString(objv[7]);
-            ret = g_renderer->addImageLayer(name, opts, cache);
+            ret = g_renderer->addImageLayer(name, opts, cache, shared, visible, minLOD, maxLOD);
+            g_renderer->setImageLayerVisibleRange(name, minRange, maxRange);
         } else if (driver[0] == 'w' && strcmp(driver, "wms") == 0) {
             osgEarth::Drivers::WMSOptions opts;
-            char *wmsLayers = Tcl_GetString(objv[7]);
-            char *format = Tcl_GetString(objv[8]);
+            char *wmsLayers = Tcl_GetString(objv[8]);
+            char *format = Tcl_GetString(objv[9]);
             bool transparent;
-            if (GetBooleanFromObj(interp, objv[9], &transparent) != TCL_OK) {
+            if (GetBooleanFromObj(interp, objv[10], &transparent) != TCL_OK) {
                 return TCL_ERROR;
             }
@@ -999,6 +1066,6 @@
             opts.transparent() = transparent;
 
-            name = Tcl_GetString(objv[10]);
-            ret = g_renderer->addImageLayer(name, opts, cache);
+            ret = g_renderer->addImageLayer(name, opts, cache, shared, visible, minLOD, maxLOD);
+            g_renderer->setImageLayerVisibleRange(name, minRange, maxRange);
         } else if (driver[0] == 'x' && strcmp(driver, "xyz") == 0) {
             osgEarth::Drivers::XYZOptions opts;
@@ -1007,7 +1074,7 @@
             //bool invertY = false;
             //opts.invertY() = invertY;
-            //opts.format() = Tcl_GetString(objv[6]);
-            name = Tcl_GetString(objv[7]);
-            ret = g_renderer->addImageLayer(name, opts, cache);
+            //opts.format() = Tcl_GetString(objv[8]);
+            ret = g_renderer->addImageLayer(name, opts, cache, shared, visible, minLOD, maxLOD);
+            g_renderer->setImageLayerVisibleRange(name, minRange, maxRange);
         } else {
             Tcl_AppendResult(interp, "unknown image driver \"", driver,
@@ -1020,5 +1087,35 @@
         }
     } else if (type[0] == 'e' && strcmp(type, "elevation") == 0) {
-        char *driver = Tcl_GetString(objv[4]);
+        char *driver = Tcl_GetString(objv[5]);
+        char *urlIn = Tcl_GetString(objv[6]);
+        std::string url = g_renderer->getCanonicalPath(std::string(urlIn));
+        if (url.empty()) {
+            Tcl_AppendResult(interp, "file not found: \"",
+                             urlIn, "\"", (char*)NULL);
+            return TCL_ERROR;
+        }
+        bool cache = true;
+        bool visible = true;
+        int minLOD = 0;
+        int maxLOD = 23;
+        if (driver[0] == 'g' && strcmp(driver, "gdal") == 0) {
+            osgEarth::Drivers::GDALOptions opts;
+            opts.url() = url;
+            g_renderer->addElevationLayer(name, opts, cache, visible, minLOD, maxLOD);
+        } else if (driver[0] == 't' && strcmp(driver, "tms") == 0) {
+            osgEarth::Drivers::TMSOptions opts;
+            //char *tmsType = Tcl_GetString(objv[7]);
+            //char *format = Tcl_GetString(objv[8]);
+            opts.url() = url;
+            //opts.tmsType() = tmsType;
+            //opts.format() = format;
+            g_renderer->addElevationLayer(name, opts, cache, visible, minLOD, maxLOD);
+        } else {
+            Tcl_AppendResult(interp, "unknown elevation driver \"", driver,
+                             "\": should be 'gdal' or 'tms'", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else if (type[0] == 'p' && strcmp(type, "point") == 0) {
+        osgEarth::Drivers::OGRFeatureOptions opts;
         char *urlIn = Tcl_GetString(objv[5]);
         std::string url = g_renderer->getCanonicalPath(std::string(urlIn));
@@ -1028,41 +1125,20 @@
             return TCL_ERROR;
         }
-
-        if (driver[0] == 'g' && strcmp(driver, "gdal") == 0) {
-            osgEarth::Drivers::GDALOptions opts;
-            opts.url() = url;
-            char *name = Tcl_GetString(objv[6]);
-            g_renderer->addElevationLayer(name, opts);
-        } else if (driver[0] == 't' && strcmp(driver, "tms") == 0) {
-            osgEarth::Drivers::TMSOptions opts;
-            //char *tmsType = Tcl_GetString(objv[6]);
-            //char *format = Tcl_GetString(objv[7]);
-            opts.url() = url;
-            //opts.tmsType() = tmsType;
-            //opts.format() = format;
-            char *name = Tcl_GetString(objv[6]);
-            g_renderer->addElevationLayer(name, opts);
-        } else {
-            Tcl_AppendResult(interp, "unknown elevation driver \"", driver,
-                             "\": should be 'gdal' or 'tms'", (char*)NULL);
-            return TCL_ERROR;
-        }
-    } else if (type[0] == 'p' && strcmp(type, "point") == 0) {
-        osgEarth::Drivers::OGRFeatureOptions opts;
-        char *urlIn = Tcl_GetString(objv[4]);
-        std::string url = g_renderer->getCanonicalPath(std::string(urlIn));
-        if (url.empty()) {
-            Tcl_AppendResult(interp, "file not found: \"",
-                             urlIn, "\"", (char*)NULL);
-            return TCL_ERROR;
-        }
-        char *name = Tcl_GetString(objv[5]);
-
+        float r, g, b;
+        if (GetFloatFromObj(interp, objv[6], &r) != TCL_OK ||
+            GetFloatFromObj(interp, objv[7], &g) != TCL_OK ||
+            GetFloatFromObj(interp, objv[8], &b) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        float ptSize;
+        if (GetFloatFromObj(interp, objv[9], &ptSize) != TCL_OK) {
+            return TCL_ERROR;
+        }
         opts.url() = url;
 
         osgEarth::Symbology::Style style;
-        osgEarth::Symbology::PointSymbol *ls = style.getOrCreateSymbol<osgEarth::Symbology::PointSymbol>();
-        ls->fill()->color() = osgEarth::Symbology::Color::Black;
-        ls->size() = 2.0f;
+        osgEarth::Symbology::PointSymbol *ps = style.getOrCreateSymbol<osgEarth::Symbology::PointSymbol>();
+        ps->fill()->color() = osgEarth::Symbology::Color(r, g, b);
+        ps->size() = ptSize;
 
         osgEarth::Symbology::RenderSymbol* rs = style.getOrCreateSymbol<osgEarth::Symbology::RenderSymbol>();
@@ -1075,8 +1151,12 @@
         geomOpts.styles()->addStyle(style);
         geomOpts.enableLighting() = false;
+        geomOpts.minRange() = 0.f;
+        geomOpts.maxRange() = FLT_MAX;
+        //geomOpts.renderOrder = int;
+        //geomOpts.depthTestEnabled = bool;
         g_renderer->addModelLayer(name, geomOpts);
     } else if (type[0] == 'p' && strcmp(type, "polygon") == 0) {
         osgEarth::Drivers::OGRFeatureOptions opts;
-        char *urlIn = Tcl_GetString(objv[4]);
+        char *urlIn = Tcl_GetString(objv[5]);
         std::string url = g_renderer->getCanonicalPath(std::string(urlIn));
         if (url.empty()) {
@@ -1085,10 +1165,15 @@
             return TCL_ERROR;
         }
-        char *name = Tcl_GetString(objv[5]);
+        float r, g, b;
+        if (GetFloatFromObj(interp, objv[6], &r) != TCL_OK ||
+            GetFloatFromObj(interp, objv[7], &g) != TCL_OK ||
+            GetFloatFromObj(interp, objv[8], &b) != TCL_OK) {
+            return TCL_ERROR;
+        }
         opts.url() = url;
 
         osgEarth::Symbology::Style style;
-        osgEarth::Symbology::PolygonSymbol *ls = style.getOrCreateSymbol<osgEarth::Symbology::PolygonSymbol>();
-        ls->fill()->color() = osgEarth::Symbology::Color::White;
+        osgEarth::Symbology::PolygonSymbol *ps = style.getOrCreateSymbol<osgEarth::Symbology::PolygonSymbol>();
+        ps->fill()->color() = osgEarth::Symbology::Color(r, g, b);
 #if 1
         osgEarth::Symbology::AltitudeSymbol *alt = style.getOrCreateSymbol<osgEarth::Symbology::AltitudeSymbol>();
@@ -1106,8 +1191,10 @@
         geomOpts.styles()->addStyle(style);
         geomOpts.enableLighting() = false;
+        geomOpts.minRange() = 0.f;
+        geomOpts.maxRange() = FLT_MAX;
         g_renderer->addModelLayer(name, geomOpts);
     } else if (type[0] == 'l' && strcmp(type, "line") == 0) {
         osgEarth::Drivers::OGRFeatureOptions opts;
-        char *urlIn = Tcl_GetString(objv[4]);
+        char *urlIn = Tcl_GetString(objv[5]);
         std::string url = g_renderer->getCanonicalPath(std::string(urlIn));
         if (url.empty()) {
@@ -1116,11 +1203,20 @@
             return TCL_ERROR;
         }
-        char *name = Tcl_GetString(objv[5]);
+        float r, g, b;
+        if (GetFloatFromObj(interp, objv[6], &r) != TCL_OK ||
+            GetFloatFromObj(interp, objv[7], &g) != TCL_OK ||
+            GetFloatFromObj(interp, objv[8], &b) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        float lineWidth;
+        if (GetFloatFromObj(interp, objv[9], &lineWidth) != TCL_OK) {
+            return TCL_ERROR;
+        }
         opts.url() = url;
 
         osgEarth::Symbology::Style style;
         osgEarth::Symbology::LineSymbol *ls = style.getOrCreateSymbol<osgEarth::Symbology::LineSymbol>();
-        ls->stroke()->color() = osgEarth::Symbology::Color::Black;
-        ls->stroke()->width() = 2.0f;
+        ls->stroke()->color() = osgEarth::Symbology::Color(r, g, b);
+        ls->stroke()->width() = lineWidth;
 #if 1
         osgEarth::Symbology::AltitudeSymbol *alt = style.getOrCreateSymbol<osgEarth::Symbology::AltitudeSymbol>();
@@ -1139,8 +1235,19 @@
         geomOpts.styles()->addStyle(style);
         geomOpts.enableLighting() = false;
+        geomOpts.minRange() = 0.f;
+        geomOpts.maxRange() = FLT_MAX;
+        if (objc > 10) {
+            float min, max;
+            if (GetFloatFromObj(interp, objv[10], &min) != TCL_OK ||
+                GetFloatFromObj(interp, objv[11], &max) != TCL_OK) {
+                return TCL_ERROR;
+            }
+            geomOpts.minRange() = min;
+            geomOpts.maxRange() = max;
+        }
         g_renderer->addModelLayer(name, geomOpts);
    } else if (type[0] == 't' && strcmp(type, "text") == 0) {
         osgEarth::Drivers::OGRFeatureOptions opts;
-        char *urlIn = Tcl_GetString(objv[4]);
+        char *urlIn = Tcl_GetString(objv[5]);
         std::string url = g_renderer->getCanonicalPath(std::string(urlIn));
         if (url.empty()) {
@@ -1149,35 +1256,39 @@
             return TCL_ERROR;
         }
-        char *content = Tcl_GetString(objv[5]);
-        char *priority = Tcl_GetString(objv[6]);
-        char *name = Tcl_GetString(objv[7]);
-
-#if 0
-        double fgR = 1.0, fgG = 1.0, fgB = 1.0;
-        double bgR = 0.0, bgG = 0.0, bgB = 0.0;
-        if (objc > 8) {
-            if (Tcl_GetDoubleFromObj(interp, objv[8], &fgR) != TCL_OK ||
-                Tcl_GetDoubleFromObj(interp, objv[9], &fgG) != TCL_OK ||
-                Tcl_GetDoubleFromObj(interp, objv[10], &fgB) != TCL_OK ||
-                Tcl_GetDoubleFromObj(interp, objv[11], &bgR) != TCL_OK ||
-                Tcl_GetDoubleFromObj(interp, objv[12], &bgG) != TCL_OK ||
-                Tcl_GetDoubleFromObj(interp, objv[13], &bgB) != TCL_OK) {
-                return TCL_ERROR;
-            }
-        }
-#endif
+        char *content = Tcl_GetString(objv[6]);
+        char *priority = Tcl_GetString(objv[7]);
+        float fgR, fgG, fgB;
+        float bgR, bgG, bgB;
+        float haloWidth, ftSize;
+        if (GetFloatFromObj(interp, objv[8], &fgR) != TCL_OK ||
+            GetFloatFromObj(interp, objv[9], &fgG) != TCL_OK ||
+            GetFloatFromObj(interp, objv[10], &fgB) != TCL_OK ||
+            GetFloatFromObj(interp, objv[11], &bgR) != TCL_OK ||
+            GetFloatFromObj(interp, objv[12], &bgG) != TCL_OK ||
+            GetFloatFromObj(interp, objv[13], &bgB) != TCL_OK ||
+            GetFloatFromObj(interp, objv[14], &haloWidth) != TCL_OK ||
+            GetFloatFromObj(interp, objv[15], &ftSize) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        bool removeDupes, declutter;
+        if (GetBooleanFromObj(interp, objv[16], &removeDupes) != TCL_OK ||
+            GetBooleanFromObj(interp, objv[17], &declutter) != TCL_OK) {
+            return TCL_ERROR;
+        }
         opts.url() = url;
 
         osgEarth::Symbology::Style style;
         osgEarth::Symbology::TextSymbol *ts = style.getOrCreateSymbol<osgEarth::Symbology::TextSymbol>();
-        ts->halo()->color() = osgEarth::Symbology::Color::Black; //::Color(bgR, bgG, bgB);
-        ts->halo()->width() = 2.0f;
-        ts->fill()->color() = osgEarth::Symbology::Color::White; //::Color(fgR, fgG, fgB);
+        ts->halo()->color() = osgEarth::Symbology::Color(bgR, bgG, bgB);
+        ts->halo()->width() = haloWidth;
+        ts->fill()->color() = osgEarth::Symbology::Color(fgR, fgG, fgB);
         ts->content() = osgEarth::Symbology::StringExpression(content);
         ts->priority() = osgEarth::Symbology::NumericExpression(priority);
-        ts->removeDuplicateLabels() = true;
-        ts->size() = 16.0f;
+        ts->removeDuplicateLabels() = removeDupes;
+        //ts->font() = "Arial";
+        ts->size() = ftSize;
         ts->alignment() = osgEarth::Symbology::TextSymbol::ALIGN_CENTER_CENTER;
-        ts->declutter() = true;
+        ts->declutter() = declutter;
+        ts->encoding() = osgEarth::Symbology::TextSymbol::ENCODING_UTF8;
 
         osgEarth::Symbology::RenderSymbol* rs = style.getOrCreateSymbol<osgEarth::Symbology::RenderSymbol>();
@@ -1190,4 +1301,15 @@
         geomOpts.styles()->addStyle(style);
         geomOpts.enableLighting() = false;
+        geomOpts.minRange() = 0.f;
+        geomOpts.maxRange() = FLT_MAX;
+        if (objc > 10) {
+            float min, max;
+            if (GetFloatFromObj(interp, objv[18], &min) != TCL_OK ||
+                GetFloatFromObj(interp, objv[19], &max) != TCL_OK) {
+                return TCL_ERROR;
+            }
+            geomOpts.minRange() = min;
+            geomOpts.maxRange() = max;
+        }
         g_renderer->addModelLayer(name, geomOpts);
     } else {
@@ -1323,5 +1445,5 @@
 
 static CmdSpec mapLayerOps[] = {
-    {"add",     1, MapLayerAddOp,       6, 0, "type driver ?url? ?args? name"},
+    {"add",     1, MapLayerAddOp,       6, 0, "name type driver ?url? ?args?"},
     {"delete",  1, MapLayerDeleteOp,    3, 4, "?name?"},
     {"move",    1, MapLayerMoveOp,      5, 5, "pos name"},
@@ -1519,13 +1641,20 @@
         return TCL_ERROR;
     }
-
+    float color[3];
+    if (GetFloatFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    osg::Vec4f bgColor(color[0],color[1],color[2],1);
     if (type == osgEarth::MapOptions::CSTYPE_PROJECTED) {
-        if (objc < 4) {
+        if (objc < 7) {
             Tcl_AppendResult(interp, "wrong # arguments: profile required for projected maps", (char*)NULL);
             return TCL_ERROR;
         }
-        char *profile = Tcl_GetString(objv[3]);
-        if (objc > 4) {
-            if (objc < 8) {
+        char *profile = Tcl_GetString(objv[6]);
+        if (objc > 7) {
+            if (objc < 11) {
                 Tcl_AppendResult(interp, "wrong # arguments: 4 bounds arguments required", (char*)NULL);
                 return TCL_ERROR;
@@ -1533,8 +1662,9 @@
             double bounds[4];
             for (int i = 0; i < 4; i++) {
-                if (Tcl_GetDoubleFromObj(interp, objv[4+i], &bounds[i]) != TCL_OK) {
+                if (Tcl_GetDoubleFromObj(interp, objv[7+i], &bounds[i]) != TCL_OK) {
                     return TCL_ERROR;
                 }
             }
+#if 1
             // Check if min > max
             if (bounds[0] > bounds[2] ||
@@ -1579,5 +1709,6 @@
                 }
             }
-            g_renderer->resetMap(type, profile, bounds);
+#endif
+            g_renderer->resetMap(type, bgColor, profile, bounds);
         } else {
             if (osgEarth::Registry::instance()->getNamedProfile(profile) == NULL) {
@@ -1586,9 +1717,9 @@
                 return TCL_ERROR;
             }
-            g_renderer->resetMap(type, profile);
+            g_renderer->resetMap(type, bgColor, profile);
         }
     } else {
         // No profile required for geocentric (3D) maps
-        g_renderer->resetMap(type);
+        g_renderer->resetMap(type, bgColor);
     }
 
@@ -1644,32 +1775,6 @@
 
 static int
-MapTerrainEdgesOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+MapTerrainColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
                   Tcl_Obj *const *objv)
-{
-    bool state;
-    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
-        return TCL_ERROR;
-    }
-    TRACE("Not implemented");
-    //g_renderer->setTerrainEdges(state);
-    return TCL_OK;
-}
-
-static int
-MapTerrainLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
-                     Tcl_Obj *const *objv)
-{
-    bool state;
-    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
-        return TCL_ERROR;
-    }
-
-    g_renderer->setTerrainLighting(state);
-    return TCL_OK;
-}
-
-static int
-MapTerrainLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
-                      Tcl_Obj *const *objv)
 {
     float color[3];
@@ -1679,6 +1784,56 @@
         return TCL_ERROR;
     }
-    TRACE("Not implemented");
-    //g_renderer->setTerrainLineColor(color);
+    g_renderer->setTerrainColor(osg::Vec4f(color[0],color[1],color[2],1));
+    return TCL_OK;
+}
+
+static int
+MapTerrainEdgesOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    g_renderer->setTerrainEdges(state);
+    return TCL_OK;
+}
+
+static int
+MapTerrainLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->setTerrainLighting(state);
+    return TCL_OK;
+}
+
+static int
+MapTerrainLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    g_renderer->setTerrainLineColor(osg::Vec4f(color[0], color[1], color[2],1));
+    return TCL_OK;
+}
+
+static int
+MapTerrainLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[3], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    g_renderer->setTerrainLineWidth(width);
     return TCL_OK;
 }
@@ -1711,7 +1866,9 @@
 
 static CmdSpec mapTerrainOps[] = {
+    {"color",     1, MapTerrainColorOp,     6, 6, "r g b"},
     {"edges",     1, MapTerrainEdgesOp,     4, 4, "bool"},
     {"lighting",  2, MapTerrainLightingOp,  4, 4, "bool"},
-    {"linecolor", 2, MapTerrainLineColorOp, 6, 6, "r g b"},
+    {"linecolor", 5, MapTerrainLineColorOp, 6, 6, "r g b"},
+    {"linewidth", 5, MapTerrainLineWidthOp, 4, 4, "val"},
     {"vertscale", 1, MapTerrainVertScaleOp, 4, 4, "val"},
     {"wireframe", 1, MapTerrainWireframeOp, 4, 4, "bool"},
@@ -1778,5 +1935,5 @@
     {"pin",      2, MapPinOp,             3, 0, "op ?params...?"},
     {"posdisp",  2, MapPositionDisplayOp, 3, 5, "bool ?format? ?precision?"},
-    {"reset",    1, MapResetOp,           3, 8, "type ?profile xmin ymin xmax ymax?"},
+    {"reset",    1, MapResetOp,           6, 11, "type r g b ?profile xmin ymin xmax ymax?"},
     {"scalebar", 1, MapScaleBarOp,        3, 4, "bool ?units?"},
     {"setpos",   1, MapSetPositionOp,     2, 4, "x y"},
@@ -2293,4 +2450,5 @@
     Tcl_CreateObjCommand(interp, "imgflush",       ImageFlushCmd,     clientData, NULL);
     Tcl_CreateObjCommand(interp, "key",            KeyCmd,            clientData, NULL);
+    Tcl_CreateObjCommand(interp, "legend",         LegendCmd,         clientData, NULL);
     Tcl_CreateObjCommand(interp, "map",            MapCmd,            clientData, NULL);
     Tcl_CreateObjCommand(interp, "mouse",          MouseCmd,          clientData, NULL);
@@ -2310,4 +2468,5 @@
     Tcl_DeleteCommand(interp, "imgflush");
     Tcl_DeleteCommand(interp, "key");
+    Tcl_DeleteCommand(interp, "legend");
     Tcl_DeleteCommand(interp, "map");
     Tcl_DeleteCommand(interp, "mouse");
Index: /geovis/trunk/geovis_protocol.txt
===================================================================
--- /geovis/trunk/geovis_protocol.txt	(revision 5104)
+++ /geovis/trunk/geovis_protocol.txt	(revision 5105)
@@ -74,10 +74,9 @@
 clientinfo <list>
 
-colormap add <colorMapName> <colorMap> <opacityMap>
+colormap add <colorMapName> <colorMap>
          (Re-)define a colormap.  If colorMapName doesn't exist, it is created,
          otherwise the colormap is redefined and all users will be updated.
-	 colorMap = Tcl list of {value r g b} control points
-         opacityMap = Tcl list of {value alpha} control points
-colormap define <colorMapName> <colorMap> <opacityMap>
+	 colorMap = Tcl list of {value r g b a} control points
+colormap define <colorMapName> <colorMap>
          (Re-)define a colormap (same as 'add')
 colormap delete <?colorMapName?>
@@ -159,9 +158,9 @@
 
 Feature/model layers:
-map layer add point <url> <layerName>
+map layer add point <url> <r> <g> <b> <size> <layerName>
     Add a point feature layer from a file or URL
-map layer add polygon <url> <layerName>
+map layer add polygon <url> <r> <g> <b> <width> <layerName>
     Add a polygon feature layer from a file or URL
-map layer add line <url> <layerName>
+map layer add line <url> <r> <g> <b> <width> <layerName>
     Add a line feature layer from a file or URL
 map layer add text <url> <layerName>
@@ -218,4 +217,6 @@
 nv>image -type image -bytes <nbytes>
   <binary RGB data>
+nv>legend <colormapName> <rmin> <rmax> <nbytes>
+  <binary RGB data>
 nv>map coords <token> <mapCoordList> <?srs?> <?verticalDatum?>
    Reply to query of map coordinates from screen coordinates.  The mapCoordList
