Index: /geovis/trunk/Makefile.in
===================================================================
--- /geovis/trunk/Makefile.in	(revision 4627)
+++ /geovis/trunk/Makefile.in	(revision 4628)
@@ -5,4 +5,8 @@
 USE_THREADS		= yes
 USE_READ_THREAD         = #yes
+USE_CACHE               = yes
+USE_OSGEARTH_TRUNK      = #yes
+USE_THROTTLING_SLEEP    = #yes
+SLEEP_AFTER_QUEUE_FRAME = yes
 
 bindir          = @bindir@
@@ -95,4 +99,16 @@
 DEFINES         += -DUSE_READ_THREAD
 endif
+ifdef USE_CACHE
+DEFINES         += -DUSE_CACHE
+endif
+ifdef USE_OSGEARTH_TRUNK
+DEFINES         += -DUSE_OSGEARTH_TRUNK
+endif
+ifdef USE_THROTTLING_SLEEP
+DEFINES         += -DUSE_THROTTLING_SLEEP
+endif
+ifdef SLEEP_AFTER_QUEUE_FRAME
+DEFINES         += -DSLEEP_AFTER_QUEUE_FRAME
+endif
 
 CXX_SWITCHES	= $(CXXFLAGS) $(EXTRA_CXXFLAGS) $(DEFINES) $(INCLUDES)
Index: /geovis/trunk/RenderServer.cpp
===================================================================
--- /geovis/trunk/RenderServer.cpp	(revision 4627)
+++ /geovis/trunk/RenderServer.cpp	(revision 4628)
@@ -367,4 +367,7 @@
     for (;;) {
         long timeout = g_renderer->getTimeout();
+#ifdef SLEEP_AFTER_QUEUE_FRAME
+        g_renderer->markFrameStart();
+#endif
         int cmdStatus = processCommands(interp, NULL, g_inBufPtr, g_fdOut, timeout);
         if (cmdStatus < 0)
@@ -391,4 +394,7 @@
                 g_stats.nFrameBytes += imgData->s() * imgData->t() * 3;
             }
+#ifdef SLEEP_AFTER_QUEUE_FRAME
+            g_renderer->markFrameEnd();
+#endif
         } else {
             //TRACE("No render required");
Index: /geovis/trunk/Renderer.cpp
===================================================================
--- /geovis/trunk/Renderer.cpp	(revision 4627)
+++ /geovis/trunk/Renderer.cpp	(revision 4628)
@@ -20,7 +20,4 @@
 #include <sys/time.h>
 #endif
-
-//#define USE_OSGEARTH_TRUNK
-#define USE_CACHE
 
 #include <osgDB/FileUtils>
@@ -89,6 +86,10 @@
     _bgColor[1] = 0;
     _bgColor[2] = 0;
-    _minFrameTime = 1.0/30.0;
+    //setMaximumFrameRateInHertz(15.0);
+    // 100MBit
+    setMaximumBitrate(1.0e8);
     _lastFrameTime = _minFrameTime;
+    TRACE("Bandwidth target: %.2f MBit", (float)(getMaximumBitrate()/1.0e6));
+    TRACE("Frame rate target: %.2f Hz", (float)getMaximumFrameRateInHertz());
 
     char *base = getenv("MAP_BASE_URI");
@@ -1026,5 +1027,5 @@
 }
 
-void Renderer::addImageLayer(const char *name,
+bool Renderer::addImageLayer(const char *name,
                              osgEarth::TileSourceOptions& opts,
                              bool makeShared,
@@ -1033,5 +1034,5 @@
     if (!_map.valid()) {
         ERROR("No map");
-        return;
+        return false;
     }
     TRACE("layer: %s", name);
@@ -1046,6 +1047,13 @@
         layerOpts.visible() = false;
     }
-    _map->addImageLayer(new osgEarth::ImageLayer(layerOpts));
+    osg::ref_ptr<osgEarth::ImageLayer> layer = new osgEarth::ImageLayer(layerOpts);
+    _map->addImageLayer(layer.get());
+    if (layer->getTileSource() == NULL || !layer->getTileSource()->isOK()) {
+        ERROR("Failed to add image layer: %s", name);
+        _map->removeImageLayer(layer.get());
+        return false;
+    }
     _needsRedraw = true;
+    return true;
 }
 
@@ -1307,6 +1315,13 @@
     TRACE("Setting window size to %dx%d", width, height);
 
+    double origBitrate = getMaximumBitrate();
+
     _windowWidth = width;
     _windowHeight = height;
+
+    setMaximumBitrate(origBitrate);
+    TRACE("Bandwidth target: %.2f MBit", (float)(getMaximumBitrate()/1.0e6));
+    TRACE("Frame rate target: %.2f Hz", (float)getMaximumFrameRateInHertz());
+
     if (_viewer.valid()) {
 #ifdef USE_OFFSCREEN_RENDERING
@@ -1647,4 +1662,22 @@
 }
 
+void Renderer::markFrameStart()
+{
+    _startFrameTime = osg::Timer::instance()->tick();
+}
+
+void Renderer::markFrameEnd()
+{
+    osg::Timer_t endFrameTick = osg::Timer::instance()->tick();
+    _lastFrameTime = osg::Timer::instance()->delta_s(_startFrameTime, endFrameTick);
+    TRACE("Frame time: %g sec", _lastFrameTime);
+#ifdef USE_THROTTLING_SLEEP
+    if (_lastFrameTime < _minFrameTime) {
+        TRACE("Sleeping for %g secs", _minFrameTime - _lastFrameTime);
+        OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(_minFrameTime - _lastFrameTime)));
+    }
+#endif
+}
+
 /**
  * \brief Cause the rendering to render a new image if needed
@@ -1657,16 +1690,20 @@
     if (_viewer.valid() && checkNeedToDoFrame()) {
         TRACE("Enter needsRedraw=%d",  _needsRedraw ? 1 : 0);
+#ifndef SLEEP_AFTER_QUEUE_FRAME
         osg::Timer_t startFrameTick = osg::Timer::instance()->tick();
+#endif
         TRACE("Before frame()");
         _viewer->frame();
         TRACE("After frame()");
+#ifndef SLEEP_AFTER_QUEUE_FRAME
         osg::Timer_t endFrameTick = osg::Timer::instance()->tick();
         _lastFrameTime = osg::Timer::instance()->delta_s(startFrameTick, endFrameTick);
         TRACE("Frame time: %g sec", _lastFrameTime);
-#if 0
-        if (frameTime < minFrameTime) {
-            TRACE("Sleeping for %g secs", minFrameTime-frameTime);
-            OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(minFrameTime-frameTime)));
-        }
+#ifdef USE_THROTTLING_SLEEP
+        if (_lastFrameTime < _minFrameTime) {
+            TRACE("Sleeping for %g secs", _minFrameTime - _lastFrameTime);
+            OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(_minFrameTime - _lastFrameTime)));
+        }
+#endif
 #endif
 #ifdef WANT_TRACE
Index: /geovis/trunk/Renderer.h
===================================================================
--- /geovis/trunk/Renderer.h	(revision 4627)
+++ /geovis/trunk/Renderer.h	(revision 4628)
@@ -191,5 +191,5 @@
     }
 
-    void addImageLayer(const char *name, osgEarth::TileSourceOptions& opts,
+    bool addImageLayer(const char *name, osgEarth::TileSourceOptions& opts,
                        bool makeShared = false, bool visible = true);
 
@@ -358,4 +358,35 @@
                        bool invertY = true);
 
+    void setMaximumFrameRateInHertz(double rate)
+    {
+        if (rate > 60.0)
+            rate = 60.0;
+        if (rate < 0.25)
+            rate = 0.25;
+        _minFrameTime = 1.0/rate;
+    }
+
+    void setMaximumBitrate(double bitsPerSecond)
+    {
+        unsigned long bitsPerFrame = (_windowWidth * _windowHeight * 3 + 16) * 8;
+        double fps = bitsPerSecond / ((double)bitsPerFrame);
+        setMaximumFrameRateInHertz(fps);
+    }
+
+    double getMaximumFrameRateInHertz()
+    {
+        return (1.0/_minFrameTime);
+    }
+
+    double getMaximumBitrate()
+    {
+        unsigned long bitsPerFrame = (_windowWidth * _windowHeight * 3 + 16) * 8;
+        return ((double)bitsPerFrame * getMaximumFrameRateInHertz());
+    }
+
+    void markFrameStart();
+
+    void markFrameEnd();
+
     long getTimeout();
 
@@ -399,4 +430,5 @@
     double _minFrameTime;
     double _lastFrameTime;
+    osg::Timer_t _startFrameTime;
 
     ColorMapHashmap _colorMaps;
Index: /geovis/trunk/RendererCmd.cpp
===================================================================
--- /geovis/trunk/RendererCmd.cpp	(revision 4627)
+++ /geovis/trunk/RendererCmd.cpp	(revision 4628)
@@ -666,4 +666,52 @@
 
 static int
+FilePutOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    long size;
+    const char *path = Tcl_GetString(objv[2]);
+    if (Tcl_GetLongFromObj(interp, objv[4], &size) != TCL_OK ||
+        size < 1) {
+        return TCL_ERROR;
+    }
+    char *data = (char *)malloc((size_t)size);
+    if (!SocketRead(data, size)) {
+        free(data);
+        return TCL_ERROR;
+    }
+    std::ostringstream fullPath;
+    //fullPath << "/tmp/" << path;
+    fullPath << path;
+    FILE *fp = fopen(fullPath.str().c_str(), "wb");
+    if (fp == NULL) {
+        free(data);
+        return TCL_ERROR;
+    }
+    int bytesWritten = fwrite(data, 1, (size_t)size, fp);
+    fclose(fp);
+    free(data);
+    return (bytesWritten == size) ? TCL_OK : TCL_ERROR;
+}
+
+static CmdSpec fileOps[] = {
+    {"put",    1, FilePutOp, 5, 5, "name type size"}
+};
+static int nFileOps = NumCmdSpecs(fileOps);
+
+static int
+FileCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nFileOps, fileOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
 ImageFlushCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
               Tcl_Obj *const *objv)
@@ -804,4 +852,6 @@
     char *type = Tcl_GetString(objv[3]);
     if (type[0] == 'i' && strcmp(type, "image") == 0) {
+        bool ret;
+        char *name;
         char *driver = Tcl_GetString(objv[4]);
         std::string url;
@@ -817,11 +867,11 @@
         if (driver[0] == 'd' && strcmp(driver, "debug") == 0) {
             osgEarth::Drivers::DebugOptions opts;
-            char *name = Tcl_GetString(objv[5]);
-            g_renderer->addImageLayer(name, opts);
+            name = Tcl_GetString(objv[5]);
+            ret = g_renderer->addImageLayer(name, opts);
         } else if (driver[0] == 'g' && strcmp(driver, "gdal") == 0) {
             osgEarth::Drivers::GDALOptions opts;
             opts.url() = url;
-            char *name = Tcl_GetString(objv[6]);
-            g_renderer->addImageLayer(name, opts);
+            name = Tcl_GetString(objv[6]);
+            ret = g_renderer->addImageLayer(name, opts);
         } else if (driver[0] == 't' && strcmp(driver, "tms") == 0) {
             osgEarth::Drivers::TMSOptions opts;
@@ -831,6 +881,6 @@
             //opts.tmsType() = tmsType;
             //opts.format() = format;
-            char *name = Tcl_GetString(objv[6]);
-            g_renderer->addImageLayer(name, opts);
+            name = Tcl_GetString(objv[6]);
+            ret = g_renderer->addImageLayer(name, opts);
         } else if (driver[0] == 'w' && strcmp(driver, "wms") == 0) {
             osgEarth::Drivers::WMSOptions opts;
@@ -846,6 +896,6 @@
             opts.transparent() = transparent;
 
-            char *name = Tcl_GetString(objv[9]);
-            g_renderer->addImageLayer(name, opts);
+            name = Tcl_GetString(objv[9]);
+            ret = g_renderer->addImageLayer(name, opts);
         } else if (driver[0] == 'x' && strcmp(driver, "xyz") == 0) {
             osgEarth::Drivers::XYZOptions opts;
@@ -855,9 +905,13 @@
             //opts.invertY() = invertY;
             //opts.format() = Tcl_GetString(objv[6]);
-            char *name = Tcl_GetString(objv[6]);
-            g_renderer->addImageLayer(name, opts);
+            name = Tcl_GetString(objv[6]);
+            ret = g_renderer->addImageLayer(name, opts);
         } else {
             Tcl_AppendResult(interp, "unknown image driver \"", driver,
                              "\": should be 'debug', 'gdal', 'tms', 'wms' or 'xyz'", (char*)NULL);
+            return TCL_ERROR;
+        }
+        if (!ret) {
+            Tcl_AppendResult(interp, "Failed to add image layer \"", name, "\"", (char*)NULL);
             return TCL_ERROR;
         }
@@ -1981,4 +2035,5 @@
     Tcl_CreateObjCommand(interp, "clientinfo",     ClientInfoCmd,     clientData, NULL);
     Tcl_CreateObjCommand(interp, "colormap",       ColorMapCmd,       clientData, NULL);
+    Tcl_CreateObjCommand(interp, "file",           FileCmd,           clientData, NULL);
     Tcl_CreateObjCommand(interp, "imgflush",       ImageFlushCmd,     clientData, NULL);
     Tcl_CreateObjCommand(interp, "key",            KeyCmd,            clientData, NULL);
@@ -1997,4 +2052,5 @@
     Tcl_DeleteCommand(interp, "clientinfo");
     Tcl_DeleteCommand(interp, "colormap");
+    Tcl_DeleteCommand(interp, "file");
     Tcl_DeleteCommand(interp, "imgflush");
     Tcl_DeleteCommand(interp, "key");
