Index: /geovis/trunk/PPMWriter.cpp
===================================================================
--- /geovis/trunk/PPMWriter.cpp	(revision 5914)
+++ /geovis/trunk/PPMWriter.cpp	(revision 5915)
@@ -50,5 +50,5 @@
     char header[200];
 
-    TRACE("Entering (%dx%d)\n", width, height);
+    FRAME("Entering (%dx%d)\n", width, height);
     // Generate the PPM binary file header
     snprintf(header, sizeof(header), "P6 %d %d %d\n", width, height, 
@@ -95,5 +95,5 @@
     response->setMessage(mesg, length, Response::DYNAMIC);
     queue->enqueue(response);
-    TRACE("Leaving (%dx%d)\n", width, height);
+    FRAME("Leaving (%dx%d)\n", width, height);
 }
 #else
@@ -127,5 +127,5 @@
     char header[200];
 
-    TRACE("Entering (%dx%d)\n", width, height);
+    FRAME("Entering (%dx%d)\n", width, height);
     // Generate the PPM binary file header
     snprintf(header, sizeof(header), "P6 %d %d %d\n", width, height,
@@ -166,5 +166,5 @@
     free(iov);
 
-    TRACE("Leaving (%dx%d)\n", width, height);
+    FRAME("Leaving (%dx%d)\n", width, height);
 }
 
Index: /geovis/trunk/RenderServer.cpp
===================================================================
--- /geovis/trunk/RenderServer.cpp	(revision 5914)
+++ /geovis/trunk/RenderServer.cpp	(revision 5915)
@@ -292,5 +292,7 @@
         }
         fflush(g_fOut);
+#ifdef TRACE_RESPONSE_QUEUE
         TRACE("Wrote response of type %d", response->type());
+#endif
         delete response;
         if (feof(g_fOut))
@@ -401,10 +403,10 @@
 
         if (g_renderer->render()) {
-            TRACE("Rendered new frame");
+            FRAME("Rendered new frame");
             imgData = g_renderer->getRenderedFrame();
             if (imgData == NULL) {
                 ERROR("Empty image");
             } else {
-                TRACE("Image: %d x %d", imgData->s(), imgData->t());
+                FRAME("Image: %d x %d", imgData->s(), imgData->t());
 
                 if (imgData->s() == g_renderer->getWindowWidth() &&
Index: /geovis/trunk/Renderer.cpp
===================================================================
--- /geovis/trunk/Renderer.cpp	(revision 5914)
+++ /geovis/trunk/Renderer.cpp	(revision 5915)
@@ -35,4 +35,5 @@
 
 #include <osgEarth/Version>
+#include <osgEarth/Registry>
 #include <osgEarth/FileUtils>
 #include <osgEarth/Cache>
@@ -691,4 +692,5 @@
 #if 1
     osgEarth::Util::EarthManipulator::Settings *settings = _manipulator->getSettings();
+
     settings->bindMouse(osgEarth::Util::EarthManipulator::ACTION_ROTATE,
                         osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON,
@@ -700,4 +702,12 @@
                         osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON,
                         osgGA::GUIEventAdapter::MODKEY_ALT, options);
+
+    if (_map->isGeocentric()) {
+        settings->setMinMaxDistance(1.0, 2.0e7);
+    } else {
+        osgEarth::GeoExtent extent = _map->getProfile()->getExtent();
+        settings->setMinMaxDistance(1.0, getMaxDistanceFromExtent(extent));
+    }
+
     _manipulator->applySettings(settings);
 #endif
@@ -762,5 +772,5 @@
             mapOpts.profile() = osgEarth::ProfileOptions();
             if (strcmp(profile, "geodetic") == 0) {
-                mapOpts.profile()->srsString() = "epsg:4326";
+                mapOpts.profile()->srsString() = "epsg:4326"; // WGS84
             } else if (strcmp(profile, "spherical-mercator") == 0) {
                 // Projection used by Google/Bing/OSM
@@ -775,6 +785,6 @@
                   bounds[0], bounds[1], bounds[2], bounds[3]);
             if (1) {
-                osgEarth::SpatialReference *fromSRS = osgEarth::SpatialReference::create("wgs84");
-                osgEarth::SpatialReference *toSRS = osgEarth::SpatialReference::create(mapOpts.profile()->srsString().get());
+                osg::ref_ptr<osgEarth::SpatialReference> fromSRS = osgEarth::SpatialReference::create("wgs84");
+                osg::ref_ptr<osgEarth::SpatialReference> toSRS = osgEarth::SpatialReference::create(mapOpts.profile()->srsString().get());
                 osgEarth::Bounds extents(bounds[0], bounds[1], bounds[2], bounds[3]);
                 extents.transform(fromSRS, toSRS);
@@ -2245,8 +2255,10 @@
         // +y = zoom out, -y = zoom in
         TRACE("camDist: %g", _manipulator->getDistance());
+#ifdef LIMIT_ZOOM_BY_MAP_SCALE
         if ((_mapScale < 0.0 && y > 0.0) ||
             (_mapScale < 0.1 && y < 0.0) ||
             (_mapScale > 40000.0 && y > 0.0))
             return;
+#endif
 #if 1
        _manipulator->zoom(0, y);
@@ -2260,8 +2272,254 @@
 }
 
+bool Renderer::getImageLayerExtent(const char *name, osgEarth::GeoExtent &ext)
+{
+    if (!_map.valid()) {
+        ERROR("No map");
+        return false;
+    }
+    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
+    if (layer != NULL) {
+        osgEarth::TileSource *ts = layer->getTileSource();
+        if (ts != NULL) {
+            ext = ts->getDataExtentsUnion();
+            if (!ext.isValid() && ts->getProfile() != NULL) {
+                ext = ts->getProfile()->getExtent();
+            }
+            INFO("Image layer %s srs: %s extent: %g %g %g %g",
+                 name, ext.getSRS() ? ext.getSRS()->getName().c_str() : "nil",
+                 ext.xMin(), ext.yMin(), ext.xMax(), ext.yMax());
+            return true;
+        }
+    } else {
+        TRACE("Image layer not found: %s", name);
+    }
+    return false;
+}
+
+bool Renderer::getElevationLayerExtent(const char *name, osgEarth::GeoExtent &ext)
+{
+    if (!_map.valid()) {
+        ERROR("No map");
+        return false;
+    }
+    osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name);
+    if (layer != NULL) {
+        osgEarth::TileSource *ts = layer->getTileSource();
+        if (ts != NULL) {
+            ext = ts->getDataExtentsUnion();
+            if (!ext.isValid() && ts->getProfile() != NULL) {
+                ext = ts->getProfile()->getExtent();
+            }
+            INFO("Elevation Layer %s srs: %s extent: %g %g %g %g",
+                 name, ext.getSRS() ? ext.getSRS()->getName().c_str() : "nil",
+                 ext.xMin(), ext.yMin(), ext.xMax(), ext.yMax());
+            return true;
+        }
+    } else {
+        TRACE("Image layer not found: %s", name);
+    }
+    return false;
+}
+
+bool Renderer::getModelLayerExtent(const char *name, osgEarth::GeoExtent &ext)
+{
+    if (!_map.valid()) {
+        ERROR("No map");
+        return false;
+    }
+    osgEarth::ModelLayer *layer = _map->getModelLayerByName(name);
+    if (layer != NULL) {
+        osgEarth::ModelSource *ms = layer->getModelSource();
+        if (ms != NULL) {
+            osgEarth::DataExtentList& dataExtents = ms->getDataExtents();
+            if (dataExtents.size() > 0) {
+                ext = dataExtents[0];
+                for (unsigned int i = 1; i < dataExtents.size(); i++) {
+                    ext.expandToInclude(dataExtents[i]);
+                }
+            }
+            if (!ext.isValid()) {
+                ERROR("Couldn't find extent of layer %s", name);
+                return false;
+            }
+            INFO("Model Layer %s srs: %s extent: %g %g %g %g",
+                 name, ext.getSRS() ? ext.getSRS()->getName().c_str() : "nil",
+                 ext.xMin(), ext.yMin(), ext.xMax(), ext.yMax());
+            return true;
+        }
+    } else {
+        TRACE("Image layer not found: %s", name);
+    }
+    return false;
+}
+
+/**
+ * \brief Set view to fit a bounding rectangle
+ *
+ * A camera distance is chosen such that the extent rectangle
+ * will fit in the vertical field of view.
+ *
+ * \param ext input GeoExtent
+ * \param durationSecs Animation duration for the view change.
+ */
+void Renderer::setViewpointFromExtent(const osgEarth::GeoExtent& ext,
+                                      double durationSecs)
+{
+    setViewpointFromRect(ext.xMin(), ext.yMin(), ext.xMax(), ext.yMax(),
+                         ext.getSRS(), durationSecs);
+}
+
+/**
+ * \brief Set view to fit a bounding rectangle
+ *
+ * A camera distance is chosen such that the extent rectangle
+ * will fit in the vertical field of view.
+ *
+ * \param xmin Minimum X coord in the given SRS
+ * \param ymin Minimum Y coord in the given SRS
+ * \param xmax Maximum X coord in the given SRS
+ * \param ymax Maximum Y coord in the given SRS
+ * \param srs Optional SRS of bounding points.  If omitted, wgs84 is assumed.
+ * \param durationSecs Animation duration for the view change.
+ */
+void Renderer::setViewpointFromRect(double xmin, double ymin,
+                                    double xmax, double ymax,
+                                    const osgEarth::SpatialReference *srs,
+                                    double durationSecs)
+{
+    if (!_viewer.valid() || !_manipulator.valid() || getMapSRS() == NULL)
+        return;
+
+    double x = 0.0, y = 0.0, distance = 0.0;
+    double x1 = xmin, y1 = ymin, x2 = xmax, y2 = ymax;
+    osgEarth::Units distanceUnits = getMapSRS()->getUnits();
+    if (getMapSRS()->isProjected() && !getMapSRS()->isPlateCarre()) {
+        INFO("Projected");
+        osg::ref_ptr<const osgEarth::SpatialReference> fromSRS = 
+            srs ? srs : osgEarth::SpatialReference::create("wgs84");
+        fromSRS->transformExtentToMBR(getMapSRS(), x1, y1, x2, y2);
+        double height = y2 - y1;
+        double fovy, aspect, near, far;
+        _viewer->getCamera()->getProjectionMatrixAsPerspective(fovy, aspect, near, far);
+        distance = height / (2. * tan(osg::DegreesToRadians(fovy)/2.));
+        INFO("fov: %g a: %g n: %g f: %g (%g, %g, %g, %g)", fovy, aspect, near, far, x1, y1, x2, y2);
+        x = x1 + (x2 - x1)/2.0;
+        y = y1 + (y2 - y1)/2.0;
+        INFO("x: %g y: %g, dist: %g", x, y, distance);
+    } else if (getMapSRS()->isGeographic() && !getMapSRS()->isPlateCarre()) {
+        // World coords are ECEF
+        INFO("Geocentric");
+        osg::ref_ptr<const osgEarth::SpatialReference> fromSRS =
+            srs ? srs : osgEarth::SpatialReference::create("wgs84");
+        fromSRS->transformExtentToMBR(getMapSRS(), x1, y1, x2, y2);
+        INFO("(%g, %g, %g, %g)", x1, y1, x2, y2);
+        // These are angular units
+        x = x1 + (x2 - x1)/2.0;
+        y = y1 + (y2 - y1)/2.0;
+        // These will be ECEF
+        osg::Vec3d world1, world2, world3, world4;
+        // bottom
+        getMapSRS()->transformToWorld(osg::Vec3d(x, y1, 0), world1);
+        // top
+        getMapSRS()->transformToWorld(osg::Vec3d(x, y2, 0), world2);
+        // Focal point on surface
+        getMapSRS()->transformToWorld(osg::Vec3d(x, y, 0), world3);
+        // point on line between top and bottom points
+        world4 = world1 + (world2 - world1)/2.0;
+        INFO("world1: %g,%g,%g world2: %g,%g,%g",
+             world1.x(), world1.y(), world1.z(),
+             world2.x(), world2.y(), world2.z());
+        INFO("world3: %g,%g,%g world4: %g,%g,%g",
+             world3.x(), world3.y(), world3.z(),
+             world4.x(), world4.y(), world4.z());
+        double height = (world2 - world1).length();
+        double fovy, aspect, near, far;
+        _viewer->getCamera()->getProjectionMatrixAsPerspective(fovy, aspect, near, far);
+        distance = height / (2. * tan(osg::DegreesToRadians(fovy)/2.));
+        distance -= (world4 - world3).length();
+        if (distance < 0.0) distance = 0.0;
+        distanceUnits = osgEarth::Units::METERS;
+        INFO("fov: %g a: %g n: %g f: %g (%g, %g, %g, %g)", fovy, aspect, near, far, x1, y1, x2, y2);
+        INFO("x: %g y: %g, dist: %g", x, y, distance);
+    } else {
+        assert(getMapSRS()->isPlateCarre());
+        ERROR("Plate Carree not supported");
+        return;
+    }
+    INFO("Map units: %d", getMapSRS()->getUnits().getType());
+    osgEarth::Viewpoint vpt;
+#ifdef NEW_VIEWPOINT_API
+    vpt.focalPoint()->set(getMapSRS(), x, y, 0.0, osgEarth::ALTMODE_ABSOLUTE);
+    vpt.range()->set(distance, distanceUnits);
+    vpt.heading()->set(0.0, osgEarth::Units::DEGREES);
+    vpt.pitch()->set(-90.0, osgEarth::Units::DEGREES);
+#else
+    vpt.x() = x;
+    vpt.y() = y;
+    vpt.z() = 0.0;
+    vpt.setRange(distance);
+    vpt.setHeading(0.0);
+    vpt.setPitch(-90.0);
+#endif
+    _manipulator->setViewpoint(vpt, durationSecs);
+    _needsRedraw = true;
+}
+
+double Renderer::getMaxDistanceFromExtent(const osgEarth::GeoExtent& extent)
+{
+    if (!_viewer.valid() || extent.getSRS() == NULL)
+        return 2.0e7;
+
+    double x = 0.0, y = 0.0, distance = 0.0;
+    double x1 = extent.xMin(), y1 = extent.yMin(), x2 = extent.xMax(), y2 = extent.yMax();
+    osgEarth::Units distanceUnits = extent.getSRS()->getUnits();
+    if (extent.getSRS()->isProjected() && !extent.getSRS()->isPlateCarre()) {
+        INFO("Projected");
+        double height = y2 - y1;
+        double fovy, aspect, near, far;
+        _viewer->getCamera()->getProjectionMatrixAsPerspective(fovy, aspect, near, far);
+        distance = height / (2. * tan(osg::DegreesToRadians(fovy)/2.));
+        INFO("fov: %g a: %g n: %g f: %g (%g, %g, %g, %g)", fovy, aspect, near, far, x1, y1, x2, y2);
+        x = x1 + (x2 - x1)/2.0;
+        y = y1 + (y2 - y1)/2.0;
+        INFO("x: %g y: %g, dist: %g", x, y, distance);
+    } else if (extent.getSRS()->isGeographic() && !extent.getSRS()->isPlateCarre()) {
+        // World coords are ECEF
+        INFO("Geocentric");
+        INFO("(%g, %g, %g, %g)", x1, y1, x2, y2);
+        // These are angular units
+        x = x1 + (x2 - x1)/2.0;
+        y = y1 + (y2 - y1)/2.0;
+        // These will be ECEF
+        osg::Vec3d world1, world2, world3, world4;
+        // bottom
+        getMapSRS()->transformToWorld(osg::Vec3d(x, y1, 0), world1);
+        // top
+        getMapSRS()->transformToWorld(osg::Vec3d(x, y2, 0), world2);
+        // Focal point on surface
+        getMapSRS()->transformToWorld(osg::Vec3d(x, y, 0), world3);
+        // point on line between top and bottom points
+        world4 = world1 + (world2 - world1)/2.0;
+        double height = (world2 - world1).length();
+        double fovy, aspect, near, far;
+        _viewer->getCamera()->getProjectionMatrixAsPerspective(fovy, aspect, near, far);
+        distance = height / (2. * tan(osg::DegreesToRadians(fovy)/2.));
+        distance -= (world4 - world3).length();
+        if (distance < 0.0) distance = 0.0;
+        distanceUnits = osgEarth::Units::METERS;
+        INFO("fov: %g a: %g n: %g f: %g (%g, %g, %g, %g)", fovy, aspect, near, far, x1, y1, x2, y2);
+        INFO("x: %g y: %g, dist: %g", x, y, distance);
+    } else {
+        assert(extent.getSRS()->isPlateCarre());
+        ERROR("Plate Carree not supported");
+    }
+
+    return distance;
+}
+
 /**
  * \brief Dolly camera to set distance from focal point
  *
- * \param[in] dist distance in map? coordinates
+ * \param[in] dist distance in world coordinate units (typically meters)
  */
 void Renderer::setCameraDistance(double dist)
@@ -2459,11 +2717,11 @@
     _lastFrameTime = osg::Timer::instance()->delta_s(_startFrameTime, endFrameTick);
     if (_lastFrameTime > _minFrameTime) {
-        TRACE("BROKE FRAME by %.2f msec", (_lastFrameTime - _minFrameTime)*1000.0f);
-    } else {
-        TRACE("Frame time: %.2f msec", _lastFrameTime*1000.0f);
+        FRAME("BROKE FRAME by %.2f msec", (_lastFrameTime - _minFrameTime)*1000.0f);
+    } else {
+        FRAME("Frame time: %.2f msec", _lastFrameTime*1000.0f);
     }
 #ifdef USE_THROTTLING_SLEEP
     if (_lastFrameTime < _minFrameTime) {
-        TRACE("Sleeping for %.2f msec", (_minFrameTime - _lastFrameTime)*1000.0f);
+        FRAME("Sleeping for %.2f msec", (_minFrameTime - _lastFrameTime)*1000.0f);
         OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(_minFrameTime - _lastFrameTime)));
     }
@@ -2480,22 +2738,22 @@
 {
     if (_viewer.valid() && checkNeedToDoFrame()) {
-        TRACE("Enter needsRedraw=%d",  _needsRedraw ? 1 : 0);
+        FRAME("Enter needsRedraw=%d",  _needsRedraw ? 1 : 0);
         _renderStartTime = osg::Timer::instance()->tick();
-        TRACE("Before frame()");
+        FRAME("Before frame()");
         _viewer->frame();
-        TRACE("After frame()");
+        FRAME("After frame()");
         _renderStopTime = osg::Timer::instance()->tick();
         _renderTime = osg::Timer::instance()->delta_s(_renderStartTime, _renderStopTime);
-        TRACE("Render time: %g msec", _renderTime * 1000.0);
+        FRAME("Render time: %g msec", _renderTime * 1000.0);
 #ifndef SLEEP_AFTER_QUEUE_FRAME
         _lastFrameTime = _renderTime;
 #ifdef USE_THROTTLING_SLEEP
         if (_lastFrameTime < _minFrameTime) {
-            TRACE("Sleeping for %.2f msec", (_minFrameTime - _lastFrameTime)*1000.0f);
+            FRAME("Sleeping for %.2f msec", (_minFrameTime - _lastFrameTime)*1000.0f);
             OpenThreads::Thread::microSleep(static_cast<unsigned int>(1.0e6*(_minFrameTime - _lastFrameTime)));
         }
 #endif
 #endif
-#ifdef WANT_TRACE
+#ifdef WANT_FRAME
         if (_viewer->getViewerStats() != NULL) {
             _viewer->getViewerStats()->report(std::cerr, _viewer->getViewerStats()->getLatestFrameNumber());
Index: /geovis/trunk/Renderer.h
===================================================================
--- /geovis/trunk/Renderer.h	(revision 5914)
+++ /geovis/trunk/Renderer.h	(revision 5915)
@@ -72,5 +72,5 @@
     virtual void operator()(osg::RenderInfo &renderInfo) const
     {
-        TRACE("Enter ScreenCaptureCallback");
+        FRAME("Enter ScreenCaptureCallback");
         int width, height;
         if (renderInfo.getCurrentCamera() == NULL) {
@@ -84,5 +84,5 @@
         width = (int)renderInfo.getCurrentCamera()->getViewport()->width();
         height = (int)renderInfo.getCurrentCamera()->getViewport()->height();
-        TRACE("readPixels: %d x %d", width, height);
+        FRAME("readPixels: %d x %d", width, height);
 #if 0 //USE_OFFSCREEN_FRAMEBUFFER
         _image = _texture->getImage();
@@ -267,4 +267,6 @@
     void moveImageLayer(const char *name, unsigned int pos);
 
+    bool getImageLayerExtent(const char *name, osgEarth::GeoExtent &ext);
+
     void setImageLayerVisibleRange(const char *name, float min, float max);
 
@@ -309,4 +311,6 @@
     void moveElevationLayer(const char *name, unsigned int pos);
 
+    bool getElevationLayerExtent(const char *name, osgEarth::GeoExtent &ext);
+
     void setElevationLayerVisibleRange(const char *name, float min, float max);
 
@@ -342,4 +346,6 @@
     void moveModelLayer(const char *name, unsigned int pos);
 
+    bool getModelLayerExtent(const char *name, osgEarth::GeoExtent &ext);
+
     //void setModelLayerVisibleRange(const char *name, float min, float max);
 
@@ -373,4 +379,14 @@
 
     void setViewpoint(const osgEarth::Viewpoint& v, double durationSecs = 0.0);
+
+    double getMaxDistanceFromExtent(const osgEarth::GeoExtent& extent);
+
+    void setViewpointFromExtent(const osgEarth::GeoExtent& ext,
+                                double durationSecs = 0.0);
+
+    void setViewpointFromRect(double xmin, double ymin,
+                              double xmax, double ymax,
+                              const osgEarth::SpatialReference *srs = NULL,
+                              double durationSecs = 0.0);
 
     void resetCamera(bool resetOrientation = true);
Index: /geovis/trunk/RendererCmd.cpp
===================================================================
--- /geovis/trunk/RendererCmd.cpp	(revision 5914)
+++ /geovis/trunk/RendererCmd.cpp	(revision 5915)
@@ -37,4 +37,5 @@
 #include <osgEarthDrivers/osg/ColorRampOptions>
 #include <osgEarthDrivers/debug/DebugOptions>
+#include <osgEarthDrivers/arcgis/ArcGISOptions>
 #include <osgEarthDrivers/gdal/GDALOptions>
 #include <osgEarthDrivers/tms/TMSOptions>
@@ -583,4 +584,57 @@
 
 static int
+CameraSetByExtentOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    double xmin, ymin, xmax, ymax;
+    double duration = 0.0;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &xmin) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &ymin) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &xmax) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &ymax) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc > 6) {
+        if (Tcl_GetDoubleFromObj(interp, objv[6], &duration) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    osg::ref_ptr<const osgEarth::SpatialReference> srs = g_renderer->getMapSRS();
+    if (objc > 7) {
+        char *srsInit = Tcl_GetString(objv[7]);
+        if (strlen(srsInit) > 0) {
+            srs = osgEarth::SpatialReference::get(srsInit);
+        }
+    }
+    g_renderer->setViewpointFromRect(xmin, ymin, xmax, ymax, srs.get(), duration);
+    return TCL_OK;
+}
+
+static int
+CameraSetByLayerExtentOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    const char *name = Tcl_GetString(objv[2]);
+    double duration = 0.0;
+    if (objc > 3) {
+        if (Tcl_GetDoubleFromObj(interp, objv[3], &duration) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    osgEarth::GeoExtent ext;
+    if (g_renderer->getImageLayerExtent(name, ext)) {
+        g_renderer->setViewpointFromExtent(ext, duration);
+        return TCL_OK;
+    } else if (g_renderer->getElevationLayerExtent(name, ext)) {
+        g_renderer->setViewpointFromExtent(ext, duration);
+        return TCL_OK;
+    } else if (g_renderer->getModelLayerExtent(name, ext)) {
+        g_renderer->setViewpointFromExtent(ext, duration);
+        return TCL_OK;
+    }
+    return TCL_ERROR;
+}
+
+static int
 CameraSetDistanceOp(ClientData clientData, Tcl_Interp *interp, int objc, 
                     Tcl_Obj *const *objv)
@@ -676,6 +730,8 @@
     {"delete",  2, CameraDeleteViewpointOp,  3, 3, "name"},
     {"dist",    2, CameraSetDistanceOp,      3, 3, "distance"},
+    {"extent",  1, CameraSetByExtentOp,      6, 8, "xmin ymin xmax ymax ?duration? ?srs?"},
     {"get",     2, CameraGetViewpointOp,     2, 2, ""},
     {"go",      2, CameraGoOp,               4, 6, "x y ?zoomFactor? ?duration?"},
+    {"lextent", 1, CameraSetByLayerExtentOp, 3, 4, "layerName ?duration?"},
     {"orient",  1, CameraOrientOp,           6, 6, "qw qx qy qz"},
     {"pan",     1, CameraPanOp,              4, 4, "panX panY"},
@@ -1391,4 +1447,12 @@
             osgEarth::Drivers::DebugOptions opts;
             ret = g_renderer->addImageLayer(name, opts, cache, coverage, shared, visible, minLOD, maxLOD);
+        } else if (driver[0] == 'a' && strcmp(driver, "arcgis") == 0) {
+            osgEarth::Drivers::ArcGISOptions opts;
+            opts.url() = url;
+            opts.token() = Tcl_GetString(objv[9]);
+            //opts.format() = Tcl_GetString(objv[10]);
+            //opts.layers() = Tcl_GetString(objv[11]);
+            ret = g_renderer->addImageLayer(name, opts, cache, coverage, shared, visible, minLOD, maxLOD);
+            g_renderer->setImageLayerVisibleRange(name, minRange, maxRange);
         } else if (driver[0] == 'g' && strcmp(driver, "gdal") == 0) {
             osgEarth::Drivers::GDALOptions opts;
@@ -2554,5 +2618,4 @@
                 }
             }
-#if 1
             // Check if min > max
             if (bounds[0] > bounds[2] ||
@@ -2577,4 +2640,8 @@
                     return TCL_ERROR;
                 }
+                // As of osgearth 2.7, these are not permitted as map profiles.
+                // Use epsg:32663 instead
+                Tcl_AppendResult(interp, "Invalid profile: can't use geographic coordinate system as projection.  Consider using an equirectangular projection (epsg:32663) instead.", (char*)NULL);
+                return TCL_ERROR;
             } else if (strcmp(profile, "spherical-mercator") == 0 ||
                        strcmp(profile, "epsg:900913") == 0 ||
@@ -2583,5 +2650,5 @@
                        strcmp(profile, "epsg:102113") == 0) {
                 for (int i = 0; i < 4; i++) {
-                    if (bounds[i] < -20037508.34 || bounds[i] > 20037508.34) {
+                    if (bounds[i] < -20037508.34278925 || bounds[i] > 20037508.34278925) {
                         Tcl_AppendResult(interp, "invalid bounds", (char*)NULL);
                         return TCL_ERROR;
@@ -2590,15 +2657,17 @@
             } else if (strcmp(profile, "epsg:32662") == 0 ||
                        strcmp(profile, "epsg:32663") == 0) {
-                if (bounds[0] < -20037508.3428 || bounds[0] > 20037508.3428 ||
-                    bounds[2] < -20037508.3428 || bounds[2] > 20037508.3428 ||
-                    bounds[1] < -10018754.1714 || bounds[1] > 10018754.1714 ||
-                    bounds[3] < -10018754.1714 || bounds[3] > 10018754.1714) {
+                // epsg:32662 is deprecated: spherical method applied to ellipsoid WGS84
+                // Equirectangular projection (WGS84/World Equidistant Cylindrical)
+                if (bounds[0] < -20037508.34278925 || bounds[0] > 20037508.34278925 ||
+                    bounds[2] < -20037508.34278925 || bounds[2] > 20037508.34278925 ||
+                    bounds[1] < -10018754.17139463 || bounds[1] > 10018754.17139463 ||
+                    bounds[3] < -10018754.17139463 || bounds[3] > 10018754.17139463) {
                     Tcl_AppendResult(interp, "invalid bounds", (char*)NULL);
                     return TCL_ERROR;
                 }
             }
-#endif
             g_renderer->resetMap(type, bgColor, profile, bounds);
         } else {
+            // If no bounds are given, this must be a named profile with implicit bounds
             if (osgEarth::Registry::instance()->getNamedProfile(profile) == NULL) {
                 Tcl_AppendResult(interp, "bad named profile \"", profile,
Index: /geovis/trunk/ResponseQueue.cpp
===================================================================
--- /geovis/trunk/ResponseQueue.cpp	(revision 5914)
+++ /geovis/trunk/ResponseQueue.cpp	(revision 5915)
@@ -48,5 +48,7 @@
 #ifdef QUEUE_ONLY_ONE_FRAME
     /* Examine the list and remove any queued responses of the same type. */
+#ifdef TRACE_RESPONSE_QUEUE
     TRACE("Before # of elements is %d", _list.size());
+#endif
     for (std::list<Response *>::iterator itr = _list.begin();
          itr != _list.end();) {
@@ -71,5 +73,7 @@
     /* Add the new response to the end of the list. */
     _list.push_back(response);
+#ifdef TRACE_RESPONSE_QUEUE
     TRACE("After # of elements is %d", _list.size());
+#endif
     if (sem_post(&_ready) < 0) {
         ERROR("can't post semaphore: %s", strerror(errno));
@@ -103,5 +107,7 @@
         response = _list.front();
         _list.pop_front();
+#ifdef TRACE_RESPONSE_QUEUE
         TRACE("Dequeued response of type %d", response->type());
+#endif
     }
     if (pthread_mutex_unlock(&_idle) != 0) {
Index: /geovis/trunk/Trace.h
===================================================================
--- /geovis/trunk/Trace.h	(revision 5914)
+++ /geovis/trunk/Trace.h	(revision 5915)
@@ -37,4 +37,9 @@
 #define TRACE(...) 
 #endif  /*WANT_TRACE*/
+#ifdef WANT_FRAME
+#define FRAME(...)	GeoVis::logMessage(LOG_DEBUG, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
+#else 
+#define FRAME(...) 
+#endif  /*WANT_FRAME*/
 #define WARN(...)	GeoVis::logMessage(LOG_WARNING, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
 #define INFO(...)	GeoVis::logMessage(LOG_INFO, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
