Compare commits

..

2 Commits

Author SHA1 Message Date
zh
f52d928b9b Merge branch 'master' of http://xny.yj-3d.com:3000/zh/sdk4.0_new 2025-10-21 18:05:06 +08:00
zh
35010675e7 提交 2025-10-21 18:04:59 +08:00
10 changed files with 110 additions and 517 deletions

View File

@ -322,12 +322,13 @@ const close = () => {
// _DialogObject = null
// }
let contentElm = document.getElementsByClassName('fly-roam')[0]
if(contentElm) {
let tableBody = contentElm.getElementsByClassName('table-body')[0];
let trList = tableBody.getElementsByClassName('tr')
for (let i = trList.length - 1; i >= 0; i--) {
tableBody.removeChild(trList[i])
}
}
repeat = 0
currentRepeat = 0

View File

@ -28,8 +28,8 @@ class ArcgisLayer extends BaseLayer {
url
});
}
if (this.options.hasOwnProperty("layer_index")) {
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(imageryProvider, this.options.layer_index)
if (this.options.hasOwnProperty("layerIndex")) {
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(imageryProvider, this.options.layerIndex)
} else {
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(imageryProvider,)
}

View File

@ -25,8 +25,8 @@ class GdImagery extends BaseLayer {
tilingScheme: this.amapMercatorTilingScheme()
})
if (this.options.hasOwnProperty("layer_index")) {
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(gdLayer, this.options.layer_index)
if (this.options.hasOwnProperty("layerIndex")) {
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(gdLayer, this.options.layerIndex)
} else {
this.entity = this.sdk.viewer.imageryLayers.addImageryProvider(gdLayer,)
}

View File

@ -105,9 +105,9 @@ class Layer extends BaseLayer {
if (!this.sdk || !this.sdk.viewer) {
return
}
if (this.options.hasOwnProperty("layer_index")) {
if (this.options.hasOwnProperty("layerIndex")) {
this.entity =
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer, this.options.layer_index)
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer, this.options.layerIndex)
} else {
this.entity =
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer,)

View File

@ -30,9 +30,9 @@ class Layer3rdparty extends BaseLayer {
let layer
layer = new Cesium.UrlTemplateImageryProvider(params)
if (this.options.hasOwnProperty("layer_index")) {
if (this.options.hasOwnProperty("layerIndex")) {
this.entity =
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer, this.options.layer_index)
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer, this.options.layerIndex)
} else {
this.entity =
this.sdk.viewer.scene.imageryLayers.addImageryProvider(layer,)

View File

@ -73,7 +73,7 @@ class BaseLayer extends BaseSource {
this.sdk.viewer.imageryLayers.raiseToTop(layer)
}
}
this.options.layer_index = this.entity._layerIndex
this.options.layerIndex = this.entity._layerIndex
return this.entity._layerIndex
}
@ -84,7 +84,7 @@ class BaseLayer extends BaseSource {
* */
layerLower() {
this.sdk.viewer.imageryLayers.lower(this.entity)
this.options.layer_index = this.entity._layerIndex
this.options.layerIndex = this.entity._layerIndex
return this.entity._layerIndex
}
@ -101,7 +101,7 @@ class BaseLayer extends BaseSource {
this.sdk.viewer.imageryLayers.raiseToTop(layer)
}
}
this.options.layer_index = this.entity._layerIndex
this.options.layerIndex = this.entity._layerIndex
return this.entity._layerIndex
}
@ -112,7 +112,7 @@ class BaseLayer extends BaseSource {
* */
layerToBottom() {
this.sdk.viewer.imageryLayers.lowerToBottom(this.entity)
this.options.layer_index = this.entity._layerIndex
this.options.layerIndex = this.entity._layerIndex
return this.entity._layerIndex
}

View File

@ -42,8 +42,8 @@ class Vector extends Base {
return
}
this.colors = colors
this.options.head_tables = options.head_tables || []
this.options.fileName = options.fileName || '未命名对象'
this.options.headTables = options.headTables || []
this.options.name = options.name || '未命名对象'
if (!this.options.path.endsWith('.kml')) {
; (this.options.color = options.color || 'rgba(0,255,184,0.5)'),
@ -120,34 +120,34 @@ class Vector extends Base {
}
async init() {
return new Promise(async (resolve, reject) => {
let url = ''
this.options.host = this.options.host || getHost()
if (this.options.host.endsWith('yjearth4.0')) {
url = this.options.host + '/api/v1/vector/load2'
} else {
url = this.options.host + '/yjearth4.0/api/v1/vector/load2'
}
url += '?path=' + this.options.path
url = this.options.host + '/gdal/import'
// url = 'json/shp.json'
let params = new FormData()
params.append('path', this.options.path)
let response = await fetch(url, {
method: 'get',
method: 'post',
headers: {
'Content-Type': 'application/json',
token: getToken(),
Authorization: 'Bearer ' + getToken()
}
Authorization: getToken()
},
body: params
})
console.log('response', response)
if (response.status === 200) {
let arrayBuffer = await response.arrayBuffer()
let uint8Array = new Uint8Array(arrayBuffer)
let string = this.decompressGzip(uint8Array)
if(this.data.length===0)
{
if (this.data.length === 0) {
this.data = JSON.parse(string)
}
console.log('this.data', this.data)
await this.formatData()
return Vector.create(this)
Vector.create(this)
resolve()
}
});
}
// 格式化数据
@ -195,16 +195,16 @@ class Vector extends Base {
}
for (let m = 0; m < this.data.list[i].features.length; m++) {
this.data.list[i].features[m].properties.id
if(!this.data.list[i].features[m].properties) {
if (!this.data.list[i].features[m].properties) {
this.data.list[i].features[m].properties = {}
}
if(!this.data.list[i].features[m].properties.id) {
if (!this.data.list[i].features[m].properties.id) {
this.data.list[i].features[m].properties.id = Cesium.createGuid()
}
this.data.list[i].features[m].id = this.data.list[i].features[m].properties.id
posConvert(
this.data.list[i].features[m].geometry,
this.data.list[i].crs_src,
this.data.list[i].crs_src || '+proj=longlat +datum=WGS84 +no_defs',
this.data.list[i].crs_dst || '+proj=longlat +datum=WGS84 +no_defs'
)
this.data.list[i].features[m].geometry.geometries ||
@ -231,12 +231,12 @@ class Vector extends Base {
// }
}
get fileName() {
return this.options.fileName
get name() {
return this.options.name
}
set fileName(v) {
this.options.fileName = v
this._elms.fileName && (this._elms.fileName.value = v)
set name(v) {
this.options.name = v
this._elms.name && (this._elms.name.value = v)
}
get field() {
@ -252,9 +252,9 @@ class Vector extends Base {
// }
// }
let label = v
for (let index = 0; index < this.options.head_tables.length; index++) {
if (this.options.head_tables[index].key === v) {
label = this.options.head_tables[index].label
for (let index = 0; index < this.options.headTables.length; index++) {
if (this.options.headTables[index].key === v) {
label = this.options.headTables[index].label
break
}
}
@ -348,13 +348,8 @@ class Vector extends Base {
) {
let url = ''
that.options.host = that.options.host || getHost()
if (that.options.host.endsWith('yjearth4.0')) {
url = that.options.host + '/api/v1/vector/getKml'
} else {
url = that.options.host + '/yjearth4.0/api/v1/vector/getKml'
}
url = that.options.host + '/fileInfo/previewLocal'
url += '?path=' + that.options.path
// url = 'json/shp.json'
fetch(url, {
method: 'get',
headers: {
@ -734,17 +729,17 @@ class Vector extends Base {
left: '180px',
top: '100px',
confirmCallBack: options => {
this.fileName = this.fileName.trim()
if (!this.fileName) {
this.fileName = '未命名对象'
this.name = this.name.trim()
if (!this.name) {
this.name = '未命名对象'
}
this.originalOptions.fileName = this.fileName
this.originalOptions.name = this.name
this.originalOptions.field = this.field
this._DialogObject.close()
DialogEvent.confirmCallBack &&
DialogEvent.confirmCallBack({
id: this.options.id,
fileName: this.originalOptions.fileName,
name: this.originalOptions.name,
field: this.originalOptions.field
})
},
@ -772,17 +767,17 @@ class Vector extends Base {
this._DialogObject.contentAppChild(contentElm)
let nameElm = contentElm.getElementsByClassName('flie-name')[0]
nameElm.value = this.options.fileName
nameElm.value = this.options.name
nameElm.addEventListener('input', () => {
this.options.fileName = nameElm.value
this.options.name = nameElm.value
})
let keyData = []
for (let key in this.geojson.features[0].properties) {
let label = key
for (let index = 0; index < this.options.head_tables.length; index++) {
if (this.options.head_tables[index].key === key) {
label = this.options.head_tables[index].label
for (let index = 0; index < this.options.headTables.length; index++) {
if (this.options.headTables[index].key === key) {
label = this.options.headTables[index].label
break
}
}
@ -820,7 +815,7 @@ class Vector extends Base {
})
this._elms.field = keyDataLegpElm
}
this._elms.fileName = nameElm
this._elms.name = nameElm
return
@ -833,9 +828,9 @@ class Vector extends Base {
for (let key in this.geojson.features[0].properties) {
let label = key
for (let index = 0; index < this.options.head_tables.length; index++) {
if (this.options.head_tables[index].key === key) {
label = this.options.head_tables[index].label
for (let index = 0; index < this.options.headTables.length; index++) {
if (this.options.headTables[index].key === key) {
label = this.options.headTables[index].label
break
}
}
@ -848,7 +843,7 @@ class Vector extends Base {
this.options.field = selectElm.value
})
this._elms.fileName = nameElm
this._elms.name = nameElm
this._elms.field = selectElm
} else {
if (this._DialogObject && this._DialogObject.close) {
@ -858,255 +853,6 @@ class Vector extends Base {
}
}
async editById(status = false, nodeId, DialogEvent = {}) {
// nodeId = this.dataArray[0].attr.id
if (this._DialogObject && this._DialogObject.close) {
this._DialogObject.close()
this._DialogObject = null
}
let cameraName = ''
let index
if (status) {
let options = {}
for (let i = 0; i < this.geojson.features.length; i++) {
if (this.geojson.features[i].id === nodeId) {
if (this.geojson.features[i].content) {
options = this.deepCopyObj(this.geojson.features[i].content)
}
index = i
break
}
}
options.id = options.id || nodeId
options.link || (options.link = {})
options.link.content || (options.link.content = [])
options.camera || (options.camera = [])
options.richTextContent || (options.richTextContent = '')
options.attributeType = options.attributeType || 'richText'
this._DialogObject = await new Dialog(this.sdk, options, {
title: '编辑属性',
left: '180px',
top: '100px',
confirmCallBack: () => {
this._DialogObject.close()
this.geojson.features[index].content = this.deepCopyObj(
this._DialogObject.attribute
)
DialogEvent.confirmCallBack &&
DialogEvent.confirmCallBack(this._DialogObject.attribute)
},
closeCallBack: () => {
// this.reset()
DialogEvent.closeCallBack && DialogEvent.closeCallBack()
}
})
this._DialogObject.event = DialogEvent
this._DialogObject.attribute = this.deepCopyObj(options)
let html = `
<div class="row">
<div class="col">
<span class="label">内容类型:</span>
<select class="input input-select attribute-select" style="width: 120px;">
<option value="richText">富文本</option>
<!--<option value="link">链接</option>
<option value="camera">摄像头</option>
<option value="sensor">传感器</option>
<option value="vr">全景图</option>-->
</select>
</div>
<div class="col attribute-content attribute-content-link">
<div class="input-group">
<input class="input link_add" type="text">
<button class="link_add_btn">+</button>
</div>
</div>
</div>
<div class="attribute-content attribute-content-richText">
<span>编辑内容:<button class="open-richText-btn">打开文本编辑器</button></span>
</div>
<div class="attribute-content attribute-content-link">
<div class="table">
<div class="table-head">
<div class="tr">
<div class="th">名称</div>
<div class="th">链接</div>
<div class="th">操作</div>
</div>
</div>
<div class="table-body">
</div>
<div class="table-empty">
<div class="empty-img"></div>
<p>暂无数据</p>
</div>
</div>
</div>
<div class="attribute-content attribute-content-camera">
<div class="row">
<div class="col">
<span class="label">编辑内容:</span>
<input class="input camera-name" type="text" style="width: 100px;">
<button class="select btn camera-select">搜索</button>
</div>
</div>
<div>
<div class="table camera-table">
<div class="table-head">
<div class="tr">
<div class="th">操作</div>
<div class="th">设备名称</div>
<div class="th" style="width: 80px; flex: 0 80px;min-width: 80px;">设备类型</div>
<div class="th" style="width: 126px; flex: 0 126px;min-width: 126px;">设备IP</div>
<div class="th" style="width: 80px; flex: 0 80px;min-width: 80px;">设备端口</div>
<div class="th" style="width: 80px; flex: 0 80px;min-width: 80px;">用户名</div>
<div class="th">密码</div>
</div>
</div>
<div class="table-body" style="display:none;">
<div class="tr">
<div class="td">
<input type="checkbox" value="2">
<span>绑定</span>
</div>
<div class="td">设备名称</div>
<div class="td">设备类型</div>
<div class="td">设备IP</div>
<div class="td">设备端口</div>
<div class="td">用户名</div>
<div class="td">密码</div>
</div>
</div>
<div class="table-empty">
<div class="empty-img"></div>
<p>暂无数据</p>
</div>
</div>
</div>
<div class=""row>
<ul class="pagination"></ul>
</div>
</div>
`
let contentElm = document.createElement('div')
contentElm.innerHTML = html
this._DialogObject.contentAppChild(contentElm)
let all_elm = contentElm.getElementsByTagName('*')
this._DialogObject._element.body.style.width = '600px'
let attributeSelectElm = contentElm.getElementsByClassName(
'attribute-select'
)[0]
let linkAddBtnElm = contentElm.getElementsByClassName('link_add_btn')[0]
let openRichTextBtnElm = contentElm.getElementsByClassName(
'open-richText-btn'
)[0]
let attributeContent = this._DialogObject._element.content.getElementsByClassName(
'attribute-content'
)
for (let i = 0; i < attributeContent.length; i++) {
if (
attributeContent[i].className.indexOf(
'attribute-content-' + this._DialogObject.attribute.attributeType
) > -1
) {
attributeContent[i].style.display = 'block'
} else {
attributeContent[i].style.display = 'none'
}
}
attributeSelectElm.addEventListener('change', () => {
this._DialogObject.attribute.attributeType = attributeSelectElm.value
for (let i = 0; i < attributeContent.length; i++) {
if (
attributeContent[i].className.indexOf(
'attribute-content-' + this._DialogObject.attribute.attributeType
) > -1
) {
attributeContent[i].style.display = 'block'
} else {
attributeContent[i].style.display = 'none'
}
}
})
linkAddBtnElm.addEventListener('click', async () => {
if (
this._DialogObject._element.content.getElementsByClassName(
'link_add'
)[0].value
) {
this._DialogObject.attribute.link.content.push({
name: '链接',
url: this._DialogObject._element.content.getElementsByClassName(
'link_add'
)[0].value
})
this._DialogObject._element.content.getElementsByClassName(
'link_add'
)[0].value = ''
this.setAttributeLinkById(
nodeId,
this._DialogObject.attribute.link.content
)
} else {
DialogEvent.clickAddLink && DialogEvent.clickAddLink(nodeId)
}
})
openRichTextBtnElm.addEventListener('click', () => {
richText.open(
nodeId,
nodeId,
this._DialogObject.attribute.richTextContent
)
richText.primaryCallBack = content => {
this._DialogObject.attribute.richTextContent = content
}
})
let cameraNameElm = contentElm.getElementsByClassName('camera-name')[0]
let cameraSelectElm = contentElm.getElementsByClassName(
'camera-select'
)[0]
cameraNameElm.addEventListener('input', () => {
cameraName = cameraNameElm.value
})
this.cameraSelect && this.cameraSelect(cameraName)
cameraSelectElm.addEventListener('click', () => {
this.cameraSelect && this.cameraSelect(cameraName)
})
// let nameElm = contentElm.getElementsByClassName('flie-name')[0]
// nameElm.value = this.options.fileName
// nameElm.addEventListener('input', ()=>{
// this.options.fileName = nameElm.value
// })
// let selectElm = contentElm.getElementsByClassName('input-select')[0]
// let option = ''
// for(let key in this.dataArray[0].attr) {
// let o = `<option value="${key}">${key}</option>`
// option = option + o
// }
// selectElm.innerHTML = option
// selectElm.value = this.options.field
// selectElm.addEventListener('input', ()=>{
// this.options.field = selectElm.value
// })
// this._elms.fileName = nameElm
// this._elms.field = selectElm
} else {
if (this._DialogObject && this._DialogObject.close) {
this._DialogObject.close()
this._DialogObject = null
}
}
}
editContentById(nodeId, content) {
for (let i = 0; i < this.geojson.features.length; i++) {
if (this.geojson.features[i].id === nodeId) {
@ -1117,7 +863,7 @@ class Vector extends Base {
}
reset() {
this.fileName = this.originalOptions.fileName
this.name = this.originalOptions.name
this.field = this.originalOptions.field
}
@ -1332,7 +1078,7 @@ class Vector extends Base {
return trsElm
}
flyTo(id, options = {}) {
async flyTo(id, options = {}) {
setActiveViewer(0)
closeRotateAround(this.sdk)
closeViewFollow(this.sdk)
@ -1392,6 +1138,7 @@ class Vector extends Base {
}
}
} else {
if (this.range) {
if (
this.options.customView &&
this.options.customView.relativePosition &&
@ -1408,17 +1155,17 @@ class Vector extends Base {
this.options.customView.orientation.roll || 0.0
)
}
let lng = this.options.customView.relativePosition.lng
let lat = this.options.customView.relativePosition.lat
let alt = this.options.customView.relativePosition.alt
let position = { lng: this.range[0], lat: this.range[1] }
position.alt = await this.getClampToHeight(position)
let lng = this.options.customView.relativePosition.lng + position.lng
let lat = this.options.customView.relativePosition.lat + position.lat
let alt = this.options.customView.relativePosition.alt + position.alt
let destination = Cesium.Cartesian3.fromDegrees(lng, lat, alt)
this.sdk.viewer.camera.flyTo({
destination: destination,
orientation: orientation
})
} else {
if (this.range) {
let array = []
this.getClampToHeight({
lng: this.range[0],
@ -1537,164 +1284,6 @@ class Vector extends Base {
this.imgEntity = []
}
cameraSelect(cameraName, page) {
const type = {
'1': '海康',
'2': '大华'
}
let host = this.options.host
if (
this._DialogObject &&
this._DialogObject._element &&
this._DialogObject._element.content
) {
let paginationElm = that._DialogObject._element.content.getElementsByClassName(
'pagination'
)[0]
let attributeCameraElm = that._DialogObject._element.content.getElementsByClassName(
'attribute-content-camera'
)[0]
if (!paginationElm) {
return
}
if (!attributeCameraElm) {
return
}
generatePagination(paginationElm, 1, 10, 1)
} else {
return
}
let url = ''
const params = {
cameraName: cameraName,
page: !page || typeof page === 'object' ? 1 : page,
pageSize: 5
}
const queryString = new URLSearchParams(params).toString()
if (host.endsWith('yjearth4.0'))
url = `${host}/api/v1/cameraData/list?${queryString}`
else url = `${host}/yjearth4.0/api/v1/cameraData/list?${queryString}`
// url = this.options.path
fetch(url, {
method: 'get',
headers: {
'Content-Type': 'application/json',
token: getToken(),
Authorization: 'Bearer ' + getToken()
}
}).then(response => {
if (response.status === 200) {
response.json().then(data => {
if (data.code === 200 || data.code === 0) {
if (
!this._DialogObject ||
!this._DialogObject._element ||
!this._DialogObject._element.content
) {
return
}
let paginationElm = this._DialogObject._element.content.getElementsByClassName(
'pagination'
)[0]
let table = this._DialogObject._element.content.getElementsByClassName(
'camera-table'
)[0]
let tableContent = table.getElementsByClassName('table-body')[0]
tableContent.innerHTML = ''
if (data.data) {
if (data.data.list && data.data.list.length > 0) {
generatePagination(
paginationElm,
data.data.total,
10,
1,
pageIndex => {
this.cameraSelect &&
this.cameraSelect(cameraName, pageIndex)
}
)
table.getElementsByClassName('table-empty')[0].style.display =
'none'
tableContent.style.display = 'inline-flex'
for (let i = 0; i < data.data.list.length; i++) {
let tr = `
<div class="tr">
<div class="td">
<input type="checkbox" value="${data.data.list[i].ID}">
<span>绑定</span>
</div>
<div class="td">${data.data.list[i].cameraName}</div>
<div class="td" style="width: 80px; flex: 0 80px; min-width: 80px;">${type[data.data.list[i].type]
}</div>
<div class="td" style="width: 126px; flex: 0 126px;min-width: 126px;">${data.data.list[i].ip
}</div>
<div class="td" style="width: 80px; flex: 0 80px;min-width: 80px;">${data.data.list[i].port
}</div>
<div class="td" style="width: 80px; flex: 0 80px;min-width: 80px;">${data.data.list[i].userName
}</div>
<div class="td">${data.data.list[i].passWord}</div>
</div>`
let trElm = document
.createRange()
.createContextualFragment(tr)
let checkbox = trElm.querySelector('input[type="checkbox"]')
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
this._DialogObject.attribute.camera.push(
data.data.list[i]
)
} else {
let newArray = this._DialogObject.attribute.camera.filter(
item => {
return item.ID !== data.data.list[i].ID
}
)
this._DialogObject.attribute.camera = newArray
}
})
tableContent.appendChild(trElm)
for (
let m = 0;
m < this._DialogObject.attribute.camera.length;
m++
) {
if (
this._DialogObject.attribute.camera[m].ID ===
data.data.list[i].ID
) {
checkbox.checked = true
break
}
}
}
}
if (data.data && data.data.total) {
generatePagination(
paginationElm,
data.data.total,
10,
1,
pageIndex => {
this.cameraSelect &&
this.cameraSelect(cameraName, pageIndex)
}
)
}
} else {
generatePagination(paginationElm, 1, 10, 1)
table.getElementsByClassName('table-empty')[0].style.display =
'flex'
tableContent.style.display = 'none'
}
} else {
console.error(data.message)
}
})
}
})
}
load(callback) {
if (this.#loaded) {
callback()

View File

@ -168,6 +168,7 @@ class YJEarth {
this.viewer = new Cesium.Viewer(this.div_id, this.options)
this.viewer.scene.imageryLayers._layers[0].notes = 'default-base-map'
this.viewer._shadows = this.viewer.shadows
this.viewer._container.style.display = 'flex'
this.viewer.scene.screenSpaceCameraController.maximumZoomDistance = 50000000

View File

@ -17,7 +17,8 @@ function on(
hls: false,
host: '',
username: '',
password: ''
password: '',
token: ''
}
) {
// window.THREE = THREE
@ -126,6 +127,7 @@ function on(
resolve()
})
} else {
setToken(options.token)
resolve()
}
}

View File

@ -20096,8 +20096,8 @@
var testing =
'<div class="compass" title="拖动外圈:旋转视图,' +
'拖动内陀螺仪:自由轨道,' +
'双击:重置视图' +
'提示您还可以按住CTRL键并拖动地图来释放轨道." data-bind="visible: showCompass, event: { mousedown: handleMouseDown, dblclick: handleDoubleClick }">' +
'双击:重置视图' +
'" data-bind="visible: showCompass, event: { mousedown: handleMouseDown, dblclick: handleDoubleClick }">' +
'<div class="compass-outer-ring-background"></div>' +
" <div class=\"compass-rotation-marker\" data-bind=\"visible: isOrbiting, style: { transform: 'rotate(-' + orbitCursorAngle + 'rad)', '-webkit-transform': 'rotate(-' + orbitCursorAngle + 'rad)', opacity: orbitCursorOpacity }, cesiumSvgPath: { path: svgCompassRotationMarker, width: 145, height: 145 }\"></div>" +
" <div class=\"compass-outer-ring\" title=\"单击并拖动以旋转相机\" data-bind=\"style: { transform: 'rotate(-' + heading + 'rad)', '-webkit-transform': 'rotate(-' + heading + 'rad)' }, cesiumSvgPath: { path: svgCompassOuterRing, width: 145, height: 145 }\"></div>" +