Index: /geovis/trunk/RenderServer.h
===================================================================
--- /geovis/trunk/RenderServer.h	(revision 4635)
+++ /geovis/trunk/RenderServer.h	(revision 4636)
@@ -21,5 +21,5 @@
 class Stats;
 
-#define GEOVIS_VERSION_STRING "0.5.0"
+#define GEOVIS_VERSION_STRING "0.6.0"
 
 #define MSECS_ELAPSED(t1, t2) \
Index: /geovis/trunk/Renderer.cpp
===================================================================
--- /geovis/trunk/Renderer.cpp	(revision 4635)
+++ /geovis/trunk/Renderer.cpp	(revision 4636)
@@ -12,4 +12,5 @@
 #include <cstdlib>
 
+#include <limits>
 #include <set>
 
@@ -986,4 +987,53 @@
 }
 
+bool Renderer::worldToScreen(std::vector<osg::Vec3d>& coords, bool invertY)
+{
+    if (!_viewer.valid()) {
+        ERROR("No viewer");
+        return false;
+    }
+    osg::Camera *cam = _viewer->getCamera();
+    osg::Matrixd MVP = cam->getViewMatrix() * cam->getProjectionMatrix();
+    const osg::Viewport *viewport = cam->getViewport();
+    for (std::vector<osg::Vec3d>::iterator itr = coords.begin();
+         itr != coords.end(); ++itr) {
+        // Get clip coords
+        osg::Vec4d pt;
+        pt = osg::Vec4d(*itr, 1.0) * MVP;
+        // Clip
+        if (pt.x() < -pt.w() ||
+            pt.x() > pt.w() ||
+            pt.y() < -pt.w() ||
+            pt.y() > pt.w() ||
+            pt.z() < -pt.w() ||
+            pt.z() > pt.w()) {
+            // Outside frustum
+            TRACE("invalid pt: %g,%g,%g,%g", pt.x(), pt.y(), pt.z(), pt.w());
+            itr->set(std::numeric_limits<double>::quiet_NaN(),
+                     std::numeric_limits<double>::quiet_NaN(),
+                     std::numeric_limits<double>::quiet_NaN());
+            continue;
+        }
+        TRACE("clip pt: %g,%g,%g,%g", pt.x(), pt.y(), pt.z(), pt.w());
+        // Perspective divide: now NDC
+        pt /= pt.w();
+#if 1
+        itr->x() = viewport->x() + viewport->width() * 0.5 + pt.x() * viewport->width() * 0.5;
+        itr->y() = viewport->y() + viewport->height() * 0.5 + pt.y() * viewport->height() * 0.5;
+        //double near = 0;
+        //double far = 1;
+        //itr->z() = (far + near) * 0.5 + (far - near) * 0.5 * pt.z();
+        itr->z() = 0.5 + 0.5 * pt.z();
+#else
+        *itr = osg::Vec3d(pt.x(), pt.y(), pt.z()) * viewport->computeWindowMatrix();
+#endif
+        if (invertY) {
+            itr->y() = viewport->height() - itr->y();
+        }
+        TRACE("screen: %g,%g,%g", itr->x(), itr->y(), itr->z());
+    }
+    return true;
+}
+
 /**
  * \brief Map screen mouse coordinates to map coordinates
@@ -1009,5 +1059,5 @@
     osg::Vec3d world;
     if (_mapNode->getTerrain()->getWorldCoordsUnderMouse(_viewer->asView(), mouseX, mouseY, world)) {
-        map.fromWorld(_mapNode->getMapSRS(), world);
+        map.fromWorld(getMapSRS(), world);
         return true;
     }
Index: /geovis/trunk/Renderer.h
===================================================================
--- /geovis/trunk/Renderer.h	(revision 4635)
+++ /geovis/trunk/Renderer.h	(revision 4636)
@@ -129,5 +129,5 @@
     virtual ~Renderer();
 
-    void setResourcePath(std::string& path)
+    void setResourcePath(const std::string& path)
     {
         TRACE("Set resource path to %s", path.c_str());
@@ -375,4 +375,6 @@
     bool worldToScreen(const osg::Vec3d& world, osg::Vec3d *screen,
                        bool invertY = true);
+
+    bool worldToScreen(std::vector<osg::Vec3d>& coords, bool invertY = true);
 
     void setMaximumFrameRateInHertz(double rate)
Index: /geovis/trunk/RendererCmd.cpp
===================================================================
--- /geovis/trunk/RendererCmd.cpp	(revision 4635)
+++ /geovis/trunk/RendererCmd.cpp	(revision 4636)
@@ -767,5 +767,5 @@
     return (*proc) (clientData, interp, objc, objv);
 }
-#if 1
+
 static int
 MapCoordsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
@@ -815,30 +815,8 @@
             // altmode absolute
             coordVec.push_back(osg::Vec3d(mapPoint.x(), mapPoint.y(), mapPoint.z()));
-#if 0
-            if (outSRS != NULL) {
-                mapPoint = mapPoint.transform(outSRS);
-                if (!mapPoint.isValid()) {
-                    Tcl_DecrRefCount(listObjPtr);
-                    Tcl_AppendResult(interp, "Could not transform map point to requested SRS", (char*)NULL);
-                    return TCL_ERROR;
-                }
-            }
-            Tcl_Obj *objPtr = Tcl_NewDoubleObj(mapPoint.x());
-            Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
-            objPtr = Tcl_NewDoubleObj(mapPoint.y());
-            Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
-            objPtr = Tcl_NewDoubleObj(mapPoint.z());
-            Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
-#endif
         } else {
             coordVec.push_back(osg::Vec3d(std::numeric_limits<double>::quiet_NaN(),
                                           std::numeric_limits<double>::quiet_NaN(),
                                           std::numeric_limits<double>::quiet_NaN()));
-#if 0
-            Tcl_Obj *objPtr = Tcl_NewStringObj("NaN", 3);
-            Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
-            Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
-            Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
-#endif
         }
     }
@@ -873,60 +851,5 @@
     return TCL_OK;
 }
-#else
-static int
-MapCoordsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
-            Tcl_Obj *const *objv)
-{
-    int x, y;
-    if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
-        Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
-        return TCL_ERROR;
-    }
-
-    osgEarth::GeoPoint mapPoint;
-    size_t length;
-    char mesg[512];
-    if (g_renderer->mapMouseCoords(x, y, mapPoint)) {
-        std::string srsInit;
-        std::string verticalDatum;
-        if (objc > 5) {
-            srsInit = Tcl_GetString(objv[5]);
-            if (objc > 6) {
-                verticalDatum = Tcl_GetString(objv[6]);
-            }
-            osgEarth::SpatialReference *outSRS =
-                osgEarth::SpatialReference::get(srsInit, verticalDatum);
-            if (outSRS == NULL) {
-                Tcl_AppendResult(interp, "bad SRS \"", srsInit.c_str(), "\"", (char*)NULL);
-                return TCL_ERROR;
-            }
-            mapPoint = mapPoint.transform(outSRS);
-            if (!mapPoint.isValid()) {
-                Tcl_AppendResult(interp, "Could not transform map point to requested SRS", (char*)NULL);
-                return TCL_ERROR;
-            }
-        }
-        // send coords to client
-        length = snprintf(mesg, sizeof(mesg),
-                          "nv>map coords %s %g %g %g %d %d {%s} {%s}\n",
-                          Tcl_GetString(objv[2]), //g_stats.nCommands,
-                          mapPoint.x(), mapPoint.y(), mapPoint.z(),
-                          x, y,
-                          mapPoint.getSRS()->getHorizInitString().c_str(),
-                          mapPoint.getSRS()->getVertInitString().c_str());
-
-        queueResponse(mesg, length, Response::VOLATILE);
-    } else {
-        // Out of range
-        length = snprintf(mesg, sizeof(mesg),
-                          "nv>map coords %s invalid %d %d\n",
-                          Tcl_GetString(objv[2]), //g_stats.nCommands,
-                          x, y);
-
-        queueResponse(mesg, length, Response::VOLATILE);
-    }
-    return TCL_OK;
-}
-#endif
+
 static int
 MapGraticuleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
@@ -1946,12 +1869,23 @@
                Tcl_Obj *const *objv)
 {
-    double x, y, z;
-    if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK ||
-        Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK ||
-        Tcl_GetDoubleFromObj(interp, objv[5], &z) != TCL_OK) {
-        return TCL_ERROR;
-    }
+    int tokenLength;
+    const char *token = Tcl_GetStringFromObj(objv[2], &tokenLength);
+    int numCoords;
+    Tcl_Obj **coords;
+    if (Tcl_ListObjGetElements(interp, objv[3], &numCoords, &coords) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (numCoords == 0) {
+        Tcl_AppendResult(interp, "no x,y,z coordinates in list", (char *)NULL);
+        return TCL_ERROR;
+    }
+    if (numCoords % 3 != 0) {
+        Tcl_AppendResult(interp, "invalid number of coordinates in list",
+                         (char *)NULL);
+        return TCL_ERROR;
+    }
+
     const osgEarth::SpatialReference *srs = NULL;
-    if (objc < 7) {
+    if (objc < 5) {
         srs = g_renderer->getMapSRS();
         if (srs == NULL) {
@@ -1960,8 +1894,8 @@
         }
     } else {
-        std::string srsInit(Tcl_GetString(objv[6]));
+        std::string srsInit(Tcl_GetString(objv[4]));
         std::string verticalDatum;
-        if (objc > 7) {
-            verticalDatum = Tcl_GetString(objv[7]);
+        if (objc > 5) {
+            verticalDatum = Tcl_GetString(objv[5]);
         }
         srs = osgEarth::SpatialReference::get(srsInit, verticalDatum);
@@ -1971,30 +1905,44 @@
         }
     }
-    // ALTMODE_RELATIVE is height above terrain, ALTMODE_ABSOLUTE means
-    // relative to the vertical datum
-    osgEarth::GeoPoint mapPoint(srs, x, y, z, osgEarth::ALTMODE_ABSOLUTE);
-    size_t length;
-    char mesg[256];
-    osg::Vec3d world;
-    osg::Vec3d screen;
-    if (g_renderer->getWorldCoords(mapPoint, &world) &&
-        g_renderer->worldToScreen(world, &screen)) {
-        // send coords to client
-        length = snprintf(mesg, sizeof(mesg),
-                          "nv>screen coords %s %g %g %g %g %g %g\n",
-                          Tcl_GetString(objv[2]),
-                          screen.x(), screen.y(), screen.z(),
-                          x, y, z);
-
-        queueResponse(mesg, length, Response::VOLATILE);
-    } else {
-        // Out of range
-        length = snprintf(mesg, sizeof(mesg),
-                          "nv>screen coords %s invalid %g %g %g\n",
-                          Tcl_GetString(objv[2]),
-                          x, y, z);
-
-        queueResponse(mesg, length, Response::VOLATILE);
-    }
+    Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    std::vector<osg::Vec3d> coordVec;
+    for (int i = 0; i < numCoords; i += 3) {
+        double x, y, z;
+        if (Tcl_GetDoubleFromObj(interp, coords[i], &x) != TCL_OK ||
+            Tcl_GetDoubleFromObj(interp, coords[i+1], &y) != TCL_OK ||
+            Tcl_GetDoubleFromObj(interp, coords[i+2], &z) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        // ALTMODE_RELATIVE is height above terrain, ALTMODE_ABSOLUTE means
+        // relative to the vertical datum
+        osgEarth::GeoPoint mapPoint(srs, x, y, z, osgEarth::ALTMODE_ABSOLUTE);
+        osg::Vec3d world;
+        if (g_renderer->getWorldCoords(mapPoint, &world)) {
+            coordVec.push_back(world);
+        } else {
+            coordVec.push_back(osg::Vec3d(std::numeric_limits<double>::quiet_NaN(),
+                                          std::numeric_limits<double>::quiet_NaN(),
+                                          std::numeric_limits<double>::quiet_NaN()));
+        }
+    }
+    g_renderer->worldToScreen(coordVec);
+    for (std::vector<osg::Vec3d>::iterator itr = coordVec.begin();
+         itr != coordVec.end(); ++itr) {
+        Tcl_Obj *objPtr = Tcl_NewDoubleObj(itr->x());
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+        objPtr = Tcl_NewDoubleObj(itr->y());
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+        objPtr = Tcl_NewDoubleObj(itr->z());
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    // send coords to client
+    int listLength;
+    const char *string = Tcl_GetStringFromObj(listObjPtr, &listLength);
+    size_t length = listLength + tokenLength + 22;
+    char *mesg = new char[length];
+    length = snprintf(mesg, length, "nv>screen coords %s {%s}\n", token, string);
+    Tcl_DecrRefCount(listObjPtr);
+    queueResponse(mesg, length, Response::VOLATILE);
+    delete [] mesg;
     return TCL_OK;
 }
@@ -2017,5 +1965,5 @@
 static CmdSpec screenOps[] = {
     {"bgcolor", 1, ScreenBgColorOp, 5, 5, "r g b"},
-    {"coords",  1, ScreenCoordsOp, 6, 8, "token x y z ?srs? ?verticalDatum?"},
+    {"coords",  1, ScreenCoordsOp, 4, 6, "token coords ?srs? ?verticalDatum?"},
     {"size",    1, ScreenSizeOp, 4, 4, "width height"}
 };
Index: /geovis/trunk/geovis_protocol.txt
===================================================================
--- /geovis/trunk/geovis_protocol.txt	(revision 4635)
+++ /geovis/trunk/geovis_protocol.txt	(revision 4636)
@@ -90,9 +90,9 @@
 
 screen bgcolor <r> <g> <b>
-screen coords <token> <x> <y> <z> <?srs?> <?verticalDatum?>
+screen coords <token> <coordList> <?srs?> <?verticalDatum?>
        Transate map coordinates to screen/mouse coordinates.
        <token> - String token, included in response
-       <x>,<y>,<z> - Map coordinates.  If srs not given, these are in the map's 
-       coordinate system.
+       <coordList> - A list of x,y,z map coordinates.  If <srs> is not given,
+       these are in the map's coordinate system.
        <srs> - Optional horizontal srs init string for coordinate system of 
        x,y,z
@@ -115,8 +115,8 @@
 == Map Commands ==
 
-map coords <token> <x> <y> <?srs?> <?verticalDatum?>
+map coords <token> <coordList> <?srs?> <?verticalDatum?>
     Translate screen/mouse coordinates into map coordinates.
     <token> - String token, included in response
-    <x>,<y> - Screen/mouse coordinates
+    <coordList> - A list of x,y screen/mouse coordinates
     <srs> - Optional horizontal srs init string for coordinate system to return
     <verticalDatum> - Optional vertical srs init string for coordinate system to
@@ -209,13 +209,14 @@
 nv>image -type image -bytes <nbytes>
   <binary RGB data>
-nv>map coords <token> <mapX> <mapY> <mapZ> <screenX> <screenY> <?srs?> <?verticalDatum?>
-   Reply to query of map coordinates from screen coordinates.  srs of the map
-   coordinates may be included if not in map's native coordinate system
-nv>map coords <token> invalid <mouseX> <mouseY>
-   Reply indicating an off-map click/query
-nv>screen coords <token> <screenX> <screenY> <screenZ> <mapX> <mapY> <mapZ>
-   Reply to query of screen coordinates from map coordinates
-nv>screen coords <token> invalid <mapX> <mapY> <mapZ>
-   Reply indicating requested point is out of the view frustum
+nv>map coords <token> <mapCoordList> <?srs?> <?verticalDatum?>
+   Reply to query of map coordinates from screen coordinates.  The mapCoordList
+   is a list of x,y,z map coordinates.  The srs of the map coordinates may be 
+   included if not in map's native coordinate system.  NaN in the results 
+   indicates off-map input coordinates.
+nv>screen coords <token> <screenCoordList>
+   Reply to query of screen coordinates from map coordinates.  The
+   screenCoordList is a list of x,y,z screen coordinates (z is the depth).  NaN
+   in the results indicates a point outside the view frustum or invalid input
+   map coordinate
 nv>ok -token <seqnum>
    Reply indicated commands through <seqnum> (numbered beginning at 0) have been
