Source code for veroviz.createCesium

from veroviz._common import *
from veroviz._validation import valCreateCesium

from veroviz._deconstructAssignments import deconstructAssignments
from veroviz._internal import delTailSlash
from veroviz._internal import delHeadSlash
from veroviz._internal import addHeadSlash
from veroviz._internal import replaceBackslashToSlash
from veroviz._internal import expandCesiumColor

from veroviz._utilities import privGetMapBoundary
from veroviz._utilities import privExportDataframe

[docs]def createCesium(assignments=None, nodes=None, startDate=None, startTime='08:00:00', postBuffer=30, cesiumDir=None, problemDir=None, nodeColor=None, nodeStyle=None, pathColor=None, pathWeight=None, pathStyle=None, pathOpacity=None): """ This function generates several files required to view a solution in Cesium. The function requires assignments and/or nodes dataframes as input. Parameters ---------- assignments: :ref:`Assignments`, Conditional, `assignments` and `nodes` can not be None at the same time An :ref:`Assignments` dataframe describing vehicle movement over time. The assignments will be displayed as routes/paths in Cesium. If a 3D model is defined in the `modelFile` column of the assignments dataframe, this object will also be displayed. nodes: :ref:`Nodes`, Conditional, `assignments` and `nodes` can not be None at the same time A :ref:`Nodes` dataframe describing the locations of nodes. These nodes will be displayed on the map in Cesium. startDate: string, Optional, format is "YYYY-MM-DD", default as today Defines the start date to be displayed in Cesium. startTime: string, Optional, format is "HH:MM:SS", default as '08:00:00' Defines the time at which the Cesium video begins, on the start date. postBuffer: int, Optional, default as 30 Specifies the additional time (in seconds) that the Cesium video will continue to run after the last assignment is completed. cesiumDir: string, Required, default as None This should be the full absolute path to the directory where Cesium is installed. For example, for Windows it might be "D:/Cesium"; for Linux it might be "/home/user/Cesium". problemDir: string, Required, default as None The path name of the generated problem directory. This path is relative to the root of Cesium. For example, if `cesiumDir = '/home/user/Cesium'` and `problemDir = 'veroviz/problems/TSP'` then the files will be generated in the directory `'/home/user/Cesium/veroviz/problems/TSP'`. nodeColor: string, Optional, default as None Overrides the `cesiumColor` column of the input `nodes` dataframe. This will define the color of all nodes displayed in Cesium. See :ref:`Cesium Style` for the collection of available colors. nodeStyle: string, Optional, default as None Overrides the `cesiumIconType` column of the input `nodes` dataframe. Currently, the only option is 'pin'. pathColor: string, Optional, default as None Overrides the `cesiumColor` column of the input `assignments` dataframe. This will define the color of all arcs displayed in Cesium. See :ref:`Cesium Style` for the collection of available colors. pathWeight: int, Optional, default as None Overrides the `cesiumWeight` column of the input `assignments` dataframe. This will define the weight (in pixels) of all arcs displayed in Cesium. See :ref:`Cesium Style` for more information. pathStyle: string, Optional, default as None Overrides the `cesiumStyle` column of the input `assignments` dataframe. This will define the style of all arcs displayed in Cesium. See :ref:`Cesium Style` for available options. pathOpacity: float in [0, 1], Optional, default as None Overrides the `cesiumOpacity` column of the input `assignments` dataframe. This will define the opacity of all arcs displayed in Cesium. See :ref:`Cesium Style` for more information. Return ------ N/A Note ---- This function generates the following files within the directory specified by input argument `problemDir`: - [problemDir].vrv (where [problemDir] is replaced by the value of `problemDir`); - config.js - displayNodes.js - displayPath.js - routes.czml Instructions for starting Cesium are provided at https://veroviz.org/documentation.html Example -------- Import veroviz and check the latest version. >>> import veroviz as vrv >>> import os >>> vrv.checkVersion() Create two nodes. >>> myNodes = vrv.createNodesFromLocs( ... locs = [[42.1538, -78.4253], ... [42.6343, -78.1146]]) >>> myNodes Move the truck from one node to the other. >>> myAssignments = vrv.getShapepoints2D( ... odID = 0, ... objectID = 'truck', ... modelFile = 'veroviz/models/ub_truck.gltf', ... modelScale = 80, ... modelMinPxSize = 20, ... startLoc = list(myNodes.loc[0][['lat', 'lon']].values), ... endLoc = list(myNodes.loc[1][['lat', 'lon']].values), ... routeType = 'euclidean2D', ... dataProvider = None, ... speedMPS = vrv.convertSpeed(55, 'miles', 'hr', 'm', 's')) Create Cesium output. >>> vrv.createCesium( ... assignments = myAssignments, ... nodes = myNodes, ... startTime = '08:00:00', ... cesiumDir = os.environ['CESIUMDIR'], ... problemDir = 'createCesium_example') """ # validation [valFlag, errorMsg, warningMsg] = valCreateCesium(assignments, nodes, startDate, startTime, postBuffer, cesiumDir, problemDir, nodeColor, pathColor, pathWeight, pathStyle, pathOpacity) if (not valFlag): print (errorMsg) return elif (config['VRV_SETTING_SHOWWARNINGMESSAGE'] and warningMsg != ""): print (warningMsg) # Set default start date as today if (startDate is None): startDate = datetime.date.today() # Some modification about slashes: # cesiumDir - no tail slash # problemDir - no head slash and no tail slash # Change all backslash to slash cesiumDir = delTailSlash(cesiumDir) problemDir = delTailSlash(problemDir) problemDir = delHeadSlash(problemDir) cesiumDir = replaceBackslashToSlash(cesiumDir) problemDir = replaceBackslashToSlash(problemDir) # In case the problemDir does not exist fullDir = '%s/%s' % (cesiumDir, problemDir) if not os.path.exists(fullDir): os.makedirs(fullDir, exist_ok=True) # Mission duration, from time zero to endtime+postBuffer availStart = _getCesiumTime(startDate, startTime, 0) if (assignments is not None): availEnd = _getCesiumTime(startDate, startTime, (max(assignments['endTimeSec']) + postBuffer)) else: availEnd = _getCesiumTime(startDate, startTime, postBuffer) # Decode Assignments dataframe to a Path dataframe (with details of a path) and a list of Assignments dataframes, and generate .js and .czml files if (assignments is not None): [path, lstSubAssignments] = _getPathsDetails(assignments) lstNonStationarySubAssignments = deconstructAssignments(assignments=assignments, includeVerticalFlag=True) # Update 'intervalStart' and 'intervalEnd' to cesiumTime for i in path.index: path.at[i, 'intervalStart'] = _getCesiumTime(startDate, startTime, path.at[i, 'startTimeSec']) path.at[i, 'intervalEnd'] = _getCesiumTime(startDate, startTime, path.at[i, 'endTimeSec']) if (path.at[i, 'endTimeSec'] >= 0) else availEnd path.drop(columns = ['startTimeSec', 'endTimeSec']) else: path = pd.DataFrame(columns=['objectID', 'czmlID', 'action']) # Write problem selector _writeSelector(fullDir, problemDir) # Write Configs mapBoundary = privGetMapBoundary(nodes=nodes, arcs=assignments, locs=None) _writeConfigs(mapBoundary, availStart, path, fullDir, problemDir) # Write Nodes _writeNodes(nodes, nodeColor, fullDir) # Write Assignments if (len(path) > 0): _writeAssignmentsJS(lstNonStationarySubAssignments, pathColor, pathWeight, pathStyle, pathOpacity, fullDir) _writeAssignmentsCZML(path, lstSubAssignments, availStart, availEnd, fullDir) else: _writeEmptyAssignments(fullDir) return
def _writeSelector(fullDir, problemDir): """ This script generates a .vrv file, in index.html, load [problemDir].vrv to load the other files Parameters ---------- fullDir: string The directory of cesium, including the name of the instance. problemDir: string The name of the instance """ # Replace "/" with ";" replacedDir = addHeadSlash(problemDir) replacedDir = replacedDir.replace("/", ";") # .vrv file path vrvFilePath = '%s/%s.vrv' % (fullDir, replacedDir) f = open(vrvFilePath, 'w') vrvStr = "~\n" f.write(vrvStr) f.close() if (config['VRV_SETTING_SHOWOUTPUTMESSAGE']): print ("Message: File selector was written to %s ..." % (vrvFilePath)) return def _writeConfigs(mapBoundary, availStart, path, fullDir, problemDir): """ This script generates config.js file Parameters ---------- mapBoundary: list, the format is [minLon, minLat, maxLon, maxLat] The boundary of the map availStart: JulianDate The start time of entire routing visualization path: dataframe A list of detail information from `_getPathsDetails()` modelIDs: list `model` column from path dataframe actions: list `action` column from path dataframe fullDir: string The directory of cesium, including the name of the instance. problemDir: string The name of the instance """ # Decode allIDs = list(dict.fromkeys(path['czmlID'].tolist())) orientationIDs = list(dict.fromkeys(path.loc[path['action'] == "move", 'czmlID'].tolist())) [[minLat, maxLon], [maxLat, minLon]] = mapBoundary # .js file path jsFilePath = '%s/config.js' % (fullDir) f = open(jsFilePath, 'w') # Head description jsStr = "// This .js file is auto-generated by `createCesium()` from VeRoViz\n" jsStr += "// The configs for cesium application go to here\n\n" # Configuration and load czml file, runRoutes() is defined in index.html jsStr += "function setConfigs() {\n" # Set map boundary jsStr += " viewer.camera.flyTo({\n" jsStr += " destination: Cesium.Rectangle.fromDegrees(%f, %f, %f, %f) \n" % (minLon, minLat, maxLon, maxLat) jsStr += " });\n" # Set clock jsStr += " viewer.clock.currentTime = Cesium.JulianDate.addSeconds('%s', 0, new Cesium.JulianDate());\n" % (availStart) # allIDs and orientationIDs jsStr += " allIDs = [\n" if (len(allIDs) > 0): for i in range(0, len(allIDs)): jsStr += " '%s', \n" % (allIDs[i]) jsStr = jsStr[:-3] jsStr += " \n" jsStr += " ];\n" jsStr += " orientationIDs = [\n" if (len(orientationIDs) > 0): for i in range(0, len(orientationIDs)): jsStr += " '%s', \n" % (orientationIDs[i]) jsStr = jsStr[:-3] jsStr += " \n" jsStr += " ];\n" # load .czml, `runRoutes()` are writen in index.html jsStr += " czmlRouteFile = '%s/routes.czml';\n" % ("/" + problemDir) jsStr += " runRoutes(czmlRouteFile, allIDs, orientationIDs);\n" # Get all unrepeated objectID, modelID, child model # The hierarchy is: objectID (one) - modelID (many), modelID (one) - child model (many) # E.g., For drones # objectID - UAV, # model - UAV-drone.gltf; UAV-drone_package.gltf, # child model - 'o-UAV-drone_package.gltf-move'; 'o-UAV-drone_package.gltf-vertical'; 'o-UAV-drone_package.gltf-stationary' objectIDs = list(dict.fromkeys(path['objectID'].tolist())) for i in range(len(objectIDs)): models = list(dict.fromkeys(path.loc[path['objectID'] == objectIDs[i], 'modelFile'].tolist())) for j in range(len(models)): childModels = list(dict.fromkeys(path.loc[(path['objectID'] == objectIDs[i]) & (path['modelFile'] == models[j]), 'action'].tolist())) strChildModels = "" for k in range(len(childModels)): strChildModels += "'o-%s-%s-%s', " % (str(objectIDs[i]).replace("'", r""), str(models[j]).replace("'", r""), str(childModels[k]).replace("'", r"")) if (strChildModels != ""): strChildModels = strChildModels[:-2] jsStr += "objectInfo['%s-%s'] = {\n" % (str(objectIDs[i]).replace("'", r""), str(models[j]).replace("'", r"\'")) jsStr += " label : '%s (%s)', \n" % (str(objectIDs[i]).replace("'", r"\'"), str(models[j]).replace("'", r"\'")) jsStr += " childModels : [%s],\n" % (strChildModels) jsStr += " scale : %s, \n" % (path.loc[(path['objectID'] == objectIDs[i]) & (path['modelFile'] == models[j]), 'modelScale'].tolist()[0]) jsStr += " minPxSize : %s \n" % (path.loc[(path['objectID'] == objectIDs[i]) & (path['modelFile'] == models[j]), 'modelMinPxSize'].tolist()[0]) jsStr += "}; \n" # Register objects jsStr += " registerObjects(objectInfo); \n" # End of the displayPaths function jsStr += "}" # Write contents and close file stream f.write(jsStr) f.close() if (config['VRV_SETTING_SHOWOUTPUTMESSAGE']): print("Message: Configs were written to %s ..." % (jsFilePath)) return def _writeNodes(nodes, cesiumIconColor, fullDir): """ This script generates displayNodes.js file Parameters ---------- nodes: :ref:`Nodes` Nodes to be appeared in cesium cesiumIconColor: string The color of nodes when displayed in Cesium. If provided, it will overrides the color in nodes dataframe. One of a collection of pre-specified colors. See :ref:`Cesium Style` fullDir: string The directory of cesium, including the name of the instance. """ # .js file path jsFilePath = '%s/displayNodes.js' % (fullDir) f = open(jsFilePath, 'w') # Head description jsStr = "// This .js file is auto-generated by `createCesium()` from VeRoViz\n" jsStr += "// Display nodes for cesium application\n\n" # Display the nodes jsStr += "function displayNodes() {\n" jsStr += " var pin = new Array;\n" if (nodes is not None): # In case there are any skipped indices indNodes = nodes.copy().reset_index(drop=True) indNodes['cesiumIconText'] = indNodes['cesiumIconText'].astype('string') indNodes['popupText'] = indNodes['popupText'].astype('string') for i in range(0, len(indNodes)): popupText = indNodes.iloc[i]['popupText'] tmpIconText = str(indNodes.iloc[i]['cesiumIconText']).replace("'", r"") jsStr += " pin[%s] = viewer.entities.add({\n" % (i) jsStr += " name : '%s',\n" % (tmpIconText) jsStr += " parent : nodePins,\n" if (popupText is not None): jsStr += " description : '%s',\n" % (str(popupText).replace("'", r"\'")) jsStr += " position : Cesium.Cartesian3.fromDegrees(%s, %s),\n" % (indNodes.iloc[i]['lon'], indNodes.iloc[i]['lat']) jsStr += " billboard : {\n" jsStr += " image : pinBuilder.fromText('%s', %s, 40).toDataURL(),\n" % (tmpIconText, expandCesiumColor(cesiumIconColor) if (cesiumIconColor != None) else expandCesiumColor(indNodes.iloc[i]['cesiumColor'])) jsStr += " verticalOrigin : Cesium.VerticalOrigin.BOTTOM\n" jsStr += " }\n" jsStr += " });\n\n" jsStr += "}" # Write contents and close file stream f.write(jsStr) f.close() if (config['VRV_SETTING_SHOWOUTPUTMESSAGE']): print("Message: Nodes were written to %s ..." % (jsFilePath)) return def _writeEmptyAssignments(fullDir): """ This script generates "empty" routes.czml and displayPaths.js files Parameters ---------- fullDir: string The directory of cesium, including the name of the instance. """ # .czml file path czmlFilePath = '%s/routes.czml' % (fullDir) f = open(czmlFilePath, 'w') czmlStr = '[ \n' czmlStr += '{ \n' czmlStr += ' "id": "document", \n' czmlStr += ' "version": "1.0" \n' czmlStr += '}\n' czmlStr += ']' # Write contents and close file stream f.write(czmlStr) f.close() # .js file path jsFilePath = '%s/displayPaths.js' % (fullDir) f = open(jsFilePath, 'w') jsStr = "// This .js file is auto-generated by `createCesium` from VeRoViz\n" jsStr += "// Display paths for cesium application\n\n" jsStr += "function displayPaths() {\n" jsStr += " // pass \n" jsStr += "}" # Write contents and close file stream f.write(jsStr) f.close() def _writeAssignmentsCZML(path, lstSubAssignments, availStart, availEnd, fullDir): """ This script generates routes.czml file Parameters ---------- path: path dataframe A list of "path", defines the details of each sub-assignments. Each "path" has an origin coordinate and a destinate coordinate, represent a group of assignments (a sub-assignment) with same odID lstSubAssignments: list of :ref:`Assignments` A list of Assignments dataframe, each dataframe will have the same odID availStart: JulianDate Start time of entire .czml file availEnd: JulianDate End time of entire .czml file fullDir: string The directory of cesium, including the name of the instance. """ # .czml file path czmlFilePath = '%s/routes.czml' % (fullDir) f = open(czmlFilePath, 'w') # Group the path, so that the path with the same czmlID can ends up with the same block czmlIDList = path['czmlID'].tolist() czmlIDList = list(dict.fromkeys(czmlIDList)) lstCzml = [] for i in range(len(czmlIDList)): lstCzml.append(path.loc[path['czmlID'] == czmlIDList[i]]) # Head of CZML file (can't comment on .czml file so there is no head description) czmlStr = '[ \n' czmlStr += '{ \n' czmlStr += ' "id": "document", \n' czmlStr += ' "version": "1.0" \n' czmlStr += '}, \n' # For each combination of 'objectID', 'modelFile'. 'action', we have a section (in fact we treat a combination of those three fields as unique 'odID') for i in range(len(lstCzml)): czmlStr += '{ \n' czmlStr += ' "id": "%s", \n' % (str(czmlIDList[i]).replace("'", r"")) czmlStr += ' "name": "%s", \n' % (str(czmlIDList[i]).replace("'", r"")) czmlStr += ' "availability": "%s/%s", \n' % (availStart, availEnd) czmlStr += ' "model": { \n' czmlStr += ' "show": true, \n' czmlStr += ' "gltf": "%s", \n' % (lstCzml[i].iloc[0]['modelFile']) czmlStr += ' "scale": %d, \n' % (lstCzml[i].iloc[0]['modelScale']/100.0) czmlStr += ' "minimumPixelSize": %d \n' % (lstCzml[i].iloc[0]['modelMinPxSize']) czmlStr += ' }, \n' czmlStr += ' "label": { \n' czmlStr += ' "fillColor":[{"rgba":[255,255,0,255]}], \n' czmlStr += ' "font":"bold 10pt Segoe UI Semibold", \n' czmlStr += ' "horizontalOrigin":"LEFT", \n' czmlStr += ' "outlineColor":{"rgba":[0,0,0,255]}, \n' czmlStr += ' "pixelOffset":{"cartesian2":[10.0,0.0]}, \n' czmlStr += ' "scale":1.0, \n' czmlStr += ' "show":[{"boolean":false}], \n' czmlStr += ' "style":"FILL", \n' czmlStr += ' "text":"Object %s", \n' % (str(lstCzml[i].iloc[0]['objectID']).replace('"', r'\"')) czmlStr += ' "verticalOrigin":"BOTTOM"\n' czmlStr += ' }, \n' czmlStr += ' "path":{ \n' czmlStr += ' "material":{"solidColor":{"color":{"rgba":[255,255,0,255]}}}, \n' czmlStr += ' "width":[{"number":2.0}], \n' czmlStr += ' "show":[{"boolean":false}] \n' czmlStr += ' }, \n' czmlStr += ' "position": [ \n' for j in range(len(lstCzml[i])): if (lstCzml[i].iloc[j]['action'] == "stationary"): czmlStr += ' { \n' czmlStr += ' "interval": "%s/%s", \n' % (lstCzml[i].iloc[j]['intervalStart'], lstCzml[i].iloc[j]['intervalEnd']) lat = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']].iloc[0]['startLat'] lon = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']].iloc[0]['startLon'] alt = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']].iloc[0]['startAltMeters'] czmlStr += ' "cartographicDegrees": [%s, %s, %s] \n' % (lon, lat, alt) czmlStr += ' }, \n' else: czmlStr += ' { \n' czmlStr += ' "interval": "%s/%s",\n' % (lstCzml[i].iloc[j]['intervalStart'], lstCzml[i].iloc[j]['intervalEnd']) czmlStr += ' "interpolationAlgorithm":"LAGRANGE", \n' czmlStr += ' "interpolationDegree": 1, \n' czmlStr += ' "epoch": "%s", \n' % (availStart) czmlStr += ' "cartographicDegrees": [ \n' # There will be a list of LLA time = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']]['startTimeSec'].tolist() lats = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']]['startLat'].tolist() lons = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']]['startLon'].tolist() alts = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']]['startAltMeters'].tolist() for k in range(0, len(lats)): czmlStr += ' %.2f, %f, %f, %f, \n' % (time[k], lons[k], lats[k], alts[k]) lastTime = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']].iloc[-1]['endTimeSec'] lastLat = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']].iloc[-1]['endLat'] lastLon = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']].iloc[-1]['endLon'] lastAlt = lstSubAssignments[lstCzml[i].iloc[j]['indexInlstShapepoints']].iloc[-1]['endAltMeters'] czmlStr += ' %.2f, %f, %f, %f \n' % (lastTime, lastLon, lastLat, lastAlt) czmlStr += '\n' czmlStr += ' ] \n' czmlStr += ' }, \n' czmlStr = czmlStr[:-3] czmlStr += '\n' czmlStr += ' ]\n' czmlStr += '}, \n' czmlStr = czmlStr[:-3] czmlStr += '\n' # Closing bracket for entire .czml file czmlStr += ']' # Write contents and close file stream f.write(czmlStr) f.close() if (config['VRV_SETTING_SHOWOUTPUTMESSAGE']): print("Message: Assignments (.czml) were written to %s ..." % (czmlFilePath)) return def _writeAssignmentsJS(lstSubAssignments, cesiumColor, cesiumWeight, cesiumStyle, cesiumOpacity, fullDir): """ This script generates the displayPaths.js file Parameters ---------- lstSubAssignments: list of :ref:`Assignments` A list of Assignments dataframes, each dataframe will have the same odID cesiumColor: string The color of arcs when displayed in Cesium. If provided, it will overrides the color in assignments dataframe. One of a collection of pre-specified colors. See :ref:`Cesium Style` cesiumWeight: int The weight of arcs when displayed in Cesium. If provided, it will overrides the weight in assignments dataframe. See :ref:`Cesium Style` cesiumStyle: string The style of arcs when displayed in Cesium. If provided, it will overrides the style in assignments dataframe. See :ref: `Cesium style` cesiumOpacity: string The opacity of arcs when displayed in Cesium. If provided, it will overrides the opacity in assignments dataframe. See :ref: `Cesium style` fullDir: string The directory of cesium, including the name of the instance. """ # .js file path jsFilePath = '%s/displayPaths.js' % (fullDir) f = open(jsFilePath, 'w') # Head description jsStr = "// This .js file is auto-generated by `createCesium` from VeRoViz\n" jsStr += "// Display paths for cesium application\n\n" # Begin of the displayPaths function jsStr += "function displayPaths() {\n" # Collect all moving objects ID for path names movingObjects = [] for i in range(len(lstSubAssignments)): newObjectID = str(lstSubAssignments[i].iloc[0]['objectID']) newObjectID = newObjectID.replace("'", r"") newObjectID = newObjectID.replace('"', r'') if (newObjectID not in movingObjects): movingObjects.append(newObjectID) strMovingObjects = "" for i in range(len(movingObjects)): strMovingObjects += "'%s', " % (movingObjects[i]) strMovingObjects = strMovingObjects[:-2] jsStr += " var pathNames = [%s]; \n" % (strMovingObjects) # Register paths jsStr += " registerPaths(pathNames); \n" # For each `odID`, draw a polyline for i in range(len(lstSubAssignments)): # Get the lat/lon/alt for each waypoint, (polygon in 3D) lstSubAssignments[i] = lstSubAssignments[i].reset_index(drop=True) assignmentLats = [] assignmentLons = [] assignmentAltMeters = [] assignmentLats.append(lstSubAssignments[i]['startLat'][0]) assignmentLons.append(lstSubAssignments[i]['startLon'][0]) assignmentAltMeters.append(lstSubAssignments[i]['startAltMeters'][0]) assignmentLats.extend(lstSubAssignments[i]['endLat'].tolist()) assignmentLons.extend(lstSubAssignments[i]['endLon'].tolist()) assignmentAltMeters.extend(lstSubAssignments[i]['endAltMeters'].tolist()) assignmentDimension = 3 if (max(assignmentAltMeters) == 0 and min(assignmentAltMeters) == 0): assignmentDimension = 2 else: assignmentDimension = 3 # Check if cesium styles have been overridden, if not, assume that for entire path the style is the same if (cesiumColor != None): color = expandCesiumColor(cesiumColor) else: color = expandCesiumColor(lstSubAssignments[i].iloc[0]['cesiumColor']) if (cesiumWeight != None): weight = cesiumWeight else: weight = lstSubAssignments[i].iloc[0]['cesiumWeight'] if (cesiumOpacity != None): opacity = cesiumOpacity else: opacity = lstSubAssignments[i].iloc[0]['cesiumOpacity'] if (cesiumStyle != None): style = cesiumStyle else: style = lstSubAssignments[i].iloc[0]['cesiumStyle'] try: style = style.lower() except: pass if (style == 'dashed'): dashLength = 40 elif (style == 'dotted'): dashLength = 10 elif (style == 'solid'): dashLength = 0 popupText = str(lstSubAssignments[i].iloc[0]['popupText']).replace("'", r"\'") tmpObjectID = str(lstSubAssignments[i].iloc[0]['objectID']).replace("'", r"") if (assignmentDimension == 3): # For each path, generate one polyline entity jsStr += " paths[%d] = viewer.entities.add({\n" % (i) jsStr += " parent: vehiclePolylines['%s'],\n" % (tmpObjectID) jsStr += " name: 'Objects %s',\n" % (tmpObjectID) if (popupText is not None): jsStr += " description: '%s',\n" % (popupText) jsStr += " polyline: {\n" jsStr += " positions: Cesium.Cartesian3.fromDegreesArrayHeights([\n" for j in range(0, len(assignmentLats)): jsStr += " %f, %f, %f, \n" % (assignmentLons[j], assignmentLats[j], assignmentAltMeters[j]) jsStr = jsStr[:-3] jsStr += " \n" jsStr += " ]),\n" jsStr += " width: %d, \n" % (weight) jsStr += " material: new Cesium.PolylineDashMaterialProperty({\n" jsStr += " dashLength: %f,\n" % (dashLength) jsStr += " color: %s.withAlpha(%f)\n" % (color, opacity) jsStr += " })\n" jsStr += " }\n" jsStr += " });\n" elif (assignmentDimension == 2): # For each path, generate one polyline entity jsStr += " paths[%d] = viewer.entities.add({\n" % (i) jsStr += " parent: vehiclePolylines['%s'],\n" % (tmpObjectID) jsStr += " name: 'Objects %s',\n" % (tmpObjectID) jsStr += " polyline: {\n" jsStr += " positions: Cesium.Cartesian3.fromDegreesArray([\n" for j in range(0, len(assignmentLats)): jsStr += " %f, %f, \n" % (assignmentLons[j], assignmentLats[j]) jsStr = jsStr[:-3] jsStr += " \n" jsStr += " ]),\n" jsStr += " clampToGround: true, \n" jsStr += " width: %d, \n" % (weight) jsStr += " material: new Cesium.PolylineDashMaterialProperty({\n" jsStr += " dashLength: %f,\n" % (dashLength) jsStr += " color: %s.withAlpha(%f)\n" % (color, opacity) jsStr += " })\n" jsStr += " }\n" jsStr += " });\n" # End of the displayPaths function jsStr += "}" # Write contents and close file stream f.write(jsStr) f.close() if (config['VRV_SETTING_SHOWOUTPUTMESSAGE']): print("Message: Assignments (.js) were written to %s ..." % (jsFilePath)) return def _getCesiumTime(startDate, startTime, timeSec): """ This script gives a JulianDate format time for cesium Parameters ---------- startDate: string, format is "YYYY-MM-DD", default as today The start date of the video generated startTime: string, format is "HH:MM:SS", default as '08:00:00' The start time of the start date timeSec: float Time past after start time Return ------ JulianDate Time in JulianDate format """ # Set time zero timeZero = dateutil.parser.parse("%s %s" % (startDate, startTime)) # Return timeSec in cesium format (semi-Julian format) cesiumTime = datetime.datetime.strftime(timeZero + datetime.timedelta(seconds = timeSec), '%Y-%m-%dT%H:%M:%SZ') return cesiumTime def _getAction(subAssignments): """ Given a group of assignments with same odID, find if this group is stationary/vertical/move. Each will be treated differently in cesium and folium. Parameters ---------- subAssignments: :ref:`Assignments` An assignment dataframe with the same odID Return ------ string An enumerate string, out put can be 'stationary'/'vertical'/'move' """ # if it is static or vertical, there should (must) be one row for correspondence `odID` if (len(subAssignments) == 1): if (subAssignments.iloc[0]['startLat'] == subAssignments.iloc[0]['endLat'] and subAssignments.iloc[0]['startLon'] == subAssignments.iloc[0]['endLon'] and subAssignments.iloc[0]['startAltMeters'] == subAssignments.iloc[0]['endAltMeters']): action = "stationary" elif (subAssignments.iloc[0]['startLat'] == subAssignments.iloc[0]['endLat'] and subAssignments.iloc[0]['startLon'] == subAssignments.iloc[0]['endLon'] and subAssignments.iloc[0]['startAltMeters'] != subAssignments.iloc[0]['endAltMeters']): action = "vertical" else: action = "move" else: action = "move" return action def _getPathsDetails(assignments): """ Given an Assignments dataframe, this script deconstruct it into a list of assignments(or called subAssignments) each subAssignment will have: the same odID; same type of movement(stationary/vertical/move) Parameters ---------- assignments: :ref:`Assignments`, Required The Assignments dataframe to be deconstructed into lists Returns ------- path: Path dataframe A list of description of each subAssignment lstSubAssignments: list of :ref:`Assignments` A list of Assignments dataframe each with the same odID """ # Icon list modelWithDuplicates = assignments['modelFile'].tolist() uniqueIconList = list(dict.fromkeys(modelWithDuplicates)) # Get path list from assignments dataframe lstSubAssignments = deconstructAssignments(assignments=assignments, includeStationaryFlag=True, includeVerticalFlag=True) # Now we prepare for .czml, the following is a Path Dataframe, which has the same length as lstSubAssignments path = pd.DataFrame(columns=['odID', 'czmlID', 'objectID', 'modelFile', 'action', 'modelScale', 'modelMinPxSize', 'startTimeSec', 'endTimeSec', 'intervalStart', 'intervalEnd', 'indexInlstShapepoints']) for i in range(len(lstSubAssignments)): tmpObjectID = str(lstSubAssignments[i].iloc[0]['objectID']).replace("'", r"") path = path.append({ 'odID': lstSubAssignments[i].iloc[0]['odID'], 'czmlID': 'o-%s-%s-%s' % (tmpObjectID, lstSubAssignments[i].iloc[0]['modelFile'], _getAction(lstSubAssignments[i])), 'objectID': lstSubAssignments[i].iloc[0]['objectID'], 'modelFile': lstSubAssignments[i].iloc[0]['modelFile'], 'action': _getAction(lstSubAssignments[i]), 'modelScale': lstSubAssignments[i].iloc[0]['modelScale'], 'modelMinPxSize': lstSubAssignments[i].iloc[0]['modelMinPxSize'], 'startTimeSec': lstSubAssignments[i].iloc[0]['startTimeSec'], 'endTimeSec': lstSubAssignments[i].iloc[-1]['endTimeSec'], 'intervalStart': "", 'intervalEnd': "", 'indexInlstShapepoints': i }, ignore_index=True) path.sort_values('odID', ascending=True) return [path, lstSubAssignments]