[
  {
    "path": "GES_Panel_1_2.py",
    "content": "#    Copyright (c) 2021 imagiscope\r\n\r\n#    Permission is hereby granted, free of charge, to any person obtaining a copy\r\n#    of this software and associated documentation files (the \"Software\"), to deal\r\n#    in the Software without restriction, including without limitation the rights\r\n#    to use the Software, and to permit persons to whom the Software is\r\n#    furnished to do so, subject to the following conditions:\r\n\r\n#    1. Not for resale (use of software tools for revenue generation excluded).\r\n#    2. Credit to: \"https://www.youtube.com/c/ImagiscopeTech\"\r\n#    3. Notification of commercial use to author.\r\n\r\n#    The above copyright notice and this permission notice shall be included in all\r\n#    copies or substantial portions of the Software.\r\n\r\n#    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n#    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n#    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n#    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n#    SOFTWARE.\r\n\r\nbl_info = {\r\n    \"name\": \"Earth Studio\",\r\n    \"author\": \"Rob Jolly - Imagiscope\",\r\n    \"description\": \"Earth Studio Tools Addon\",\r\n    \"blender\": (2, 80, 0),\r\n    \"version\": (1, 2, 2),\r\n    \"location\": \"View3D\",\r\n    \"warning\": \"\",\r\n    \"category\": \"Import-Export\"\r\n}\r\n\r\nimport bpy, json, mathutils, math, bmesh\r\nfrom mathutils import *\r\nfrom bpy.props import EnumProperty\r\nfrom xml.dom.minidom import parse\r\nfrom xml.dom.minidom import Node\r\nfrom xml.etree import cElementTree as ElementTree\r\n\r\nimport numpy\r\n \r\n                \r\nclass GES_OT_Path(bpy.types.PropertyGroup):\r\n    p_data: bpy.props.StringProperty(name=\"0000\",subtype='FILE_PATH',default=r\"\")\r\n    p_movie: bpy.props.StringProperty(name=\"data\",subtype='FILE_PATH',default=r\"\")\r\n    p_kml: bpy.props.StringProperty(name=\"kml\",subtype='FILE_PATH',default=r\"\")\r\n    p_refdata: bpy.props.StringProperty(name=\"refdata\",subtype='FILE_PATH',default=r\"\")\r\n    \r\n    p_objexpfolder: bpy.props.StringProperty(name=\"expdata\",subtype='DIR_PATH',default=r\"//\")\r\n    p_objexp: bpy.props.StringProperty(name=\"expdata\",subtype='FILE_NAME',default=r\"ObjectKML\")\r\n    \r\n    v_curve: bpy.props.EnumProperty(name=\"Curve\",items=[('NURBS',\"Nurbs\",\"\"),('POLY',\"Poly\",\"\")])\r\n    \r\n    def trackitems(self,context):\r\n        t_trks = []\r\n        objects = bpy.data.objects[\"_GES_WORLD\"].children\r\n        for obj in objects:\r\n            \r\n            if obj.type == \"MESH\":\r\n                if  obj.data.name[0:4] == \"Plan\": #mod for some international languages\r\n                    t_trks.append(( obj.name, obj.name,\"\"))\r\n        \r\n        return t_trks\r\n    v_snapto: bpy.props.EnumProperty(\r\n        name = \"Snap to\",\r\n        description = \"Snap to TrackPoint in _GES_WORLD\",\r\n        items =  trackitems \r\n    )\r\n    \r\n    v_terrain: bpy.props.BoolProperty(name=\"Follow Terrain\",description=\"Align the KML route with supplied JSON TrackPoints.\", default = True) \r\n    \r\n    v_elevation: bpy.props.IntProperty(name=\"Add Elevation (m)\", default=0, min=-100, max=10000, \r\n        description=\"Add elevation to Route (0 is none, 1000 = 1km, 100000 = 10km)\" )\r\n        \r\n    v_reduce: bpy.props.IntProperty(name=\"Point Reduction\", default=2, min=0, max=100, \r\n        description=\"Reduce KML points based on clustering (1 is less reduction, 100 is more reduction)\" )\r\n    v_prox: bpy.props.IntProperty(name=\"Match Proximity (m)\", default=1, min=1, max=1000,\r\n        description=\"Set altitude based on meters (approx) to trackpoint (1 is closer, 100 more forgiving)\")\r\n    \r\n    v_bevel: bpy.props.FloatProperty(name=\"Bevel Depth\", default=0, min=0, max=5, \r\n        description=\"Starting Route Bevel (0 = none)\" )\r\n    \r\n    v_objlinecolor: bpy.props.FloatVectorProperty(name=\"Line Color\", subtype='COLOR', default=[0.0,1.0,0.0])\r\n    v_objfillcolor: bpy.props.FloatVectorProperty(name=\"Fill Color\", subtype='COLOR', default=[1.0,1.0,1.0])\r\n    \r\n    v_objlinewidth: bpy.props.IntProperty(name=\"Line Width\", default=0, min=0, max=50, \r\n        description=\"Width of line (0 = none)\" )\r\n        \r\n   \r\n    v_objfillopacity: bpy.props.IntProperty(name=\"Fill Opacity\", default=100, min=1, max=100)\r\n    \r\n    def nontrackitems(self,context):\r\n        t_trks = []\r\n        objects = bpy.context.scene.objects\r\n        for obj in objects:\r\n            nonges = False # Only display root obects - no cameras, no lights, no GES items\r\n            if (obj.parent) == None:\r\n                nonges = True\r\n            if (obj.type) == 'LIGHT' or (obj.type) == 'CAMERA':\r\n                nonges = False\r\n            if obj.name[0:5] != \"_GES_\" and nonges == True and obj.name[0:7] != \"Marker_\" :\r\n                t_trks.append(( obj.name, obj.name,\"\"))\r\n            \r\n        return t_trks\r\n    v_mtemplate: bpy.props.EnumProperty(\r\n        name = \"Template\",\r\n        description = \"Marker Template\",\r\n        items =  nontrackitems \r\n    )\r\n    v_mlookat: bpy.props.BoolProperty(name=\"Face to Camera\",description=\"Align the Marker to the Camera.\", default = True) \r\n\r\n    \r\n# Earth Studio import panel\r\nclass GES_PT_ImportPanel(bpy.types.Panel):\r\n    bl_label = \"Earth Studio Import\"\r\n    bl_idname = \"GES_PT_ImportPanel\"\r\n    bl_space_type = 'VIEW_3D'\r\n    bl_region_type = 'UI'\r\n    bl_category = 'Earth Studio'\r\n    \r\n    def draw(self,context):\r\n        layout = self.layout\r\n        row = layout.row()\r\n        row.label(text=\"Footage (mp4 or first jpeg):\")\r\n        row = layout.row()\r\n        row.prop(bpy.context.scene.GES_OT_Path, \"p_movie\", text=\"\",icon=\"IMAGE_DATA\")\r\n        row = layout.row()\r\n        row.label(text=\"Earth Studio JSON File:\")\r\n        row = layout.row()\r\n        row.prop(bpy.context.scene.GES_OT_Path, \"p_data\", text=\"\",icon=\"VIEW_CAMERA\")\r\n        row = layout.row()\r\n        fa = bpy.context.scene.GES_OT_Path.p_movie\r\n        fb  = bpy.context.scene.GES_OT_Path.p_data\r\n        if fa != '' and fb != '': # ensure both selections have 'text' (simple validation)\r\n            row.operator(\"scene.pre_ges\", text=\"Import Earth Studio\" )\r\n        if fa == '' or fb == '':\r\n            row.operator(\"scene.is_void\", text=\"Select Files\" , icon=\"LOCKED\")\r\n\r\n\r\n# Object to KML panel             \r\nclass GES_PT_ObjectKMLPanel(bpy.types.Panel):\r\n    bl_label = \"Export Object as KML\"\r\n    bl_idname = \"GES_PT_ObjectKMLPanel\"\r\n    bl_space_type = 'VIEW_3D'\r\n    bl_region_type = 'UI' \r\n    bl_category = 'Earth Studio'\r\n    bl_options = {'DEFAULT_CLOSED'}\r\n    \r\n    def draw(self,context):\r\n        objects = bpy.context.scene.objects\r\n        hasGES = 0\r\n        for obj in objects:\r\n           \r\n            if obj.name == \"_GES_WORLD\":\r\n                hasGES=1\r\n        \r\n        if hasGES == 1:\r\n            selobj = bpy.context.active_object\r\n           \r\n            if selobj:\r\n                \r\n                if selobj.type == \"MESH\" or selobj.type == \"CURVE\":\r\n                    layout = self.layout\r\n                    \r\n                    row = layout.row()\r\n                    \r\n                    row.label(text=\"Selected: \" + selobj.name)\r\n                    if selobj.type == \"CURVE\":\r\n                        row = layout.row()\r\n                        row.label(text=\"- Curve Optimized\")    \r\n                    row = layout.box()\r\n                    row.prop(bpy.context.scene.GES_OT_Path, \"v_objfillcolor\")\r\n                    row.prop(bpy.context.scene.GES_OT_Path, \"v_objfillopacity\")\r\n                    row = layout.box()\r\n                    row.prop(bpy.context.scene.GES_OT_Path, \"v_objlinecolor\")\r\n                    row.prop(bpy.context.scene.GES_OT_Path, \"v_objlinewidth\")\r\n                    row = layout.row()\r\n                    row.label(text=\"Destination Folder:\")\r\n                    row = layout.row()\r\n                    row.prop(bpy.context.scene.GES_OT_Path, \"p_objexpfolder\", text=\"\")\r\n                    row = layout.row()\r\n                    row.label(text=\"Filename:\" )\r\n                    row = layout.row()\r\n                    row.prop(bpy.context.scene.GES_OT_Path, \"p_objexp\", text=\"\")\r\n                    row = layout.row()\r\n                    row.operator(\"scene.pre_objkml\", text=\"Export Object as KML\" ).action = \"pri\"\r\n                else:\r\n                    layout = self.layout\r\n               \r\n                    row = layout.row()\r\n                    row.label(text=\"Invalid: Mesh or Curve Only\")\r\n                    row = layout.row()\r\n                    row.label(text=\"Try Converting to Mesh\")\r\n            else:\r\n                layout = self.layout\r\n                row = layout.row()\r\n                row.label(text=\"Select Object\", icon=\"LOCKED\")\r\n                row = layout.row()   \r\n           \r\n        if hasGES == 0: # 'disable' section if there is no imported project\r\n            layout = self.layout\r\n            row = layout.row()\r\n            row.label(text=\"Import Earth Studio first\", icon=\"LOCKED\")\r\n            row = layout.row()      \r\n       \r\n    \r\n# KML import panel               \r\nclass GES_PT_KMLPanel(bpy.types.Panel):\r\n    bl_label = \"Import KML Route\" # Import/Export\"\r\n    bl_idname = \"GES_PT_KMLPanel\"\r\n    bl_space_type = 'VIEW_3D'\r\n    bl_region_type = 'UI' \r\n    bl_category = 'Earth Studio'\r\n    bl_options = {'DEFAULT_CLOSED'}\r\n    \r\n    def draw(self,context):\r\n        objects = bpy.context.scene.objects\r\n        hasGES = 0\r\n        for obj in objects:\r\n           \r\n            if obj.name == \"_GES_WORLD\":\r\n                hasGES=1\r\n                \r\n        if hasGES == 1: # enabled\r\n           \r\n            context.area.tag_redraw()\r\n            layout = self.layout\r\n            row = layout.row()\r\n            row.label(text=\"KML File (route):\")\r\n            row = layout.row()\r\n            row.prop(bpy.context.scene.GES_OT_Path, \"p_kml\", text=\"\", icon=\"WORLD\")\r\n            row = layout.row()\r\n            row.prop(bpy.context.scene.GES_OT_Path, \"v_snapto\")\r\n            row = layout.row()\r\n            row.prop(bpy.context.scene.GES_OT_Path, \"v_curve\")\r\n            row = layout.row()\r\n            row.prop(bpy.context.scene.GES_OT_Path, \"v_bevel\")\r\n            row = layout.row()\r\n            row.prop(bpy.context.scene.GES_OT_Path, \"v_elevation\")\r\n            row = layout.row()\r\n            row.prop(bpy.context.scene.GES_OT_Path, \"v_terrain\")\r\n            if str(bpy.context.scene.GES_OT_Path.v_terrain) == \"True\":\r\n                row = layout.box()\r\n                row.label(text=\"Reference JSON File:\")\r\n                row.prop(bpy.context.scene.GES_OT_Path, \"p_refdata\", text=\"\", icon=\"LIBRARY_DATA_DIRECT\")\r\n                row.prop(bpy.context.scene.GES_OT_Path, \"v_reduce\")\r\n                row.prop(bpy.context.scene.GES_OT_Path, \"v_prox\")\r\n            row = layout.row()\r\n            \r\n            fa = bpy.context.scene.GES_OT_Path.p_kml\r\n            fb  = bpy.context.scene.GES_OT_Path.p_refdata\r\n            if fa != '': #(simple validation)\r\n                row.operator(\"scene.pre_kml\", text=\"Import KML Route\" ).action = \"pri\"\r\n               \r\n            if fa == '':\r\n                row.operator(\"scene.is_void\", text=\"Select Files\" , icon=\"LOCKED\")\r\n        if hasGES == 0: # 'disable' section if there is no imported project\r\n            layout = self.layout\r\n            row = layout.row()\r\n            row.label(text=\"Import Earth Studio first\", icon=\"LOCKED\")\r\n            row = layout.row()\r\n                  \r\n# Marker Panel\r\nclass GES_PT_MarkerPanel(bpy.types.Panel):\r\n    bl_label = \"Trackpoint Marker Tool\"\r\n    bl_idname = \"GES_PT_MarkerPanel\"\r\n    bl_space_type = 'VIEW_3D'\r\n    bl_region_type = 'UI'\r\n    bl_category = 'Earth Studio'\r\n    bl_options = {'DEFAULT_CLOSED'}\r\n    \r\n    def draw(self,context):\r\n        objects = bpy.context.scene.objects\r\n        hasGES = 0\r\n        for obj in objects:\r\n           \r\n            if obj.name == \"_GES_WORLD\":\r\n                hasGES=1\r\n           \r\n                    \r\n        if hasGES == 1: # enabled\r\n           \r\n            context.area.tag_redraw()\r\n            layout = self.layout\r\n            row = layout.row()\r\n            row.label(text=\"Add Marker for each Trackpoint\")\r\n            row = layout.row()\r\n            row.label(text=\"1. Create Template Marker\")\r\n            row = layout.row()\r\n            row.label(text=\"   - Parent all objects to object/empty\")\r\n            row = layout.row()\r\n            row.label(text=\"   - Text Object will use Trackpoint name\")\r\n            row = layout.row()\r\n            row.label(text=\"   - Origin of Parent is rotation point\")\r\n            row = layout.row()\r\n            row.label(text=\"2. Select Template Marker\")\r\n            row = layout.row()\r\n            row.label(text=\"3. Use 'Face..' to always point to Camera\")\r\n            row = layout.row()\r\n            row.prop(bpy.context.scene.GES_OT_Path, \"v_mtemplate\")\r\n            \r\n            row = layout.row()\r\n            row.prop(bpy.context.scene.GES_OT_Path, \"v_mlookat\")\r\n           \r\n            row = layout.row()\r\n            \r\n           \r\n            row.operator(\"scene.pre_marker\", text=\"Create Markers\" )\r\n               \r\n           \r\n        if hasGES == 0: # 'disable' section if there is no imported project\r\n            layout = self.layout\r\n            row = layout.row()\r\n            row.label(text=\"Import Earth Studio first\", icon=\"LOCKED\")\r\n            row = layout.row()\r\n            \r\n# Help Panel\r\nclass GES_PT_InfoPanel(bpy.types.Panel):\r\n    bl_label = \"Help\"\r\n    bl_idname = \"GES_PT_InfoPanel\"\r\n    bl_space_type = 'VIEW_3D'\r\n    bl_region_type = 'UI' \r\n    bl_category = 'Earth Studio'\r\n    bl_options = {'DEFAULT_CLOSED'}\r\n    \r\n    def draw(self,context):\r\n        layout = self.layout\r\n       \r\n        row = layout.row()\r\n        row.label(text=\"Tutorials\")\r\n        row = layout.row()\r\n        op = row.operator('wm.url_open', text=\"View on YouTube\")\r\n        op.url = \"https://www.youtube.com/c/ImagiscopeTech\"\r\n       \r\n        row = layout.row()\r\n\r\n# Void class - returns nothing\r\nclass isvoid(bpy.types.Operator):\r\n    bl_idname = \"scene.is_void\"\r\n    bl_label = \"GES is void\"\r\n    \r\n    def execute(self, context): \r\n        return {'FINISHED'}\r\n\r\n# check files   \r\nclass preobjKML(bpy.types.Operator):\r\n    bl_idname = \"scene.pre_objkml\"\r\n    bl_label = \"GES PRE KML\"\r\n    \r\n    action: EnumProperty(items=[('pri','pri','pri'),('sec','sec','sec')])\r\n        \r\n    def execute(self, context):\r\n        if self.action == \"pri\":\r\n            objecttokml()\r\n        \r\n        return {'FINISHED'}\r\n\r\n\r\n# check files   \r\nclass preKML(bpy.types.Operator):\r\n    bl_idname = \"scene.pre_kml\"\r\n    bl_label = \"GES PRE KML\"\r\n    \r\n    action: EnumProperty(items=[('pri','pri','pri'),('sec','sec','sec')])\r\n        \r\n    def execute(self, context):\r\n        if self.action == \"pri\":\r\n            importkml()\r\n       \r\n        return {'FINISHED'}\r\n\r\n\r\nclass preGES(bpy.types.Operator):\r\n    bl_idname = \"scene.pre_ges\"\r\n    bl_label = \"GES PRE GES\"\r\n    \r\n    def execute(self, context):\r\n        fa = bpy.context.scene.GES_OT_Path.p_movie\r\n        fb  = bpy.context.scene.GES_OT_Path.p_data\r\n        if fa != '' and fb != '':\r\n            importges()\r\n            objects = bpy.context.scene.objects\r\n            hasGES = 0\r\n            for obj in objects:\r\n                if obj.name == \"_GES_WORLD\":\r\n                    hasGES=1\r\n            if hasGES == 1:  \r\n                t_trks = []\r\n                objects = bpy.data.objects[\"_GES_WORLD\"].children\r\n                for obj in objects:\r\n                    if obj.type == \"MESH\":\r\n                        if  obj.data.name[0:4] == \"Plan\":\r\n                            t_trks.append(( obj.name, obj.name,\"\"))\r\n                v_snapto = bpy.props.EnumProperty(\r\n                     name = \"Snap to\",description = \"Snap to TrackPoint in _GES_WORLD\",items = t_trks  )\r\n\r\n \r\n        return {'FINISHED'}\r\n\r\n# check files   \r\nclass preMarker(bpy.types.Operator):\r\n    bl_idname = \"scene.pre_marker\"\r\n    bl_label = \"GES PRE MARKER\"\r\n    \r\n           \r\n    def execute(self, context):\r\n        makemarkers()\r\n        return {'FINISHED'}\r\n        \r\n# Info Popup\r\ndef ShowMessageBox(message = \"\", title = \"Information\", icon = 'INFO'):\r\n    def draw(self, context):\r\n        self.layout.label(text = message)\r\n    bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)\r\n          \r\ndef importges():\r\n    \r\n    cam = bpy.context.scene.camera\r\n    if not cam: # add a camera if deleted\r\n        \r\n        cam_d = bpy.data.cameras.new(name='Camera')\r\n        cam_o = bpy.data.objects.new('Camera', cam_d)\r\n        bpy.data.collections['Collection'].objects.link(cam_o)\r\n        cam = cam_o\r\n        bpy.context.scene.camera = cam\r\n        \r\n    scene = bpy.context.scene\r\n    \r\n    # load JSON file for evaluation\r\n    # Sample format: jfilename = \"D:/Local/Project/Beach/beach/beach.json\"\r\n    jfilename = bpy.path.abspath(bpy.context.scene.GES_OT_Path.p_data)\r\n\r\n    jfile = open(jfilename,'r')\r\n    camdata = json.load(jfile)\r\n    jfile.close\r\n    hasTrack = False\r\n    for cd in camdata:\r\n        if cd == \"trackPoints\":\r\n            hasTrack=True\r\n     # check trackpoints\r\n    if hasTrack == False:\r\n        ShowMessageBox( \"Ensure Earth Studio project has Trackpoints (min 1) and export JSON file with trackpoints.\",\"Import Aborted - No Trackpoints Found\",\"ERROR\") \r\n    else:\r\n        \r\n        # load the GES render files as background for camera\r\n        # Sample format: ifiles = \"D:/Local/Project/Beach/beach/footage/beach_0000.jpeg\"\r\n        ifiles = bpy.path.abspath(bpy.context.scene.GES_OT_Path.p_movie)\r\n\r\n        img = bpy.data.movieclips.load(ifiles)\r\n        cam.data.show_background_images = True\r\n        cam.data.clip_end = 10000\r\n\r\n        bg = cam.data.background_images.new()\r\n        bg.clip = img\r\n        bg.alpha = 1\r\n        bg.source = \"MOVIE_CLIP\"\r\n\r\n        # evaluate number of frames\r\n        s_end = camdata[\"numFrames\"]\r\n\r\n        # set scene duration\r\n        scene.frame_start = 1\r\n        scene.frame_end = s_end \r\n        scene.frame_set(1)\r\n\r\n        # function for alignment scaling\r\n        def scale_from_vector(v):\r\n            mat = Matrix.Identity(4)\r\n            for i in range(3):\r\n                mat[i][i] = v[i]\r\n            return mat   \r\n                \r\n        # set coords for positioning data starting at center of Blender global coordinates\r\n        psx = 0\r\n        psy = 0\r\n        psz = 0 \r\n\r\n        # load trackpoints\r\n        for f in range (0,len(camdata[\"trackPoints\"])):\r\n            \r\n            px = camdata[\"trackPoints\"][f][\"position\"][\"x\"]\r\n            py = camdata[\"trackPoints\"][f][\"position\"][\"y\"]\r\n            pz = camdata[\"trackPoints\"][f][\"position\"][\"z\"]\r\n            \r\n            rlat = camdata[\"trackPoints\"][f][\"coordinate\"][\"position\"][\"attributes\"][0][\"value\"][\"relative\"]\r\n            rlng = camdata[\"trackPoints\"][f][\"coordinate\"][\"position\"][\"attributes\"][1][\"value\"][\"relative\"]\r\n            \r\n            if f==0:\r\n                psx = px\r\n                psy = py\r\n                psz = pz\r\n                \r\n            rlat = 360 * (rlat) - 180\r\n            rlng = (89.9999*2) * (rlng ) - 89.9999\r\n            \r\n            bpy.ops.mesh.primitive_plane_add()\r\n            trk = bpy.context.selected_objects[0]\r\n            trk.name = str(f + 1) + \". \" + camdata[\"trackPoints\"][f][\"name\"]\r\n          \r\n            trk.location.x = (px-psx) / 100\r\n            trk.location.y = (py-psy) / 100\r\n            trk.location.z = (pz-psz) / 100\r\n            \r\n            trk.rotation_euler[1] = math.radians(90-rlng)\r\n            trk.rotation_euler[2] = math.radians(rlat)\r\n            trk.scale = (0.1,0.1,0.1)\r\n            \r\n            calt = camdata[\"trackPoints\"][f][\"coordinate\"][\"position\"][\"attributes\"][2][\"value\"][\"relative\"]\r\n            trk['X'] = px\r\n            trk['Y'] = py\r\n            trk['Z'] = pz\r\n            trk['LAT'] = rlng # real lat - mislabeled\r\n            trk['LNG'] = rlat # real lng - mislabeled\r\n            trk['ALT'] = 65117481 * (calt) + 1 \r\n            \r\n            if f==0:\r\n                # create parent object - parent used to align position on earth with Blender global coordinates\r\n                bpy.ops.object.empty_add(type='SINGLE_ARROW', location=(0,0,0))\r\n                ges_parent = bpy.context.selected_objects[0]\r\n                ges_parent.name = \"_GES_WORLD\"\r\n                \r\n                # align parent perpendicular to first track point\r\n                loc_src, rot_src, scale_src = trk.matrix_world.decompose()\r\n                loc_dst, rot_dst, scale_dst = ges_parent.matrix_world.decompose()\r\n\r\n                axis = Vector((0.0, 0.0, 1.0))\r\n                z1 = rot_src @ axis\r\n                z2 = rot_dst @ axis\r\n                q = z2.rotation_difference(z1)\r\n\r\n                ges_parent.matrix_world = (\r\n                    Matrix.Translation(loc_dst) @\r\n                    (q @ rot_dst).to_matrix().to_4x4() @\r\n                    scale_from_vector(scale_dst)\r\n                )\r\n                \r\n                # change x,y to negative values of x,y\r\n                ges_parent.rotation_euler[0] = -ges_parent.rotation_euler[0]\r\n                ges_parent.rotation_euler[1] = -ges_parent.rotation_euler[1]\r\n                      \r\n            # move trackpoint to GES parent\r\n            trk.parent = ges_parent\r\n         \r\n\r\n        # Camera Information\r\n        cam.delta_rotation_euler.y = 180 * math.pi / 180\r\n\r\n        for f in range (0,s_end + 1):\r\n            px = camdata[\"cameraFrames\"][f][\"position\"][\"x\"] \r\n            py = camdata[\"cameraFrames\"][f][\"position\"][\"y\"] \r\n            pz = camdata[\"cameraFrames\"][f][\"position\"][\"z\"] \r\n           \r\n                \r\n            rx = float(camdata[\"cameraFrames\"][f][\"rotation\"][\"x\"])\r\n            ry = camdata[\"cameraFrames\"][f][\"rotation\"][\"y\"] \r\n            rz = camdata[\"cameraFrames\"][f][\"rotation\"][\"z\"]\r\n            \r\n            # position set in relation to first frame - scale to 1/100\r\n            cam.location.x = (px-psx) / 100\r\n            cam.location.y = (py-psy) / 100\r\n            cam.location.z = (pz-psz) / 100\r\n         \r\n            eul = mathutils.Euler((0.0, 0.0, 0.0), 'XYZ')\r\n            \r\n            eul.rotate_axis('X', math.radians(-rx))\r\n            eul.rotate_axis('Y', math.radians(ry ))\r\n            eul.rotate_axis('Z', math.radians(-rz+180))\r\n            \r\n            cam.rotation_euler = eul\r\n          \r\n            cam.keyframe_insert(data_path=\"location\", index=-1, frame=f + 1)\r\n            cam.keyframe_insert(data_path=\"rotation_euler\", index=-1, frame=f + 1)\r\n            \r\n            \r\n        # camera \"lens\" based on 20 degree Filed of View (default value)\r\n        cam.data.sensor_width = 35 \r\n        cam.data.type = 'PERSP'\r\n        cam.data.lens_unit = 'FOV'\r\n        cam.data.angle = math.radians(34.8)\r\n\r\n        # move camera to GES parent\r\n        cam.parent = ges_parent\r\n\r\n        bpy.context.scene.frame_current = 1   \r\n        area = next(area for area in bpy.context.screen.areas if area.type == 'VIEW_3D')\r\n        area.spaces[0].region_3d.view_perspective = 'CAMERA'\r\n\r\ndef importkml():\r\n    earth = 6371010.1 #earth radius, in meters\r\n    add_elev = float(bpy.context.scene.GES_OT_Path.v_elevation)\r\n    sn = bpy.data.objects[bpy.context.scene.GES_OT_Path.v_snapto]\r\n    \r\n    tralt = sn[\"ALT\"]\r\n    \r\n    objects = bpy.data.objects[\"_GES_WORLD\"].children\r\n    v_zerotrack = \"\" #initial center plane\r\n    for obj in objects:\r\n        if obj.type == \"MESH\":\r\n            if  obj.data.name[0:5] == \"Plane\":\r\n                if v_zerotrack == \"\":\r\n                    v_zerotrack = obj.name\r\n                    \r\n    # function for alignment scaling\r\n    def scale_from_vector(v):\r\n        mat = Matrix.Identity(4)\r\n        for i in range(3):\r\n            mat[i][i] = v[i]\r\n        return mat \r\n\r\n    # function to measure distance between two coordinates\r\n    def measure(lat1, lon1, lat2, lon2):\r\n       \r\n        dLat = lat2 * math.pi / 180 - lat1 * math.pi / 180\r\n        dLon = lon2 * math.pi / 180 - lon1 * math.pi / 180\r\n        a = math.sin(dLat/2) * math.sin(dLat/2) + math.cos(lat1 * math.pi / 180) * math.cos(lat2 * math.pi / 180) * math.sin(dLon/2) * math.sin(dLon/2)\r\n        c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))\r\n        d = earth * c\r\n        return d\r\n\r\n\r\n    # make a new curve\r\n    crv = bpy.data.curves.new('crv', 'CURVE')\r\n    crv.dimensions = '3D'\r\n\r\n    # make a new spline in that curve\r\n    spline = crv.splines.new(type=bpy.context.scene.GES_OT_Path.v_curve)\r\n    spline.resolution_u = 6\r\n    spline.order_u = 12\r\n    \r\n\r\n    # load kml file for evaluation\r\n    xfilename = bpy.path.abspath(bpy.context.scene.GES_OT_Path.p_kml)\r\n    domData = parse(xfilename)\r\n    coor = domData.getElementsByTagName(\"coordinates\")\r\n    \r\n    pl = \"\"\r\n    if coor.length != 0:\r\n        for i in range (0,coor.length ):\r\n            if coor[i].parentNode.nodeName != \"Point\":\r\n                pl = coor[0].firstChild.nodeValue.strip()\r\n                pl = pl.replace(\"\\n\",\"  \")\r\n                while pl.find(\"  \") != -1:\r\n                    pl = pl.replace (\"  \",\" \")\r\n                #print (\"coord found\")\r\n    if coor.length == 0 or pl == \"\": # try gx:coord method\r\n        gxcoor = domData.getElementsByTagName(\"gx:coord\")\r\n        print (\"xx\")\r\n        if gxcoor.length != 0:\r\n            for i in range (0,gxcoor.length): #format like coordinates\r\n                if i != 0:\r\n                    pl+= \" \"\r\n                pl +=  str(gxcoor[i].firstChild.nodeValue.strip()).replace(\" \",\",\")\r\n            print (pl)\r\n        elif gxcoor.length == 0:\r\n            return\r\n    #print (pl)\r\n    \r\n    pt = pl.split(' ')\r\n    #if add_elev != 0:\r\n    if str(bpy.context.scene.GES_OT_Path.v_terrain) != 'True': # replace anchor value with trackpoint alt\r\n        tt = pt[0].split(\",\")\r\n        pt[0] = str(tt[0]) + \",\" + str(tt[1]) + \",\" + str(tralt)\r\n    pl = pt[0] + \" \" + pl # insert start point twice (anchor)\r\n    pt = pl.split(' ')\r\n    \r\n    # add placeholder end coordinate\r\n    pt.append (str(0) + \",\" + str(0) + \",\" + str(0) )  \r\n   \r\n    # load JSON file for evaluation\r\n    # Sample format: jfilename = \"D:/Local/Project/Beach/beach/beach.json\"\r\n   \r\n    if str(bpy.context.scene.GES_OT_Path.v_terrain) == 'True':\r\n        jfilename = bpy.path.abspath(bpy.context.scene.GES_OT_Path.p_refdata)\r\n\r\n        jfile = open(jfilename,'r')\r\n        camdata = json.load(jfile)\r\n        jfile.close\r\n\r\n   \r\n    lpt = 0   \r\n    \r\n    if str(bpy.context.scene.GES_OT_Path.v_terrain) == 'True':\r\n        # calculate altitude based on track points, incline/decline from A to B\r\n        prox = bpy.context.scene.GES_OT_Path.v_prox /10000 # set altitude base on \"closeness\" to trackpoint - default 0.001 (0.0001 is closer, 0.01 more forgiving)\r\n        for i in range (0,len(pt)):\r\n            plat = float(pt[i].split(',')[0])\r\n            plng = float(pt[i].split(',')[1])\r\n            fnd = 0\r\n            for z in range (0,len(camdata[\"trackPoints\"])):\r\n                if fnd == 0:\r\n                    tlat =camdata[\"trackPoints\"][z][\"coordinate\"][\"position\"][\"attributes\"][0][\"value\"][\"relative\"]\r\n                    tlng =camdata[\"trackPoints\"][z][\"coordinate\"][\"position\"][\"attributes\"][1][\"value\"][\"relative\"]\r\n                    talt =camdata[\"trackPoints\"][z][\"coordinate\"][\"position\"][\"attributes\"][2][\"value\"][\"relative\"]\r\n                   \r\n                    tname = camdata[\"trackPoints\"][z][\"name\"]\r\n                    xlat = 360 * (tlat) - 180\r\n                    xlng = (89.9999*2) * (tlng ) - 89.9999\r\n                    xalt = 65117481 * (talt) + 1 # base elevation \r\n\r\n                    if i == 0:\r\n                        xalt = xalt - add_elev\r\n                        pt[i] = str(plat) + \",\" + str(plng) + \",\" + str(xalt)\r\n                    if (xlat - (plat)  < prox and xlat - (plat)  > - prox) and (xlng - (plng)  < prox and xlng - (plng)  > -prox):\r\n                        pt[i] = str(plat) + \",\" + str(plng) + \",\" + str(xalt)\r\n                        fnd = 1\r\n                        \r\n                            \r\n            if (fnd == 1 and i > 0) or (i == len(pt)-1 and fnd == 0):\r\n                pplat = float(pt[lpt].split(',')[0])\r\n                pplng = float(pt[lpt].split(',')[1])\r\n                ppalt = float(pt[lpt].split(',')[2])\r\n                for d in range (lpt + 1,i ):\r\n                    if i == len(pt)-1 and fnd == 0: \r\n                        xlat = pplat\r\n                        xlng = pplng\r\n                        xalt = ppalt\r\n                    \r\n                    dlat = float(pt[d].split(',')[0])\r\n                    dlng = float(pt[d].split(',')[1])      \r\n                    d1 = measure(pplat,pplng,dlat,dlng) # distance between last alt and this\r\n                    d2 = measure(dlat,dlng,xlat,xlng) # distance between trackpoint alt and this\r\n                    dratio = d1/(d1+d2)\r\n                    newalt =  ppalt + ((xalt - ppalt) * dratio)\r\n                    \r\n                    pt[d] = str(dlat) + \",\" + str(dlng) + \",\" + str(newalt)\r\n                    \r\n                lpt = i   \r\n    \r\n        # reset start point with elevation change\r\n        #stln = pt[0].split(\",\")\r\n        #pt[0] = str(stln[0]) + \",\" + str(stln[1]) + \",\" + str(float(stln[2]) + add_elev)\r\n    \r\n        \r\n    # convert lat/lon to points in 3D space on globe\r\n    prevx = 0\r\n    prevy = 0\r\n    redval =  bpy.context.scene.GES_OT_Path.v_reduce  # reduce KML points based on closeness - default 10 (1 is closer (less reduction), 100 further away (more reduction))\r\n    pn = []\r\n    for i in range (0,len(pt)-1): \r\n        lon = float(pt[i].split(',')[0])\r\n        lat = float(pt[i].split(',')[1])\r\n        alt = float(pt[i].split(',')[2])\r\n        \r\n        if str(bpy.context.scene.GES_OT_Path.v_terrain) != 'True' and i==0:\r\n            alt = alt - (add_elev)\r\n        phi = (90 - lat) * (math.pi / 180);\r\n        theta = (lon + 180) * (math.pi / 180);\r\n        ox = -((earth + alt) * math.sin(phi) * math.cos(theta));\r\n        oy = -((earth + alt) * math.sin(phi) * math.sin(theta));\r\n        oz = ((earth + alt) * math.cos(phi))\r\n        \r\n        if (prevx + redval < ox or prevx - redval > ox) and (prevy + redval < oy or prevy - redval > oy) or i<2 or redval==0:\r\n            pn.append( pt[i] + ',' + str(ox) + ',' + str(oy) + ',' + str(oz) )\r\n            prevx = ox\r\n            prevy = oy\r\n        \r\n    # set coordinates to spline\r\n    flat = float(pn[0].split(',')[0])\r\n    flng = float(pn[0].split(',')[1])\r\n    psx = float(pn[0].split(',')[3])\r\n    psy = float(pn[0].split(',')[4])\r\n    psz = float(pn[0].split(',')[5])\r\n\r\n    spline.points.add(len(pn)-1)\r\n   \r\n    for p, new_co in zip(spline.points, pn):\r\n        \r\n        px = float(new_co.split(',')[3])\r\n        py = float(new_co.split(',')[4])\r\n        pz = float(new_co.split(',')[5])\r\n        p.co = (float((px - psx) / 100), float((py -psy) / 100), float((pz - psz) / 100), 1.0)\r\n\r\n    #if add_elev != 0:\r\n    for i in range (1,len(pn)):\r\n        spline.points[i-1].co = spline.points[i].co\r\n\r\n    # create curve object\r\n    obj = bpy.data.objects.new('RoutePath', crv) \r\n    \r\n    \r\n    \r\n    # align path to surface of the globe \r\n    print (v_zerotrack)\r\n    obj.rotation_euler[1] = bpy.data.objects[v_zerotrack].rotation_euler[1] #math.radians(90-flng)\r\n    obj.rotation_euler[2] = bpy.data.objects[v_zerotrack].rotation_euler[2] #math.radians(flat)\r\n\r\n\r\n    bpy.data.collections['Collection'].objects.link(obj)\r\n\r\n    #re-align to global system\r\n    bpy.ops.object.empty_add(type='SINGLE_ARROW', location=(0,0,0)) # create empty container\r\n    ges_path = bpy.context.selected_objects[0]\r\n    ges_path.name = \"_GES_PATH\"\r\n\r\n    loc_src, rot_src, scale_src = obj.matrix_world.decompose()\r\n    loc_dst, rot_dst, scale_dst = ges_path.matrix_world.decompose()\r\n\r\n    axis = Vector((0.0, 0.0, 1.0))\r\n    z1 = rot_src @ axis\r\n    z2 = rot_dst @ axis\r\n    q = z2.rotation_difference(z1)\r\n\r\n    # set rotation based on object matrix\r\n    ges_path.matrix_world = (\r\n        Matrix.Translation(loc_dst) @\r\n        (q @ rot_dst).to_matrix().to_4x4() @\r\n        scale_from_vector(scale_dst)\r\n    )\r\n\r\n    # change x,y to negative values of x,y\r\n    ges_path.rotation_euler[0] = -ges_path.rotation_euler[0]\r\n    ges_path.rotation_euler[1] = -ges_path.rotation_euler[1]\r\n    \r\n    # creates anchor object - used to ensure path remains at height\r\n    bpy.ops.object.empty_add(type='SINGLE_ARROW', location=(0,0,0)) # create empty container\r\n    ges_start = bpy.context.selected_objects[0]\r\n    ges_start.name = \"Anchor Empty\"\r\n    ges_start.parent = ges_path\r\n    ges_start.parent_type = 'OBJECT'\r\n   \r\n        \r\n    # reset rotation on obj\r\n    obj.rotation_euler[1] = math.radians(0)\r\n    obj.rotation_euler[2] = math.radians(0)\r\n\r\n    # add object to parent\r\n    obj.parent = ges_path\r\n    obj.parent_type = 'OBJECT'\r\n    \r\n    obj.data.bevel_depth = bpy.context.scene.GES_OT_Path.v_bevel\r\n   \r\n    ges_path.location = sn.matrix_world.to_translation()\r\n\r\n    domData.unlink()  \r\n\r\ndef makemarkers():\r\n    mkrcnt = 0 # Counter for information\r\n\r\n    # Load template objects\r\n    mk = bpy.data.objects[bpy.context.scene.GES_OT_Path.v_mtemplate]\r\n    \r\n    # Create new collection\r\n    if \"GESMarkers\" not in bpy.data.collections:\r\n        collection = bpy.data.collections.new(\"GESMarkers\")\r\n        bpy.context.scene.collection.children.link(collection)\r\n        \r\n    # Cycle through GES trackpoints (except hidden ones)\r\n    objects = bpy.data.objects[\"_GES_WORLD\"].children \r\n\r\n    for obj in objects:\r\n        if obj.type == \"MESH\":\r\n            if  obj.data.name[0:5] == \"Plane\" and obj.visible_get():\r\n                # Trackpoint name and clean up\r\n                newtext = obj.name\r\n                spx = newtext.split(' ')[0].replace(\".\",\"\")\r\n                if spx.isdecimal():\r\n                    newtext = newtext.replace(spx + \". \",\"\")\r\n                if mk.type == \"EMPTY\":\r\n                    # Create new empth\r\n                    mk2 = bpy.data.objects.new('Marker_' + newtext, None)\r\n                else: \r\n                     # Create copy of marker\r\n                    mk2 = bpy.data.objects.new('Marker_' + newtext, mk.data.copy())\r\n                # Set location, scale and rotation for Parent object\r\n                mk2.location = obj.matrix_world.translation\r\n                mk2.scale = mk.scale\r\n                mk2.rotation_euler = mk.rotation_euler\r\n                \r\n                # Add track to camera\r\n                if str(bpy.context.scene.GES_OT_Path.v_mlookat) == \"True\":\r\n                    constraint = mk2.constraints.new(type='TRACK_TO')\r\n                    constraint.target = bpy.data.objects['Camera']\r\n                    constraint.track_axis=\"TRACK_Z\"\r\n                # Move (link) to Collection\r\n                bpy.data.collections[\"GESMarkers\"].objects.link(mk2)\r\n                # Clone children (really, we're doing that)\r\n                kids = mk.children              \r\n                for kid in kids:\r\n                   \r\n                    k2 = bpy.data.objects.new( kid.name + '_' + newtext, kid.data.copy())\r\n                    k2.matrix_local = kid.matrix_local\r\n                    if k2.type == 'FONT': # if a text object, set the text value to trackpoint name\r\n                        k2.data.body = newtext\r\n                    k2.parent = mk2  \r\n                   \r\n                    bpy.data.collections[\"GESMarkers\"].objects.link(k2)\r\n             \r\n                mkrcnt += 1\r\n    ShowMessageBox( str(mkrcnt) + \" Markers Created\") \r\n\r\ndef objecttokml():\r\n\r\n    wobj = bpy.data.objects['_GES_WORLD']\r\n    anc = wobj.children[0] #load first trackpoint\r\n    ancp = wobj\r\n\r\n    src_obj = bpy.context.active_object \r\n    C = bpy.context\r\n    \r\n    # create a copy of object to modify for export\r\n    obj = src_obj.copy()\r\n    obj.data = src_obj.data.copy()\r\n    obj.animation_data_clear()\r\n    C.collection.objects.link(obj)\r\n\r\n    src_obj.select_set(False)\r\n    obj.select_set(True) #select the text obj\r\n    bpy.context.view_layer.objects.active = obj\r\n    bpy.context.view_layer.update()\r\n    rmode = False\r\n    \r\n    # if object is a curve (a path) then covert the curve to a mesh\r\n    if obj.parent:\r\n        if obj.parent.name[0:9] == \"_GES_PATH\":\r\n            obj.data.splines[0].resolution_u = 2\r\n\r\n            override = bpy.context.copy()\r\n            bpy.ops.object.convert(override,target='MESH')  \r\n            bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')\r\n            rmode = True\r\n                       \r\n    bpy.data.objects[src_obj.name].select_set(False)\r\n    bpy.data.objects[obj.name].select_set(True)\r\n\r\n    bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)\r\n    \r\n    # apply world transformation (#1) - using 1/100 scale and lat/long euler\r\n    bpy.ops.transform.resize(value=(.10, .10, .10))\r\n    obj.rotation_euler[2] = anc.rotation_euler[2]\r\n    bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)\r\n  \r\n    earth = 6371010.1 #earth radius, in meters\r\n    t_location = wobj.matrix_world.inverted() @ obj.location\r\n    # get object starting location in world space\r\n    tx = t_location.x \r\n    ty = t_location.y \r\n    tz = t_location.z \r\n\r\n    pn = []\r\n    fn = []\r\n\r\n    bpy.context.view_layer.update()\r\n  \r\n    # create inverted matrix for world and anchor\r\n    winvert =  wobj.matrix_world.inverted() \r\n    ainvert = anc.matrix_world.inverted()\r\n\r\n    # cycle faces and extract vertices data with world and anchor matrix mutiplied\r\n    for f in obj.data.polygons:\r\n        pn.append(\"face\")\r\n        for v in f.vertices: \r\n            if rmode == True:\r\n                t_vertex = winvert @ ainvert @ obj.data.vertices[v].co \r\n            else:\r\n                t_vertex = winvert @ ainvert @ obj.data.vertices[v].co \r\n            pn.append(str(tx + t_vertex.x ) + \",\" + str(ty + t_vertex.y ) + \",\" + str(tz + t_vertex.z ))\r\n         \r\n    firstface=True\r\n    startagain =\"0\"\r\n  \r\n    # create kml header\r\n    fn.append (\"<?xml version='1.0' encoding='UTF-8'?><kml xmlns='http://www.opengis.net/kml/2.2'>\")\r\n    fn.append (\"<Document>\")\r\n    fn.append (\"<name>Exported from Blender</name>\")\r\n    fn.append ('<Style id=\"xstyle\">')\r\n    fn.append (\"<PolyStyle>\")\r\n    fopacity = \"00\"\r\n    if bpy.context.scene.GES_OT_Path.v_objfillopacity != 0:\r\n        v = int(bpy.context.scene.GES_OT_Path.v_objfillopacity * 255 / 100)\r\n        fopacity = hex(v)[2:]\r\n    \r\n    fcolor = str(rgb_to_hex (bpy.context.scene.GES_OT_Path.v_objfillcolor))\r\n    \r\n    fn.append (\"<color>\" + fopacity + fcolor +\"</color>\")\r\n    ol = \"1\"\r\n    if str(bpy.context.scene.GES_OT_Path.v_objlinewidth) == \"0\":\r\n        ol = \"0\"\r\n    fn.append (\"<outline>\" + ol +\"</outline>\")\r\n    fn.append (\"<fill>1</fill>\")\r\n    fn.append (\"</PolyStyle>\")\r\n    fn.append (\"<LineStyle>\")\r\n\r\n    lncolor = str(rgb_to_hex (bpy.context.scene.GES_OT_Path.v_objlinecolor))\r\n\r\n    fn.append (\"<color>FF\" + lncolor +\"</color>\")\r\n    fn.append (\"<width>\" + str(bpy.context.scene.GES_OT_Path.v_objlinewidth) +\"</width>\")\r\n    fn.append (\"</LineStyle>\")\r\n    fn.append (\"</Style>\")\r\n    fn.append (\"<Placemark><name>\" + str(obj.name) + \"</name><visibility>1</visibility>\")\r\n    fn.append(\"<styleUrl>#xstyle</styleUrl>\") \r\n    fn.append (\"<MultiGeometry>\")\r\n\r\n    # write point parameters (lat/long/alt) for each face\r\n    for f in pn:\r\n        if f == \"face\":\r\n            # for each face, start a new polygon\r\n            if startagain != \"0\":\r\n                fn.append (startagain)\r\n                startagain = \"0\"\r\n            if firstface == False: \r\n                fn.append (\"</coordinates></LinearRing></outerBoundaryIs>\")\r\n                fn.append (\"</Polygon>\")\r\n            fn.append (\"<Polygon><extrude>0</extrude><altitudeMode>absolute</altitudeMode>\")\r\n            fn.append (\"<outerBoundaryIs><LinearRing><coordinates>\")\r\n            firstface = False\r\n            \r\n        else:\r\n            xoff = (float(f.split(',')[0]) * 100) + float(anc['X'])\r\n            yoff = (float(f.split(',')[1]) * 100) + float(anc['Y'])\r\n            zoff = (float(f.split(',')[2]) * 100) + float(anc['Z']) \r\n            \r\n            lat = float(anc['LAT'])\r\n            lon = float(anc['LNG'])\r\n\r\n            edia2= earth + .00001 # used for non-spherical calculations - setting to small difference as GES uses globe\r\n            \r\n            # reverse blender coordinate infomation into lat/long/alt - fancy math (thanks google)\r\n            f = (earth - edia2) / earth\r\n            e_sq = f * (2 - f)                       \r\n            eps = e_sq / (1.0 - e_sq)\r\n            p = math.sqrt(xoff * xoff + yoff * yoff)\r\n            q = math.atan2((zoff * earth), (p * edia2))\r\n            sin_q = math.sin(q)\r\n            cos_q = math.cos(q)\r\n            sin_q_3 = sin_q * sin_q * sin_q\r\n            cos_q_3 = cos_q * cos_q * cos_q\r\n            phi = math.atan2((zoff + eps * edia2 * sin_q_3), (p - e_sq * earth * cos_q_3))\r\n            lam = math.atan2(yoff, xoff)\r\n            v = earth / math.sqrt(1.0 - e_sq * math.sin(phi) * math.sin(phi))\r\n            h   = (p / math.cos(phi)) - v\r\n\r\n            ylat = math.degrees(phi)\r\n            ylon = math.degrees(lam)\r\n\r\n            fn.append(str(ylon) + \",\" + str(ylat) + \",\" + str(h))\r\n            if startagain == \"0\":\r\n                startagain = str(ylon) + \",\" + str(ylat) + \",\" + str(h)\r\n\r\n    if startagain != \"0\":\r\n        fn.append (startagain)  \r\n    # kml footer       \r\n    fn.append (\"</coordinates></LinearRing></outerBoundaryIs>\")\r\n    fn.append (\"</Polygon>\")\r\n    fn.append (\"</MultiGeometry></Placemark></Document></kml>\")\r\n\r\n    strout = \"\"\r\n    # spit out array into string\r\n    for f in fn:\r\n        strout += (str(f) + \" \")\r\n\r\n    # save the file\r\n    outputPath = bpy.path.abspath(bpy.context.scene.GES_OT_Path.p_objexpfolder + bpy.context.scene.GES_OT_Path.p_objexp + \".kml\")\r\n    fileObject = open(outputPath, 'w')\r\n    fileObject.write(strout) \r\n    fileObject.close()\r\n    \r\n    # remove copied object\r\n    bpy.ops.object.delete()\r\n\r\n    # set focus back to original object\r\n    bpy.data.objects[src_obj.name].select_set(True)\r\n    ShowMessageBox( str(bpy.context.scene.GES_OT_Path.p_objexp) + \".kml saved.\") \r\n    \r\ndef rgb_to_hex(color):\r\n\r\n    strip_n_pad = lambda stp: str(stp[2:]).zfill(2) \r\n    zcol = \"\".join([strip_n_pad(hex(int(col * 255))) for col in color])\r\n    rcol = zcol[4:6] + zcol[2:4] + zcol[0:2] # earth format\r\n   \r\n    return rcol\r\n\r\ndef prettyPrint(element, level=0):\r\n    '''\r\n    Printing in elementTree requires a little massaging\r\n    Function taken from elementTree site:\r\n    http://effbot.org/zone/element-lib.htm#prettyprint\r\n\r\n    '''\r\n    indent = '\\n' + level * '  '\r\n    if len(element):\r\n        if not element.text or not element.text.strip():\r\n            element.text = indent + '  '\r\n\r\n        if not element.tail or not element.tail.strip():\r\n            element.tail = indent\r\n\r\n        for element in element:\r\n            prettyPrint(element, level + 1)\r\n\r\n        if not element.tail or not element.tail.strip():\r\n            element.tail = indent\r\n\r\n    else:\r\n        if level and (not element.tail or not element.tail.strip()):\r\n            element.tail = indent\r\n\r\n    return element\r\n\r\n    \r\ndef register():\r\n    bpy.utils.register_class(GES_PT_ImportPanel)\r\n    bpy.utils.register_class(GES_PT_KMLPanel)\r\n    bpy.utils.register_class(GES_PT_ObjectKMLPanel)\r\n    bpy.utils.register_class(GES_PT_MarkerPanel)\r\n    bpy.utils.register_class(GES_PT_InfoPanel)\r\n    bpy.utils.register_class(GES_OT_Path)\r\n    bpy.utils.register_class(preobjKML)\r\n    bpy.utils.register_class(preKML)\r\n    bpy.utils.register_class(preGES)\r\n    bpy.utils.register_class(preMarker)\r\n    bpy.utils.register_class(isvoid)\r\n    \r\n    bpy.types.Scene.GES_OT_Path = bpy.props.PointerProperty(type=GES_OT_Path)\r\n    \r\ndef unregister():\r\n    bpy.utils.unregister_class(GES_PT_ImportPanel)\r\n    bpy.utils.unregister_class(GES_PT_KMLPanel)\r\n    bpy.utils.unregister_class(GES_PT_ObjectKMLPanel)\r\n    bpy.utils.unregister_class(GES_PT_MarkerPanel)\r\n    bpy.utils.unregister_class(GES_PT_InfoPanel)\r\n    bpy.utils.unregister_class(GES_OT_Path)\r\n    bpy.utils.unregister_class(preobjKML)\r\n    bpy.utils.unregister_class(preKML)\r\n    bpy.utils.unregister_class(preGES)\r\n    bpy.utils.unregister_class(preMarker)\r\n    bpy.utils.unregister_class(isvoid)\r\n    \r\nif __name__ == \"__main__\":\r\n    register() "
  },
  {
    "path": "LICENSE",
    "content": "Modified MIT License\n\nCopyright (c) 2021 imagiscope\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\n1. Not for resale (use of software tools for revenue generation excluded).\n2. Credit to: \"https://www.youtube.com/c/ImagiscopeTech\"\n3. Notification of commercial use to author.\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# EarthStudioTools\nBlender Panel to import Google Earth Studio, KML Routes, and 3D Masking\n\nCheck out videos for usage: https://www.youtube.com/imagiscopetech\n\nRob\n"
  },
  {
    "path": "Version 1.2 update",
    "content": "Version 1.2.2:\n- Added code for new KML layout\n\nVersion 1.2.1:\n- fixed Marker Maker code\n\nVersion 1.2:\n- New 3D object to KML\n- Updated Marker Maker\n- various fixes and error control (for international users, deleting object, etc)\n- removed export KML (as 3D object to KML achieves better results)\n\nVersion 1.1:\n- NEW Trackpoint Marker Tool to generate Markers for Points of Interest based on a template you design (text will use name of trackpoint)\n- Update to KML importer - will now import KML files from Google My Maps\n- Improved messaging - alert on common error: when no trackpoints are in the JSON file, import will abort and message will be displayed to correct the issue (add trackpoint and export JSON file from GES).\n- Improvements for multiple KML files (fixes alignment issues)\n"
  }
]