vtkjs_helper.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import base64
  2. import json
  3. import re
  4. import os
  5. import shutil
  6. import sys
  7. import zipfile
  8. try:
  9. import zlib
  10. compression = zipfile.ZIP_DEFLATED
  11. except:
  12. compression = zipfile.ZIP_STORED
  13. # -----------------------------------------------------------------------------
  14. def convertDirectoryToZipFile(directoryPath):
  15. if os.path.isfile(directoryPath):
  16. return
  17. zipFilePath = "%s.zip" % directoryPath
  18. zf = zipfile.ZipFile(zipFilePath, mode="w")
  19. try:
  20. for dirName, subdirList, fileList in os.walk(directoryPath):
  21. for fname in fileList:
  22. fullPath = os.path.join(dirName, fname)
  23. relPath = "%s" % (os.path.relpath(fullPath, directoryPath))
  24. zf.write(fullPath, arcname=relPath, compress_type=compression)
  25. finally:
  26. zf.close()
  27. shutil.rmtree(directoryPath)
  28. shutil.move(zipFilePath, directoryPath)
  29. # -----------------------------------------------------------------------------
  30. def addDataToViewer(dataPath, srcHtmlPath):
  31. if os.path.isfile(dataPath) and os.path.exists(srcHtmlPath):
  32. dstDir = os.path.dirname(dataPath)
  33. dstHtmlPath = os.path.join(dstDir, "%s.html" % os.path.basename(dataPath)[:-6])
  34. # Extract data as base64
  35. with open(dataPath, "rb") as data:
  36. dataContent = data.read()
  37. base64Content = base64.b64encode(dataContent)
  38. base64Content = base64Content.decode().replace("\n", "")
  39. # Create new output file
  40. with open(srcHtmlPath, mode="r", encoding="utf-8") as srcHtml:
  41. with open(dstHtmlPath, mode="w", encoding="utf-8") as dstHtml:
  42. for line in srcHtml:
  43. if "</body>" in line:
  44. dstHtml.write("<script>\n")
  45. dstHtml.write('var contentToLoad = "%s";\n\n' % base64Content)
  46. dstHtml.write(
  47. 'Glance.importBase64Dataset("%s" , contentToLoad, glanceInstance.proxyManager);\n'
  48. % os.path.basename(dataPath)
  49. )
  50. dstHtml.write("glanceInstance.showApp();\n")
  51. dstHtml.write("</script>\n")
  52. dstHtml.write(line)
  53. # -----------------------------------------------------------------------------
  54. def zipAllTimeSteps(directoryPath):
  55. if os.path.isfile(directoryPath):
  56. return
  57. class UrlCounterDict(dict):
  58. Counter = 0
  59. def GetUrlName(self, name):
  60. if name not in self.keys():
  61. self[name] = str(objNameToUrls.Counter)
  62. self.Counter = self.Counter + 1
  63. return self[name]
  64. def InitIndex(sourcePath, destObj):
  65. with open(sourcePath, "r") as sourceFile:
  66. sourceData = sourceFile.read()
  67. sourceObj = json.loads(sourceData)
  68. for key in sourceObj:
  69. destObj[key] = sourceObj[key]
  70. # remove vtkHttpDataSetReader information
  71. for obj in destObj["scene"]:
  72. obj.pop(obj["type"])
  73. obj.pop("type")
  74. def getUrlToNameDictionary(indexObj):
  75. urls = {}
  76. for obj in indexObj["scene"]:
  77. urls[obj[obj["type"]]["url"]] = obj["name"]
  78. return urls
  79. def addDirectoryToZip(
  80. dirname, zipobj, storedData, rootIdx, timeStep, objNameToUrls
  81. ):
  82. # Update root index.json file from index.json of this timestep
  83. with open(os.path.join(dirname, "index.json"), "r") as currentIdxFile:
  84. currentIdx = json.loads(currentIdxFile.read())
  85. urlToName = getUrlToNameDictionary(currentIdx)
  86. rootTimeStepSection = rootIdx["animation"]["timeSteps"][timeStep]
  87. for key in currentIdx:
  88. if key == "scene" or key == "version":
  89. continue
  90. rootTimeStepSection[key] = currentIdx[key]
  91. for obj in currentIdx["scene"]:
  92. objName = obj["name"]
  93. rootTimeStepSection[objName] = {}
  94. rootTimeStepSection[objName]["actor"] = obj["actor"]
  95. rootTimeStepSection[objName]["actorRotation"] = obj["actorRotation"]
  96. rootTimeStepSection[objName]["mapper"] = obj["mapper"]
  97. rootTimeStepSection[objName]["property"] = obj["property"]
  98. # For every object in the current timestep
  99. for folder in sorted(os.listdir(dirname)):
  100. currentItem = os.path.join(dirname, folder)
  101. if os.path.isdir(currentItem) is False:
  102. continue
  103. # Write all data array of the current timestep in the archive
  104. for filename in os.listdir(os.path.join(currentItem, "data")):
  105. fullpath = os.path.join(currentItem, "data", filename)
  106. if os.path.isfile(fullpath) and filename not in storedData:
  107. storedData.add(filename)
  108. relPath = os.path.join("data", filename)
  109. zipobj.write(fullpath, arcname=relPath, compress_type=compression)
  110. # Write the index.json containing pointers to these data arrays
  111. # while replacing every basepath as '../../data'
  112. objIndexFilePath = os.path.join(dirname, folder, "index.json")
  113. with open(objIndexFilePath, "r") as objIndexFile:
  114. objIndexObjData = json.loads(objIndexFile.read())
  115. for elm in objIndexObjData.keys():
  116. try:
  117. if "ref" in objIndexObjData[elm].keys():
  118. objIndexObjData[elm]["ref"]["basepath"] = "../../data"
  119. if "arrays" in objIndexObjData[elm].keys():
  120. for array in objIndexObjData[elm]["arrays"]:
  121. array["data"]["ref"]["basepath"] = "../../data"
  122. except AttributeError:
  123. continue
  124. currentObjName = urlToName[folder]
  125. objIndexRelPath = os.path.join(
  126. objNameToUrls.GetUrlName(currentObjName), str(timeStep), "index.json"
  127. )
  128. zipobj.writestr(
  129. objIndexRelPath,
  130. json.dumps(objIndexObjData, indent=2),
  131. compress_type=compression,
  132. )
  133. # ---
  134. zipFilePath = "%s.zip" % directoryPath
  135. currentDirectory = os.path.abspath(os.path.join(directoryPath, os.pardir))
  136. rootIndexPath = os.path.join(currentDirectory, "index.json")
  137. rootIndexFile = open(rootIndexPath, "r")
  138. rootIndexObj = json.loads(rootIndexFile.read())
  139. zf = zipfile.ZipFile(zipFilePath, mode="w")
  140. try:
  141. # We copy the scene from an index of a specific timestep to the root index
  142. # Scenes should all have the same objects so only do it for the first one
  143. isSceneInitialized = False
  144. # currentlyAddedData set stores hashes of every data we already added to the
  145. # vtkjs archive to prevent data duplication
  146. currentlyAddedData = set()
  147. # Regex that folders storing timestep data from paraview should follow
  148. reg = re.compile(r"^" + os.path.basename(directoryPath) + r"\.[0-9]+$")
  149. # We assume an object will not be deleted from a timestep to another so we create a generic index.json for each object
  150. genericIndexObj = {}
  151. genericIndexObj["series"] = []
  152. timeStep = 0
  153. for item in rootIndexObj["animation"]["timeSteps"]:
  154. genericIndexObj["series"].append({})
  155. genericIndexObj["series"][timeStep]["url"] = str(timeStep)
  156. genericIndexObj["series"][timeStep]["timeStep"] = float(item["time"])
  157. timeStep = timeStep + 1
  158. # Keep track of the url for every object
  159. objNameToUrls = UrlCounterDict()
  160. timeStep = 0
  161. # zip all timestep directories
  162. for folder in sorted(os.listdir(currentDirectory)):
  163. fullPath = os.path.join(currentDirectory, folder)
  164. if os.path.isdir(fullPath) and reg.match(folder):
  165. if not isSceneInitialized:
  166. InitIndex(os.path.join(fullPath, "index.json"), rootIndexObj)
  167. isSceneInitialized = True
  168. addDirectoryToZip(
  169. fullPath,
  170. zf,
  171. currentlyAddedData,
  172. rootIndexObj,
  173. timeStep,
  174. objNameToUrls,
  175. )
  176. shutil.rmtree(fullPath)
  177. timeStep = timeStep + 1
  178. # Write every index.json holding time information for each object
  179. for name in objNameToUrls:
  180. zf.writestr(
  181. os.path.join(objNameToUrls[name], "index.json"),
  182. json.dumps(genericIndexObj, indent=2),
  183. compress_type=compression,
  184. )
  185. # Update root index.json urls and write it in the archive
  186. for obj in rootIndexObj["scene"]:
  187. obj["id"] = obj["name"]
  188. obj["type"] = "vtkHttpDataSetSeriesReader"
  189. obj["vtkHttpDataSetSeriesReader"] = {}
  190. obj["vtkHttpDataSetSeriesReader"]["url"] = objNameToUrls[obj["name"]]
  191. zf.writestr(
  192. "index.json", json.dumps(rootIndexObj, indent=2), compress_type=compression
  193. )
  194. os.remove(rootIndexPath)
  195. finally:
  196. zf.close()
  197. shutil.move(zipFilePath, directoryPath)
  198. # -----------------------------------------------------------------------------
  199. # Main
  200. # -----------------------------------------------------------------------------
  201. if __name__ == "__main__":
  202. if len(sys.argv) < 2:
  203. print(
  204. "Usage: directoryToFile /path/to/directory.vtkjs [/path/to/ParaViewGlance.html]"
  205. )
  206. else:
  207. fileName = sys.argv[1]
  208. convertDirectoryToZipFile(fileName)
  209. if len(sys.argv) == 3:
  210. addDataToViewer(fileName, sys.argv[2])