天天看點

Krpano學習:在C#中修改全景場景屬性(C#操作全景vtour.xml檔案及相關瓦片資料/panos/*.tiles)

  搜尋在C#中操作xml可找到很多相關教程,此處就不細說了。krpano的全景xml檔案和傳統xml檔案的不同僅在于它們的根不一樣,vtour.xml檔案的根為:

<krpano version="1.19" title="">
</krpano>
           

  每個全景一般都包含有include、skin_settings、action這三個子元素,之後才是場景scene,scene裡有自己的屬性,還有view、hotspot等子元素。

  還需要明确的是,每個場景都一一個唯一名稱,原始名稱為圖檔名(沒有字尾),在xml檔案裡,name訓示這個唯一名稱,在panos瓦片檔案夾裡,每個場景瓦片的檔案夾名則為這個唯一名稱,加入全景照片為123.jpg,則:

//場景名稱,唯一
//注意如果不要scene,則場景名稱不能以數字開頭,但如果去掉scene,則很多地方也要跟着修改(同樣,熱點名稱也不能以數字開頭)
name="scene_123" 
//在浏覽場景時顯示的名稱,可以修改
title="123"
//瓦片檔案夾名稱,後面會加上.tiles
“123.tiles”
           

  下面來看具體的操作:

在進行操作前要引用命名空間

加載xml檔案及儲存

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlpath); //xml絕對路徑 加載xml檔案

RenameScene(xmlDoc, "name1", "title1"); //修改title
MoveScene(xmlDoc, ["name1", "name2"]); //更改場景順序
DeleteScene(xmlDoc, "name1"); //删除場景
RaiseScene(xmlDoc, ["name1", "name2"], "raisename", "after"); //添加場景
ReplaceScene(xmlDoc, ["name1", "name2"]); //替換場景
SetView(xmlDoc, "name1", "10", "20.5"); //設定進入場景時的初始視角

xmlDoc.Save(xmlpath); //儲存xml檔案
           

修改場景顯示名稱:操作xml檔案

/// <summary>
/// 修改場景顯示名稱title
/// </summary>
/// <param name="xmlDoc">xml檔案</param>
/// <param name="sceneid">場景唯一名稱name</param>
/// <param name="scenename">scenename</param>
/// <returns>成功/失敗</returns>
private string RenameScene(XmlDocument xmlDoc, string sceneid, string scenename)
{
    try
    {
        XmlNode renameNode = xmlDoc.DocumentElement.SelectSingleNode("/krpano/scene[@name='scene_" + sceneid + "']");
        XmlElement element = (XmlElement)renameNode;
        element.SetAttribute("title", scenename);
        return "success";
    }
    catch (Exception ex)
    {
        return "failed";
    }
}
           

更換場景順序:操作xml檔案

/// <summary>
/// 更換場景順序:根據新順序将要排放的場景結點放到棧或者隊列中,然後重新排位置,注意棧和隊列的出入順序
/// </summary>
/// <param name="xmlDoc">xml檔案</param>
/// <param name="sceneid">按新順序排列的場景名稱</param>
/// <returns>成功/失敗</returns>
public string MoveScene(XmlDocument xmlDoc, string[] sceneid)
{
    try
    {
        XmlNode rootNode = xmlDoc.SelectSingleNode("/krpano/action").ParentNode;
        Stack<XmlNode> tourNode = new Stack<XmlNode>();
        for (int len=sceneid.Length, i = len - 1; i > -1; i--)
        {
            XmlNode moveNode = xmlDoc.DocumentElement.SelectSingleNode("/krpano/scene[@name='scene_" + sceneid[i] + "']");
            tourNode.Push(moveNode);
            rootNode.RemoveChild(moveNode);
        }
        while (tourNode.Count() > 0)
        {
            rootNode.AppendChild(tourNode.Pop());
        }
        return "success";
    }
    catch (Exception ex)
    {
        return "failed";
    }
}
           

删除場景:操作xml檔案、panos/*.tiles檔案夾

/// <summary>
/// 删除場景
/// </summary>
/// <param name="xmlDoc">xml檔案</param>
/// <param name="sceneid">場景名稱</param>
/// <returns>成功/失敗</returns>
public string DeleteScene(XmlDocument xmlDoc, string sceneid)
{
    try
    {
        XmlNode selectNode = xmlDoc.SelectSingleNode("/krpano/scene[@name='scene_" + sceneid + "']");
        selectNode.ParentNode.RemoveChild(selectNode);
        //删除場景對應的瓦片圖檔檔案夾,注意路徑
        //後面有“true”,表示删除這個檔案夾及其子目錄
        Directory.Delete("../panos/" + sceneid + ".tiles", true);
        return "success";
    }
    catch (Exception ex)
    {
        return "failed";
    }
}
           

添加場景:操作xml檔案、panos/*.tiles檔案夾

/// <summary>
/// 添加場景:把其他全景裡的一些場景添加到這個場景中來
/// </summary>
/// <param name="xmlDoc">xml檔案</param>
/// <param name="sceneid">要添加的場景的名稱</param>
/// <param name="sceneseat">參考位置的場景名稱:要添加在這個場景前後</param>
/// <param name="sceneab">before/after添加在參考位置的前面或者後面</param>
/// <returns>成功/失敗</returns>
public string RaiseScene(XmlDocument xmlDoc, string[] sceneid, string sceneseat, string sceneab)
{
    int len = sceneid.Length
    //被添加場景所在的xml檔案
    XmlDocument xmlDoc_move = new XmlDocument();
    xmlDoc_move.Load(xml_movepath);
    try
    {
        //移動場景對應的瓦片圖檔檔案夾
        //此處我移動新全景裡所有檔案,如果隻是特定場景可以根據場景名稱找到對應瓦片然後移動
        int count = Directory.GetDirectories("../panos").Length;
        List<string> folders = new List<string>(Directory.GetDirectories("../panos"));
        for (int i = 0; i < count; i++)
        {
            string realtile = "../panos/" + Path.GetFileName(folders[i]);
            if (!Directory.Exists(realtile))
                Directory.Move(folders[i], realtile);
            else
                return;
        }
        
        //添加其實是将場景結點從一個xml檔案移動到另一個xml檔案
        //由于棧的出入規定,這個地方不用棧用連結清單要更容易懂些
        Stack<XmlNode> tourNode = new Stack<XmlNode>();
        for (int i = len - 1; i > -1; i++)
        {
            //如果場景在原xml中不是按順序排列,則用第一句,按場景name添加進棧
            //如果場景在原xml中按順序排列,則可用第二句,直接按順序添加
            tourNode.Push(xmlDoc_move.DocumentElement.SelectSingleNode("/krpano/scene[@name='scene_" + sceneid[i] + "']");
            tourNode.Push(xmlDoc_move.DocumentElement.SelectSingleNode("/krpano/scene[last()-" + i + "]"));
        }
        //直接加在xml檔案末尾,目前順序為倒序添加,如果是正序,則上面的循環要從i=0開始
        while (len > 0)
        {
            //從其他xml檔案引用結點要用import,類似的從一個datatable引用行列到另一個datatable也要用DataTable.ImportRow()等
            XmlNode markNode = xmlDoc.ImportNode(tourNode.Pop(), true);
            xmlDoc.DocumentElement.AppendChild(markNode);
            len--;
        }
        //添加在指定場景的前面或者後面,按原始順序排列
        XmlNode rootNode = xmlDoc.SelectSingleNode("/krpano/action").ParentNode;
        XmlNode seatNode = xmlDoc.DocumentElement.SelectSingleNode("/krpano/scene[@name='scene_" + sceneseat + "']");
        if (sceneab == "before")
        {
            while (tourNode.Count() > 0)
            {
	            XmlNode markNode = xmlDoc.ImportNode(tourNode.Pop(), true);
                rootNode.InsertBefore(markNode, seatNode);
            }
        }
        else
        {
            while (tourNode.Count() > 0)
            {
	            XmlNode markNode = xmlDoc.ImportNode(tourNode.Pop(), true);
                rootNode.InsertAfter(markNode, seatNode);
            }
        }
        return "success";
    }
    catch (Exception ex)
    {
        return "failed";
    }
}
           

替換場景:操作xml檔案、panos/*.tiles檔案夾

/// <summary>
/// 替換場景
/// </summary>
/// <param name="xmlDoc">xml檔案</param>
/// <param name="sceneid">被替換的場景的名稱</param>
/// <returns>成功/失敗</returns>
public string ReplaceScene(XmlDocument xmlDoc, string[] sceneid)
{
    int len = sceneid.Length
    //被添加場景所在的xml檔案
    XmlDocument xmlDoc_move = new XmlDocument();
    xmlDoc_move.Load(xml_movepath);
    try
    {
        Stack<XmlNode> tourNode = new Stack<XmlNode>();
        XmlNode rootNode = xmlDoc.SelectSingleNode("/krpano/action").ParentNode;
        //用其他xml檔案裡的場景替換目前xml檔案中的場景
        for (int i = len - 1; i > -1; i++)
        {
            //如果場景在原xml中不是按順序排列,則用第一句,按場景name添加進棧
            //如果場景在原xml中按順序排列,則可用第二句,直接按順序添加
            //注意場景入棧順序應和sceneid内被替換順序的反序一一對應,也即出棧順序和sceneid順序一一對應
            tourNode.Push(xmlDoc_move.DocumentElement.SelectSingleNode("/krpano/scene[@name='scene_" + sceneid[i] + "']");
            tourNode.Push(xmlDoc_move.DocumentElement.SelectSingleNode("/krpano/scene[last()-" + i + "]"));
        }
        for (int i = 0; i < len; i++)
        {
            XmlNode markNode = xmlDoc.ImportNode(tourNode.Pop(), true);
            rootNode.ReplaceChild(markNode, xmlDoc.DocumentElement.SelectSingleNode("/krpano/scene[@name='scene_" + sceneid[i] + "']"));                    
        }
        //移動替換場景瓦片,删除被替換場景瓦片
        int count = Directory.GetDirectories("../panos").Length;
        List<string> folders = new List<string>(Directory.GetDirectories("../panos"));
        for (int i = 0; i < count; i++)
        {
            string realtile = "../panos/" + Path.GetFileName(folders[i]);
            if (!Directory.Exists(realtile))
                Directory.Move(folders[i], realtile);
            else
                return;
        }
        for (int i = 0; i < len; i++)
        {
            Directory.Delete("../panos/" + sceneid[i] + ".tiles", true); 
        }

        //同一個xml檔案裡場景替換
        for (int i = 0; i < len; i++)
        {
            //假設替換場景為最後的幾個場景,如果是特定場景用SelectSingleNode標明
            XmlNode moveNode = rootNode.LastChild;
            XmlElement element = (XmlElement)moveNode;
            tourNode.Push(moveNode);
            rootNode.RemoveChild(moveNode);
        }
        for (int i = 0; i < len; i++)
        {
            rootNode.ReplaceChild(tourNode.Pop(), xmlDoc.DocumentElement.SelectSingleNode("/krpano/scene[@name='scene_" + array[i] + "']"));
            Directory.Delete("../panos/" + sceneid[i] + ".tiles", true); 
        }
        return "success";
    }
    catch (Exception ex)
    {
        return "failed";
    }
}
           

設定場景初始視角:操作xml檔案

/// <summary>
/// 設定場景初始視角
/// </summary>
/// <param name="markid">markid</param>
/// <param name="sceneid">sceneid</param>
/// <param name="viewhlookat">viewhlookat</param>
/// <param name="viewvlookat">viewvlookat</param>
/// <returns>成功/失敗</returns>
public string SetView(XmlDocument xmlDoc, string sceneid, string viewhlookat, string viewvlookat)
{
    try
    {
        XmlNode reviseNode = xmlDoc.DocumentElement.SelectSingleNode("/krpano/scene[@name='" + sceneid + "']").FirstChild;
        XmlElement element = (XmlElement)reviseNode;
        element.SetAttribute("hlookat", viewhlookat);
        element.SetAttribute("vlookat", viewvlookat);
        xmlDoc.Save(markidxmlpath);
        return "success";
    }
    catch (Exception ex)
    {
        return "failed";
    }
}
           

附錄:用兩張全景照片在MAKE VTOUR (NORMAL) droplet.bat上生成全景的xml檔案

<krpano version="1.19" title="Virtual Tour">
	<include url="skin/vtourskin.xml" />
	<!-- customize skin settings: maps, gyro, webvr, thumbnails, tooltips, layout, design, ... -->
	<skin_settings maps="false"
	               maps_type="google"
	               maps_bing_api_key=""
	               maps_google_api_key=""
	               maps_zoombuttons="false"
	               gyro="true"
	               webvr="true"
	               webvr_gyro_keeplookingdirection="false"
	               webvr_prev_next_hotspots="true"
	               littleplanetintro="false"
	               title="true"
	               thumbs="true"
	               thumbs_width="120" thumbs_height="80" thumbs_padding="10" thumbs_crop="0|40|240|160"
	               thumbs_opened="false"
	               thumbs_text="false"
	               thumbs_dragging="true"
	               thumbs_onhoverscrolling="false"
	               thumbs_scrollbuttons="false"
	               thumbs_scrollindicator="false"
	               thumbs_loop="false"
	               tooltips_buttons="false"
	               tooltips_thumbs="false"
	               tooltips_hotspots="false"
	               tooltips_mapspots="false"
	               deeplinking="false"
	               loadscene_flags="MERGE"
	               loadscene_blend="OPENBLEND(0.5, 0.0, 0.75, 0.05, linear)"
	               loadscene_blend_prev="SLIDEBLEND(0.5, 180, 0.75, linear)"
	               loadscene_blend_next="SLIDEBLEND(0.5,   0, 0.75, linear)"
	               loadingtext="loading..."
	               layout_width="100%"
	               layout_maxwidth="814"
	               controlbar_width="-24"
	               controlbar_height="40"
	               controlbar_offset="20"
	               controlbar_offset_closed="-40"
	               controlbar_overlap.no-fractionalscaling="10"
	               controlbar_overlap.fractionalscaling="0"
	               design_skin_images="vtourskin.png"
	               design_bgcolor="0x2D3E50"
	               design_bgalpha="0.8"
	               design_bgborder="0"
	               design_bgroundedge="1"
	               design_bgshadow="0 4 10 0x000000 0.3"
	               design_thumbborder_bgborder="3 0xFFFFFF 1.0"
	               design_thumbborder_padding="2"
	               design_thumbborder_bgroundedge="0"
	               design_text_css="color:#FFFFFF; font-family:Arial;"
	               design_text_shadow="1"
	               />

	<!-- startup action - load the first scene -->
	<action name="startup" autorun="onstart">
		if(startscene === null OR !scene[get(startscene)], copy(startscene,scene[0].name); );
		loadscene(get(startscene), null, MERGE);
		if(startactions !== null, startactions() );
	</action>

	<scene name="scene_Ghm1_color" title="Ghm1_color" onstart="" thumburl="panos/Ghm1_color.tiles/thumb.jpg" lat="" lng="" heading="">
		<view hlookat="0" vlookat="0" fovtype="MFOV" fov="120" maxpixelzoom="2.0" fovmin="70" fovmax="140" limitview="range" vlookatmin="-54.349" vlookatmax="54.349" />
		<preview url="panos/Ghm1_color.tiles/preview.jpg" />
		<image>
			<cube url="panos/Ghm1_color.tiles/pano_%s.jpg" />
			<cube url="panos/Ghm1_color.tiles/mobile/pano_%s.jpg" devices="mobile" />
		</image>
		<!-- place your scene hotspots here -->
	</scene>

	<scene name="scene_Ghm2_color" title="Ghm2_color" onstart="" thumburl="panos/Ghm2_color.tiles/thumb.jpg" lat="" lng="" heading="">
		<view hlookat="0" vlookat="0" fovtype="MFOV" fov="120" maxpixelzoom="2.0" fovmin="70" fovmax="140" limitview="range" vlookatmin="-54.349" vlookatmax="54.349" />
		<preview url="panos/Ghm2_color.tiles/preview.jpg" />
		<image>
			<cube url="panos/Ghm2_color.tiles/pano_%s.jpg" />
			<cube url="panos/Ghm2_color.tiles/mobile/pano_%s.jpg" devices="mobile" />
		</image>
		<!-- place your scene hotspots here -->
	</scene>
</krpano>