/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2004-2015 HUBzero Foundation, LLC * * Author: Leif Delgass */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Trace.h" #include "CmdProc.h" #include "ReadBuffer.h" #include "Types.h" #include "RendererCmd.h" #include "RenderServer.h" #include "Renderer.h" #include "IData.h" #include "Stats.h" #include "PPMWriter.h" #include "TGAWriter.h" #include "ResponseQueue.h" #ifdef USE_READ_THREAD #include "CommandQueue.h" #endif #include "Util.h" using namespace GeoVis; static int lastCmdStatus; #ifndef USE_THREADS static ssize_t SocketWrite(const void *bytes, size_t len) { size_t ofs = 0; ssize_t bytesWritten; while ((bytesWritten = write(g_fdOut, (const char *)bytes + ofs, len - ofs)) > 0) { ofs += bytesWritten; if (ofs == len) break; } if (bytesWritten < 0) { ERROR("write: %s", strerror(errno)); } return bytesWritten; } #endif static bool SocketRead(char *bytes, size_t len) { if (len == 0) return true; ReadBuffer::BufferStatus status; status = g_inBufPtr->followingData((unsigned char *)bytes, len); g_stats.nDataBytes += len; TRACE("followingData status: %d", status); return (status == ReadBuffer::OK); } ssize_t GeoVis::queueResponse(const void *bytes, size_t len, Response::AllocationType allocType, Response::ResponseType type) { #ifdef USE_THREADS Response *response = new Response(type); response->setMessage((unsigned char *)bytes, len, allocType); g_outQueue->enqueue(response); return (ssize_t)len; #else return SocketWrite(bytes, len); #endif } static int ExecuteCommand(Tcl_Interp *interp, Tcl_DString *dsPtr) { int result; #ifdef WANT_TRACE char *str = Tcl_DStringValue(dsPtr); std::string cmd(str); cmd.erase(cmd.find_last_not_of(" \n\r\t")+1); TRACE("command %lu: '%s'", g_stats.nCommands, cmd.c_str()); #endif lastCmdStatus = TCL_OK; result = Tcl_EvalEx(interp, Tcl_DStringValue(dsPtr), Tcl_DStringLength(dsPtr), TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL); Tcl_DStringSetLength(dsPtr, 0); if (lastCmdStatus == TCL_BREAK) { return TCL_BREAK; } lastCmdStatus = result; if (result != TCL_OK) { TRACE("Error: %d", result); } return result; } static int GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, bool *boolPtr) { int value; if (Tcl_GetBooleanFromObj(interp, objPtr, &value) != TCL_OK) { return TCL_ERROR; } *boolPtr = (bool)value; return TCL_OK; } #if 0 static short GetShortFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, short *valuePtr) { int value; if (Tcl_GetIntFromObj(interp, objPtr, &value) != TCL_OK) { return TCL_ERROR; } *valuePtr = (short)value; return TCL_OK; } #endif static unsigned short GetUShortFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, unsigned short *valuePtr) { int value; if (Tcl_GetIntFromObj(interp, objPtr, &value) != TCL_OK) { return TCL_ERROR; } *valuePtr = (unsigned short)value; return TCL_OK; } static unsigned int GetUIntFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, unsigned int *valuePtr) { long value; if (Tcl_GetLongFromObj(interp, objPtr, &value) != TCL_OK) { return TCL_ERROR; } *valuePtr = (unsigned int)value; return TCL_OK; } static int GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, float *valuePtr) { double value; if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) { return TCL_ERROR; } *valuePtr = (float)value; return TCL_OK; } static osgEarth::Symbology::TextSymbol::Alignment ParseTextAlignment(const char *str, osgEarth::Symbology::TextSymbol::Alignment defAlign = osgEarth::Symbology::TextSymbol::ALIGN_LEFT_BASE_LINE) { osgEarth::Symbology::TextSymbol::Alignment alignment = defAlign; if (str[0] == 'l' && strcmp(str, "left_top") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_LEFT_TOP; } else if (str[0] == 'l' && strcmp(str, "left_center") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_LEFT_CENTER; } else if (str[0] == 'l' && strcmp(str, "left_bottom") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_LEFT_BOTTOM; } else if (str[0] == 'c' && strcmp(str, "center_top") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_CENTER_TOP; } else if (str[0] == 'c' && strcmp(str, "center_center") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_CENTER_CENTER; } else if (str[0] == 'c' && strcmp(str, "center_bottom") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_CENTER_BOTTOM; } else if (str[0] == 'r' && strcmp(str, "right_top") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_RIGHT_TOP; } else if (str[0] == 'r' && strcmp(str, "right_center") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_RIGHT_CENTER; } else if (str[0] == 'r' && strcmp(str, "right_bottom") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_RIGHT_BOTTOM; } else if (str[0] == 'l' && strcmp(str, "left_baseline") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_LEFT_BASE_LINE; } else if (str[0] == 'c' && strcmp(str, "center_baseline") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_CENTER_BASE_LINE; } else if (str[0] == 'r' && strcmp(str, "right_baseline") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_RIGHT_BASE_LINE; } else if (str[0] == 'l' && strcmp(str, "left_bottom_baseline") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_LEFT_BOTTOM_BASE_LINE; } else if (str[0] == 'c' && strcmp(str, "center_bottom_baseline") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_CENTER_BOTTOM_BASE_LINE; } else if (str[0] == 'r' && strcmp(str, "right_bottom_baseline") == 0) { alignment = osgEarth::Symbology::TextSymbol::ALIGN_RIGHT_BOTTOM_BASE_LINE; } return alignment; } static osgEarth::Symbology::IconSymbol::Alignment ParseIconAlignment(const char *str, osgEarth::Symbology::IconSymbol::Alignment defAlign = osgEarth::Symbology::IconSymbol::ALIGN_CENTER_BOTTOM) { osgEarth::Symbology::IconSymbol::Alignment alignment = defAlign; if (str[0] == 'l' && strcmp(str, "left_top") == 0) { alignment = osgEarth::Symbology::IconSymbol::ALIGN_LEFT_TOP; } else if (str[0] == 'l' && strcmp(str, "left_center") == 0) { alignment = osgEarth::Symbology::IconSymbol::ALIGN_LEFT_CENTER; } else if (str[0] == 'l' && strcmp(str, "left_bottom") == 0) { alignment = osgEarth::Symbology::IconSymbol::ALIGN_LEFT_BOTTOM; } else if (str[0] == 'c' && strcmp(str, "center_top") == 0) { alignment = osgEarth::Symbology::IconSymbol::ALIGN_CENTER_TOP; } else if (str[0] == 'c' && strcmp(str, "center_center") == 0) { alignment = osgEarth::Symbology::IconSymbol::ALIGN_CENTER_CENTER; } else if (str[0] == 'c' && strcmp(str, "center_bottom") == 0) { alignment = osgEarth::Symbology::IconSymbol::ALIGN_CENTER_BOTTOM; } else if (str[0] == 'r' && strcmp(str, "right_top") == 0) { alignment = osgEarth::Symbology::IconSymbol::ALIGN_RIGHT_TOP; } else if (str[0] == 'r' && strcmp(str, "right_center") == 0) { alignment = osgEarth::Symbology::IconSymbol::ALIGN_RIGHT_CENTER; } else if (str[0] == 'r' && strcmp(str, "right_bottom") == 0) { alignment = osgEarth::Symbology::IconSymbol::ALIGN_RIGHT_BOTTOM; } return alignment; } static osgEarth::Symbology::InstanceSymbol::Placement ParseInstancePlacement(const char *str, osgEarth::Symbology::InstanceSymbol::Placement defPlacement = osgEarth::Symbology::InstanceSymbol::PLACEMENT_VERTEX) { osgEarth::Symbology::InstanceSymbol::Placement placement = defPlacement; if (str[0] == 'v' && strcmp(str, "vertex") == 0) { placement = osgEarth::Symbology::InstanceSymbol::PLACEMENT_VERTEX; } else if (str[0] == 'c' && strcmp(str, "centroid") == 0) { placement = osgEarth::Symbology::InstanceSymbol::PLACEMENT_CENTROID; } else if (str[0] == 'i' && strcmp(str, "interval") == 0) { placement = osgEarth::Symbology::InstanceSymbol::PLACEMENT_INTERVAL; } else if (str[0] == 'r' && strcmp(str, "random") == 0) { placement = osgEarth::Symbology::InstanceSymbol::PLACEMENT_RANDOM; } return placement; } static osgEarth::Symbology::AltitudeSymbol::Clamping ParseClamping(const char *str, osgEarth::Symbology::AltitudeSymbol::Clamping defTechnique = osgEarth::Symbology::AltitudeSymbol::CLAMP_NONE) { osgEarth::Symbology::AltitudeSymbol::Clamping clamping = defTechnique; if (str[0] == 'n' && strcmp(str, "none") == 0) { clamping = osgEarth::Symbology::AltitudeSymbol::CLAMP_NONE; } else if (str[0] == 't' && strcmp(str, "terrain") == 0) { clamping = osgEarth::Symbology::AltitudeSymbol::CLAMP_TO_TERRAIN; } else if (str[0] == 'r' && strcmp(str, "relative") == 0) { clamping = osgEarth::Symbology::AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN; } else if (str[0] == 'a' && strcmp(str, "absolute") == 0) { clamping = osgEarth::Symbology::AltitudeSymbol::CLAMP_ABSOLUTE; } return clamping; } static osgEarth::Symbology::AltitudeSymbol::Technique ParseClampingTechnique(const char *str, osgEarth::Symbology::AltitudeSymbol::Technique defTechnique = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_DRAPE) { osgEarth::Symbology::AltitudeSymbol::Technique technique = defTechnique; if (str[0] == 'd' && strcmp(str, "drape") == 0) { technique = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_DRAPE; } else if (str[0] == 'g' && strcmp(str, "gpu") == 0) { technique = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_GPU; } else if (str[0] == 's' && strcmp(str, "scene") == 0) { technique = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_SCENE; } else if (str[0] == 'm' && strcmp(str, "map") == 0) { technique = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_MAP; } return technique; } static osgEarth::Symbology::Stroke::LineCapStyle ParseLineCapStyle(const char *str, osgEarth::Symbology::Stroke::LineCapStyle defLineCap = osgEarth::Symbology::Stroke::LINECAP_FLAT) { osgEarth::Symbology::Stroke::LineCapStyle lineCap = defLineCap; if (str[0] == 'f' && strcmp(str, "flat") == 0) { lineCap = osgEarth::Symbology::Stroke::LINECAP_FLAT; } else if (str[0] == 's' && strcmp(str, "square") == 0) { lineCap = osgEarth::Symbology::Stroke::LINECAP_SQUARE; } else if (str[0] == 'r' && strcmp(str, "round") == 0) { lineCap = osgEarth::Symbology::Stroke::LINECAP_ROUND; } return lineCap; } static osgEarth::Symbology::Stroke::LineJoinStyle ParseLineJoinStyle(const char *str, osgEarth::Symbology::Stroke::LineJoinStyle defLineJoin = osgEarth::Symbology::Stroke::LINEJOIN_MITRE) { osgEarth::Symbology::Stroke::LineJoinStyle lineJoin = defLineJoin; if (str[0] == 'm' && (strcmp(str, "mitre") == 0 || strcmp(str, "miter") == 0)) { lineJoin = osgEarth::Symbology::Stroke::LINEJOIN_MITRE; } else if (str[0] == 'r' && strcmp(str, "round") == 0) { lineJoin = osgEarth::Symbology::Stroke::LINEJOIN_ROUND; } return lineJoin; } static int CameraDeleteViewpointOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { char *name = Tcl_GetString(objv[2]); g_renderer->removeNamedViewpoint(name); return TCL_OK; } static int CameraCenterOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double x, y; if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK) { return TCL_ERROR; } double zoom = 1.0; if (objc > 4) { if (Tcl_GetDoubleFromObj(interp, objv[4], &zoom) != TCL_OK) { return TCL_ERROR; } } double duration = 1.0; if (objc > 5) { if (Tcl_GetDoubleFromObj(interp, objv[5], &duration) != TCL_OK) { return TCL_ERROR; } } osgEarth::Viewpoint vpt = g_renderer->getViewpoint(); vpt.focalPoint()->x() = x; vpt.focalPoint()->y() = y; //vpt.focalPoint()->z() = mapPoint.z(); vpt.range()->set(vpt.range()->getValue() * zoom, osgEarth::Units::METERS); g_renderer->setViewpoint(vpt, duration); return TCL_OK; } static int CameraGetViewpointOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { osgEarth::Viewpoint view = g_renderer->getViewpoint(); std::ostringstream oss; size_t len = 0; oss << "nv>camera get " << view.focalPoint()->x() << " " << view.focalPoint()->y() << " " << view.focalPoint()->z() << " " << view.heading()->getValue() << " " << view.pitch()->getValue() << " " << view.range()->getValue() << " {" << ((view.focalPoint()->getSRS() == NULL) ? "" : view.focalPoint()->getSRS()->getHorizInitString()) << "}" << " {" << ((view.focalPoint()->getSRS() == NULL) ? "" : view.focalPoint()->getSRS()->getVertInitString()) << "}" << "\n"; std::string ostr = oss.str(); len = ostr.size(); #ifdef USE_THREADS queueResponse(ostr.c_str(), len, Response::VOLATILE); #else ssize_t bytesWritten = SocketWrite(ostr.c_str(), len); if (bytesWritten < 0) { return TCL_ERROR; } #endif /*USE_THREADS*/ return TCL_OK; } static int CameraGoOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int x, y; if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK || Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) { return TCL_ERROR; } double zoom = 1.0; if (objc > 4) { if (Tcl_GetDoubleFromObj(interp, objv[4], &zoom) != TCL_OK) { return TCL_ERROR; } } double duration = 1.0; if (objc > 5) { if (Tcl_GetDoubleFromObj(interp, objv[5], &duration) != TCL_OK) { return TCL_ERROR; } } osgEarth::GeoPoint mapPoint; if (g_renderer->mapMouseCoords(x, y, mapPoint)) { osgEarth::Viewpoint vpt = g_renderer->getViewpoint(); vpt.focalPoint() = mapPoint; vpt.range()->set(vpt.range()->getValue() * zoom, osgEarth::Units::METERS); g_renderer->setViewpoint(vpt, duration); } else { // Out of map bounds } return TCL_OK; } static int CameraOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double quat[4]; if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) { return TCL_ERROR; } g_renderer->setCameraOrientation(quat); return TCL_OK; } static int CameraPanOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double x, y; if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK) { return TCL_ERROR; } g_renderer->panCamera(x, y); return TCL_OK; } static int CameraResetOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { if (objc == 3) { const char *string = Tcl_GetString(objv[2]); char c = string[0]; if ((c != 'a') || (strcmp(string, "all") != 0)) { Tcl_AppendResult(interp, "bad camera reset option \"", string, "\": should be all", (char*)NULL); return TCL_ERROR; } g_renderer->resetCamera(true); } else { g_renderer->resetCamera(false); } return TCL_OK; } static int CameraRestoreViewpointOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { 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; } } if (!g_renderer->restoreNamedViewpoint(name, duration)) { Tcl_AppendResult(interp, "camera viewpoint \"", name, "\" not found", (char*)NULL); return TCL_ERROR; } return TCL_OK; } static int CameraRotateOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double x, y; if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK) { return TCL_ERROR; } g_renderer->rotateCamera(x, y); return TCL_OK; } static int CameraSaveViewpointOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { char *name = Tcl_GetString(objv[2]); g_renderer->saveNamedViewpoint(name); return TCL_OK; } 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 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->getTerrainMaskLayerExtent(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; } Tcl_AppendResult(interp, "Invalid layer \"", name, "\" or could not determine layer extent", (char*)NULL); return TCL_ERROR; } static int CameraSetDistanceOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double dist; if (Tcl_GetDoubleFromObj(interp, objv[2], &dist) != TCL_OK) { return TCL_ERROR; } g_renderer->setCameraDistance(dist); return TCL_OK; } static int CameraSetViewpointOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double x, y, z, heading, pitch, distance; double duration = 0.0; if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[4], &z) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[5], &heading) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[6], &pitch) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[7], &distance) != TCL_OK) { return TCL_ERROR; } if (objc > 8) { if (Tcl_GetDoubleFromObj(interp, objv[8], &duration) != TCL_OK) { return TCL_ERROR; } } const osgEarth::SpatialReference *srs = g_renderer->getMapSRS(); if (objc > 9) { char *srsInit = Tcl_GetString(objv[9]); if (strlen(srsInit) > 0) { if (objc > 10) { char *vertDatum = Tcl_GetString(objv[10]); srs = osgEarth::SpatialReference::get(srsInit, vertDatum); } else { srs = osgEarth::SpatialReference::get(srsInit); } if (srs == NULL) { ERROR("Couldn't get SRS from init string: %s", srsInit); return TCL_ERROR; } } } osgEarth::Viewpoint view; view.focalPoint() = osgEarth::GeoPoint(srs, x, y, z); view.heading() = osgEarth::Angle(heading, osgEarth::Units::DEGREES); view.pitch() = osgEarth::Angle(pitch, osgEarth::Units::DEGREES); view.range() = osgEarth::Distance(distance, osgEarth::Units::METERS); g_renderer->setViewpoint(view, duration); return TCL_OK; } static int CameraThrowOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { bool state; if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) { return TCL_ERROR; } g_renderer->setThrowingEnabled(state); return TCL_OK; } static int CameraZoomOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double z; if (Tcl_GetDoubleFromObj(interp, objv[2], &z) != TCL_OK) { return TCL_ERROR; } g_renderer->zoomCamera(z); return TCL_OK; } static CmdSpec cameraOps[] = { {"center", 1, CameraCenterOp, 4, 6, "x y ?zoomFactor? ?duration?"}, {"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"}, {"reset", 4, CameraResetOp, 2, 3, "?all?"}, {"restore", 4, CameraRestoreViewpointOp, 3, 4, "name ?duration?"}, {"rotate", 2, CameraRotateOp, 4, 4, "azimuth elevation"}, {"save", 2, CameraSaveViewpointOp, 3, 3, "name"}, {"set", 2, CameraSetViewpointOp, 8, 11, "x y z heading pitch distance ?duration? ?srs? ?vertDatum?"}, {"throw", 1, CameraThrowOp, 3, 3, "bool"}, {"zoom", 1, CameraZoomOp, 3, 3, "zoomAmount"} }; static int nCameraOps = NumCmdSpecs(cameraOps); static int CameraCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nCameraOps, cameraOps, CMDSPEC_ARG1, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int ClientInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_DString ds; Tcl_Obj *objPtr, *listObjPtr, **items; int numItems; char buf[BUFSIZ]; const char *string; int length; int result; static bool first = true; /* Client arguments. */ if (Tcl_ListObjGetElements(interp, objv[1], &numItems, &items) != TCL_OK || numItems % 2 != 0) { return TCL_ERROR; } const char *username = NULL; const char *hubURL = NULL; //const char *session = NULL; for (int i = 0; i < numItems; i+=2) { const char *name = Tcl_GetString(items[i]); const char *val = Tcl_GetString(items[i+1]); if (strcmp(name, "user") == 0) { username = val; } if (strcmp(name, "hub_url") == 0) { hubURL = val; } //if (strcmp(name, "session") == 0) { // session = val; //} } if (username != NULL) { IData::iDataInit(username, hubURL); } /* First try to see if we already have an open descriptor */ int fd = getStatsFile(); if (fd < 0) { /* Use the initial client key value pairs as the parts for generating * a unique file name. */ fd = GeoVis::getStatsFile(Tcl_GetString(objv[1])); if (fd < 0) { Tcl_AppendResult(interp, "can't open stats file: ", Tcl_PosixError(interp), (char *)NULL); return TCL_ERROR; } } listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); Tcl_IncrRefCount(listObjPtr); if (first) { first = false; objPtr = Tcl_NewStringObj("render_start", 12); Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); /* renderer */ objPtr = Tcl_NewStringObj("renderer", 8); Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); objPtr = Tcl_NewStringObj("geovis", 6); Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); /* pid */ objPtr = Tcl_NewStringObj("pid", 3); Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(getpid())); /* host */ objPtr = Tcl_NewStringObj("host", 4); Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); gethostname(buf, BUFSIZ-1); buf[BUFSIZ-1] = '\0'; objPtr = Tcl_NewStringObj(buf, -1); Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); /* date */ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date", 4)); strcpy(buf, ctime(&GeoVis::g_stats.start.tv_sec)); buf[strlen(buf) - 1] = '\0'; Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(buf, -1)); /* date_secs */ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date_secs", 9)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewLongObj(GeoVis::g_stats.start.tv_sec)); } else { objPtr = Tcl_NewStringObj("render_info", 11); Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); struct timeval now; gettimeofday(&now, NULL); /* date */ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date", 4)); strcpy(buf, ctime(&now.tv_sec)); buf[strlen(buf) - 1] = '\0'; Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(buf, -1)); /* date_secs */ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date_secs", 9)); Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewLongObj(now.tv_sec)); } /* client items */ for (int i = 0; i < numItems; i++) { Tcl_ListObjAppendElement(interp, listObjPtr, items[i]); } Tcl_DStringInit(&ds); string = Tcl_GetStringFromObj(listObjPtr, &length); Tcl_DStringAppend(&ds, string, length); Tcl_DStringAppend(&ds, "\n", 1); result = GeoVis::writeToStatsFile(fd, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); Tcl_DStringFree(&ds); Tcl_DecrRefCount(listObjPtr); return result; } static int ColorMapAddOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { const char *name = Tcl_GetString(objv[2]); int cmapc; Tcl_Obj **cmapv = NULL; if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) { return TCL_ERROR; } if ((cmapc % 5) != 0) { Tcl_AppendResult(interp, "wrong # elements in colormap: should be ", "{ value r g b a... }", (char*)NULL); return TCL_ERROR; } osg::TransferFunction1D *colorMap = new osg::TransferFunction1D; colorMap->allocate(256); 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; } // 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]), "\": should be in the range [0,1]", (char*)NULL); delete colorMap; return TCL_ERROR; } } colorMap->setColor(val[0], osg::Vec4f(val[1], val[2], val[3], val[4]), false); } colorMap->updateImage(); g_renderer->addColorMap(name, colorMap); return TCL_OK; } static int ColorMapDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { if (objc == 3) { const char *name = Tcl_GetString(objv[2]); #if 0 if (strcmp(name, "default") == 0 || strcmp(name, "grayDefault") == 0) { WARN("Cannot delete a default color map"); return TCL_ERROR; } #endif g_renderer->deleteColorMap(name); } else { g_renderer->deleteColorMap("all"); } return TCL_OK; } static int ColorMapNumTableEntriesOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int numEntries; if (Tcl_GetIntFromObj(interp, objv[2], &numEntries) != TCL_OK) { const char *str = Tcl_GetString(objv[2]); if (str[0] == 'd' && strcmp(str, "default") == 0) { numEntries = -1; } else { Tcl_AppendResult(interp, "bad colormap resolution value \"", str, "\": should be a positive integer or \"default\"", (char*)NULL); return TCL_ERROR; } } else if (numEntries < 1) { Tcl_AppendResult(interp, "bad colormap resolution value \"", Tcl_GetString(objv[2]), "\": should be a positive integer or \"default\"", (char*)NULL); return TCL_ERROR; } if (objc == 4) { const char *name = Tcl_GetString(objv[3]); g_renderer->setColorMapNumberOfTableEntries(name, numEntries); } else { g_renderer->setColorMapNumberOfTableEntries("all", numEntries); } return TCL_OK; } static CmdSpec colorMapOps[] = { {"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?"} }; static int nColorMapOps = NumCmdSpecs(colorMapOps); static int ColorMapCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nColorMapOps, colorMapOps, CMDSPEC_ARG1, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } 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; } if (g_renderer->getCacheDirectory().empty()) { g_renderer->setupCache(); } std::string dirPath = osgDB::getFilePath(path); std::string fileName = osgDB::getSimpleFileName(path); std::ostringstream fullPath; fullPath << g_renderer->getCacheDirectory() << "/" << dirPath; if (!dirPath.empty()) { TRACE("Make dir: %s", fullPath.str().c_str()); osgDB::makeDirectory(fullPath.str()); fullPath << "/"; } fullPath << fileName; 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); TRACE("Wrote %d bytes to %s", bytesWritten, fullPath.str().c_str()); return (bytesWritten == size) ? TCL_OK : TCL_ERROR; } static CmdSpec fileOps[] = { {"put", 1, FilePutOp, 5, 5, "path 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) { lastCmdStatus = TCL_BREAK; return TCL_OK; } static int KeyPressOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int key; if (Tcl_GetIntFromObj(interp, objv[2], &key) != TCL_OK) { return TCL_ERROR; } g_renderer->keyPress(key); return TCL_OK; } static int KeyReleaseOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int key; if (Tcl_GetIntFromObj(interp, objv[2], &key) != TCL_OK) { return TCL_ERROR; } g_renderer->keyRelease(key); return TCL_OK; } static CmdSpec keyOps[] = { {"press", 1, KeyPressOp, 3, 3, "key"}, {"release", 1, KeyReleaseOp, 3, 3, "key"}, }; static int nKeyOps = NumCmdSpecs(keyOps); static int KeyCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nKeyOps, keyOps, CMDSPEC_ARG1, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } 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; if (objc > 4) { if (GetBooleanFromObj(interp, objv[4], &opaque) != TCL_OK) { return TCL_ERROR; } } float bgColor[3]; if (objc > 5) { if (GetFloatFromObj(interp, objv[5], &bgColor[0]) != TCL_OK || GetFloatFromObj(interp, objv[6], &bgColor[1]) != TCL_OK || GetFloatFromObj(interp, objv[7], &bgColor[2]) != TCL_OK) { return TCL_ERROR; } } else { memset(bgColor, 0, sizeof(float)*3); } osg::ref_ptr 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; } static int MapAttributionOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int len; char *str = Tcl_GetStringFromObj(objv[2], &len); #if 0 Tcl_DString attrib; Tcl_DStringInit(&attrib); Tcl_Encoding encoding = Tcl_GetEncoding(interp, "identity"); Tcl_ExternalToUtfDString(encoding, str, len, &attrib); Tcl_FreeEncoding(encoding); str = Tcl_DStringValue(&attrib); #endif g_renderer->setAttribution(str); #if 0 Tcl_DStringFree(&attrib); #endif return TCL_OK; } static int MapBoxClearOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { g_renderer->clearBoxSelection(); return TCL_OK; } static int MapBoxEndOp(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; } g_renderer->updateBoxSelection(x, y); // Notify client of selected region double latMin, latMax, longMin, longMax; latMin = latMax = longMin = longMax = 0; g_renderer->getBoxSelection(&latMin, &latMax, &longMin, &longMax); std::ostringstream oss; oss << "nv>select region " << longMin << " " << latMin << " " << longMax << " " << latMax << "\n"; std::string ostr = oss.str(); size_t len = ostr.size(); queueResponse(ostr.c_str(), len, Response::VOLATILE); return TCL_OK; } static int MapBoxInitOp(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; } g_renderer->initBoxSelection(x, y); return TCL_OK; } static int MapBoxUpdateOp(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; } g_renderer->updateBoxSelection(x, y); return TCL_OK; } static CmdSpec mapBoxOps[] = { {"clear", 1, MapBoxClearOp, 3, 3, ""}, {"end", 1, MapBoxEndOp, 5, 5, "x y"}, {"init", 1, MapBoxInitOp, 5, 5, "x y"}, {"update", 1, MapBoxUpdateOp, 5, 5, "x y"}, }; static int nMapBoxOps = NumCmdSpecs(mapBoxOps); static int MapBoxOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nMapBoxOps, mapBoxOps, CMDSPEC_ARG2, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int MapCoordsOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { 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 coordinates in list", (char *)NULL); return TCL_ERROR; } if (numCoords & 1) { Tcl_AppendResult(interp, "odd number of x, y coordinates in list", (char *)NULL); return TCL_ERROR; } const osgEarth::SpatialReference *outSRS = NULL; if (objc > 4) { std::string srsInit; std::string verticalDatum; srsInit = Tcl_GetString(objv[4]); if (objc > 5) { verticalDatum = Tcl_GetString(objv[5]); } outSRS = osgEarth::SpatialReference::get(srsInit, verticalDatum); if (outSRS == NULL) { Tcl_AppendResult(interp, "bad SRS \"", srsInit.c_str(), "\"", (char*)NULL); return TCL_ERROR; } } Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); std::vector coordVec; for (int i = 0; i < numCoords; i += 2) { int x, y; if ((Tcl_GetIntFromObj(interp, coords[i], &x) != TCL_OK) || (Tcl_GetIntFromObj(interp, coords[i+1], &y) != TCL_OK)) { return TCL_ERROR; } osgEarth::GeoPoint mapPoint; if (g_renderer->mapMouseCoords(x, y, mapPoint)) { // altmode absolute coordVec.push_back(osg::Vec3d(mapPoint.x(), mapPoint.y(), mapPoint.z())); } else { coordVec.push_back(osg::Vec3d(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); } } if (outSRS != NULL) { g_renderer->getMapSRS()->transform(coordVec, outSRS); } else { outSRS = g_renderer->getMapSRS(); } for (std::vector::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 + outSRS->getHorizInitString().length() + outSRS->getVertInitString().length() + 25; char *mesg = new char[length]; length = snprintf(mesg, length, "nv>map coords %s {%s} {%s} {%s}\n", token, string, outSRS->getHorizInitString().c_str(), outSRS->getVertInitString().c_str()); Tcl_DecrRefCount(listObjPtr); queueResponse(mesg, length, Response::VOLATILE); delete [] mesg; return TCL_OK; } static int MapGraticuleOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { bool state; if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) { return TCL_ERROR; } if (objc > 3) { Renderer::GraticuleType type; char *typeStr = Tcl_GetString(objv[3]); if (typeStr[0] == 'g' && strcmp(typeStr, "geodetic") == 0) { type = Renderer::GRATICULE_GEODETIC; } else if (typeStr[0] == 's' && strcmp(typeStr, "shader") == 0) { type = Renderer::GRATICULE_SHADER; } else if (typeStr[0] == 'u' && strcmp(typeStr, "utm") == 0) { type = Renderer::GRATICULE_UTM; } else if (typeStr[0] == 'm' && strcmp(typeStr, "mgrs") == 0) { type = Renderer::GRATICULE_MGRS; } else { return TCL_ERROR; } g_renderer->setGraticule(state, type); } else { g_renderer->setGraticule(state); } return TCL_OK; } static int MapLayerAddOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { char *name = Tcl_GetString(objv[3]); char *type = Tcl_GetString(objv[4]); unsigned int pos = UINT_MAX; bool cache = true; bool visible = true; if (type[0] == 'i' && strcmp(type, "image") == 0) { bool ret; char *driver = Tcl_GetString(objv[5]); std::string url; if (objc > 6) { char *urlIn = Tcl_GetString(objv[6]); url = g_renderer->getCanonicalPath(std::string(urlIn)); if (url.empty()) { Tcl_AppendResult(interp, "file not found: \"", urlIn, "\"", (char*)NULL); return TCL_ERROR; } } if (objc > 7) { if (GetBooleanFromObj(interp, objv[7], &cache) != TCL_OK) { return TCL_ERROR; } } bool coverage = false; if (objc > 8) { if (GetBooleanFromObj(interp, objv[8], &coverage) != TCL_OK) { return TCL_ERROR; } } bool shared = false; unsigned int minLOD = 0; unsigned int maxLOD = 23; float minRange = 0.f; float maxRange = FLT_MAX; if (driver[0] == 'a' && strcmp(driver, "agglite") == 0) { osgEarth::Drivers::AGGLiteOptions aggOpts; const char *featureDriver = Tcl_GetString(objv[9]); const char *format = Tcl_GetString(objv[10]); if (format && strlen(format) > 0 && strcmp(format, "json") != 0 && strcmp(format, "gml") != 0) { Tcl_AppendResult(interp, "unknown format \"", format, "\": should be 'json' or 'gml'", (char*)NULL); return TCL_ERROR; } const char *typeName = Tcl_GetString(objv[11]); long ssSize; if (Tcl_GetLongFromObj(interp, objv[12], &ssSize) != TCL_OK) { return TCL_ERROR; } char *styleSheetStr = (char *)malloc((size_t)(ssSize+1L)); if (!SocketRead(styleSheetStr, ssSize)) { free(styleSheetStr); return TCL_ERROR; } styleSheetStr[ssSize] = '\0'; osgEarth::Config styleConf("style", styleSheetStr); free(styleSheetStr); styleConf.add("type", "text/css"); TRACE("style CSS: %s", styleConf.value().c_str()); osgEarth::Config stylesheetConf; stylesheetConf.add(styleConf); aggOpts.styles() = new osgEarth::Symbology::StyleSheet(stylesheetConf); aggOpts.optimizeLineSampling() = true; //aggOpts.gamma() = 1.3; if (objc > 13) { long scriptSize; if (Tcl_GetLongFromObj(interp, objv[13], &scriptSize) != TCL_OK) { return TCL_ERROR; } char *scriptStr = NULL; if (scriptSize > 0) { scriptStr = (char *)malloc((size_t)scriptSize); if (!SocketRead(scriptStr, scriptSize)) { free(scriptStr); return TCL_ERROR; } } std::string scripts(scriptStr, scriptSize); free(scriptStr); if (!scripts.empty()) { TRACE("script: %s", scripts.c_str()); osg::ref_ptr scriptDef = new osgEarth::Symbology::StyleSheet::ScriptDef(scripts); aggOpts.styles()->setScript(scriptDef.get()); } else { TRACE("no script"); } } if (objc > 14) { long selectorsSize; if (Tcl_GetLongFromObj(interp, objv[14], &selectorsSize) != TCL_OK) { return TCL_ERROR; } char *selectorsStr = (char *)malloc((size_t)selectorsSize); if (!SocketRead(selectorsStr, selectorsSize)) { free(selectorsStr); return TCL_ERROR; } Tcl_Obj *selectorsObj = Tcl_NewStringObj(selectorsStr, selectorsSize); free(selectorsStr); int numSelectors; Tcl_Obj **selectors; if (Tcl_ListObjGetElements(interp, selectorsObj, &numSelectors, &selectors) != TCL_OK) { return TCL_ERROR; } for (int i = 0; i < numSelectors; i++) { int numFields; Tcl_Obj **fields; if (Tcl_ListObjGetElements(interp, selectors[i], &numFields, &fields) != TCL_OK || numFields % 2 != 0) { return TCL_ERROR; } std::map fieldMap; std::map::iterator itr; for (int f = 0; f < numFields; f+=2) { char *name = Tcl_GetString(fields[f]); char *value = Tcl_GetString(fields[f+1]); fieldMap[name] = value; TRACE("selector[%s] = %s", name, value); } osgEarth::Symbology::StyleSelector ss; itr = fieldMap.find("name"); if (itr != fieldMap.end()) { ss.name() = itr->second; } itr = fieldMap.find("style"); if (itr != fieldMap.end()) { ss.styleName() = itr->second; } itr = fieldMap.find("styleExpression"); if (itr != fieldMap.end()) { TRACE("selector: %s", itr->second.c_str()); ss.styleExpression() = osgEarth::Symbology::StringExpression(itr->second); } itr = fieldMap.find("query"); if (itr != fieldMap.end()) { osgEarth::Symbology::Query query; query.expression() = itr->second; itr = fieldMap.find("queryOrderBy"); if (itr != fieldMap.end()) { query.orderby() = itr->second; } itr = fieldMap.find("queryBounds"); if (itr != fieldMap.end()) { double xmin, ymin, xmax, ymax; if (sscanf(itr->second.c_str(), "%lf %lf %lf %lf", &xmin, &ymin, &xmax, &ymax) != 4) { return TCL_ERROR; } TRACE("query bounds: %g %g %g %g", xmin, ymin, xmax, ymax); query.bounds() = osgEarth::Bounds(xmin, ymin, xmax, ymax); } ss.query() = query; } aggOpts.styles()->selectors().push_back(ss); } Tcl_DecrRefCount(selectorsObj); } if (featureDriver[0] == 'd' && strcmp(featureDriver, "db") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; // Get unmodified connection string opts.connection() = Tcl_GetString(objv[6]); opts.layer() = typeName; aggOpts.featureOptions() = opts; } else if (featureDriver[0] == 'o' && strcmp(featureDriver, "ogr") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; if (osgDB::getLowerCaseFileExtension(url) == "csv") { url = loadCSVLayer(url.c_str()); } opts.url() = url; aggOpts.featureOptions() = opts; } else if (featureDriver[0] == 'o' && strcmp(featureDriver, "tfs") == 0) { osgEarth::Drivers::TFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.format() = format; aggOpts.featureOptions() = opts; } else if (featureDriver[0] == 'o' && strcmp(featureDriver, "wfs") == 0) { osgEarth::Drivers::WFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.outputFormat() = format; opts.typeName() = typeName; aggOpts.featureOptions() = opts; } else { Tcl_AppendResult(interp, "unknown feature driver \"", driver, "\": should be 'db', 'ogr', 'tfs', or 'wfs'", (char*)NULL); return TCL_ERROR; } ret = g_renderer->addImageLayer(name, aggOpts, pos, cache, coverage, shared, visible, minLOD, maxLOD); } else if (driver[0] == 'c' && strcmp(driver, "colorramp") == 0) { osgEarth::Drivers::ColorRampOptions colorRampOpts; char *edriver = Tcl_GetString(objv[9]); char *profile = Tcl_GetString(objv[10]); char *colormap = Tcl_GetString(objv[11]); if (edriver[0] == 'g' && strcmp(edriver, "gdal") == 0) { osgEarth::Drivers::GDALOptions opts; opts.url() = url; osgEarth::ElevationLayerOptions elevOpts(name, opts); if (!cache) { elevOpts.cachePolicy() = osgEarth::CachePolicy(osgEarth::CachePolicy::USAGE_NO_CACHE); } if (profile != NULL) { elevOpts.driver()->profile() = osgEarth::ProfileOptions(profile); } #if 0 { osgEarth::ProfileOptions profile; profile.srsString() = srsString; profile.vsrsString() = vsrsString; profile.bounds() = osgEarth::Bounds(xmin, ymin, xmax, ymax); elevOpts.driver()->profile() = profile; } #endif colorRampOpts.elevationLayer() = elevOpts; } else if (edriver[0] == 't' && strcmp(edriver, "tms") == 0) { osgEarth::Drivers::TMSOptions opts; //char *tmsType = Tcl_GetString(objv[9]); //char *format = Tcl_GetString(objv[10]); opts.url() = url; //opts.tmsType() = tmsType; //opts.format() = format; osgEarth::ElevationLayerOptions elevOpts(name, opts); if (!cache) { elevOpts.cachePolicy() = osgEarth::CachePolicy(osgEarth::CachePolicy::USAGE_NO_CACHE); } if (profile != NULL) { elevOpts.driver()->profile() = osgEarth::ProfileOptions(profile); } colorRampOpts.elevationLayer() = elevOpts; } colorRampOpts.ramp() = g_renderer->getColorMapFilePath(colormap); ret = g_renderer->addImageLayer(name, colorRampOpts, pos, cache, coverage, shared, visible, minLOD, maxLOD); } else if (driver[0] == 'd' && strcmp(driver, "debug") == 0) { osgEarth::Drivers::DebugOptions opts; ret = g_renderer->addImageLayer(name, opts, pos, cache, coverage, shared, visible, minLOD, maxLOD); } else if (driver[0] == 'a' && strcmp(driver, "arcgis") == 0) { osgEarth::Drivers::ArcGISOptions opts; opts.url() = url; if (objc > 9) { opts.token() = Tcl_GetString(objv[9]); } if (objc > 10) { opts.layers() = Tcl_GetString(objv[10]); } //opts.format() = Tcl_GetString(objv[11]); ret = g_renderer->addImageLayer(name, opts, pos, 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; opts.url() = url; ret = g_renderer->addImageLayer(name, opts, pos, cache, coverage, shared, visible, minLOD, maxLOD); g_renderer->setImageLayerVisibleRange(name, minRange, maxRange); } else if (driver[0] == 't' && strcmp(driver, "tms") == 0) { osgEarth::Drivers::TMSOptions opts; opts.url() = url; //opts.tmsType() = Tcl_GetString(objv[9]); //opts.format() = Tcl_GetString(objv[10]); ret = g_renderer->addImageLayer(name, opts, pos, cache, coverage, 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[9]); char *format = Tcl_GetString(objv[10]); bool transparent; if (GetBooleanFromObj(interp, objv[11], &transparent) != TCL_OK) { return TCL_ERROR; } opts.url() = url; opts.layers() = wmsLayers; opts.format() = format; opts.transparent() = transparent; if (objc > 12) { char *times = Tcl_GetString(objv[12]); opts.times() = times; } if (objc > 13) { double frameSeconds; if (Tcl_GetDoubleFromObj(interp, objv[13], &frameSeconds) != TCL_OK) { return TCL_ERROR; } opts.secondsPerFrame() = frameSeconds; } ret = g_renderer->addImageLayer(name, opts, pos, cache, coverage, shared, visible, minLOD, maxLOD); g_renderer->setImageLayerVisibleRange(name, minRange, maxRange); } else if (driver[0] == 'x' && strcmp(driver, "xyz") == 0) { osgEarth::Drivers::XYZOptions opts; opts.url() = url; opts.profile() = osgEarth::ProfileOptions("global-mercator"); //bool invertY = false; //opts.invertY() = invertY; //opts.format() = Tcl_GetString(objv[9]); ret = g_renderer->addImageLayer(name, opts, pos, cache, coverage, shared, visible, minLOD, maxLOD); g_renderer->setImageLayerVisibleRange(name, minRange, maxRange); } 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; } } else if (type[0] == 'e' && strcmp(type, "elevation") == 0) { 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; } if (objc > 7) { if (GetBooleanFromObj(interp, objv[7], &cache) != TCL_OK) { return TCL_ERROR; } } // GDAL does not report vertical datum // Options: geodetic (default), egm84, egm96, egm2008 const char *verticalDatum = NULL; if (objc > 8) { verticalDatum = Tcl_GetString(objv[8]); } 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, pos, cache, visible, verticalDatum, minLOD, maxLOD); } else if (driver[0] == 't' && strcmp(driver, "tms") == 0) { osgEarth::Drivers::TMSOptions opts; opts.url() = url; //opts.tmsType() = Tcl_GetString(objv[8]); //opts.format() = Tcl_GetString(objv[9]); g_renderer->addElevationLayer(name, opts, pos, cache, visible, verticalDatum, minLOD, maxLOD); } else if (driver[0] == 'w' && strcmp(driver, "wcs") == 0) { osgEarth::Drivers::WCSOptions opts; opts.url() = url; if (objc > 9) { opts.identifier() = Tcl_GetString(objv[9]); } if (objc > 10) { // default = 'm' opts.elevationUnit() = Tcl_GetString(objv[10]); } if (objc > 11) { // default = 'image/GeoTIFF' opts.format() = Tcl_GetString(objv[11]); } //opts.srs() = Tcl_GetString(objv[12]); //opts.rangeSubset() = Tcl_GetString(objv[13]); g_renderer->addElevationLayer(name, opts, pos, cache, visible, verticalDatum, minLOD, maxLOD); } else { Tcl_AppendResult(interp, "unknown elevation driver \"", driver, "\": should be 'gdal' or 'tms'", (char*)NULL); return TCL_ERROR; } } else if (type[0] == 'f' && strcmp(type, "feature") == 0) { // Generic feature geometry layer char *driver = Tcl_GetString(objv[5]); char *format = Tcl_GetString(objv[6]); if (format && strlen(format) > 0 && strcmp(format, "json") != 0 && strcmp(format, "gml") != 0) { Tcl_AppendResult(interp, "unknown format \"", format, "\": should be 'json' or 'gml'", (char*)NULL); return TCL_ERROR; } char *typeName = Tcl_GetString(objv[7]); char *urlIn = Tcl_GetString(objv[8]); std::string url; if (driver[0] == 'd' && strcmp(driver, "db") == 0) { url = urlIn; } else { url = g_renderer->getCanonicalPath(std::string(urlIn)); if (url.empty()) { Tcl_AppendResult(interp, "file not found: \"", urlIn, "\"", (char*)NULL); return TCL_ERROR; } } if (GetBooleanFromObj(interp, objv[9], &cache) != TCL_OK) { return TCL_ERROR; } long ssSize; if (Tcl_GetLongFromObj(interp, objv[10], &ssSize) != TCL_OK) { return TCL_ERROR; } osgEarth::Drivers::FeatureGeomModelOptions geomOpts; char *styleSheetStr = (char *)malloc((size_t)(ssSize+1L)); if (!SocketRead(styleSheetStr, ssSize)) { free(styleSheetStr); return TCL_ERROR; } styleSheetStr[ssSize] = '\0'; osgEarth::Config styleConf("style", styleSheetStr); free(styleSheetStr); styleConf.add("type", "text/css"); TRACE("style CSS: %s", styleConf.value().c_str()); #if 1 geomOpts.styles() = new osgEarth::Symbology::StyleSheet(); // For relative paths: //styleConf.setReferrer(g_renderer->getCacheDirectory()); std::string cssString = styleConf.value(); std::vector blocks; osgEarth::Symbology::CssUtils::split(cssString, blocks); for (std::vector::iterator itr = blocks.begin(); itr != blocks.end(); ++itr) { osgEarth::Config blockConf(styleConf); blockConf.value() = *itr; osgEarth::Symbology::Style style(blockConf); TRACE("Style: %s", style.getName().c_str()); TRACE("%s", itr->c_str()); if (style.has()) { TRACE("Found icon symbol"); osgEarth::Symbology::IconSymbol *is = style.get(); if (is->url().isSet()) { TRACE("Icon url before: expr: '%s' eval: '%s'", is->url()->expr().c_str(), is->url()->eval().c_str()); // eval() will try to evaluate the expr() std::string path = g_renderer->getCanonicalPath(is->url()->eval()); //setInfix() to set the expr() string, setLiteral() will quote is->url()->setLiteral(path); TRACE("Icon url after: %s", path.c_str()); } } if (style.has()) { osgEarth::Symbology::ModelSymbol *ms = style.get(); if (ms->url().isSet()) { // eval() will try to evaluate the expr() std::string path = g_renderer->getCanonicalPath(ms->url()->eval()); //setInfix() to set the expr() string, setLiteral() will quote ms->url()->setLiteral(path); } } // Need to create a new style otherwise the original CSS is used // without the re-written URLs geomOpts.styles()->addStyle(osgEarth::Symbology::Style(style.getConfig(false))); } #else osgEarth::Config stylesheetConf; stylesheetConf.add(styleConf); geomOpts.styles() = new osgEarth::Symbology::StyleSheet(stylesheetConf); #endif if (objc > 11) { long scriptSize; if (Tcl_GetLongFromObj(interp, objv[11], &scriptSize) != TCL_OK) { return TCL_ERROR; } char *scriptStr = NULL; if (scriptSize > 0) { scriptStr = (char *)malloc((size_t)scriptSize); if (!SocketRead(scriptStr, scriptSize)) { free(scriptStr); return TCL_ERROR; } } std::string scripts(scriptStr, scriptSize); free(scriptStr); if (!scripts.empty()) { TRACE("script: %s", scripts.c_str()); osg::ref_ptr scriptDef = new osgEarth::Symbology::StyleSheet::ScriptDef(scripts); geomOpts.styles()->setScript(scriptDef.get()); } else { TRACE("no script"); } } if (objc > 12) { long selectorsSize; if (Tcl_GetLongFromObj(interp, objv[12], &selectorsSize) != TCL_OK) { return TCL_ERROR; } char *selectorsStr = (char *)malloc((size_t)selectorsSize); if (!SocketRead(selectorsStr, selectorsSize)) { free(selectorsStr); return TCL_ERROR; } Tcl_Obj *selectorsObj = Tcl_NewStringObj(selectorsStr, selectorsSize); free(selectorsStr); int numSelectors; Tcl_Obj **selectors; if (Tcl_ListObjGetElements(interp, selectorsObj, &numSelectors, &selectors) != TCL_OK) { return TCL_ERROR; } TRACE("Num Selectors: %d", numSelectors); for (int i = 0; i < numSelectors; i++) { int numFields; Tcl_Obj **fields; if (Tcl_ListObjGetElements(interp, selectors[i], &numFields, &fields) != TCL_OK || numFields % 2 != 0) { return TCL_ERROR; } std::map fieldMap; std::map::iterator itr; for (int f = 0; f < numFields; f+=2) { char *name = Tcl_GetString(fields[f]); char *value = Tcl_GetString(fields[f+1]); fieldMap[name] = value; TRACE("selector[%s] = %s", name, value); } osgEarth::Symbology::StyleSelector ss; itr = fieldMap.find("name"); if (itr != fieldMap.end()) { ss.name() = itr->second; } itr = fieldMap.find("style"); if (itr != fieldMap.end()) { ss.styleName() = itr->second; } itr = fieldMap.find("styleExpression"); if (itr != fieldMap.end()) { TRACE("selector: %s", itr->second.c_str()); ss.styleExpression() = osgEarth::Symbology::StringExpression(itr->second); } itr = fieldMap.find("query"); if (itr != fieldMap.end()) { osgEarth::Symbology::Query query; query.expression() = itr->second; itr = fieldMap.find("queryOrderBy"); if (itr != fieldMap.end()) { query.orderby() = itr->second; } itr = fieldMap.find("queryBounds"); if (itr != fieldMap.end()) { double xmin, ymin, xmax, ymax; if (sscanf(itr->second.c_str(), "%lf %lf %lf %lf", &xmin, &ymin, &xmax, &ymax) != 4) { return TCL_ERROR; } TRACE("query bounds: %g %g %g %g", xmin, ymin, xmax, ymax); query.bounds() = osgEarth::Bounds(xmin, ymin, xmax, ymax); } ss.query() = query; } geomOpts.styles()->selectors().push_back(ss); } Tcl_DecrRefCount(selectorsObj); } bool terrainPatch = false; if (objc > 13) { if (GetBooleanFromObj(interp, objv[13], &terrainPatch) != TCL_OK) { return TCL_ERROR; } } bool lighting = true; geomOpts.enableLighting() = lighting; geomOpts.minRange() = 0.f; geomOpts.maxRange() = FLT_MAX; if (objc > 14) { float min, max; if (GetFloatFromObj(interp, objv[14], &min) != TCL_OK || GetFloatFromObj(interp, objv[15], &max) != TCL_OK) { return TCL_ERROR; } geomOpts.minRange() = min; geomOpts.maxRange() = max; } if (driver[0] == 'd' && strcmp(driver, "db") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; opts.connection() = url; opts.layer() = typeName; geomOpts.featureOptions() = opts; } else if (driver[0] == 'o' && strcmp(driver, "ogr") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; if (osgDB::getLowerCaseFileExtension(url) == "csv") { url = loadCSVLayer(url.c_str()); } opts.url() = url; geomOpts.featureOptions() = opts; } else if (driver[0] == 't' && strcmp(driver, "tfs") == 0) { osgEarth::Drivers::TFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.format() = format; geomOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wfs") == 0) { osgEarth::Drivers::WFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.outputFormat() = format; opts.typeName() = typeName; geomOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wkt_file") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; opts.geometryUrl() = url; geomOpts.featureOptions() = opts; } else { Tcl_AppendResult(interp, "unknown feature driver \"", driver, "\": should be 'db', 'ogr', 'tfs', 'wfs', or 'wkt_file'", (char*)NULL); return TCL_ERROR; } g_renderer->addModelLayer(name, geomOpts, pos, cache, lighting, visible, terrainPatch); } else if (type[0] == 'm' && strcmp(type, "mask") == 0) { char *driver = Tcl_GetString(objv[5]); char *format = Tcl_GetString(objv[6]); if (format && strlen(format) > 0 && strcmp(format, "json") != 0 && strcmp(format, "gml") != 0) { Tcl_AppendResult(interp, "unknown format \"", format, "\": should be 'json' or 'gml'", (char*)NULL); return TCL_ERROR; } char *typeName = Tcl_GetString(objv[7]); osgEarth::Drivers::FeatureMaskOptions maskOpts; std::string url; if (objc > 8) { if (driver[0] == 'w' && strcmp(driver, "wkt") == 0) { url = Tcl_GetString(objv[8]); if (url.empty()) { Tcl_AppendResult(interp, "Empty WKT length", (char*)NULL); return TCL_ERROR; } } else { char *urlIn = Tcl_GetString(objv[8]); url = g_renderer->getCanonicalPath(std::string(urlIn)); if (url.empty()) { Tcl_AppendResult(interp, "file not found: \"", urlIn, "\"", (char*)NULL); return TCL_ERROR; } } } if (driver[0] == 'd' && strcmp(driver, "db") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; opts.connection() = url; opts.layer() = typeName; maskOpts.featureOptions() = opts; } else if (driver[0] == 'o' && strcmp(driver, "ogr") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; opts.url() = url; maskOpts.featureOptions() = opts; } else if (driver[0] == 't' && strcmp(driver, "tfs") == 0) { osgEarth::Drivers::TFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.format() = format; maskOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wfs") == 0) { osgEarth::Drivers::WFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.outputFormat() = format; opts.typeName() = typeName; maskOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wkt") == 0) { // Inline geometry, url is length of following data char *ptr; long len = strtol(url.c_str(), &ptr, 10); if (len <= 0 || *ptr != '\0') { Tcl_AppendResult(interp, "Failed to parse WKT length: \"", url.c_str(), "\"", (char*)NULL); return TCL_ERROR; } char *wkt = (char *)malloc((size_t)len); if (!SocketRead(wkt, len)) { free(wkt); Tcl_AppendResult(interp, "Failed to read WKT string", (char*)NULL); return TCL_ERROR; } osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; TRACE("Geometry: \"%s\"", wkt); osgEarth::Config conf("geometry", std::string(wkt)); free(wkt); opts.geometryConfig() = conf; maskOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wkt_file") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; opts.geometryUrl() = url; maskOpts.featureOptions() = opts; } else { Tcl_AppendResult(interp, "unknown mask driver \"", driver, "\": should be 'db', 'ogr', 'tfs', 'wfs', 'wkt', or 'wkt_file'", (char*)NULL); return TCL_ERROR; } int minLOD = 0; if (objc > 9) { if (Tcl_GetIntFromObj(interp, objv[9], &minLOD) != TCL_OK) { return TCL_ERROR; } } g_renderer->addTerrainMaskLayer(name, maskOpts, (unsigned)minLOD); } else if (type[0] == 'm' && strcmp(type, "model") == 0) { char *driver = Tcl_GetString(objv[5]); if (strcmp(driver, "simple") != 0) { return TCL_ERROR; } osgEarth::Drivers::SimpleModelOptions modelOpts; std::string url; if (objc > 6) { char *urlIn = Tcl_GetString(objv[6]); url = g_renderer->getCanonicalPath(std::string(urlIn)); if (url.empty()) { Tcl_AppendResult(interp, "file not found: \"", urlIn, "\"", (char*)NULL); return TCL_ERROR; } } modelOpts.url() = url; double x, y, z; x = y = z = 0.0; if (objc > 8) { if (Tcl_GetDoubleFromObj(interp, objv[7], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[8], &y) != TCL_OK) { return TCL_ERROR; } } if (objc > 9) { if (Tcl_GetDoubleFromObj(interp, objv[9], &z) != TCL_OK) { return TCL_ERROR; } } modelOpts.location() = osg::Vec3d(x, y, z); double rotx = 0.0, roty = 0.0, rotz = 0.0; if (objc > 12) { if (Tcl_GetDoubleFromObj(interp, objv[10], &rotx) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[11], &roty) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[12], &rotz) != TCL_OK) { return TCL_ERROR; } } if (rotx != 0.0 || roty != 0.0 || rotz != 0.0) { modelOpts.orientation() = osg::Vec3d(rotx, roty, rotz); } modelOpts.paged() = false; bool lighting = true; bool terrainPatch = false; if (objc > 13) { if (GetBooleanFromObj(interp, objv[13], &terrainPatch) != TCL_OK) { return TCL_ERROR; } } g_renderer->addModelLayer(name, modelOpts, pos, cache, lighting, visible, terrainPatch); } else if (type[0] == 'i' && strcmp(type, "icon") == 0) { char *driver = Tcl_GetString(objv[5]); char *format = Tcl_GetString(objv[6]); if (format && strlen(format) > 0 && strcmp(format, "json") != 0 && strcmp(format, "gml") != 0) { Tcl_AppendResult(interp, "unknown format \"", format, "\": should be 'json' or 'gml'", (char*)NULL); return TCL_ERROR; } char *typeName = Tcl_GetString(objv[7]); char *urlIn = Tcl_GetString(objv[8]); std::string url = g_renderer->getCanonicalPath(std::string(urlIn)); if (url.empty()) { Tcl_AppendResult(interp, "file not found: \"", urlIn, "\"", (char*)NULL); return TCL_ERROR; } if (GetBooleanFromObj(interp, objv[9], &cache) != TCL_OK) { return TCL_ERROR; } char *icon = Tcl_GetString(objv[10]); char *scaleExpr = Tcl_GetString(objv[11]); char *headingExpr = Tcl_GetString(objv[12]); bool declutter = false; if (GetBooleanFromObj(interp, objv[13], &declutter) != TCL_OK) { return TCL_ERROR; } char *placementStr = Tcl_GetString(objv[14]); char *alignStr = Tcl_GetString(objv[15]); bool lighting = true; osgEarth::Symbology::Style style; osgEarth::Symbology::IconSymbol *is = style.getOrCreateSymbol(); if (icon != NULL && strlen(icon) > 0) { std::string iconFile = g_renderer->getIconFile(icon); if (!iconFile.empty()) { is->url()->setLiteral(iconFile); } else { is->url() = osgEarth::Symbology::StringExpression(icon); } } else { is->url()->setLiteral(g_renderer->getPinIcon()); } if (scaleExpr != NULL && strlen(scaleExpr) > 0) { is->scale() = osgEarth::Symbology::NumericExpression(scaleExpr); } if (headingExpr != NULL && strlen(headingExpr) > 0) { is->heading() = osgEarth::Symbology::NumericExpression(headingExpr); } is->declutter() = declutter; is->occlusionCull() = false; is->occlusionCullAltitude() = 200000; is->alignment() = ParseIconAlignment(alignStr, osgEarth::Symbology::IconSymbol::ALIGN_CENTER_BOTTOM); is->placement() = ParseInstancePlacement(placementStr, osgEarth::Symbology::InstanceSymbol::PLACEMENT_VERTEX); //is->density() = 1.0f; // For PLACEMENT_INTERVAL/RANDOM #if 0 osgEarth::Symbology::AltitudeSymbol *alt = style.getOrCreateSymbol(); alt->clamping() = osgEarth::Symbology::AltitudeSymbol::CLAMP_TO_TERRAIN; alt->technique() = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_DRAPE; //alt->technique() = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_GPU; //alt->verticalOffset() = osgEarth::Symbology::NumericExpression(); //alt->verticalScale() = osgEarth::Symbology::NumericExpression(); #endif #ifdef USE_DEPTH_OFFSET osgEarth::Symbology::RenderSymbol* rs = style.getOrCreateSymbol(); rs->depthOffset()->enabled() = true; rs->depthOffset()->minBias() = 1000; #endif osgEarth::Drivers::FeatureGeomModelOptions geomOpts; geomOpts.styles() = new osgEarth::Symbology::StyleSheet(); geomOpts.styles()->addStyle(style); geomOpts.enableLighting() = false; geomOpts.minRange() = 0.f; geomOpts.maxRange() = FLT_MAX; if (objc > 16) { float min, max; if (GetFloatFromObj(interp, objv[16], &min) != TCL_OK || GetFloatFromObj(interp, objv[17], &max) != TCL_OK) { return TCL_ERROR; } geomOpts.minRange() = min; geomOpts.maxRange() = max; } //geomOpts.renderOrder() = int; //geomOpts.depthTestEnabled() = bool; if (driver[0] == 'o' && strcmp(driver, "ogr") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; if (osgDB::getLowerCaseFileExtension(url) == "csv") { url = loadCSVLayer(url.c_str()); } opts.url() = url; geomOpts.featureOptions() = opts; } else if (driver[0] == 't' && strcmp(driver, "tfs") == 0) { osgEarth::Drivers::TFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.format() = format; geomOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wfs") == 0) { osgEarth::Drivers::WFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.outputFormat() = format; opts.typeName() = typeName; geomOpts.featureOptions() = opts; } else { Tcl_AppendResult(interp, "unknown feature driver \"", driver, "\": should be 'ogr', 'tfs', or 'wfs'", (char*)NULL); return TCL_ERROR; } g_renderer->addModelLayer(name, geomOpts, pos, cache, lighting, visible); } else if (type[0] == 'p' && strcmp(type, "point") == 0) { char *driver = Tcl_GetString(objv[5]); char *format = Tcl_GetString(objv[6]); if (format && strlen(format) > 0 && strcmp(format, "json") != 0 && strcmp(format, "gml") != 0) { Tcl_AppendResult(interp, "unknown format \"", format, "\": should be 'json' or 'gml'", (char*)NULL); return TCL_ERROR; } char *typeName = Tcl_GetString(objv[7]); char *urlIn = Tcl_GetString(objv[8]); std::string url = g_renderer->getCanonicalPath(std::string(urlIn)); if (url.empty()) { Tcl_AppendResult(interp, "file not found: \"", urlIn, "\"", (char*)NULL); return TCL_ERROR; } if (GetBooleanFromObj(interp, objv[9], &cache) != TCL_OK) { return TCL_ERROR; } float r, g, b; if (GetFloatFromObj(interp, objv[10], &r) != TCL_OK || GetFloatFromObj(interp, objv[11], &g) != TCL_OK || GetFloatFromObj(interp, objv[12], &b) != TCL_OK) { return TCL_ERROR; } float ptSize; if (GetFloatFromObj(interp, objv[13], &ptSize) != TCL_OK) { return TCL_ERROR; } bool lighting = true; osgEarth::Symbology::Style style; osgEarth::Symbology::PointSymbol *ps = style.getOrCreateSymbol(); ps->fill()->color() = osgEarth::Symbology::Color(r, g, b); ps->size() = ptSize; osgEarth::Symbology::AltitudeSymbol::Clamping clamping = osgEarth::Symbology::AltitudeSymbol::CLAMP_TO_TERRAIN; if (objc > 14) { clamping = ParseClamping(Tcl_GetString(objv[14]), clamping); } osgEarth::Symbology::AltitudeSymbol::Technique technique = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_GPU; if (objc > 15) { technique = ParseClampingTechnique(Tcl_GetString(objv[15]), technique); } osgEarth::Symbology::AltitudeSymbol *alt = style.getOrCreateSymbol(); alt->clamping() = clamping; alt->technique() = technique; //alt->verticalOffset() = osgEarth::Symbology::NumericExpression(); //alt->verticalScale() = osgEarth::Symbology::NumericExpression(); #if 1 || defined(USE_DEPTH_OFFSET) osgEarth::Symbology::RenderSymbol* rs = style.getOrCreateSymbol(); rs->depthOffset()->enabled() = true; rs->depthOffset()->minBias() = 1000; #endif osgEarth::Drivers::FeatureGeomModelOptions geomOpts; geomOpts.styles() = new osgEarth::Symbology::StyleSheet(); geomOpts.styles()->addStyle(style); geomOpts.enableLighting() = false; geomOpts.minRange() = 0.f; geomOpts.maxRange() = FLT_MAX; if (objc > 16) { float min, max; if (GetFloatFromObj(interp, objv[16], &min) != TCL_OK || GetFloatFromObj(interp, objv[17], &max) != TCL_OK) { return TCL_ERROR; } geomOpts.minRange() = min; geomOpts.maxRange() = max; } //geomOpts.renderOrder() = int; //geomOpts.depthTestEnabled() = bool; if (driver[0] == 'o' && strcmp(driver, "ogr") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; if (osgDB::getLowerCaseFileExtension(url) == "csv") { url = loadCSVLayer(url.c_str()); } opts.url() = url; geomOpts.featureOptions() = opts; } else if (driver[0] == 't' && strcmp(driver, "tfs") == 0) { osgEarth::Drivers::TFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.format() = format; geomOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wfs") == 0) { osgEarth::Drivers::WFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.outputFormat() = format; opts.typeName() = typeName; geomOpts.featureOptions() = opts; } else { Tcl_AppendResult(interp, "unknown feature driver \"", driver, "\": should be 'ogr', 'tfs', or 'wfs'", (char*)NULL); return TCL_ERROR; } g_renderer->addModelLayer(name, geomOpts, pos, cache, lighting, visible); } else if (type[0] == 'p' && strcmp(type, "polygon") == 0) { char *driver = Tcl_GetString(objv[5]); char *format = Tcl_GetString(objv[6]); if (format && strlen(format) > 0 && strcmp(format, "json") != 0 && strcmp(format, "gml") != 0) { Tcl_AppendResult(interp, "unknown format \"", format, "\": should be 'json' or 'gml'", (char*)NULL); return TCL_ERROR; } char *typeName = Tcl_GetString(objv[7]); char *urlIn = Tcl_GetString(objv[8]); std::string url = g_renderer->getCanonicalPath(std::string(urlIn)); if (url.empty()) { Tcl_AppendResult(interp, "file not found: \"", urlIn, "\"", (char*)NULL); return TCL_ERROR; } if (GetBooleanFromObj(interp, objv[9], &cache) != TCL_OK) { return TCL_ERROR; } float r, g, b; if (GetFloatFromObj(interp, objv[10], &r) != TCL_OK || GetFloatFromObj(interp, objv[11], &g) != TCL_OK || GetFloatFromObj(interp, objv[12], &b) != TCL_OK) { return TCL_ERROR; } float lineWidth = 0.0f; if (objc > 13) { if (GetFloatFromObj(interp, objv[13], &lineWidth) != TCL_OK) { return TCL_ERROR; } } float strokeR = 0, strokeG = 0, strokeB = 0; if (objc > 14) { if (GetFloatFromObj(interp, objv[14], &strokeR) != TCL_OK || GetFloatFromObj(interp, objv[15], &strokeG) != TCL_OK || GetFloatFromObj(interp, objv[16], &strokeB) != TCL_OK) { return TCL_ERROR; } } osgEarth::Symbology::Style style; osgEarth::Symbology::PolygonSymbol *ps = style.getOrCreateSymbol(); ps->fill()->color() = osgEarth::Symbology::Color(r, g, b); if (lineWidth > 0.0f) { osgEarth::Symbology::LineSymbol *ls = style.getOrCreateSymbol(); ls->stroke()->color() = osgEarth::Symbology::Color(strokeR, strokeG, strokeB); ls->stroke()->width() = lineWidth; ls->stroke()->widthUnits() = osgEarth::Units::PIXELS; ls->stroke()->lineCap() = osgEarth::Symbology::Stroke::LINECAP_FLAT; // _SQUARE, _ROUND //ls->stroke()->roundingRatio() = 0.4f; ls->stroke()->lineJoin() = osgEarth::Symbology::Stroke::LINEJOIN_MITRE; // _ROUND //ls->stroke()->stipplePattern() = 0; // Bitmask: unsigned short //ls->stroke()->stippleFactor() = 0; // num reps: unsigned } osgEarth::Symbology::AltitudeSymbol::Clamping clamping = osgEarth::Symbology::AltitudeSymbol::CLAMP_TO_TERRAIN; if (objc > 17) { clamping = ParseClamping(Tcl_GetString(objv[17]), clamping); } osgEarth::Symbology::AltitudeSymbol::Technique technique = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_GPU; if (objc > 18) { technique = ParseClampingTechnique(Tcl_GetString(objv[18]), technique); } osgEarth::Symbology::AltitudeSymbol *alt = style.getOrCreateSymbol(); alt->clamping() = clamping; alt->technique() = technique; //alt->verticalOffset() = osgEarth::Symbology::NumericExpression(); //alt->verticalScale() = osgEarth::Symbology::NumericExpression(); #if 1 || defined(USE_DEPTH_OFFSET) osgEarth::Symbology::RenderSymbol* rs = style.getOrCreateSymbol(); rs->depthOffset()->enabled() = true; rs->depthOffset()->minBias() = 1000; #endif bool terrainPatch = false; if (objc > 19) { if (GetBooleanFromObj(interp, objv[19], &terrainPatch) != TCL_OK) { return TCL_ERROR; } } bool lighting = true; osgEarth::Drivers::FeatureGeomModelOptions geomOpts; geomOpts.styles() = new osgEarth::Symbology::StyleSheet(); geomOpts.styles()->addStyle(style); geomOpts.enableLighting() = lighting; geomOpts.minRange() = 0.f; geomOpts.maxRange() = FLT_MAX; if (objc > 21) { float min, max; if (GetFloatFromObj(interp, objv[20], &min) != TCL_OK || GetFloatFromObj(interp, objv[21], &max) != TCL_OK) { return TCL_ERROR; } geomOpts.minRange() = min; geomOpts.maxRange() = max; } if (driver[0] == 'o' && strcmp(driver, "ogr") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; if (osgDB::getLowerCaseFileExtension(url) == "csv") { url = loadCSVLayer(url.c_str()); } opts.url() = url; geomOpts.featureOptions() = opts; } else if (driver[0] == 't' && strcmp(driver, "tfs") == 0) { osgEarth::Drivers::TFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.format() = format; geomOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wfs") == 0) { osgEarth::Drivers::WFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.outputFormat() = format; opts.typeName() = typeName; geomOpts.featureOptions() = opts; } else { Tcl_AppendResult(interp, "unknown feature driver \"", driver, "\": should be 'ogr', 'tfs', or 'wfs'", (char*)NULL); return TCL_ERROR; } g_renderer->addModelLayer(name, geomOpts, pos, cache, lighting, visible, terrainPatch); } else if (type[0] == 'l' && strcmp(type, "line") == 0) { char *driver = Tcl_GetString(objv[5]); char *format = Tcl_GetString(objv[6]); if (format && strlen(format) > 0 && strcmp(format, "json") != 0 && strcmp(format, "gml") != 0) { Tcl_AppendResult(interp, "unknown format \"", format, "\": should be 'json' or 'gml'", (char*)NULL); return TCL_ERROR; } char *typeName = Tcl_GetString(objv[7]); char *urlIn = Tcl_GetString(objv[8]); std::string url = g_renderer->getCanonicalPath(std::string(urlIn)); if (url.empty()) { Tcl_AppendResult(interp, "file not found: \"", urlIn, "\"", (char*)NULL); return TCL_ERROR; } if (GetBooleanFromObj(interp, objv[9], &cache) != TCL_OK) { return TCL_ERROR; } float r, g, b; if (GetFloatFromObj(interp, objv[10], &r) != TCL_OK || GetFloatFromObj(interp, objv[11], &g) != TCL_OK || GetFloatFromObj(interp, objv[12], &b) != TCL_OK) { return TCL_ERROR; } float lineWidth; osgEarth::Units units = osgEarth::Units::PIXELS; if (!osgEarth::Units::parse(Tcl_GetString(objv[13]), lineWidth, units, osgEarth::Units::PIXELS)) { Tcl_AppendResult(interp, "Unkown units: \"", Tcl_GetString(objv[13]), "\"", (char*)NULL); return TCL_ERROR; } osgEarth::Symbology::Stroke::LineCapStyle cap = osgEarth::Symbology::Stroke::LINECAP_FLAT; osgEarth::Symbology::Stroke::LineJoinStyle join = osgEarth::Symbology::Stroke::LINEJOIN_MITRE; if (objc > 14) { cap = ParseLineCapStyle(Tcl_GetString(objv[14]), cap); } if (objc > 15) { join = ParseLineJoinStyle(Tcl_GetString(objv[15]), join); } bool lighting = true; osgEarth::Symbology::Style style; osgEarth::Symbology::LineSymbol *ls = style.getOrCreateSymbol(); ls->stroke()->color() = osgEarth::Symbology::Color(r, g, b); ls->stroke()->width() = lineWidth; ls->stroke()->widthUnits() = units; ls->stroke()->minPixels() = 1.0f; ls->stroke()->lineCap() = cap; //ls->stroke()->roundingRatio() = 0.4f; ls->stroke()->lineJoin() = join; if (objc > 16) { unsigned short stipplePattern = 0U; unsigned int stippleFactor = 1U; if (GetUShortFromObj(interp, objv[16], &stipplePattern) != TCL_OK || GetUIntFromObj(interp, objv[17], &stippleFactor) != TCL_OK) { return TCL_ERROR; } if (stipplePattern > 0U) { ls->stroke()->stipplePattern() = stipplePattern; // Bitmask: unsigned short ls->stroke()->stippleFactor() = stippleFactor; // num reps: unsigned } } osgEarth::Symbology::AltitudeSymbol::Clamping clamping = osgEarth::Symbology::AltitudeSymbol::CLAMP_TO_TERRAIN; if (objc > 18) { clamping = ParseClamping(Tcl_GetString(objv[18]), clamping); } osgEarth::Symbology::AltitudeSymbol::Technique technique = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_GPU; if (objc > 18) { technique = ParseClampingTechnique(Tcl_GetString(objv[19]), technique); } osgEarth::Symbology::AltitudeSymbol *alt = style.getOrCreateSymbol(); alt->clamping() = clamping; alt->technique() = technique; //alt->clampingResolution() = 1.0f; //alt->binding() = osgEarth::Symbology::AltitudeSymbol::BINDING_VERTEX; //BINDING_CENTROID //alt->verticalOffset() = osgEarth::Symbology::NumericExpression(); //alt->verticalScale() = osgEarth::Symbology::NumericExpression(); #if 1 || defined(USE_DEPTH_OFFSET) osgEarth::Symbology::RenderSymbol* rs = style.getOrCreateSymbol(); rs->depthOffset()->enabled() = true; rs->depthOffset()->minBias() = 1000; #endif osgEarth::Drivers::FeatureGeomModelOptions geomOpts; geomOpts.styles() = new osgEarth::Symbology::StyleSheet(); geomOpts.styles()->addStyle(style); geomOpts.enableLighting() = false; geomOpts.minRange() = 0.f; geomOpts.maxRange() = FLT_MAX; if (objc > 20) { float min, max; if (GetFloatFromObj(interp, objv[20], &min) != TCL_OK || GetFloatFromObj(interp, objv[21], &max) != TCL_OK) { return TCL_ERROR; } geomOpts.minRange() = min; geomOpts.maxRange() = max; } if (driver[0] == 'o' && strcmp(driver, "ogr") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; if (osgDB::getLowerCaseFileExtension(url) == "csv") { url = loadCSVLayer(url.c_str()); } opts.url() = url; geomOpts.featureOptions() = opts; } else if (driver[0] == 't' && strcmp(driver, "tfs") == 0) { osgEarth::Drivers::TFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.format() = format; geomOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wfs") == 0) { osgEarth::Drivers::WFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.outputFormat() = format; opts.typeName() = typeName; geomOpts.featureOptions() = opts; } else { Tcl_AppendResult(interp, "unknown feature driver \"", driver, "\": should be 'ogr', 'tfs', or 'wfs'", (char*)NULL); return TCL_ERROR; } g_renderer->addModelLayer(name, geomOpts, pos, cache, lighting, visible); } else if (type[0] == 't' && strcmp(type, "text") == 0) { char *driver = Tcl_GetString(objv[5]); char *format = Tcl_GetString(objv[6]); if (format && strlen(format) > 0 && strcmp(format, "json") != 0 && strcmp(format, "gml") != 0) { Tcl_AppendResult(interp, "unknown format \"", format, "\": should be 'json' or 'gml'", (char*)NULL); return TCL_ERROR; } char *typeName = Tcl_GetString(objv[7]); char *urlIn = Tcl_GetString(objv[8]); std::string url = g_renderer->getCanonicalPath(std::string(urlIn)); if (url.empty()) { Tcl_AppendResult(interp, "file not found: \"", urlIn, "\"", (char*)NULL); return TCL_ERROR; } if (GetBooleanFromObj(interp, objv[9], &cache) != TCL_OK) { return TCL_ERROR; } char *content = Tcl_GetString(objv[10]); char *priority = Tcl_GetString(objv[11]); float fgR, fgG, fgB; float bgR, bgG, bgB; float haloWidth, ftSize; osg::Vec2s pixelOffset(0, 0); if (GetFloatFromObj(interp, objv[12], &fgR) != TCL_OK || GetFloatFromObj(interp, objv[13], &fgG) != TCL_OK || GetFloatFromObj(interp, objv[14], &fgB) != TCL_OK || GetFloatFromObj(interp, objv[15], &bgR) != TCL_OK || GetFloatFromObj(interp, objv[16], &bgG) != TCL_OK || GetFloatFromObj(interp, objv[17], &bgB) != TCL_OK || GetFloatFromObj(interp, objv[18], &haloWidth) != TCL_OK || GetFloatFromObj(interp, objv[19], &ftSize) != TCL_OK) { return TCL_ERROR; } bool declutter; if (GetBooleanFromObj(interp, objv[20], &declutter) != TCL_OK) { return TCL_ERROR; } osgEarth::Symbology::TextSymbol::Alignment alignment = ParseTextAlignment(Tcl_GetString(objv[21])); int xoff, yoff; if (Tcl_GetIntFromObj(interp, objv[22], &xoff) != TCL_OK || Tcl_GetIntFromObj(interp, objv[23], &yoff) != TCL_OK) { return TCL_ERROR; } pixelOffset.x() = (short)xoff; pixelOffset.y() = (short)yoff; bool lighting = true; osgEarth::Symbology::Style style; osgEarth::Symbology::TextSymbol *ts = style.getOrCreateSymbol(); 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); if (priority != NULL && strlen(priority) > 0) { ts->priority() = osgEarth::Symbology::NumericExpression(priority); } //ts->font() = "Arial"; ts->size() = ftSize; ts->alignment() = alignment; ts->declutter() = declutter; ts->pixelOffset() = pixelOffset; ts->encoding() = osgEarth::Symbology::TextSymbol::ENCODING_UTF8; #ifdef USE_DEPTH_OFFSET osgEarth::Symbology::RenderSymbol* rs = style.getOrCreateSymbol(); rs->depthOffset()->enabled() = true; rs->depthOffset()->minBias() = 1000; #endif osgEarth::Drivers::FeatureGeomModelOptions geomOpts; geomOpts.styles() = new osgEarth::Symbology::StyleSheet(); geomOpts.styles()->addStyle(style); geomOpts.enableLighting() = false; geomOpts.minRange() = 0.f; geomOpts.maxRange() = FLT_MAX; if (objc > 24) { float min, max; if (GetFloatFromObj(interp, objv[24], &min) != TCL_OK || GetFloatFromObj(interp, objv[25], &max) != TCL_OK) { return TCL_ERROR; } geomOpts.minRange() = min; geomOpts.maxRange() = max; } if (driver[0] == 'o' && strcmp(driver, "ogr") == 0) { osgEarth::Drivers::OGRFeatureOptions opts; opts.name() = name; if (osgDB::getLowerCaseFileExtension(url) == "csv") { url = loadCSVLayer(url.c_str()); } opts.url() = url; geomOpts.featureOptions() = opts; } else if (driver[0] == 't' && strcmp(driver, "tfs") == 0) { osgEarth::Drivers::TFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.format() = format; geomOpts.featureOptions() = opts; } else if (driver[0] == 'w' && strcmp(driver, "wfs") == 0) { osgEarth::Drivers::WFSFeatureOptions opts; opts.name() = name; opts.url() = url; opts.outputFormat() = format; opts.typeName() = typeName; geomOpts.featureOptions() = opts; } else { Tcl_AppendResult(interp, "unknown feature driver \"", driver, "\": should be 'ogr', 'tfs', or 'wfs'", (char*)NULL); return TCL_ERROR; } g_renderer->addModelLayer(name, geomOpts, pos, cache, lighting, visible); } else { Tcl_AppendResult(interp, "unknown map layer type \"", type, "\": should be 'image', 'elevation' or 'model'", (char*)NULL); return TCL_ERROR; } return TCL_OK; } static int MapLayerDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { if (objc > 3) { char *name = Tcl_GetString(objv[3]); g_renderer->removeImageLayer(name); g_renderer->removeElevationLayer(name); g_renderer->removeTerrainMaskLayer(name); g_renderer->removeModelLayer(name); } else { g_renderer->clearMap(); } return TCL_OK; } static int MapLayerMoveOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int pos; if (Tcl_GetIntFromObj(interp, objv[3], &pos) != TCL_OK) { return TCL_ERROR; } char *name = Tcl_GetString(objv[4]); if (pos < 0) { Tcl_AppendResult(interp, "bad layer pos ", pos, ": must be positive", (char*)NULL); return TCL_ERROR; } g_renderer->moveImageLayer(name, (unsigned int)pos); g_renderer->moveElevationLayer(name, (unsigned int)pos); g_renderer->moveModelLayer(name, (unsigned int)pos); return TCL_OK; } static int MapLayerOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double opacity; if (Tcl_GetDoubleFromObj(interp, objv[3], &opacity) != TCL_OK) { return TCL_ERROR; } char *name = Tcl_GetString(objv[4]); if (opacity < 0.0 || opacity > 1.0) { Tcl_AppendResult(interp, "bad layer opacity ", opacity, ": must be [0,1]", (char*)NULL); return TCL_ERROR; } g_renderer->setImageLayerOpacity(name, opacity); g_renderer->setModelLayerOpacity(name, opacity); return TCL_OK; } static int MapLayerNamesOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { std::vector layers; if (objc < 4) { g_renderer->getElevationLayerNames(layers); g_renderer->getTerrainMaskLayerNames(layers); g_renderer->getImageLayerNames(layers); g_renderer->getModelLayerNames(layers); } else { char *type = Tcl_GetString(objv[3]); if (type[0] == 'i' && strcmp(type, "image") == 0) { g_renderer->getImageLayerNames(layers); } else if (type[0] == 'e' && strcmp(type, "elevation") == 0) { g_renderer->getElevationLayerNames(layers); } else if (type[0] == 'm' && strcmp(type, "mask") == 0) { g_renderer->getTerrainMaskLayerNames(layers); } else if (type[0] == 'm' && strcmp(type, "model") == 0) { g_renderer->getModelLayerNames(layers); } else { Tcl_AppendResult(interp, "uknown type \"", type, "\": must be image, elevation, mask or model", (char*)NULL); return TCL_ERROR; } } std::ostringstream oss; size_t len = 0; oss << "nv>map names {"; len += 14; for (size_t i = 0; i < layers.size(); i++) { oss << "\"" << layers[i] << "\""; len += 2 + layers[i].length(); if (i < layers.size() - 1) { oss << " "; len++; } } oss << "}\n"; len += 2; #ifdef USE_THREADS queueResponse(oss.str().c_str(), len, Response::VOLATILE); #else ssize_t bytesWritten = SocketWrite(oss.str().c_str(), len); if (bytesWritten < 0) { return TCL_ERROR; } #endif /*USE_THREADS*/ return TCL_OK; } static int MapLayerVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { bool visible; if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) { return TCL_ERROR; } if (objc > 4) { char *name = Tcl_GetString(objv[4]); g_renderer->setImageLayerVisibility(name, visible); g_renderer->setElevationLayerVisibility(name, visible); g_renderer->setModelLayerVisibility(name, visible); } else { std::vector layers; g_renderer->getImageLayerNames(layers); g_renderer->getElevationLayerNames(layers); g_renderer->getModelLayerNames(layers); for (std::vector::iterator itr = layers.begin(); itr != layers.end(); ++itr) { g_renderer->setImageLayerVisibility(itr->c_str(), visible); g_renderer->setElevationLayerVisibility(itr->c_str(), visible); g_renderer->setModelLayerVisibility(itr->c_str(), visible); } } return TCL_OK; } static CmdSpec mapLayerOps[] = { {"add", 1, MapLayerAddOp, 6, 0, "name type driver ?url? ?args?"}, {"delete", 1, MapLayerDeleteOp, 3, 4, "?name?"}, {"move", 1, MapLayerMoveOp, 5, 5, "pos name"}, {"names", 1, MapLayerNamesOp, 3, 4, "?type?"}, {"opacity", 1, MapLayerOpacityOp, 5, 5, "opacity name"}, {"visible", 1, MapLayerVisibleOp, 4, 5, "bool ?name?"}, }; static int nMapLayerOps = NumCmdSpecs(mapLayerOps); static int MapLayerOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nMapLayerOps, mapLayerOps, CMDSPEC_ARG2, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int MapLoadOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { char *opt = Tcl_GetString(objv[2]); if (opt[0] == 'f' && strcmp(opt, "file") == 0) { g_renderer->loadEarthFile(Tcl_GetString(objv[3])); } else if (opt[0] == 'u' && strcmp(opt, "url") == 0) { std::ostringstream path; path << "server:" << Tcl_GetString(objv[3]); g_renderer->loadEarthFile(path.str().c_str()); } else if (opt[0] == 'd' && strcmp(opt, "data") == 0) { opt = Tcl_GetString(objv[3]); if (opt[0] != 'f' || strcmp(opt, "follows") != 0) { return TCL_ERROR; } int len; if (Tcl_GetIntFromObj(interp, objv[4], &len) != TCL_OK) { return TCL_ERROR; } // Read Earth file from socket char *buf = (char *)malloc((size_t)len); SocketRead(buf, (size_t)len); std::ostringstream path; path << "/tmp/tmp" << getpid() << ".earth"; const char *pathStr = path.str().c_str(); FILE *tmpFile = fopen(pathStr, "w"); fwrite(buf, len, 1, tmpFile); fclose(tmpFile); g_renderer->loadEarthFile(pathStr); unlink(pathStr); free(buf); } else { return TCL_ERROR; } return TCL_OK; } static int MapPinAddOp(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; } char *label = NULL; if (objc > 5) { label = Tcl_GetString(objv[5]); } if (g_renderer->getMapSRS() == NULL) { Tcl_AppendResult(interp, "Could not get map SRS", (char*)NULL); return TCL_ERROR; } // Get lat/long double latitude, longitude; if (!g_renderer->mouseToLatLong(x, y, &latitude, &longitude)) { USER_ERROR("Can't add pin here"); return TCL_OK; } g_renderer->addPlaceNode(latitude, longitude, label); return TCL_OK; } static int MapPinDeleteOp(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; } g_renderer->deletePlaceNode(x, y); return TCL_OK; } static int MapPinHoverOp(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; } g_renderer->hoverPlaceNode(x, y); return TCL_OK; } static CmdSpec mapPinOps[] = { {"add", 1, MapPinAddOp, 5, 6, "x y ?label?"}, {"delete", 1, MapPinDeleteOp, 5, 5, "x y"}, {"hover", 1, MapPinHoverOp, 5, 5, "x y"}, }; static int nMapPinOps = NumCmdSpecs(mapPinOps); static int MapPinOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nMapPinOps, mapPinOps, CMDSPEC_ARG2, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int MapPositionDisplayOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { bool state; if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) { return TCL_ERROR; } Renderer::CoordinateDisplayType type = Renderer::COORDS_LATLONG_DECIMAL_DEGREES; if (state && objc > 3) { const char *str = Tcl_GetString(objv[3]); if (str[0] == 'l' && strcmp(str, "latlong_decimal_degrees") == 0) { type = Renderer::COORDS_LATLONG_DECIMAL_DEGREES; } else if (str[0] == 'l' && strcmp(str, "latlong_degrees_decimal_minutes") == 0) { type = Renderer::COORDS_LATLONG_DEGREES_DECIMAL_MINUTES; } else if (str[0] == 'l' && strcmp(str, "latlong_degrees_minutes_seconds") == 0) { type = Renderer::COORDS_LATLONG_DEGREES_MINUTES_SECONDS; } else if (str[0] == 'm' && strcmp(str, "mgrs") == 0) { type = Renderer::COORDS_MGRS; } else { Tcl_AppendResult(interp, "invalid type: \"", str, "\": should be 'latlong_decimal_degrees', 'latlong_degrees_decimal_minutes', 'latlong_degrees_minutes_seconds', or 'mgrs'", (char*)NULL); return TCL_ERROR; } } if (state && objc > 4) { int precision; if (Tcl_GetIntFromObj(interp, objv[4], &precision) != TCL_OK) { return TCL_ERROR; } g_renderer->setCoordinateReadout(state, type, precision); } else { g_renderer->setCoordinateReadout(state, type); } return TCL_OK; } static int MapResetOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { char *typeStr = Tcl_GetString(objv[2]); osgEarth::MapOptions::CoordinateSystemType type; if (typeStr[0] == 'g' && strcmp(typeStr, "geocentric") == 0) { type = osgEarth::MapOptions::CSTYPE_GEOCENTRIC; } else if (typeStr[0] == 'g' && strcmp(typeStr, "geocentric_cube") == 0) { type = osgEarth::MapOptions::CSTYPE_GEOCENTRIC_CUBE; } else if (typeStr[0] == 'p' && strcmp(typeStr, "projected") == 0) { type = osgEarth::MapOptions::CSTYPE_PROJECTED; } else { Tcl_AppendResult(interp, "bad map type \"", typeStr, "\": must be geocentric or projected", (char*)NULL); 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 < 7) { Tcl_AppendResult(interp, "wrong # arguments: profile required for projected maps", (char*)NULL); return TCL_ERROR; } 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; } double bounds[4]; for (int i = 0; i < 4; i++) { if (Tcl_GetDoubleFromObj(interp, objv[7+i], &bounds[i]) != TCL_OK) { return TCL_ERROR; } } // Check if min > max if (bounds[0] > bounds[2] || bounds[1] > bounds[3]) { Tcl_AppendResult(interp, "invalid bounds", (char*)NULL); return TCL_ERROR; } // Note: plate-carre generates same SRS as others, but with // _is_plate_carre flag set // In map profile, _is_plate_carre is forced on for // geographic+projected SRS if (strcmp(profile, "geodetic") == 0 || strcmp(profile, "epsg:4326") == 0 || strcmp(profile, "wgs84") == 0 || strcmp(profile, "plate-carre") == 0 || strcmp(profile, "plate-carree") == 0) { if (bounds[0] < -180. || bounds[0] > 180. || bounds[2] < -180. || bounds[2] > 180. || bounds[1] < -90. || bounds[1] > 90. || bounds[3] < -90. || bounds[3] > 90.) { Tcl_AppendResult(interp, "invalid bounds", (char*)NULL); 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 || strcmp(profile, "epsg:3785") == 0 || strcmp(profile, "epsg:3857") == 0 || strcmp(profile, "epsg:102113") == 0) { for (int i = 0; i < 4; i++) { if (bounds[i] < -20037508.34278925 || bounds[i] > 20037508.34278925) { Tcl_AppendResult(interp, "invalid bounds", (char*)NULL); return TCL_ERROR; } } } else if (strcmp(profile, "epsg:32662") == 0 || strcmp(profile, "epsg:32663") == 0) { // 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; } } 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, "\": must be e.g. 'global-geodetic', 'global-mercator'...", (char*)NULL); return TCL_ERROR; } g_renderer->resetMap(type, bgColor, profile); } } else { // No profile required for geocentric (3D) maps g_renderer->resetMap(type, bgColor); } return TCL_OK; } static int MapScaleBarOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { bool state; if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) { return TCL_ERROR; } g_renderer->setScaleBar(state); if (state && objc > 3) { const char *unitStr = Tcl_GetString(objv[3]); ScaleBarUnits units; if (unitStr[0] == 'm' && strcmp(unitStr, "meters") == 0) { units = UNITS_METERS; } else if (unitStr[0] == 'f' && strcmp(unitStr, "feet") == 0) { units = UNITS_INTL_FEET; } else if (unitStr[0] == 'u' && strcmp(unitStr, "us_survey_feet") == 0) { units = UNITS_US_SURVEY_FEET; } else if (unitStr[0] == 'n' && strcmp(unitStr, "nautical_miles") == 0) { units = UNITS_NAUTICAL_MILES; } else { Tcl_AppendResult(interp, "bad units \"", unitStr, "\": must be 'meters', 'feet', 'us_survey_feet' or 'nautical_miles'", (char*)NULL); return TCL_ERROR; } g_renderer->setScaleBarUnits(units); } return TCL_OK; } static int MapSequencePauseOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { const char *layer = Tcl_GetString(objv[3]); bool ret = g_renderer->sequencePause(layer); if (!ret) { Tcl_AppendResult(interp, "Layer \"", layer, "\" doesn't exist or has no sequence", (char *)NULL); return TCL_ERROR; } return TCL_OK; } static int MapSequencePlayOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { const char *layer = Tcl_GetString(objv[3]); bool ret = g_renderer->sequencePlay(layer); if (!ret) { Tcl_AppendResult(interp, "Layer \"", layer, "\" doesn't exist or has no sequence", (char *)NULL); return TCL_ERROR; } return TCL_OK; } static int MapSequenceSeekOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { const char *layer = Tcl_GetString(objv[4]); int frame; if (Tcl_GetIntFromObj(interp, objv[3], &frame) != TCL_OK) { return TCL_ERROR; } bool ret = g_renderer->sequenceSeek(layer, (unsigned int)frame); if (!ret) { Tcl_AppendResult(interp, "Layer \"", layer, "\" doesn't exist or has no sequence", (char *)NULL); return TCL_ERROR; } return TCL_OK; } static CmdSpec mapSequenceOps[] = { {"pause", 2, MapSequencePauseOp, 4, 4, "layerName"}, {"play", 2, MapSequencePlayOp, 4, 4, "layerName"}, {"seek", 1, MapSequenceSeekOp, 5, 5, "frame layerName"}, }; static int nMapSequenceOps = NumCmdSpecs(mapSequenceOps); static int MapSequenceOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nMapSequenceOps, mapSequenceOps, CMDSPEC_ARG2, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int MapSetPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { if (objc < 3) { g_renderer->clearReadout(); } else { int x, y; if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK || Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) { return TCL_ERROR; } g_renderer->setReadout(x, y); } return TCL_OK; } static int MapTerrainAmbientOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { float ambient; if (GetFloatFromObj(interp, objv[3], &ambient) != TCL_OK) { return TCL_ERROR; } g_renderer->setSkyAmbient(ambient); return TCL_OK; } static int MapTerrainColorOp(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->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; } static int MapTerrainVertScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double scale; if (Tcl_GetDoubleFromObj(interp, objv[3], &scale) != TCL_OK) { return TCL_ERROR; } g_renderer->setTerrainVerticalScale(scale); return TCL_OK; } static int MapTerrainWireframeOp(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->setTerrainWireframe(state); return TCL_OK; } static CmdSpec mapTerrainOps[] = { {"ambient", 1, MapTerrainAmbientOp, 4, 4, "val"}, {"color", 1, MapTerrainColorOp, 6, 6, "r g b"}, {"edges", 1, MapTerrainEdgesOp, 4, 4, "bool"}, {"lighting", 2, MapTerrainLightingOp, 4, 4, "bool"}, {"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"}, }; static int nMapTerrainOps = NumCmdSpecs(mapTerrainOps); static int MapTerrainOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nMapTerrainOps, mapTerrainOps, CMDSPEC_ARG2, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int MapTimeOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { osgEarth::DateTime now; int year, month, day; double hours; year = now.year(); month = now.month(); day = now.day(); hours = now.hours(); if (objc > 2) { if (Tcl_GetDoubleFromObj(interp, objv[2], &hours) != TCL_OK) { return TCL_ERROR; } } if (objc > 3) { if (Tcl_GetIntFromObj(interp, objv[3], &day) != TCL_OK) { return TCL_ERROR; } } if (objc > 4) { if (Tcl_GetIntFromObj(interp, objv[4], &month) != TCL_OK) { return TCL_ERROR; } } if (objc > 5) { if (Tcl_GetIntFromObj(interp, objv[5], &year) != TCL_OK) { return TCL_ERROR; } } g_renderer->setEphemerisTime(year, month, day, hours); return TCL_OK; } static CmdSpec mapOps[] = { {"attrib", 1, MapAttributionOp, 3, 3, "string"}, {"box", 1, MapBoxOp, 3, 0, "op ?params..."}, {"coords", 1, MapCoordsOp, 4, 6, "token coords ?srs? ?verticalDatum?"}, {"grid", 1, MapGraticuleOp, 3, 4, "bool ?type?"}, {"layer", 2, MapLayerOp, 3, 0, "op ?params...?"}, {"load", 2, MapLoadOp, 4, 5, "options"}, {"pin", 2, MapPinOp, 3, 0, "op ?params...?"}, {"posdisp", 2, MapPositionDisplayOp, 3, 5, "bool ?format? ?precision?"}, {"reset", 1, MapResetOp, 6, 11, "type r g b ?profile xmin ymin xmax ymax?"}, {"scalebar", 1, MapScaleBarOp, 3, 4, "bool ?units?"}, {"sequence", 3, MapSequenceOp, 3, 0, "op ?params...?"}, {"setpos", 3, MapSetPositionOp, 2, 4, "x y"}, {"terrain", 1, MapTerrainOp, 3, 0, "op ?params...?"}, {"time", 1, MapTimeOp, 2, 6, "?hours? ?day? ?month? ?year?"}, }; static int nMapOps = NumCmdSpecs(mapOps); static int MapCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nMapOps, mapOps, CMDSPEC_ARG1, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int MouseClickOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int button; double x, y; if (Tcl_GetIntFromObj(interp, objv[2], &button) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK) { return TCL_ERROR; } g_renderer->mouseClick(button, x, y); return TCL_OK; } static int MouseDoubleClickOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int button; double x, y; if (Tcl_GetIntFromObj(interp, objv[2], &button) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK) { return TCL_ERROR; } g_renderer->mouseDoubleClick(button, x, y); return TCL_OK; } static int MouseDragOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int button; double x, y; if (Tcl_GetIntFromObj(interp, objv[2], &button) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK) { return TCL_ERROR; } g_renderer->mouseDrag(button, x, y); return TCL_OK; } static int MouseMotionOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { double x, y; if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK) { return TCL_ERROR; } g_renderer->mouseMotion(x, y); return TCL_OK; } static int MouseReleaseOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int button; double x, y; if (Tcl_GetIntFromObj(interp, objv[2], &button) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK || Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK) { return TCL_ERROR; } g_renderer->mouseRelease(button, x, y); return TCL_OK; } static int MouseScrollOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int direction; if (Tcl_GetIntFromObj(interp, objv[2], &direction) != TCL_OK) { return TCL_ERROR; } g_renderer->mouseScroll(direction); return TCL_OK; } static CmdSpec mouseOps[] = { {"click", 1, MouseClickOp, 5, 5, "button x y"}, {"dblclick", 2, MouseDoubleClickOp, 5, 5, "button x y"}, {"drag", 2, MouseDragOp, 5, 5, "button x y"}, {"motion", 1, MouseMotionOp, 4, 4, "x y"}, {"release", 1, MouseReleaseOp, 5, 5, "button x y"}, {"scroll", 1, MouseScrollOp, 3, 3, "direction"}, }; static int nMouseOps = NumCmdSpecs(mouseOps); static int MouseCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nMouseOps, mouseOps, CMDSPEC_ARG1, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int PlacardConfigOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int namec; Tcl_Obj **namev = NULL; const char *layerName = Tcl_GetString(objv[5]); if (Tcl_ListObjGetElements(interp, objv[2], &namec, &namev) != TCL_OK) { return TCL_ERROR; } if (namec % 2 != 0) { Tcl_AppendResult(interp, "invalid attribute list", (char *)NULL); return TCL_ERROR; } Placard placardConf; for (int i = 0; i < namec; i+=2) { std::string name(Tcl_GetString(namev[i])); std::string label(Tcl_GetString(namev[i+1])); placardConf.addEntry(name, label); } osgEarth::Config styleConf("style", Tcl_GetString(objv[3])); styleConf.add("type", "text/css"); placardConf.setStyle(osgEarth::Symbology::Style(styleConf)); float padding; if (GetFloatFromObj(interp, objv[4], &padding) != TCL_OK) { return TCL_ERROR; } placardConf.setPadding(padding); g_renderer->setPlacardConfig(placardConf, layerName); return TCL_OK; } static int PlacardEnableOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { const char *layerName = Tcl_GetString(objv[3]); bool enable; if (GetBooleanFromObj(interp, objv[2], &enable) != TCL_OK) { return TCL_ERROR; } g_renderer->enablePlacard(layerName, enable); return TCL_OK; } static CmdSpec placardOps[] = { {"config", 1, PlacardConfigOp, 6, 6, "attrlist style padding layerName"}, {"enable", 1, PlacardEnableOp, 4, 4, "bool layerName"}, }; static int nPlacardOps = NumCmdSpecs(placardOps); static int PlacardCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nPlacardOps, placardOps, CMDSPEC_ARG1, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int RendererRenderOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { g_renderer->eventuallyRender(); return TCL_OK; } static CmdSpec rendererOps[] = { {"render", 1, RendererRenderOp, 2, 2, ""}, }; static int nRendererOps = NumCmdSpecs(rendererOps); static int RendererCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nRendererOps, rendererOps, CMDSPEC_ARG1, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int ScreenBgColorOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { float color[3]; if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK || GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK || GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) { return TCL_ERROR; } g_renderer->setBackgroundColor(color); return TCL_OK; } static int ScreenCoordsOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { 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 < 5) { srs = g_renderer->getMapSRS(); if (srs == NULL) { Tcl_AppendResult(interp, "Could not determine map SRS", (char*)NULL); return TCL_ERROR; } } else { std::string srsInit(Tcl_GetString(objv[4])); std::string verticalDatum; if (objc > 5) { verticalDatum = Tcl_GetString(objv[5]); } srs = osgEarth::SpatialReference::get(srsInit, verticalDatum); if (srs == NULL) { Tcl_AppendResult(interp, "bad SRS \"", srsInit.c_str(), "\"", (char*)NULL); return TCL_ERROR; } } Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); std::vector 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::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); } } g_renderer->worldToScreen(coordVec); for (std::vector::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; } static int ScreenSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int width, height; if (Tcl_GetIntFromObj(interp, objv[2], &width) != TCL_OK || Tcl_GetIntFromObj(interp, objv[3], &height) != TCL_OK) { return TCL_ERROR; } g_renderer->setWindowSize(width, height); return TCL_OK; } static CmdSpec screenOps[] = { {"bgcolor", 1, ScreenBgColorOp, 5, 5, "r g b"}, {"coords", 1, ScreenCoordsOp, 4, 6, "token coords ?srs? ?verticalDatum?"}, {"size", 1, ScreenSizeOp, 4, 4, "width height"} }; static int nScreenOps = NumCmdSpecs(screenOps); static int ScreenCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nScreenOps, screenOps, CMDSPEC_ARG1, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } static int SelectAddOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int numIDs; Tcl_Obj **ids; if (Tcl_ListObjGetElements(interp, objv[2], &numIDs, &ids) != TCL_OK) { return TCL_ERROR; } if (numIDs == 0) { Tcl_AppendResult(interp, "no IDs in list", (char *)NULL); return TCL_ERROR; } std::vector featureIDs; for (int i = 0; i < numIDs; i++) { long id; if (Tcl_GetLongFromObj(interp, ids[i], &id) != TCL_OK) { return TCL_ERROR; } featureIDs.push_back((unsigned long)id); } const char *layerName = Tcl_GetString(objv[3]); g_renderer->selectFeatures(featureIDs, layerName, false); return TCL_OK; } static int SelectClearOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { g_renderer->clearSelection(); return TCL_OK; } static int SelectDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int numIDs; Tcl_Obj **ids; if (Tcl_ListObjGetElements(interp, objv[2], &numIDs, &ids) != TCL_OK) { return TCL_ERROR; } if (numIDs == 0) { Tcl_AppendResult(interp, "no IDs in list", (char *)NULL); return TCL_ERROR; } std::vector featureIDs; for (int i = 0; i < numIDs; i++) { long id; if (Tcl_GetLongFromObj(interp, ids[i], &id) != TCL_OK) { return TCL_ERROR; } featureIDs.push_back((unsigned long)id); } const char *layerName = Tcl_GetString(objv[3]); g_renderer->deselectFeatures(featureIDs, layerName); return TCL_OK; } static int SelectFeatureOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { int numIDs; Tcl_Obj **ids; if (Tcl_ListObjGetElements(interp, objv[2], &numIDs, &ids) != TCL_OK) { return TCL_ERROR; } if (numIDs == 0) { Tcl_AppendResult(interp, "no IDs in list", (char *)NULL); return TCL_ERROR; } std::vector featureIDs; for (int i = 0; i < numIDs; i++) { long id; if (Tcl_GetLongFromObj(interp, ids[i], &id) != TCL_OK) { return TCL_ERROR; } featureIDs.push_back((unsigned long)id); } const char *layerName = Tcl_GetString(objv[3]); g_renderer->selectFeatures(featureIDs, layerName); return TCL_OK; } static int SelectModeOp(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { const char *modeStr = Tcl_GetString(objv[2]); // Parse mode string Renderer::SelectMode mode = Renderer::SELECT_OFF; if (modeStr[0] == 'o' && strcmp(modeStr, "off") == 0) { mode = Renderer::SELECT_OFF; } else if (modeStr[0] == 'o' && strcmp(modeStr, "on") == 0) { mode = Renderer::SELECT_ON; } else { Tcl_AppendResult(interp, "bad select mode \"", modeStr, "\": must be 'on' or 'off'", (char*)NULL); return TCL_ERROR; } g_renderer->setSelectMode(mode); return TCL_OK; } static CmdSpec selectOps[] = { {"clear", 1, SelectClearOp, 2, 2, ""}, {"fadd", 2, SelectAddOp, 4, 4, "idlist layerName"}, {"fdelete", 2, SelectDeleteOp, 4, 4, "idlist layerName"}, {"feature", 2, SelectFeatureOp, 4, 4, "idlist layerName"}, {"mode", 1, SelectModeOp, 3, 3, "mode"}, }; static int nSelectOps = NumCmdSpecs(selectOps); static int SelectCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Tcl_ObjCmdProc *proc; proc = GetOpFromObj(interp, nSelectOps, selectOps, CMDSPEC_ARG1, objc, objv, 0); if (proc == NULL) { return TCL_ERROR; } return (*proc) (clientData, interp, objc, objv); } #ifdef USE_READ_THREAD int GeoVis::queueCommands(Tcl_Interp *interp, ClientData clientData, ReadBuffer *inBufPtr) { Tcl_DString commandString; Tcl_DStringInit(&commandString); fd_set readFds; FD_ZERO(&readFds); FD_SET(inBufPtr->file(), &readFds); while (inBufPtr->isLineAvailable() || (select(inBufPtr->file()+1, &readFds, NULL, NULL, NULL) > 0)) { size_t numBytes; unsigned char *buffer; /* A short read is treated as an error here because we assume that we * will always get commands line by line. */ if (inBufPtr->getLine(&numBytes, &buffer) != ReadBuffer::OK) { /* Terminate the server if we can't communicate with the client * anymore. */ if (inBufPtr->status() == ReadBuffer::ENDFILE) { TRACE("Exiting server on EOF from client"); return -1; } else { ERROR("Exiting server, failed to read from client: %s", strerror(errno)); return -1; } } Tcl_DStringAppend(&commandString, (char *)buffer, numBytes); if (Tcl_CommandComplete(Tcl_DStringValue(&commandString))) { // Add to queue Command *command = new Command(Command::COMMAND); command->setMessage((unsigned char *)Tcl_DStringValue(&commandString), Tcl_DStringLength(&commandString), Command::VOLATILE); g_inQueue->enqueue(command); Tcl_DStringSetLength(&commandString, 0); } FD_SET(inBufPtr->file(), &readFds); } return 1; } #endif /** * \brief Execute commands from client in Tcl interpreter * * In this threaded model, the select call is for event compression. We * want to execute render server commands as long as they keep coming. * This lets us execute a stream of many commands but render once. This * benefits camera movements, screen resizing, and opacity changes * (using a slider on the client). The down side is you don't render * until there's a lull in the command stream. If the client needs an * image, it can issue an "imgflush" command. That breaks us out of the * read loop. */ int GeoVis::processCommands(Tcl_Interp *interp, ClientData clientData, ReadBuffer *inBufPtr, int fdOut, struct timeval *timeout) { int ret = 1; int status = TCL_OK; Tcl_DString command; Tcl_DStringInit(&command); fd_set readFds; struct timeval tv, *tvPtr; FD_ZERO(&readFds); FD_SET(inBufPtr->file(), &readFds); bool polling = false; if (timeout->tv_sec >= 0L) { tv.tv_sec = timeout->tv_sec; tv.tv_usec = timeout->tv_usec; polling = tv.tv_sec == 0 && tv.tv_usec == 0; tvPtr = &tv; } else { // Block until data available tvPtr = NULL; TRACE("Blocking on select()"); } while (inBufPtr->isLineAvailable() || (ret = select(inBufPtr->file()+1, &readFds, NULL, NULL, tvPtr)) > 0) { size_t numBytes; unsigned char *buffer; /* A short read is treated as an error here because we assume that we * will always get commands line by line. */ if (inBufPtr->getLine(&numBytes, &buffer) != ReadBuffer::OK) { /* Terminate the server if we can't communicate with the client * anymore. */ if (inBufPtr->status() == ReadBuffer::ENDFILE) { TRACE("Exiting server on EOF from client"); return -1; } else { ERROR("Exiting server, failed to read from client: %s", strerror(errno)); return -1; } } #if 0 Tcl_DString tmp; Tcl_DStringInit(&tmp); Tcl_Encoding encoding = Tcl_GetEncoding(interp, "identity"); TRACE("Encoding name: %s", Tcl_GetEncodingName(encoding)); Tcl_ExternalToUtfDString(encoding, (const char *)buffer, numBytes, &tmp); Tcl_FreeEncoding(encoding); Tcl_DStringAppend(&command, Tcl_DStringValue(&tmp), Tcl_DStringLength(&tmp)); Tcl_DStringFree(&tmp); #else Tcl_DStringAppend(&command, (char *)buffer, numBytes); #endif if (Tcl_CommandComplete(Tcl_DStringValue(&command))) { struct timeval start, finish; gettimeofday(&start, NULL); g_stats.nCommands++; status = ExecuteCommand(interp, &command); gettimeofday(&finish, NULL); g_stats.cmdTime += (MSECS_ELAPSED(start, finish) / 1.0e+3); if (status == TCL_BREAK) { return 2; /* This was caused by a "imgflush" * command. Break out of the read loop * and allow a new image to be * rendered. */ } else { //if (status != TCL_OK) { ret = 0; if (handleError(interp, clientData, status, fdOut) < 0) { return -1; } } if (status == TCL_OK) { ret = 3; } } polling = true; tv.tv_sec = tv.tv_usec = 0L; /* On successive reads, we break out * if no data is available. */ FD_SET(inBufPtr->file(), &readFds); tvPtr = &tv; } if (!polling && ret == 0 && timeout->tv_sec > 0L) { // If idle timeout expired, disconnect TRACE("Exiting server after timeout waiting for client command"); return -1; } return ret; } /** * \brief Send error message to client socket */ int GeoVis::handleError(Tcl_Interp *interp, ClientData clientData, int status, int fdOut) { const char *string; int nBytes; if (status != TCL_OK) { string = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); nBytes = strlen(string); if (nBytes > 0) { TRACE("status=%d errorInfo=(%s)", status, string); std::ostringstream oss; oss << "nv>viserror -type internal_error -token " << g_stats.nCommands << " -bytes " << nBytes << "\n" << string; std::string ostr = oss.str(); nBytes = ostr.length(); if (queueResponse(ostr.c_str(), nBytes, Response::VOLATILE, Response::ERROR) < 0) { return -1; } } } std::string msg = getUserMessages(); nBytes = msg.length(); if (nBytes > 0) { string = msg.c_str(); TRACE("userError=(%s)", string); std::ostringstream oss; oss << "nv>viserror -type error -token " << g_stats.nCommands << " -bytes " << nBytes << "\n" << string; std::string ostr = oss.str(); nBytes = ostr.length(); if (queueResponse(ostr.c_str(), nBytes, Response::VOLATILE, Response::ERROR) < 0) { return -1; } clearUserMessages(); } return 0; } /** * \brief Make Tcl interpreter safe, set encoding and add commands */ void GeoVis::initTcl(Tcl_Interp *interp, ClientData clientData) { TRACE("LANG: %s", getenv("LANG")); Tcl_GetEncodingNames(interp); TRACE("Supported encodings: %s", Tcl_GetStringResult(interp)); int result = Tcl_Eval(interp, "encoding system\n"); if (result == TCL_OK) { TRACE("Current system encoding: %s", Tcl_GetStringResult(interp)); } else { ERROR("Couldn't determine system encoding"); } const char *encoding = "utf-8"; if (Tcl_SetSystemEncoding(interp, encoding) != TCL_OK) { TRACE("Failed to set Tcl encoding to %s", encoding); } else { TRACE("Set system encoding to %s", encoding); } Tcl_MakeSafe(interp); Tcl_CreateObjCommand(interp, "camera", CameraCmd, clientData, NULL); 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); Tcl_CreateObjCommand(interp, "legend", LegendCmd, clientData, NULL); Tcl_CreateObjCommand(interp, "map", MapCmd, clientData, NULL); Tcl_CreateObjCommand(interp, "mouse", MouseCmd, clientData, NULL); Tcl_CreateObjCommand(interp, "placard", PlacardCmd, clientData, NULL); Tcl_CreateObjCommand(interp, "renderer", RendererCmd, clientData, NULL); Tcl_CreateObjCommand(interp, "screen", ScreenCmd, clientData, NULL); Tcl_CreateObjCommand(interp, "select", SelectCmd, clientData, NULL); } /** * \brief Delete Tcl commands and interpreter */ void GeoVis::exitTcl(Tcl_Interp *interp) { Tcl_DeleteCommand(interp, "camera"); Tcl_DeleteCommand(interp, "clientinfo"); Tcl_DeleteCommand(interp, "colormap"); Tcl_DeleteCommand(interp, "file"); Tcl_DeleteCommand(interp, "imgflush"); Tcl_DeleteCommand(interp, "key"); Tcl_DeleteCommand(interp, "legend"); Tcl_DeleteCommand(interp, "map"); Tcl_DeleteCommand(interp, "mouse"); Tcl_DeleteCommand(interp, "placard"); Tcl_DeleteCommand(interp, "renderer"); Tcl_DeleteCommand(interp, "screen"); Tcl_DeleteCommand(interp, "select"); Tcl_DeleteInterp(interp); }