搜尋在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>