Files
PM_test/libs/gsimViewer/main.js
2026-06-12 17:14:03 +09:00

4485 lines
193 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// import {HMCesium} from '../../hmCesium.js';
// import {HMCesium} from 'http://172.16.42.101:9000/api/hmCesium/hmCesium.min.js';
// import {HMCesium} from 'http://172.16.42.101:5151/data/lib2_origin/hmCesium.min.js';
// import {HMCesium} from 'https://api.digitalarchive.work/hmCesium/hmCesium.min.js';
let searchParam = new URLSearchParams(window.location.search);
let initPosition;
if(searchParam.get('lon') && searchParam.get('lat')&&searchParam.get('height')){
initPosition = [searchParam.get('lon'), searchParam.get('lat'), searchParam.get('height'),6.28, -1.569];
}
let file = await readGSIM(searchParam.get('path'));
if(file){
initPosition = [file.flyTo[0],file.flyTo[1],file.flyTo[2]+3000];
}
let hmCesium = new HMCesium('mapContainer', // Cesium widget을 포함할 DOM 요소나 ID
{
// terrainUrl : 'http://gsim.hanmaceng.co.kr:5151/terrain_upzip/unzip_total_',
// terrainUrl : 'http://172.16.42.112:9000/terrain/unzip',
terrainUrl : file.terrain ? file.terrain : 'http://gsim.hanmaceng.co.kr:4040/terrain/total_',
imageryUrl : [
['URL',"https://a.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png"],
['WMTS','https://api.vworld.kr/req/wmts/1.0.0/729B62A2-3E48-3E62-B180-7EF5EA3C07F4/Base/{TileMatrix}/{TileRow}/{TileCol}.png'],
['WMTS','https://api.vworld.kr/req/wmts/1.0.0/729B62A2-3E48-3E62-B180-7EF5EA3C07F4/Satellite/{TileMatrix}/{TileRow}/{TileCol}.jpeg'],
['WMTS','https://api.vworld.kr/req/wmts/1.0.0/729B62A2-3E48-3E62-B180-7EF5EA3C07F4/Hybrid/{TileMatrix}/{TileRow}/{TileCol}.png'],
['URL',"https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}&hl=en&scale=2&apistyle=s.e:l.i|p.v:off"],
['URL',"https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}&hl=en&scale=2&apistyle=s.e:l.i|p.v:off"],
['URL',"https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png"],
['URL',"https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&hl=en&scale=2&apistyle=s.e:l.i|p.v:off"],
['URL',"https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png"]
],
setView:(initPosition)?initPosition:[126.72219913, 38.0632799256, 12283626, 6.28, -1.569],
// flyTo:(initPosition)?[initPosition[0], initPosition[1],initPosition[2]/10,initPosition[3],initPosition[4]]:[126.72219913, 38.0632799256, 1445272, 6.28, -1.569],
mapMode : '3D',
rotate2D : false,
mapEmphasize :(file.empMap<1000)? {
restrictCamera : true, //bool // true/false를 통해 카메라를 제한된 구역 안에서만 움직일 수 있게 해놓는다
center : [file.flyTo[0],file.flyTo[1],0],//lon, lat, height // 카메라의 중심점 설정
range : 1500,//전체 폴리곤 범위 //default : 1500 // 카메라가 움직일 수 있는 전체 영역을 지정한다.
holeRange : file.empMap,//강조 구역 범위 //단위 : km, default : 30 // empMap에 들어가보면 holeRange에 *1000이 되어있다. Cesium의 거리 unit은 m이기 때문에 km인것이다.
}:undefined,
},
{
//dom_option
slider : document.getElementById('slider'),
mouseInfo: document.querySelector('.footer-left'),
},
{
//function_option
}
);
hmCesium.viewer.scene.light = new Cesium.DirectionalLight({
direction: Cesium.Cartesian3.clone(hmCesium.viewer.scene.camera.directionWC),
intensity: 3.0,
color: Cesium.Color.WHITE
});
hmCesium.imageryLayer = {};
{
//searchparam에 .gsim경로 적어서 보내기...path는 main이 될 gsim파일
const searchParam = new URLSearchParams(window.location.search);
//searchParam 받아온 후에 주소창은 기본주소만 남도록 변경(개발 후에 적용)
// window.history.replaceState(null, null, '/')
//hmCesium에 path들어온 순차적으로 정보 넣기
// hmCesium.main = await readGSIM(searchParam.get('path'));
hmCesium.main = file;
//실제 로드부분
if(hmCesium.main.type == 'tileMap'){
await loadTileMap(hmCesium.main.url,hmCesium.main.name);
if(hmCesium.main.flyTo){
hmCesium.viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(hmCesium.main.flyTo[0],hmCesium.main.flyTo[1],hmCesium.main.flyTo[2])
});
}
hmCesium.initCamera = hmCesium.main.flyTo;
document.getElementById('model-list').style.display = 'none';
}else if(hmCesium.main.type == 'geoJson'){
await loadGeoJson(hmCesium.main);
if(hmCesium.main.flyTo){
hmCesium.viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(hmCesium.main.flyTo[0],hmCesium.main.flyTo[1],hmCesium.main.flyTo[2])
});
}
hmCesium.initCamera = hmCesium.main.flyTo;
document.getElementById('model-list').style.display = 'none';
//vietnam 특수
if(hmCesium.main.name == 'vietnam'){
await makeVietnamBill();
}
}else if(hmCesium.main.type == 'billboard'){
await loadBillboard(hmCesium.main);
if(hmCesium.main.flyTo){
hmCesium.viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(hmCesium.main.flyTo[0],hmCesium.main.flyTo[1],hmCesium.main.flyTo[2])
});
}
hmCesium.initCamera = hmCesium.main.flyTo;
document.getElementById('model-list').style.display = 'none';
}else if(hmCesium.main.type=='3dTile' || !hmCesium.main.type){
await loadTileset(hmCesium.main);
// 다른이동은 이벤트 발생안함.
if(Object.keys(hmCesium.projectManager.tileset.main).length > 0){
hmCesium.viewer.flyTo(hmCesium.projectManager.tileset.main[Object.keys(hmCesium.projectManager.tileset.main)[0]]);
}
document.getElementById('model-list').style.display = 'none';
}else if(hmCesium.main.type == 'total'){
//gsim용
//유저 정보 확인
let userRes= await axios.get(`/auth/status`);
hmCesium.user = userRes.data.user;
//terrain 변경
// if(hmCesium.main.terrain){
// await changeTerrain(hmCesium.main.terrain);
// }
//empMap은 flyTo가 꼭 필요함
// if(hmCesium.main.empMap){
// let option = { mapEmphasize : {
// restrictCamera : true, //bool // true/false를 통해 카메라를 제한된 구역 안에서만 움직일 수 있게 해놓는다
// center : [hmCesium.main.flyTo[0],hmCesium.main.flyTo[1],0],//lon, lat, height // 카메라의 중심점 설정
// range : 1500,//전체 폴리곤 범위 //default : 1500 // 카메라가 움직일 수 있는 전체 영역을 지정한다.
// holeRange : hmCesium.main.empMap,//강조 구역 범위 //단위 : km, default : 30 // empMap에 들어가보면 holeRange에 *1000이 되어있다. Cesium의 거리 unit은 m이기 때문에 km인것이다.
// }}
// await hmCesium.hmUtil.empMap.setEmphasize(option);
// }
//화면 이동
if(hmCesium.main.flyTo){
// hmCesium.viewer.camera.setView({
// destination : Cesium.Cartesian3.fromDegrees(hmCesium.main.flyTo[0],hmCesium.main.flyTo[1],hmCesium.main.flyTo[2]+2000)
// });
hmCesium.viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(hmCesium.main.flyTo[0],hmCesium.main.flyTo[1],hmCesium.main.flyTo[2])
});
hmCesium.initCamera = hmCesium.main.flyTo;
}
// document.getElementById('loading').style.display= 'none';
//메뉴 활성화
//title
document.getElementById('project-title').innerHTML = hmCesium.main.title;
document.getElementById('project-title').style.display = 'block';
//라벨생성, 이슈생성 쿼리 확인 후 버튼 보이기
if(hmCesium.main.query){
if(hmCesium.main.query.setLabel){
document.getElementById('func-label-btn').style.display = 'flex';
}
if(hmCesium.main.query.setIssue){
document.getElementById('func-issue-btn').style.display = 'flex';
}
}
//목록 만들기
if(hmCesium.main.model){
let modelList = document.getElementById('model-list');
let modelListRight = document.getElementById('model-list-right');
let ul = modelList.querySelector('ul');
ul.innerHTML = '';
//우측 리스트
let right_ul = modelListRight.querySelector('ul');
//라벨가져오기 셀렉트박스
let selectBox = document.getElementById('label-get-list');
selectBox.innerHTML = '';
let keys = Object.keys(hmCesium.main.model);
for(let i = 0; i < keys.length; i++){
let li = document.createElement('li');
li.innerHTML = `<div class="list-title">
<p>${keys[i].split('__')[0]}</p>
<h4>${keys[i].split('__')[1]}</h4>
</div>`;
let right_li = li.cloneNode(true);
ul.appendChild(li);
right_ul.appendChild(right_li);
li.querySelector('.list-title').addEventListener('click',async (e)=>{
if(hmCesium.curModel == keys[i]) return;
await resetLayerMode();
hmCesium.curModel = keys[i];
//레이어 셋팅
await setLayer(hmCesium.main.model[hmCesium.curModel].dLayer);
//이전 리스트 박스 삭제
let oldListBox = ul.querySelector('.list-box');
if(oldListBox)
oldListBox.remove();
let listBox = document.createElement('div');
listBox.classList.add('list-box');
let tilesetKeys = []
let shpKey = [];
if(hmCesium.main.model[keys[i]].tileset){
hmCesium.viewer.scene.groundPrimitives.removeAll();
for(let i = 0 ; i < hmCesium.viewer.dataSources._dataSources.length; i++){
if(hmCesium.viewer.dataSources._dataSources[i].name == 'labels'){
hmCesium.viewer.dataSources.remove(hmCesium.viewer.dataSources._dataSources[i]);
}
}
tilesetKeys = Object.keys(hmCesium.main.model[keys[i]].tileset);
}else if(hmCesium.main.model[keys[i]].shp){
hmCesium.viewer.scene.groundPrimitives.removeAll();
for(let i = 0 ; i < hmCesium.viewer.dataSources._dataSources.length; i++){
if(hmCesium.viewer.dataSources._dataSources[i].name == 'labels'){
hmCesium.viewer.dataSources.remove(hmCesium.viewer.dataSources._dataSources[i]);
}
}
shpKey = Object.keys(hmCesium.main.model[keys[i]].shp);
}else if(hmCesium.main.model[keys[i]].polyline){
await loadGeoJson(hmCesium.main.model[keys[i]]);
await makeVietnamBill(hmCesium.main.model[keys[i]].label);
}
for(let j = 0; j < tilesetKeys.length; j++){
let section = document.createElement('div');
section.classList.add('list-box-section');
section.innerHTML = `<label class="checkbox-label layer" style="opacity: 1;">
<div class="checkbox-label-left">
<img class="icon visibility-icon" src="./svg/icon-visibility.svg" alt="icon-visibility" />
<input type="checkbox" name="list-set" id="plate"
data-icon-visible="./svg/icon-visibility.svg"
data-icon-invisible="./svg/icon-invisibility.svg" checked/>
<span class="checkbox-custom-inbox"></span>
${tilesetKeys[j]}
</div>
</label>
<div style="display:flex; flex-direction:row;">
<input type="color" class="model-color-picker" value="#ffffff">
<div class="z-scaleBar-gauge" style="padding:0 2px;">
<input type="range" min="0" max="1" step="0.01" value="0" class="slider">
</div>
</div>`;
listBox.appendChild(section);
//section Event
//on/off
section.querySelector('#plate').addEventListener('click',(e)=>{
let label = e.target.closest('.checkbox-label');
let visibilityIcon = label.querySelector('.visibility-icon');
let checkbox = e.target.closest('input[type="checkbox"]');
let visibleSrc = checkbox.dataset.iconVisible;
let invisibleSrc = checkbox.dataset.iconInvisible;
if (!checkbox.checked) {
label.style.opacity = '0.2';
if (visibilityIcon) visibilityIcon.src = invisibleSrc;
} else {
label.style.opacity = '1';
if (visibilityIcon) visibilityIcon.src = visibleSrc;
}
let id = tilesetKeys[j];
hmCesium.projectManager.tileset.main[id].show = checkbox.checked;
hmCesium.viewer.scene.render();
});
//color change
section.querySelector('.model-color-picker').addEventListener('input',hmCesium.hmUtil.ThrottleTimer((e)=>{
let id = tilesetKeys[j];
let color = e.target.closest('input[type="color"]').value;
let opacity = e.target.closest('input[type="color"]').parentNode.querySelector('input[type="range"]').value;
hmCesium.projectManager.tileset.main[id].style = new Cesium.Cesium3DTileStyle({
color : `color("${color}",${1-opacity})`
});
hmCesium.projectManager.tileset.main[id].color = color;
hmCesium.projectManager.tileset.main[id].opacity = 1-opacity;
hmCesium.viewer.scene.render();
}),50);
//opacity
section.querySelector('.z-scaleBar-gauge .slider').addEventListener('input', (e)=>{
let id = tilesetKeys[j];
let color = e.target.closest('input[type="range"]').parentNode.parentNode.querySelector('input[type="color"]').value;
let opacity = e.target.closest('input[type="range"]').value;
hmCesium.projectManager.tileset.main[id].style = new Cesium.Cesium3DTileStyle({
color : `color("${color}",${1-opacity})`
});
hmCesium.projectManager.tileset.main[id].color = color;
hmCesium.projectManager.tileset.main[id].opacity = 1-opacity;
hmCesium.viewer.scene.render();
})
}
///shp
for(let j = 0; j < shpKey.length; j++){
let section = document.createElement('div');
section.classList.add('list-box-section');
section.innerHTML = `<label class="checkbox-label layer" style="opacity: 1;">
<div class="checkbox-label-left">
<img class="icon visibility-icon" src="./svg/icon-visibility.svg" alt="icon-visibility" />
<input type="checkbox" name="list-set" id="plate"
data-icon-visible="./svg/icon-visibility.svg"
data-icon-invisible="./svg/icon-invisibility.svg" checked/>
<span class="checkbox-custom-inbox"></span>
${shpKey[j]}
</div>
</label>
<div style="display:flex; flex-direction:row;">
</div>`;
listBox.appendChild(section);
//section Event
//on/off
section.querySelector('#plate').addEventListener('click',(e)=>{
let label = e.target.closest('.checkbox-label');
let visibilityIcon = label.querySelector('.visibility-icon');
let checkbox = e.target.closest('input[type="checkbox"]');
let visibleSrc = checkbox.dataset.iconVisible;
let invisibleSrc = checkbox.dataset.iconInvisible;
if (!checkbox.checked) {
label.style.opacity = '0.2';
if (visibilityIcon) visibilityIcon.src = invisibleSrc;
} else {
label.style.opacity = '1';
if (visibilityIcon) visibilityIcon.src = visibleSrc;
}
let id = shpKey[j];
hmCesium.imageryLayer['main'].show = checkbox.checked;
hmCesium.viewer.scene.render();
});
}
//shp end
li.appendChild(listBox);
await deleteTileset('main');
await deleteAllTileMap('main');
// await resetMode();
if(hmCesium.main.model[keys[i]].tileset){
await loadTileset(hmCesium.main.model[keys[i]].tileset);
}else if(hmCesium.main.model[keys[i]].shp){
//shp load
await loadTileMap(hmCesium.main.model[keys[i]].shp[shpKey[0]],'main');
}
li.parentNode.querySelectorAll('li').forEach(ele=>{
ele.classList.remove('select-list');
})
li.classList.add('select-list');
})
right_li.querySelector('.list-title').addEventListener('click',async (e)=>{
if(hmCesium.splitModel == keys[i]) return;
hmCesium.splitModel = keys[i];
//이전 리스트 박스 삭제
let oldListBox = right_ul.querySelector('.list-box');
if(oldListBox)
oldListBox.remove();
let listBox = document.createElement('div');
listBox.classList.add('list-box');
let tilesetKeys = []
let shpKey = [];
if(hmCesium.main.model[keys[i]].tileset){
tilesetKeys = Object.keys(hmCesium.main.model[keys[i]].tileset);
}else{
shpKey = Object.keys(hmCesium.main.model[keys[i]].shp);
}
for(let j = 0; j < tilesetKeys.length; j++){
let section = document.createElement('div');
section.classList.add('list-box-section');
section.innerHTML = `<label class="checkbox-label layer" style="opacity: 1;">
<div class="checkbox-label-left">
<img class="icon visibility-icon" src="./svg/icon-visibility.svg" alt="icon-visibility" />
<input type="checkbox" name="list-set" id="plate"
data-icon-visible="./svg/icon-visibility.svg"
data-icon-invisible="./svg/icon-invisibility.svg" checked/>
<span class="checkbox-custom-inbox"></span>
${tilesetKeys[j]}
</div>
</label>
<div style="display:flex; flex-direction:row;">
<input type="color" class="model-color-picker" value="#ffffff">
<div class="z-scaleBar-gauge" style="padding:0 2px;">
<input type="range" min="0" max="1" step="0.01" value="0" class="slider">
</div>
</div>`;
listBox.appendChild(section);
//section Event
//on/off
section.querySelector('#plate').addEventListener('click',(e)=>{
let label = e.target.closest('.checkbox-label');
let visibilityIcon = label.querySelector('.visibility-icon');
let checkbox = e.target.closest('input[type="checkbox"]');
let visibleSrc = checkbox.dataset.iconVisible;
let invisibleSrc = checkbox.dataset.iconInvisible;
if (!checkbox.checked) {
label.style.opacity = '0.2';
if (visibilityIcon) visibilityIcon.src = invisibleSrc;
} else {
label.style.opacity = '1';
if (visibilityIcon) visibilityIcon.src = visibleSrc;
}
let id = tilesetKeys[j];
hmCesium.projectManager.tileset.split[id].show = checkbox.checked;
hmCesium.viewer.scene.render();
});
//color change
section.querySelector('.model-color-picker').addEventListener('input',hmCesium.hmUtil.ThrottleTimer((e)=>{
let id = tilesetKeys[j];
let color = e.target.closest('input[type="color"]').value;
let opacity = e.target.closest('input[type="color"]').parentNode.querySelector('input[type="range"]').value;
hmCesium.projectManager.tileset.split[id].style = new Cesium.Cesium3DTileStyle({
color : `color("${color}",${1-opacity})`
});
hmCesium.projectManager.tileset.split[id].color = color;
hmCesium.projectManager.tileset.split[id].opacity = 1-opacity;
hmCesium.viewer.scene.render();
}),50);
//opacity
section.querySelector('.z-scaleBar-gauge .slider').addEventListener('input', (e)=>{
let id = tilesetKeys[j];
let color = e.target.closest('input[type="range"]').parentNode.parentNode.querySelector('input[type="color"]').value;
let opacity = e.target.closest('input[type="range"]').value;
hmCesium.projectManager.tileset.split[id].style = new Cesium.Cesium3DTileStyle({
color : `color("${color}",${1-opacity})`
});
hmCesium.projectManager.tileset.split[id].color = color;
hmCesium.projectManager.tileset.split[id].opacity = 1-opacity;
hmCesium.viewer.scene.render();
})
}
///shp
for(let j = 0; j < shpKey.length; j++){
let section = document.createElement('div');
section.classList.add('list-box-section');
section.innerHTML = `<label class="checkbox-label layer" style="opacity: 1;">
<div class="checkbox-label-left">
<img class="icon visibility-icon" src="./svg/icon-visibility.svg" alt="icon-visibility" />
<input type="checkbox" name="list-set" id="plate"
data-icon-visible="./svg/icon-visibility.svg"
data-icon-invisible="./svg/icon-invisibility.svg" checked/>
<span class="checkbox-custom-inbox"></span>
${shpKey[j]}
</div>
</label>
<div style="display:flex; flex-direction:row;">
</div>`;
listBox.appendChild(section);
//section Event
//on/off
section.querySelector('#plate').addEventListener('click',(e)=>{
let label = e.target.closest('.checkbox-label');
let visibilityIcon = label.querySelector('.visibility-icon');
let checkbox = e.target.closest('input[type="checkbox"]');
let visibleSrc = checkbox.dataset.iconVisible;
let invisibleSrc = checkbox.dataset.iconInvisible;
if (!checkbox.checked) {
label.style.opacity = '1';
if (visibilityIcon) visibilityIcon.src = invisibleSrc;
} else {
label.style.opacity = '1';
if (visibilityIcon) visibilityIcon.src = visibleSrc;
}
let id = shpKey[j];
hmCesium.imageryLayer['split'].show = checkbox.checked;
hmCesium.viewer.scene.render();
});
}
//shp end
right_li.appendChild(listBox);
await deleteTileset('split');
await deleteAllTileMap('split');
// await resetMode();
if(hmCesium.main.model[keys[i]].tileset){
await loadTileset(hmCesium.main.model[keys[i]].tileset, 'split');
}else if(hmCesium.main.model[keys[i]].shp){
//shp load
await loadTileMap(hmCesium.main.model[keys[i]].shp[shpKey[0]],'split');
}
right_li.parentNode.querySelectorAll('li').forEach(ele=>{
ele.classList.remove('select-list');
})
right_li.classList.add('select-list');
})
let selectLi = document.createElement('li');
selectLi.innerHTML = `${keys[i].split('__')[1]}`;
selectLi.id = `select-${keys[i]}`;
selectBox.appendChild(selectLi);
selectLi.addEventListener('click',()=>{
document.getElementById('label-get-selected').innerHTML = `${keys[i].split('__')[1]}`;
document.getElementById('label-get-selected').selectedModel = keys[i];
document.querySelector('.dropdown').classList.toggle('open');
})
}
modelList.style.display = 'flex';
}
//리스트 접기
document.querySelector('#model-list .window-header').addEventListener('click', (e)=>{
document.querySelector('#model-list .icon').classList.toggle('fold-icon');
if(document.querySelector('#model-list .icon').classList.contains('fold-icon')){
document.getElementById('left-list-title').innerHTML = `${hmCesium.curModel.split('__')[1]}`;
document.querySelector('#model-list .window-body').style.display = 'none';
}else{
document.getElementById('left-list-title').innerHTML = hmCesium.projectManager.flag.split?'좌측화면 모델':`모델기반(3D)`;
document.querySelector('#model-list .window-body').style.display = 'flex';
}
})
document.querySelector('#model-list-right .window-header').addEventListener('click', (e)=>{
document.querySelector('#model-list-right .icon').classList.toggle('fold-icon');
if(document.querySelector('#model-list-right .icon').classList.contains('fold-icon')){
document.getElementById('right-list-title').innerHTML = `${document.querySelector('#model-list-right li .list-box').parentElement.querySelector('h4').innerHTML}`;
document.querySelector('#model-list-right .window-body').style.display = 'none';
}else{
document.getElementById('right-list-title').innerHTML = `우측화면 모델`;
document.querySelector('#model-list-right .window-body').style.display = 'flex';
}
})
//중앙 메뉴 활성화
document.getElementById('func-btns').style.display = 'flex';
//레이어버튼 활성화
document.getElementById('set-layer').style.display = 'flex';
//마지막 모델 리스트 강제 클릭
document.getElementById('model-list').querySelector('ul').lastElementChild.querySelector('.list-title').click();
}
}
async function resetLayerMode(){
let onMode = document.getElementById('func-btns').querySelector('.on');
if(onMode && onMode.id != 'func-split-btn'){
onMode.click();
}
if(document.getElementById('func-label-add').style.display == 'flex'){
document.getElementById('func-label-add').style.display = 'none';
}
if(document.getElementById('func-issue-add').style.display == 'flex'){
document.getElementById('func-issue-add').style.display = 'none';
}
//layer전체 종료
if(document.getElementById('wpb-layer-btn').querySelector('input').checked){
document.getElementById('wpb-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('wpb-layer-btn').querySelector('input').dispatchEvent(event);
}
if(document.getElementById('plane-layer-btn').querySelector('input').checked){
document.getElementById('plane-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('plane-layer-btn').querySelector('input').dispatchEvent(event);
}
if(document.getElementById('siteLine-layer-btn').querySelector('input').checked){
document.getElementById('siteLine-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('siteLine-layer-btn').querySelector('input').dispatchEvent(event);
}
if(document.getElementById('label-layer-btn').querySelector('input').checked){
document.getElementById('label-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('label-layer-btn').querySelector('input').dispatchEvent(event);
}
if(document.getElementById('issue-layer-btn').querySelector('input').checked){
document.getElementById('issue-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('issue-layer-btn').querySelector('input').dispatchEvent(event);
}
//measure layer 청소
initMeasure('clear');
}
//전체 모드 종료
async function resetMode(){
hmCesium.resetTileMode('split');
hmCesium.resetTileMode('overlap');
// hmCesium.resetTileMode('zScale');
// hmCesium.resetTileMode('wireframe');
}
async function readGSIM(path){
try {
const response = await fetch(path);
// 응답 상태 확인
if (!response.ok) {
throw new Error(`HTTP 오류! 상태: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error(`파일을 읽는 중 오류 발생: ${error.message}`);
}
}
async function loadTileMap(imgTileUrl, imgLayerName){
const viewer = hmCesium.viewer;
const imageryLayer = Cesium.ImageryLayer.fromProviderAsync(
Cesium.TileMapServiceImageryProvider.fromUrl(
Cesium.buildModuleUrl(imgTileUrl)
)
);
viewer.imageryLayers.add(imageryLayer);
imageryLayer.name = imgLayerName;
imageryLayer.show = true;
hmCesium.imageryLayer[imgLayerName] = imageryLayer;
if(imgLayerName == 'split'){
hmCesium.imageryLayer[imgLayerName].splitDirection = Cesium.SplitDirection.RIGHT;
}
if(hmCesium.projectManager.flag.split && imgLayerName == 'main'){
hmCesium.imageryLayer[imgLayerName].splitDirection = Cesium.SplitDirection.LEFT;
}
}
async function deleteAllTileMap(name){
let key = Object.keys(hmCesium.imageryLayer);
for(let i = 0; i < key.length; i++){
if(key[i] == name){
hmCesium.viewer.imageryLayers.remove(hmCesium.imageryLayer[key[i]]);
delete hmCesium.imageryLayer[key[i]];
}
}
}
async function loadGeoJson(json){
document.getElementById('progress').style.display = 'flex';
let polyline, polygon, point;
let progressCount = 0;
if(json.point){
await readGeoJSONFile(`${json.point}`)
.then((data) => {
if (data) {
point = data;
}
})
.catch((error) => {
console.error("point read 오류 발생:", error);
});
}
if(json.polyline){
let url = json.polyline;
if(url instanceof Object){
let key = Object.keys(url);
url = url[key[0]];
}
await readGeoJSONFile(`${url}`)
.then((data) => {
if (data) {
polyline = data;
}
})
.catch((error) => {
console.error("polyline read 오류 발생:", error);
});
}
if(json.polygon){
await readGeoJSONFile(`${json.polygon}`)
.then((data) => {
if (data) {
polygon = data;
}
})
.catch((error) => {
console.error("polygon read 오류 발생:", error);
});
}
let collection = new Cesium.PrimitiveCollection();
if(point){
progressCount++;
// let pointDataSource = await Cesium.GeoJsonDataSource.load(point);
// collection.add(pointDataSource);
//point 적용해야함
}
if(polygon){
progressCount++;
let geometryArray = [];
let outlineGeometryArray = [];
Cesium.GeoJsonDataSource.load(polygon).then((geojson)=>{
geojson.entities.values.map(entity => {
let polygonPosition = entity.polygon.hierarchy._value.positions
let polygonGeometry = new Cesium.GeometryInstance({
geometry: new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(polygonPosition),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
arcType: Cesium.ArcType.GEODESIC,
height: 0,
extrudedHeight: 0
}),
attributes: {
color: new Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString((json.color)?json.color[entity._name]+'50':getRandomHexColor()+'50')),
}
});
geometryArray.push(polygonGeometry);
//외곽선
let outlineGeometry = new Cesium.GeometryInstance({
geometry: new Cesium.PolygonOutlineGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(polygonPosition),
arcType: Cesium.ArcType.GEODESIC
}),
attributes: {
color: new Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString((json.color)?json.color[entity._name]:getRandomHexColor())),
}
});
outlineGeometryArray.push(outlineGeometry);
})
let polygonPrimitive = new Cesium.Primitive({
geometryInstances: geometryArray,
asynchronous: false,
appearance: new Cesium.PerInstanceColorAppearance({ closed: true, flat:true}),
releaseGeometryInstances: false,
allowPicking: false,
});
collection.add(polygonPrimitive);
// 폴리곤 외곽선 Primitive
let outlinePrimitive = new Cesium.Primitive({
geometryInstances: outlineGeometryArray,
asynchronous: false,
appearance: new Cesium.PerInstanceColorAppearance({
flat: true,
renderState: {
lineWidth: Math.min(1.0, hmCesium.viewer.scene.maximumAliasedLineWidth)
}
}),
releaseGeometryInstances: false,
allowPicking: false,
});
collection.add(outlinePrimitive);
hmCesium.viewer.scene.groundPrimitives.add(collection);
hmCesium.viewer.scene.requestRender();
progressCount--;
if(progressCount == 0){
document.getElementById('progress').style.display = 'none';
}
})
}
if(polyline){
progressCount++;
let geometryArray = [];
Cesium.GeoJsonDataSource.load(polyline).then((geojson)=>{
geojson.entities.values.map(entity => {
let polylinePosition = entity.polyline.positions._value;
let polylineGeometry = new Cesium.GeometryInstance({
geometry: new Cesium.GroundPolylineGeometry({
positions: polylinePosition,
width: 8,
arcType: Cesium.ArcType.GEODESIC
}),
attributes: {
color: new Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString((json.color)?json.color[entity._name]:getRandomHexColor())),
}
});
geometryArray.push(polylineGeometry);
})
let polylinePrimitive = new Cesium.GroundPolylinePrimitive({
geometryInstances: geometryArray,
asynchronous: false,
appearance: new Cesium.PolylineColorAppearance(),
releaseGeometryInstances: false,
allowPicking: false,
});
collection.add(polylinePrimitive);
hmCesium.viewer.scene.groundPrimitives.add(collection);
hmCesium.viewer.scene.requestRender();
progressCount--;
if(progressCount == 0){
document.getElementById('progress').style.display = 'none';
}
})
}
}
async function loadBillboard(json){
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.left = '-9999px';
container.style.top = '-9999px';
container.innerHTML = json.html;
document.body.appendChild(container);
const element = container.firstChild;
//이미지 로딩 (작동X)
const images = container.querySelectorAll('img');
const imagePromises = Array.from(images).map(img => {
return new Promise(resolve => {
if (img.complete) {
resolve();
} else {
img.onload = resolve;
img.onerror = resolve;//에러도 우선은 패스
}
});
});
Promise.all(imagePromises).then(() => {
const elementRect = element.getBoundingClientRect();
const width = elementRect.width+4 || 164; // 최소 너비+4
const height = elementRect.height+4 || 66; // 최소 높이+4
const data = `
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
<foreignObject width="100%" height="100%">
${new XMLSerializer().serializeToString(element)}
</foreignObject>
</svg>
`;
// SVG를 Base64로 인코딩
const svgBlob = new Blob([data], { type: 'image/svg+xml' });
const reader = new FileReader();
let position = Cesium.Cartesian3.fromDegrees(json.position[0],json.position[1],json.position[2])
reader.onload = function(e) {
// Base64 인코딩된 데이터 URL
const dataUrl = e.target.result;
// 실제 이미지 생성 및 빌보드에 추가
const img = new Image();
img.onload = function() {
// 이미지가 로드되면 빌보드 생성
hmCesium.viewer.entities.add({
position: position,
billboard: {
image: dataUrl,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
width: width,
height: height,
pixelOffset : new Cesium.Cartesian2(0,-50),
}
});
hmCesium.viewer.entities.add({
position: position,
billboard: {
image: './img/icon_progress.svg',
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
}
});
document.body.removeChild(container);
};
img.onerror = function() {
console.error('이미지 변환 실패');
document.body.removeChild(container);
};
img.src = dataUrl;
};
reader.readAsDataURL(svgBlob);
});
}
//test용 랜덤핵사코드
function getRandomHexColor() {
const randomColor = Math.floor(Math.random() * 16777215).toString(16);
return '#' + randomColor.padStart(6, '0');
}
async function readGeoJSONFile(fileUrl) {
try {
const response = await fetch(fileUrl);
if (response.ok) {
const geoJSONData = await response.json();
return geoJSONData;
} else {
throw new Error("파일을 불러오는 데 문제가 발생했습니다.");
}
} catch (error) {
console.error("오류:", error);
return null;
}
}
async function loadTileset(json, isCompare){
//iscompare는 split, overlap
if(!isCompare){
await deleteTileset('main');
}else{
await deleteTileset(isCompare);
}
const promise = Object.keys(json).map(async key =>{
if(json[key] != null){
if(!isCompare){
if(key != 'name')
await hmCesium.setTiles('main', key, json[key]);
}else{
if(key != 'name')
await hmCesium.setTiles(isCompare, key, json[key]);
}
}
});
await Promise.all(promise);
}
async function deleteTileset(tileset){
let keys = Object.keys(hmCesium.projectManager.tileset[tileset]);
if(keys.length > 0 ){
for(let i = 0; i < keys.length; i++){
await hmCesium.deleteTileset(tileset, keys[i]);
}
}
}
// document.getElementById('split').addEventListener('click', ()=>{
// if(document.getElementById('sub-gsim').style.display == 'none'){//on
// //모델 리스트
// document.getElementById('sub-gsim').style.display = 'flex';
// // 슬라이더
// document.getElementById('slider').style.display = 'block';
// //모드
// hmCesium.projectManager.setTileMode('split');
// }else{//off
// //모델 리스트
// document.getElementById('sub-gsim').style.display = 'none';
// // 슬라이더
// document.getElementById('slider').style.display = 'none';
// //모드
// hmCesium.projectManager.split.resetSplit();
// }
// })
async function changeTerrain(url){
await hmCesium.hmUtil.setTerrain(hmCesium.viewer, true, url);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////*** 지도 기본기능(왼쪽, 오른쪽하단) ***///////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//왼쪽 하단
//원위치로 돌아오기
document.getElementById('set-location').addEventListener('click',()=>{
if(hmCesium.initCamera){
hmCesium.viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(hmCesium.initCamera[0],hmCesium.initCamera[1],hmCesium.initCamera[2])
});
}else{
if(hmCesium.projectManager.tileset.terrain){
hmCesium.viewer.flyTo(hmCesium.projectManager.tileset.terrain);
}else if(hmCesium.projectManager.tileset.main){
hmCesium.viewer.flyTo(hmCesium.projectManager.tileset.main);
}
}
});
//정북정렬
document.getElementById('set-north').addEventListener('click',()=>{
hmCesium.camera_setNorth(true);
})
//탑뷰
document.getElementById('set-topView').addEventListener('click',()=>{
hmCesium.camera_setVertical(true);
})
//baseMap선택
document.getElementById('set-baseMap').addEventListener('click',()=>{
if(document.getElementById('baseMap-modal').style.display == 'none'){
document.getElementById('baseMap-modal').style.display = 'block';
document.getElementById('set-baseMap').classList.add('on');
}else{
document.getElementById('baseMap-modal').style.display = 'none';
document.getElementById('set-baseMap').classList.remove('on');
}
})
document.getElementById('baseMap-modal').querySelectorAll('input').forEach(ele => {
ele.addEventListener('change',(e)=>{
let value = e.target.value;
changeBaseMap(value);
})
});
document.getElementById('baseMap-modal').querySelector('.modal-close').addEventListener('click',()=>{
document.getElementById('baseMap-modal').style.display = 'none';
document.getElementById('set-baseMap').classList.remove('on');
})
function changeBaseMap(value){
let index = 0;
switch(value){
case 'vworld-hybrid':
index = 3;
break;
case 'vworld-satellite':
index = 2;
break;
case 'vworld-normal':
index = 1;
break;
case 'google-hybrid':
index = 4;
break;
case 'google-satellite':
index = 5;
break;
case 'google-normal':
index = 7;
break;
case 'carto-normal':
index = 0;
break;
case 'carto-light':
index = 6;
break;
case 'carto-dark':
index = 8;
break;
}
hmCesium.changeBaseMap(index);
if(index == 3){
hmCesium.hmUtil.baseMap[2].show = true;
}
}
// //레이어
// document.getElementById('set-layer').addEventListener('click',()=>{
// if(document.getElementById('layer-modal').style.display =='none'){
// document.getElementById('layer-modal').style.display = 'block';
// }else{
// document.getElementById('layer-modal').style.display = 'none';
// }
// })
// //오른쪽하단
// //종단축척
// document.getElementById('set-zScale').addEventListener('click',()=>{
// if(document.getElementById('zScale-slider').style.display=='none'){
// document.getElementById('zScale-slider').style.display = 'block';
// }else{
// document.getElementById('zScale-slider').style.display = 'none';
// }
// })
hmCesium.logScale = 1;
document.getElementById('zScale-slider').addEventListener('input',(e)=>{
let value = 0;
switch(e.target.value){
case '1':
value = 1;
break;
case '2':
value = 1.1;
break;
case '3':
value = 1.25;
break;
case '4':
value = 1.5;
break;
case '5':
value = 1.85;
break;
case '6':
value = 2;
break;
case '7':
value = 2.5;
break;
case '8':
value = 3;
break;
case '9':
value = 4;
break;
case '10':
value = 5;
break;
}
if(e.target.value == 0) value = 0;
// document.getElementById('zScale').querySelector('.value').innerText = 'x'+Number(value).toFixed(1);
hmCesium.viewer.scene.verticalExaggeration = value;
//카메라 높이 맞추기
const cartesian = hmCesium.viewer.scene.pickPosition({x:hmCesium.viewer.container.clientWidth/2,y:hmCesium.viewer.container.clientHeight/2});
if(cartesian){
let heightFactor = new Cesium.Cartographic.fromCartesian(cartesian).height/hmCesium.logScale;
let height = hmCesium.viewer.camera.positionCartographic.height + (heightFactor*((value<1)?0:(value-hmCesium.logScale)));
hmCesium.viewer.camera.setView({
destination: Cesium.Cartesian3.fromRadians(hmCesium.viewer.camera.positionCartographic.longitude, hmCesium.viewer.camera.positionCartographic.latitude, height),
orientation: {
heading : hmCesium.viewer.camera.heading,
pitch : hmCesium.viewer.camera.pitch,
roll : hmCesium.viewer.camera.roll
}
});
}
hmCesium.logScale = ((value < 1)? 1:value);
// roadData
// if(hmCesium.hmUtil.roadDataLayer){
// let bills = hmCesium.hmUtil.roadDataLayer._billboards;
// bills.forEach(item =>{
// item.position = new Cesium.Cartesian3.fromDegrees(item.conv[0],item.conv[1],item.settingHeight*parseFloat(hmCesium.viewer.scene.verticalExaggeration));
// });
// }
// if(hmCesium.hmUtil.roadLineLayer){
// let entities = hmCesium.hmUtil.roadLineLayer._entityCollection._entities._array;
// entities.forEach(item =>{
// let positions = item.origin;
// let new_positions = [];
// for(let i = 0; i < positions.length; i++){
// let carto = Cesium.Cartographic.fromCartesian(positions[i]);
// new_positions.push(new Cesium.Cartesian3.fromRadians(carto.longitude, carto.latitude, carto.height * parseFloat(hmCesium.viewer.scene.verticalExaggeration)));
// }
// item.polyline.positions.setValue(new_positions);
// });
// }
})
document.getElementById('set-layer').addEventListener('click',(e)=>{
if(document.getElementById('layer-modal').style.display == 'none'){
document.getElementById('layer-modal').style.display = 'block';
document.getElementById('set-layer').classList.add('on');
}else{
document.getElementById('layer-modal').style.display = 'none';
document.getElementById('set-layer').classList.remove('on');
}
});
document.getElementById('layer-modal').querySelector('.modal-close').addEventListener('click',()=>{
document.getElementById('layer-modal').style.display = 'none';
document.getElementById('set-layer').classList.remove('on');
});
//레이어 모달 기능 추가
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////*** 지도 기본기능(왼쪽, 오른쪽하단) END ***//////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////*** GSIM 기능 (중앙) ***///////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//전체 취소
function resetAllMode(){
let onMode = document.getElementById('func-btns').querySelector('.on');
if(onMode){
onMode.click();
}
if(document.getElementById('func-label-add').style.display == 'flex'){
document.getElementById('func-label-add').style.display = 'none';
}
if(document.getElementById('func-issue-add').style.display == 'flex'){
document.getElementById('func-issue-add').style.display = 'none';
}
}
////////// 이슈, 라벨, 측정 등 지도 클릭 이벤트 발생 시 마우스 커서 div 변경 관련
// 마우스 커서 div 추가
function changeCursor(e) {
document.getElementById('changeCursor').style.display = 'block';
document.getElementById('changeCursor').style.left = (e.clientX - 20) + 'px';
document.getElementById('changeCursor').style.top = (e.clientY - 20) + 'px';
}
// 마우스 커서 div 원상복구
function resetCursor() {
document.getElementById('changeCursor').style.display = 'none';
document.body.removeEventListener('mousemove', changeCursor);
}
// 마우스 커서 div 및 스크린 이벤트 원상복구
function initCursorAndScreenEvent() {
resetCursor();
hmCesium.viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
//분할비교
document.getElementById('func-split-btn').addEventListener('click',()=>{
if(!document.getElementById('func-split-btn').classList.contains('on')){
resetAllMode();
document.getElementById('slider').style.display = 'block';
document.getElementById('model-list-right').style.display = 'block';
document.getElementById('left-list-title').innerText = '좌측화면 모델';
hmCesium.projectManager.setTileMode('split');
if(hmCesium.imageryLayer.main){
hmCesium.imageryLayer.main.splitDirection = Cesium.SplitDirection.LEFT;
}
//마지막 모델 리스트 강제 클릭
document.getElementById('model-list-right').querySelector('ul').lastElementChild.querySelector('.list-title').click();
}else{
document.getElementById('slider').style.display = 'none';
document.getElementById('model-list-right').style.display = 'none';
document.getElementById('left-list-title').innerText = '모델기반(3D)';
hmCesium.projectManager.split.resetSplit();
if(hmCesium.imageryLayer.main){
hmCesium.imageryLayer.main.splitDirection = Cesium.SplitDirection.NONE;
}
if(hmCesium.imageryLayer.split){
deleteAllTileMap('split');
}
hmCesium.splitModel = undefined;
}
document.getElementById('func-split-btn').classList.toggle('on');
})
//선형클리핑
document.getElementById('func-clipping-btn').addEventListener('click',async ()=>{
if(!document.getElementById('func-clipping-btn').classList.contains('on')){
resetAllMode();
if(document.getElementById('wpb-layer-btn').classList.contains('disabled')){
return alert('모델의 선형 중심선이 존재하지 않습니다.');
}
document.getElementById('func-clipping').style.display = 'flex';
//선형중심선 강제 on
if(!document.getElementById('wpb-layer-btn').querySelector('input').checked){
document.getElementById('wpb-layer-btn').querySelector('input').checked = true;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('wpb-layer-btn').querySelector('input').dispatchEvent(event);
}
//initClipping
await compareCrossSection();
}else{
document.getElementById('func-clipping').style.display = 'none';
endCompareCrossSection();
}
document.getElementById('func-clipping-btn').classList.toggle('on');
})
//선형클리핑 init
async function compareCrossSection(){
hmCesium.compareRoadData = {}; //roadData 초기화
await loadRoadDataForCompare();
setRangeBar(document.querySelector('#key-map-list').firstChild.querySelector('h4').innerText);
}
hmCesium.viewer.scene.preRender.addEventListener(()=>{
let rotate = -Cesium.Math.toDegrees(hmCesium.viewer.camera.heading);
//svg 돌리기
document.getElementById('key-map').style.transform = `rotate(${rotate}deg) scale(1,-1)`;
lightPreRender();
});
function lightPreRender(){
const camDir = hmCesium.viewer.scene.camera.directionWC;
// 너무 수평일 때 생기는 음영을 줄이기 위한 살짝 아래쪽 바이어스(선택)
const bias = new Cesium.Cartesian3(0.0, 0.0, -0.15);
const dir = Cesium.Cartesian3.add(camDir, bias, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(dir, dir);
hmCesium.viewer.scene.light.direction = dir;
}
//선형 클리핑 end
function endCompareCrossSection(){
document.getElementById('clipping-key-map').style.display = 'none';
hmCesium.compareRoadData = undefined;
hmCesium.viewer.scene.screenSpaceCameraController.enableCollisionDetection = true;
hmCesium.reverseClipping = false;
hmCesium.cameraFollowClipping = false;
hmCesium.autoPlay = false;
document.getElementById('clipping-camera-follow').classList.remove('on');
document.getElementById('clipping-camera-play').classList.remove('on');
document.getElementById('clipping-inverse').classList.remove('on');
Object.keys(hmCesium.projectManager.tileset.main).forEach(item=>{
if(hmCesium.projectManager.tileset.main[item] != undefined && !(hmCesium.projectManager.tileset.main[item] instanceof Array)){
if(hmCesium.projectManager.tileset.main[item].clippingPlanes){
hmCesium.projectManager.tileset.main[item].clippingPlanes.removeAll();
}
}
});
}
//RoadData 읽어오기
async function loadRoadDataForCompare(){
let jsonPath = hmCesium.main.model[hmCesium.curModel].dLayer['wpb'];
let projection = `EPSG:${hmCesium.main.model[hmCesium.curModel].projection}`;
let jsonObj;
async function readJSONFile(fileUrl) {
try {
const response = await fetch(fileUrl);
if (response.ok) {
const geoJSONData = await response.json();
return geoJSONData;
} else {
throw new Error("파일을 불러오는 데 문제가 발생했습니다.");
}
} catch (error) {
console.error("오류:", error);
return null;
}
}
await readJSONFile(jsonPath).then((data) => {
if (data) {
jsonObj = data;
}
}).catch((error)=> console.error('오류발생 : ',error));
let length = jsonObj.length;
for (let j = 0; j < length; j++) {
let pointArr = [];
const stPoints = jsonObj[j].StationPoints;
const stPointsLength = stPoints.length;
for (let i = 0; i < stPointsLength; i++) {
// const conv = proj4('EPSG:5186').inverse([stPoints[i].Point.X, stPoints[i].Point.Y]);//4326 변환
//==> transform 뽑아두기
proj4.defs([
['EPSG:5185', '+proj=tmerc +lat_0=38 +lon_0=125 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
['EPSG:5186', '+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
['EPSG:5187', '+proj=tmerc +lat_0=38 +lon_0=129 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
['EPSG:5188', '+proj=tmerc +lat_0=38 +lon_0=131 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
['EPSG:3124', '+proj=tmerc +lat_0=0 +lon_0=123 +k=0.99995 +x_0=500000 +y_0=0 +ellps=clrk66 +towgs84=-127.62,-67.24,-47.04,3.068,-4.903,-1.578,-1.06 +units=m +no_defs +type=crs'],
['EPSG:32242', '+proj=utm +zone=42 +ellps=WGS72 +towgs84=0,0,4.5,0,0,0.554,0.219 +units=m +no_defs +type=crs'],
['EPSG:32642', '+proj=utm +zone=42 +datum=WGS84 +units=m +no_defs +type=crs'],
['EPSG:4326', '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'],
['EPSG:4978', '+proj=geocent +datum=WGS84 +units=m +no_defs'],
]);
const height = stPoints[i].Point.Z + 0.5; //0.5m 띄워서 표현
const station = stPoints[i].Station;
if (station) {
if(station % 1 == 0){
let sPoint, ePoint;
if(projection == 'EPSG:4978'){
let carto = Cesium.Cartographic.fromCartesian(new Cesium.Cartesian3(stPoints[i].Point.X, stPoints[i].Point.Y, stPoints[i].Point.Z));
let cartoHeight = carto.height + 0.5;
let conv = proj4('EPSG:4326', 'EPSG:5186', [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)]);
if(i>0){
let prev = Cesium.Cartographic.fromCartesian(new Cesium.Cartesian3(stPoints[i-1].Point.X, stPoints[i-1].Point.Y, stPoints[i-1].Point.Z));
let prevHeight = prev.height + 0.5;
let prevConv = proj4('EPSG:4326', 'EPSG:5186', [Cesium.Math.toDegrees(prev.longitude), Cesium.Math.toDegrees(prev.latitude)]);
sPoint = new Cesium.Cartesian3(prevConv[0], prevConv[1],prevHeight);
ePoint = new Cesium.Cartesian3(conv[0], conv[1],cartoHeight);
}else{
let next = Cesium.Cartographic.fromCartesian(new Cesium.Cartesian3(stPoints[i+1].Point.X, stPoints[i+1].Point.Y, stPoints[i+1].Point.Z));
let nextHeight = next.height + 0.5;
let nextConv = proj4('EPSG:4326', 'EPSG:5186', [Cesium.Math.toDegrees(next.longitude), Cesium.Math.toDegrees(next.latitude)]);
sPoint = new Cesium.Cartesian3(conv[0], conv[1],cartoHeight);
ePoint = new Cesium.Cartesian3(nextConv[0], nextConv[1],nextHeight);
}
}else{
if(i>0){
sPoint = new Cesium.Cartesian3(stPoints[i-1].Point.X, stPoints[i-1].Point.Y,stPoints[i-1].Point.Z);
ePoint = new Cesium.Cartesian3(stPoints[i].Point.X, stPoints[i].Point.Y,height);
}else{
sPoint = new Cesium.Cartesian3(stPoints[i].Point.X, stPoints[i].Point.Y,stPoints[i].Point.Z);
ePoint = new Cesium.Cartesian3(stPoints[i+1].Point.X, stPoints[i+1].Point.Y,height);//높이는 그려주는 포인트에 맞추기
}
}
let perpendicularPosition = getPerpendicularPoint(sPoint, ePoint, (projection !== 'EPSG:4978')?projection:'EPSG:5186');
if(!hmCesium.compareRoadData[jsonObj[j].RoadName]){
hmCesium.compareRoadData[jsonObj[j].RoadName] = {};
}
hmCesium.compareRoadData[jsonObj[j].RoadName][station] = perpendicularPosition;
}
}
}
}
// data 읽어올때마다 minimap도 초기화해야함
// 4978로 지정된 것들은 다 5186으로 변경해서 표시
makeRoadKeyMap(jsonObj, projection);
}
//선형지점 수직 point 계산
function getPerpendicularPoint(pointA, pointB, projection = 'EPSG:5186'){
let direction = Cesium.Cartesian3.subtract(pointB, pointA, new Cesium.Cartesian3());
let len = 10;
direction = Cesium.Cartesian3.normalize(direction, direction);
let perpendicularDirection = new Cesium.Cartesian3(direction.y, -direction.x, 0.0);
let perpenPosition = [];
let a = Cesium.Cartesian3.add(pointB, Cesium.Cartesian3.multiplyByScalar(perpendicularDirection, len/2, new Cesium.Cartesian3()),new Cesium.Cartesian3());
a.z = pointB.z;
let b = Cesium.Cartesian3.add(pointB, Cesium.Cartesian3.multiplyByScalar(perpendicularDirection, -len/2, new Cesium.Cartesian3()),new Cesium.Cartesian3());
b.z = pointB.z;
let convertA = proj4(projection).inverse([a.x, a.y]);//4326 변환
let convertB = proj4(projection).inverse([b.x, b.y]);//4326 변환
perpenPosition.push(new Cesium.Cartesian3.fromDegrees(convertA[0], convertA[1], a.z));
perpenPosition.push(new Cesium.Cartesian3.fromDegrees(convertB[0], convertB[1], b.z));
return perpenPosition;
}
//roadkeyMap 만들기
function makeRoadKeyMap(json, projection){
let svg = document.getElementById('key-map');
let list = document.getElementById('key-map-list');
list.innerHTML = ``;
//전체 point
let totalPositionX = [];
let totalPositionY = [];
let pathArray = [];
let color = ['#FFFFFF', '#FF3A2D','#FFAD33','#5CD25F','#1EC6FF'];
let idx = -1;
json.forEach(road=>{
idx++;
let pathData = "M ";
for(let i = 0; i < road.StationPoints.length; i++){
if(projection !== 'EPSG:4978'){
totalPositionX.push(road.StationPoints[i].Point.X);
totalPositionY.push(road.StationPoints[i].Point.Y);
let x = road.StationPoints[i].Point.X;
let y = road.StationPoints[i].Point.Y;
pathData += `${x},${y} `;
}else{
let carto = Cesium.Cartographic.fromCartesian(new Cesium.Cartesian3(road.StationPoints[i].Point.X,road.StationPoints[i].Point.Y,road.StationPoints[i].Point.Z));
let conv = proj4('EPSG:4326', 'EPSG:5186', [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)])
// totalPositionX.push(carto.longitude);
// totalPositionY.push(carto.latitude);
// let x = carto.longitude;
// let y = carto.latitude;
totalPositionX.push(conv[0]);
totalPositionY.push(conv[1]);
let x = conv[0];
let y = conv[1];
pathData += `${x},${y} `;
}
}
let path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", pathData);
path.setAttribute("stroke", color[idx%5]);
path.setAttribute("fill", "none");
path.setAttribute("stroke-width", '10');
path.id = `road-${road.RoadId}`;
path.name = road.RoadName;
path.style.cursor = 'pointer';
// path.setAttribute("transform", `scale(1, 0.998)`);
// path.setAttribute("transform", `translate(0, -1000)`);
pathArray.push(path);
//list
// list.innerHTML += `<div id="road-${road.RoadId}" style="color:${color[idx%5]};">${road.RoadName}</div>`;
list.innerHTML += `<li id="road-${road.RoadId}" style="color:${color[idx%5]};">
<img class="icon" src="./svg/icon-label-dot-white.svg" alt="icon-label-dot-white">
<h4>${road.RoadName}</h4>
</li>`;
//path이벤트
path.addEventListener('mouseover', ()=>{
path.setAttribute('stroke-width', '90');
list.querySelector(`#road-${road.RoadId} h4`).style.fontWeight = '900';
})
path.addEventListener('mouseout', ()=>{
path.setAttribute("stroke-width", '10');
list.querySelector(`#road-${road.RoadId} h4`).style.fontWeight = '300';
})
path.addEventListener('click',()=>{
//해당 선 선택
setRangeBar(path.name);
document.getElementById('clipping-key-map').style.display = 'none';
})
let selectedPath;
// list이벤트
list.querySelectorAll('li').forEach(item=>{
item.addEventListener('mouseover',()=>{
for(let i = 0; i < pathArray.length; i++){
if(pathArray[i].id == item.id){
pathArray[i].setAttribute('stroke-width', '90');
}
}
item.querySelector('h4').style.fontWeight = '900';
})
item.addEventListener('mouseout',()=>{
for(let i = 0; i < pathArray.length; i++){
if(pathArray[i].id == item.id){
pathArray[i].setAttribute('stroke-width', '10');
}
}
item.querySelector('h4').style.fontWeight = '300';
})
item.addEventListener('click',()=>{
//해당 선 선택
for(let i = 0; i < pathArray.length; i++){
if(pathArray[i].id == item.id){
setRangeBar(pathArray[i].name);
document.getElementById('clipping-key-map').style.display = 'none';
}
}
})
});
});
//전체 station의 최대, 최소
let minX = totalPositionX.reduce((min, current) => Math.min(min, current), Infinity) - 50;
let maxX = totalPositionX.reduce((max, current) => Math.max(max, current), -Infinity) + 50;
let minY = totalPositionY.reduce((min, current) => Math.min(min, current), Infinity) - 50;
let maxY = totalPositionY.reduce((max, current) => Math.max(max, current), -Infinity) + 50;
svg.setAttribute('viewBox', `${minX} ${minY} ${maxX-minX} ${maxY-minY}`);
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
svg.setAttribute('transform', `scale(1, -1)`);
for(let i = 0; i<pathArray.length;i++){
svg.appendChild(pathArray[i]);
}
}
// 선형 클리핑 슬라이더 init
function setRangeBar(roadName){
document.getElementById('compare-road').innerText = roadName;
let data = Object.keys(hmCesium.compareRoadData[roadName]);
let intData = data.map(Number);
let min = intData.reduce((currentMin, value) => Math.min(currentMin, value), Infinity);
let max = intData.reduce((currentMax, value) => Math.max(currentMax, value), -Infinity);
document.querySelector('#compare-toolbar input').max = max;
document.querySelector('#compare-toolbar input').min = min;
document.querySelector('#compare-toolbar input').value = min;
document.querySelector('#compare-toolbar input').style.background = `linear-gradient(to right, #fff ${0}%, #aaa ${0}%)`;
setClipping(roadName,min);
}
// 스테이션 선택 이벤트(클리핑)
function setClipping(roadName, stationNum){
Object.keys(hmCesium.projectManager.tileset.main).forEach(item=>{
if(hmCesium.projectManager.tileset.main[item] != undefined && !(hmCesium.projectManager.tileset.main[item] instanceof Array)){
createClippingPlane(hmCesium.projectManager.tileset.main[item], roadName, stationNum);
}
})
document.querySelector('#station-number').innerText = stationNum;
if(hmCesium.cameraFollowClipping){
cameraFollowClipping(roadName, stationNum);
}
}
//카메라 따라가기
function cameraFollowClipping(roadName, stationNum){
if(hmCesium.compareRoadData[roadName][stationNum] == undefined) return;
let pointA = hmCesium.compareRoadData[roadName][stationNum][0];
let pointB = hmCesium.compareRoadData[roadName][stationNum][1];
let normal = getPlanNormal(pointA, pointB);
Cesium.Cartesian3.normalize(normal, normal);
let centerPosition = getCenterPosition([pointA, pointB]);
let moveDist = (hmCesium.reverseClipping)?75:-75;
let moveVec = Cesium.Cartesian3.multiplyByScalar(normal, moveDist, new Cesium.Cartesian3());
let cameraPosition = Cesium.Cartesian3.add(centerPosition, moveVec, new Cesium.Cartesian3());
let cameraCarto = Cesium.Cartographic.fromCartesian(cameraPosition);
let up_cameraP = Cesium.Cartesian3.fromRadians(cameraCarto.longitude, cameraCarto.latitude, cameraCarto.height + 7);
let dir = Cesium.Cartesian3.subtract(centerPosition, up_cameraP,new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(dir,dir);
let up = calcUpVec(pointA, pointB, up_cameraP);
hmCesium.viewer.camera.setView({
destination : up_cameraP,
orientation : {
direction : dir,
up: up
}
})
}
//카메라 upVec 계산
function calcUpVec(A, B, cam){
const bCam = Cesium.Cartesian3.subtract(B, cam, new Cesium.Cartesian3());
const aCam = Cesium.Cartesian3.subtract(A, cam, new Cesium.Cartesian3());
let normal = Cesium.Cartesian3.cross(aCam, bCam, new Cesium.Cartesian3());
if(hmCesium.reverseClipping){
Cesium.Cartesian3.multiplyByScalar(normal, -1, normal);
}
Cesium.Cartesian3.normalize(normal, normal);
return normal;
}
//clipping plane 생성 후 적용(tileset, roadName, stationNum)
function createClippingPlane(tileset, roadName, stationNum){
if(!hmCesium.compareRoadData || hmCesium.compareRoadData[roadName][stationNum] == undefined) return;
let isRight = false;
// 클리핑은 main만 진행
// if(
// tileset == hmCesium.projectManager.tileset.split ||
// tileset == hmCesium.projectManager.tileset.custom ||
// tileset == hmCesium.projectManager.tileset.custom5 ||
// tileset == hmCesium.projectManager.tileset.custom6 ||
// tileset == hmCesium.projectManager.tileset.custom7
// )
// isRight = true;
if(!tileset.clippingPlanes){
tileset.clippingPlanes = new Cesium.ClippingPlaneCollection({
planes:[],
unionClippingRegions:false,
edgeWidth:8.0,//모서리 굵기
edgeColor:(isRight)?Cesium.Color.RED:Cesium.Color.BLUE,//모서리 색상
enabled:true,
// modelMatrix : Cesium.Matrix4.inverse(tileset._clippingPlanesOriginMatrix, new Cesium.Matrix4()),
// modelMatrix : transform,
});
}
let pointA = hmCesium.compareRoadData[roadName][stationNum][0];
let pointB = hmCesium.compareRoadData[roadName][stationNum][1];
//typeA
// let inverse = Cesium.Matrix4.inverse(tileset._clippingPlanesOriginMatrix, new Cesium.Matrix4());
// let convertA = Cesium.Matrix4.multiplyByPoint(inverse, pointA, new Cesium.Cartesian3());
// let convertB = Cesium.Matrix4.multiplyByPoint(inverse, pointB, new Cesium.Cartesian3());
// let normal2 = getPlanNormal(convertA, convertB);
// Cesium.Cartesian3.normalize(normal2, normal2);
// let distance2 = getPlanDistance(normal2, getCenterPosition([convertA, convertB]));
// let plane2 = new Cesium.ClippingPlane(normal2, distance2);
// console.log(plane2);
//typeB
let normal = getPlanNormal(pointA, pointB);
Cesium.Cartesian3.normalize(normal, normal);
if(hmCesium.reverseClipping){
Cesium.Cartesian3.multiplyByScalar(normal, -1, normal);
}
let distance = getPlanDistance(normal, getCenterPosition([pointA, pointB]));
let plane = new Cesium.ClippingPlane(normal, distance);
//collection에 modelMatrix를 적용하면 흔들리는 현상때문에 미리 transform을 곱한 상태로 collection에 밀어넣기
plane = Cesium.Plane.transform(plane, Cesium.Matrix4.inverse(tileset._clippingPlanesOriginMatrix, new Cesium.Matrix4()))
//testType
// Cesium.Matrix4.multiplyByPoint(inverse, normal, normal);
// let distPoint = getCenterPosition([pointA, pointB]);
// let convertPoint = Cesium.Matrix4.multiplyByPoint(inverse, distPoint, new Cesium.Cartesian3());
// let dist = getPlanDistance(normal, convertPoint);
// let plane3 = new Cesium.ClippingPlane(normal, dist);
// console.log(plane3);
//typeC
// let formal = new Cesium.Cartesian3(pointA.x,pointA.y,pointA.z-1);
// let convertA = Cesium.Cartesian3.subtract(pointA, formal, new Cesium.Cartesian3());
// let convertB = Cesium.Cartesian3.subtract(pointB, formal, new Cesium.Cartesian3());
// let normal = getPlanNormal(convertA, convertB);
// Cesium.Cartesian3.normalize(normal, normal);
// let distance = getPlanDistance(normal, getCenterPosition([convertA, convertB]));
// let plane = new Cesium.ClippingPlane(normal, distance);
// let transform = Cesium.Matrix4.fromTranslation(formal, new Cesium.Matrix4());
// let collection = new Cesium.ClippingPlaneCollection({
// planes:[plane],
// unionClippingRegions:false,
// edgeWidth:1.0,//모서리 굵기
// edgeColor:Cesium.Color.RED,//모서리 색상
// enabled:true,
// modelMatrix : Cesium.Matrix4.inverse(tileset._clippingPlanesOriginMatrix, new Cesium.Matrix4()),
// // modelMatrix : transform,
// });
// tileset._clippingPlanesOriginMatrix = Cesium.Matrix4.IDENTITY;
// tileset.clippingPlanes = collection;
//새롭게 적용하면 _target오류가 발생하기 때문에 clippingPlane만 만들어서 collection에 넣어주는 형태로 변경
tileset.clippingPlanes.removeAll();
tileset.clippingPlanes.add(plane);
// tileset.clippingPlanes.modelMatrix = Cesium.Matrix4.inverse(tileset._clippingPlanesOriginMatrix, new Cesium.Matrix4())
}
//clipping plan distance 계산
function getPlanDistance(normal, center){
let globalCenteredPlane = new Cesium.Plane(normal, 0);
let distance = Cesium.Plane.getPointDistance(globalCenteredPlane, center);
// return -(distance-5);
return -distance;
}
//clipping plan normal 계산(진행방향 기준 왼쪽 오른쪽 정해줘야함. default 왼쪽 clipping_CCW) => 면이 표출되는 normal(clipping될 normal)
function getPlanNormal(pointA, pointB, direction='right'){
let pointACarto = Cesium.Cartographic.fromCartesian(pointA);
let pointBCarto = Cesium.Cartographic.fromCartesian(pointB);
//두 점의 높이를 높여 총 4개의 점으로 normal계산(left일때 진행방향은 pointA->pointA_h->pointB_h->pointB)
let pointA_h = Cesium.Cartesian3.fromRadians(pointACarto.longitude, pointACarto.latitude, pointACarto.height + 10);
let pointB_h = Cesium.Cartesian3.fromRadians(pointBCarto.longitude, pointBCarto.latitude, pointBCarto.height + 10);
let array=[];
if(direction == 'left'){
array.push(pointA);
array.push(pointA_h);
array.push(pointB_h);
array.push(pointB);
}else{ //right normal
array.push(pointB);
array.push(pointB_h);
array.push(pointA_h);
array.push(pointA);
}
let dirA = Cesium.Cartesian3.subtract(array[1], array[0], new Cesium.Cartesian3());
let dirB = Cesium.Cartesian3.subtract(array[2], array[0], new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(dirA, dirA);
Cesium.Cartesian3.normalize(dirB, dirB);
let normal = Cesium.Cartesian3.cross(dirA,dirB,new Cesium.Cartesian3());
return normal;
}
//array의 중심점 반환
function getCenterPosition(array){
let len = array.length;
let sum = new Cesium.Cartesian3();
for(let i =0; i< len; i++){
Cesium.Cartesian3.add(sum, array[i], sum);
}
Cesium.Cartesian3.divideByScalar(sum, len, sum);
return sum;
}
//카메라 따라가기 on/off
document.getElementById('clipping-camera-follow').addEventListener('click',()=>{
document.getElementById('clipping-camera-follow').classList.toggle('on');
hmCesium.cameraFollowClipping = document.getElementById('clipping-camera-follow').classList.contains('on');
hmCesium.viewer.scene.screenSpaceCameraController.enableCollisionDetection = !hmCesium.cameraFollowClipping;
setClipping(document.getElementById('compare-road').innerText,document.querySelector('#compare-toolbar input').value);
})
//카메라 반전
document.getElementById('clipping-inverse').addEventListener('click',()=>{
document.getElementById('clipping-inverse').classList.toggle('on');
hmCesium.reverseClipping = document.getElementById('clipping-inverse').classList.contains('on');
setClipping(document.getElementById('compare-road').innerText,document.querySelector('#compare-toolbar input').value);
})
//플레이
document.getElementById('clipping-camera-play').addEventListener('click',()=>{
document.getElementById('clipping-camera-play').classList.toggle('on');
hmCesium.autoPlay = document.getElementById('clipping-camera-play').classList.contains('on');
if(document.getElementById('clipping-camera-play').classList.contains('on')){//on
if(!document.getElementById('clipping-camera-follow').classList.contains('on')){
document.getElementById('clipping-camera-follow').click();
}
setTimeout(autoPlay, 15);
}else{//off
if(document.getElementById('clipping-camera-follow').classList.contains('on')){
document.getElementById('clipping-camera-follow').click();
}
}
})
//카메라 오토play
function autoPlay(){
if(hmCesium.autoPlay){
let input = document.querySelector('#compare-toolbar input');
if(parseInt(input.max) > parseInt(input.value)){
input.value++;
setClipping(document.getElementById('compare-road').innerText,input.value);
setTimeout(autoPlay,15);
}else{
document.getElementById('auto-play').click();
}
}
}
//선형슬라이더
document.querySelector('#compare-toolbar input').addEventListener('input',(e)=>{
setClipping(document.getElementById('compare-road').innerText,e.target.value);
})
//목록보이기
document.getElementById('compare-road').addEventListener('click',()=>{
if(document.getElementById('clipping-key-map').style.display == 'flex'){
document.getElementById('clipping-key-map').style.display = 'none';
}else{
document.getElementById('clipping-key-map').style.display = 'flex';
}
});
document.getElementById('compare-road2').addEventListener('click',()=>{
if(document.getElementById('clipping-key-map').style.display == 'flex'){
document.getElementById('clipping-key-map').style.display = 'none';
}else{
document.getElementById('clipping-key-map').style.display = 'flex';
}
});
//목록 닫기버튼
document.querySelector('#clipping-key-map .window-img-container img.icon').addEventListener('click',()=>{
document.getElementById('clipping-key-map').style.display = 'none';
})
//투명도
document.getElementById('func-opacity-btn').addEventListener('click',(e)=>{
if(document.getElementById('func-opacity').style.display == 'none'){
resetAllMode();
document.getElementById('func-opacity').style.display = 'flex';
document.querySelectorAll('.list-box-section input[type="range"]').forEach(ele=>{
ele.value = 0;
});
document.querySelectorAll('.list-box-section input[type="color"]').forEach(ele=>{
ele.value = '#ffffff';
});
}else{
document.getElementById('func-opacity').querySelector('input').value = 0;
document.getElementById('func-opacity').querySelector('p').innerHTML = '0%';
document.getElementById('func-opacity').style.display = 'none';
}
let keys = Object.keys(hmCesium.projectManager.tileset.main);
for(let i = 0; i < keys.length; i++){
hmCesium.changeOpacity(hmCesium.projectManager.tileset.main[keys[i]], 0);
}
document.getElementById('func-opacity-btn').classList.toggle('on');
});
//투명도 조절
document.getElementById('func-opacity').querySelector('input').addEventListener('input', (e)=>{
let value = e.target.value;
e.target.parentNode.parentNode.querySelector('p').innerHTML = value + '%';
if(value == 1) value = 0.01;
let keys = Object.keys(hmCesium.projectManager.tileset.main);
for(let i = 0; i < keys.length; i++){
hmCesium.changeOpacity(hmCesium.projectManager.tileset.main[keys[i]], value);
// hmCesium.projectManager.tileset.main[keys[i]].style = new Cesium.Cesium3DTileStyle({
// color : `color("${hmCesium.projectManager.tileset.main[keys[i]].color?hmCesium.projectManager.tileset.main[keys[i]].color:'white'}",${1-value})`
// });
}
});
//측정
document.getElementById('func-measurement-btn').addEventListener('click',()=>{
if(document.getElementById('func-measurement').style.display == 'none'){
resetAllMode();
document.getElementById('func-measurement').style.display = 'flex';
}else{
document.getElementById('func-measurement').style.display = 'none';
}
document.getElementById('func-measurement-btn').classList.toggle('on');
});
//각 버튼 연결
document.getElementById('func-measurement').querySelectorAll('.xs-icon-btn').forEach(ele=>{
ele.addEventListener('click', (e)=>{
let type = e.target.closest('.xs-icon-btn').id;
let id = '';
switch(type){
case 'slope-btn':
id = 'slope';
break;
case 'location-btn':
id = 'location';
break;
case 'distance-btn':
id = 'straightDistance';
break;
case 'horizontal-btn':
id = 'horizontalDistance';
break;
case 'vertical-btn':
id = 'verticalDistance';
break;
case 'measure-delete-btn':
id = 'clear';
break;
/* profile, area 없음 */
}
initMeasure(id);
})
})
// measureLabel div에 preRender 적용해서 지도에 위치 고정
hmCesium.viewer.scene.preRender.addEventListener(()=>{
if(hmCesium.hmUtil.measureLabelDivArr && hmCesium.hmUtil.measureLabelDivArr.length > 0){
for(let i =0; i < hmCesium.hmUtil.measureLabelDivArr.length; i++){
let cesiumCanvas = hmCesium.viewer.canvas.getBoundingClientRect();
let div = hmCesium.hmUtil.measureLabelDivArr[i];
let origin_position = div.cartesian3;
let screen_position = Cesium.SceneTransforms.worldToWindowCoordinates(hmCesium.viewer.scene, origin_position);
if (screen_position) {
let divRect = hmCesium.hmUtil.measureLabelDivArr[i].getBoundingClientRect();
div.style.left = (cesiumCanvas.left + screen_position.x - divRect.width / 2) + 'px';
div.style.top = (cesiumCanvas.top + screen_position.y - (divRect.height + 10)) + 'px';
}
}
}
});
// 측정 모드 진입
function initMeasure(id) {
// hmCesium.hmUtil에 measureLayer 생성
if(!hmCesium.hmUtil.measureLayer){
hmCesium.hmUtil.measureLayer = getDataSource('measureLayer');
hmCesium.hmUtil.measureLabelDivArr = [];
hmCesium.hmUtil.measureLabelEntityArr = [];
hmCesium.hmUtil.measureGeometryEntityArr = [];
}
// 기존 측정 이벤트 해제 및 진행중이던 측정 객체 삭제
measureEnd();
if (id == 'clear' || id == 'close') {
measureClear();
} else {
// 마우스 커서 div 추가
document.body.addEventListener('mousemove', changeCursor);
if (id == 'location') measureLoaction();
else if (id == 'straightDistance') measureStraightDistance();
else if (id == 'horizontalDistance') measureHorizontalDistance();
else if (id == 'verticalDistance') measureVerticalDistance();
else if (id == 'area') measureArea();
// else if (id == 'profile') measureProfile();
else if (id == 'slope') measureSlope();
}
}
//경사도 측정
function measureSlope(){
hmCesium.slopeContinue = true;
hmCesium.slopeResult = [];
hmCesium.viewer.screenSpaceEventHandler.setInputAction(slopePickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
let option = {
name : 'slope',//이름
color : '#00ffff',//색상
aerial : { // 표출할 거리 = 표출 속성(text)
text:`@value m`, //값 @value로 표시
textShow : false,
lineShow : true,
},
resultShow : false, //최종결과 show
callback : {
onDrawing : function(entity) {
entity.stopEditing();
hmCesium.endDraw();
hmCesium.originSlope = entity.polyline.positions._value;
entity.entityCollection.remove(entity);
let fPosition_carto = Cesium.Cartographic.fromCartesian(entity.polyline.positions._value[0]);
let sPosition_carto = Cesium.Cartographic.fromCartesian(entity.polyline.positions._value[1]);
let option = {
name : 'test',
color : '#00ffff',
position : [
[Cesium.Math.toDegrees(fPosition_carto.longitude),Cesium.Math.toDegrees(fPosition_carto.latitude), fPosition_carto.height],
[Cesium.Math.toDegrees(sPosition_carto.longitude),Cesium.Math.toDegrees(sPosition_carto.latitude), sPosition_carto.height],
]
}
setTimeout(()=>{
hmCesium.setPolyline(option);
let m_slope = Cesium.Cartesian3.distance(entity.polyline.positions._value[0], Cesium.Cartesian3.fromRadians(sPosition_carto.longitude, sPosition_carto.latitude, fPosition_carto.height)) / Math.abs(fPosition_carto.height - sPosition_carto.height);
// let labelPosition = Cesium.Cartesian3.midpoint(entity.polyline.positions._value[0],entity.polyline.positions._value[1], new Cesium.Cartesian3());
let labelPosition = entity.polyline.positions._value[1];
// let text = `<div class="slope-circle" style="background-color: #00ffff"></div>측정 경사 1 : ${m_slope.toFixed(2)} </br>
// <div class="slope-circle" style="background-color: #ff0000"></div>최단 경사 평균 ${(hmCesium.slopeResult.length>0)?('1 : '+calcAvg(hmCesium.slopeResult).toFixed(2)):' - 최단 경사 측정 실패'}`;
let text = `
<div class="point-title">
<div class="point-wrap">
<p>측정경사</p>
<h3>1 : ${m_slope.toFixed(2)}</h3>
</div>
<div class="point-wrap">
<p>최단 경사 평균</p>
<h3 class="type-em-red">${(hmCesium.slopeResult.length>0)?('1 : '+calcAvg(hmCesium.slopeResult).toFixed(2)):' - 최단 경사 측정 실패'}</h3>
</div>
</div>
`;
addMeasureLabelDiv(labelPosition, text, Cesium.SceneTransforms.worldToWindowCoordinates(hmCesium.viewer.scene, labelPosition), hmCesium.hmUtil.measureLayer);
stopEditing(undefined, entity);
},500);
},
}
}
hmCesium.getDistance(option);
};
function calcAvg(arr) {
const sum = arr.reduce((acc, val) => acc + val, 0);
return sum / arr.length;
}
function slopePickEvent(e){
let rectLength = 3;
let pointNum = rectLength * 5;
const cartesian = hmCesium.viewer.scene.pickPosition(e.position);
const cartesian_right = hmCesium.viewer.scene.pickPosition(new Cesium.Cartesian2(e.position.x + 20, e.position.y));
const cartesian_left = hmCesium.viewer.scene.pickPosition(new Cesium.Cartesian2(e.position.x - 20, e.position.y));
if(Cesium.defined(cartesian)){
let optionPoint = {
name:'test',
color:'#00ffff',
position : {
latitude:Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cartesian).latitude),
longitude:Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(cartesian).longitude),
height:Cesium.Cartographic.fromCartesian(cartesian).height,
}
}
hmCesium.setPoint(optionPoint);
if(hmCesium.slopeContinue){//첫번째 클릭
hmCesium.pickPosition = [];
let points = makeRectPoint2(cartesian, cartesian_left, cartesian_right, rectLength);
let startLine = splitLine(points.startS, points.startE, pointNum);
let endLine = splitLine(points.endS, points.endE, pointNum);
for(let i = 0; i < pointNum; i++){
if(getIntersectionPoint(startLine[i], endLine[i], rectLength) == undefined)
continue;
hmCesium.pickPosition.push(getIntersectionPoint(startLine[i], endLine[i], rectLength));
}
hmCesium.slopeContinue = false;
}else{//두번째 클릭
hmCesium.viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
///////첫번째 라인
hmCesium.pickPosition.forEach(position =>{
let viewCarto = Cesium.Cartographic.fromCartesian(position);
let option = {
name:'test',
color:'#00ffff',
position : {
latitude:Cesium.Math.toDegrees(viewCarto.latitude),
longitude:Cesium.Math.toDegrees(viewCarto.longitude),
height:viewCarto.height,
}
}
hmCesium.setPoint(option);
})
let startPositionArray = [];
hmCesium.pickPosition.forEach(car3 =>{
startPositionArray.push([car3.x, car3.y, car3.z]);
});
let startConvertedPositionsArray = convertCoord(startPositionArray, '4978', '5186');
// let startInterpolrations = calculatePolynomial(startConvertedPositionsArray, 2);
let startInterpolrations = calculatePolynomialWithRotation(startConvertedPositionsArray, 2);
let start_inter_cartoArray = convertCoord(startInterpolrations, '5186', '4326');
let startSplit = [];
start_inter_cartoArray.forEach(item =>{
startSplit.push(Cesium.Cartesian3.fromDegrees(item[0], item[1], item[2]));
})
let startSplineOption = {
name :'test',
color : '#00ff00',
position:start_inter_cartoArray,
};
hmCesium.setPolyline(startSplineOption);
hmCesium.pickPosition = [];
///////두번째 라인
let points = makeRectPoint2(cartesian, cartesian_left, cartesian_right, rectLength);
let startLine = splitLine(points.startS, points.startE, pointNum);
let endLine = splitLine(points.endS, points.endE, pointNum);
for(let i = 0; i < pointNum; i++){
if(getIntersectionPoint(startLine[i], endLine[i], rectLength) == undefined)
continue;
hmCesium.pickPosition.push(getIntersectionPoint(startLine[i], endLine[i], rectLength));
let viewCarto = Cesium.Cartographic.fromCartesian(hmCesium.pickPosition[hmCesium.pickPosition.length-1]);
let option = {
name:'test',
color:'#00ffff',
position : {
latitude:Cesium.Math.toDegrees(viewCarto.latitude),
longitude:Cesium.Math.toDegrees(viewCarto.longitude),
height:viewCarto.height,
}
}
hmCesium.setPoint(option);
}
///////실제 경사비교
// let startCenter = getLineCenter(startSplit);
let startPoint = getSplitPoint(startSplit, 5);//시작점 5개로
/////////////////////n차 interpolration
let positionArray = [];
hmCesium.pickPosition.forEach(car3 =>{
positionArray.push([car3.x, car3.y, car3.z]);
});
let convertedPositionsArray = convertCoord(positionArray, '4978', '5186');
// let interpolrations = calculatePolynomial(convertedPositionsArray, 2);
let interpolrations = calculatePolynomialWithRotation(convertedPositionsArray, 2);
let inter_cartoArray = convertCoord(interpolrations, '5186', '4326');
let endSplit = [];
inter_cartoArray.forEach(item =>{
endSplit.push(Cesium.Cartesian3.fromDegrees(item[0], item[1], item[2]));
})
let splineOption = {
name :'test',
color : '#00ff00',
position:inter_cartoArray,
};
hmCesium.setPolyline(splineOption);
////////////////////////////////////////
startPoint.forEach(car3=>{
let shortestIdx = 0;
let dist=999;
endSplit.forEach((item, idx)=>{
let curDist = Cesium.Cartesian3.distance(item, car3);
if(curDist < dist){
dist = curDist;
shortestIdx = idx;
}
});
endSplit.forEach((item, idx)=>{
let option = {
name :'test',
color : (idx == shortestIdx)?((shortestIdx == 0|| shortestIdx == endSplit.length-1)? '#000000':'#ff0000'):'#888888',
position:[
[Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(car3).longitude),Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(car3).latitude),Cesium.Cartographic.fromCartesian(car3).height],
[Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(item).longitude),Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(item).latitude),Cesium.Cartographic.fromCartesian(item).height],
],
};
if((idx == shortestIdx)){
hmCesium.setPolyline(option);
}
})
let zeroStart =Cesium.Cartesian3.fromRadians(Cesium.Cartographic.fromCartesian(car3).longitude, Cesium.Cartographic.fromCartesian(car3).latitude);
let zeroEnd = Cesium.Cartesian3.fromRadians(Cesium.Cartographic.fromCartesian(endSplit[shortestIdx]).longitude, Cesium.Cartographic.fromCartesian(endSplit[shortestIdx]).latitude);
let slope = Cesium.Cartesian3.distance(zeroStart,zeroEnd)/Math.abs(Cesium.Cartographic.fromCartesian(car3).height - Cesium.Cartographic.fromCartesian(endSplit[shortestIdx]).height);
//슬로프 계산값
if(!(shortestIdx == 0|| shortestIdx == endSplit.length-1)){
hmCesium.slopeResult.push(slope);
makeSlopeResult(car3, endSplit[shortestIdx], slope,endSplit[shortestIdx-1]);
}
});
}
}
}
// 결과 삼각형 그리기(start, end, result,refPosition)
function makeSlopeResult(start, end, result, refPosition){
let midOnLine = getLineCenter([start, end]);
//외접원 반지름
let radius = Math.sqrt(1 + result * result)/2;
//중점에서 상단 dir
let topDir = calculateNormalVector(start, end, refPosition);
//외접원 중심
let circleCenter = Cesium.Cartesian3.add(midOnLine, Cesium.Cartesian3.multiplyByScalar(topDir, radius*0.10, new Cesium.Cartesian3()),new Cesium.Cartesian3());
let upDir = Cesium.Cartesian3.subtract(end,start,new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(upDir, upDir);
let downDir = Cesium.Cartesian3.multiplyByScalar(upDir, -1, new Cesium.Cartesian3());
let upVertex = Cesium.Cartesian3.add(circleCenter, Cesium.Cartesian3.multiplyByScalar(upDir, radius, new Cesium.Cartesian3()),new Cesium.Cartesian3());
let downVertex = Cesium.Cartesian3.add(circleCenter, Cesium.Cartesian3.multiplyByScalar(downDir, radius, new Cesium.Cartesian3()),new Cesium.Cartesian3());
let upCarto = Cesium.Cartographic.fromCartesian(upVertex);
let downCarto = Cesium.Cartographic.fromCartesian(downVertex);
if(upCarto.height < downCarto.height){
let tempVertex = upVertex;
upVertex = downVertex;
downVertex = tempVertex;
let tempCarto = upCarto;
upCarto = downCarto;
downCarto = tempCarto;
}
let rightAngleVertex = Cesium.Cartesian3.fromRadians(downCarto.longitude, downCarto.latitude, upCarto.height);
let rightAngleCarto = Cesium.Cartographic.fromCartesian(rightAngleVertex);
hmCesium.viewer.entities.add({
polygon: {
hierarchy : [upVertex, downVertex, rightAngleVertex],
material : Cesium.Color.fromCssColorString('rgba(0,0,0,0.25)'),
perPositionHeight : true,
show : true,
}
})
let lineOption = {
color : 'rgba(0,0,0,0.35)',
position : [
[Cesium.Math.toDegrees(upCarto.longitude), Cesium.Math.toDegrees(upCarto.latitude), upCarto.height],
[Cesium.Math.toDegrees(downCarto.longitude), Cesium.Math.toDegrees(downCarto.latitude), downCarto.height],
[Cesium.Math.toDegrees(rightAngleCarto.longitude), Cesium.Math.toDegrees(rightAngleCarto.latitude), rightAngleCarto.height],
[Cesium.Math.toDegrees(upCarto.longitude), Cesium.Math.toDegrees(upCarto.latitude), upCarto.height],
],
}
hmCesium.setPolyline(lineOption);
//결과 text넣기
//height
let heightMid = getLineCenter([downVertex, rightAngleVertex]);
let heightTextDir = Cesium.Cartesian3.subtract(rightAngleVertex, upVertex, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(heightTextDir, heightTextDir);
let heightTextPosition = Cesium.Cartesian3.add(heightMid, Cesium.Cartesian3.multiplyByScalar(heightTextDir,0.07,new Cesium.Cartesian3()),new Cesium.Cartesian3());
let heightTextPositionCarto = Cesium.Cartographic.fromCartesian(heightTextPosition);
let heightTextOption = {
text : '1',
style : 0,
color : '#ffffff',
position : {
latitude : Cesium.Math.toDegrees(heightTextPositionCarto.latitude),
longitude : Cesium.Math.toDegrees(heightTextPositionCarto.longitude),
height : heightTextPositionCarto.height,
},
offset : [0,0],
}
hmCesium.setText(heightTextOption);
//distance
let distMid = getLineCenter([upVertex, rightAngleVertex]);
let distTextDir = Cesium.Cartesian3.subtract(rightAngleVertex, downVertex, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(distTextDir, distTextDir);
let distTextPosition = Cesium.Cartesian3.add(distMid, Cesium.Cartesian3.multiplyByScalar(distTextDir,0.04,new Cesium.Cartesian3()),new Cesium.Cartesian3());
let distTextPositionCarto = Cesium.Cartographic.fromCartesian(distTextPosition);
let distTextOption = {
text : result.toFixed(2),
style : 0,
color : '#ffffff',
position : {
latitude : Cesium.Math.toDegrees(distTextPositionCarto.latitude),
longitude : Cesium.Math.toDegrees(distTextPositionCarto.longitude),
height : distTextPositionCarto.height,
},
offset : [0,0],
}
hmCesium.setText(distTextOption);
}
//normalVec 구하기
function calculateNormalVector(p1, p2, p3) {
// 두 벡터를 계산
let vector1 = Cesium.Cartesian3.subtract(p2, p1, new Cesium.Cartesian3());
let vector2 = Cesium.Cartesian3.subtract(p3, p1, new Cesium.Cartesian3());
// 법선 벡터 계산
let normal = Cesium.Cartesian3.cross(vector1, vector2, new Cesium.Cartesian3());
// 노말 벡터의 단위 벡터로 정규화
Cesium.Cartesian3.normalize(normal, normal);
//혹시나 아래쪽 향할때는 반대 벡터...
let angle = Cesium.Math.toDegrees(Cesium.Cartesian3.angleBetween(p1,normal))
if(angle > 90){
normal = Cesium.Cartesian3.multiplyByScalar(normal, -1, new Cesium.Cartesian3());
}
return normal;
}
function getLineCenter(array){
let halfDist = 0;
for(let i =1;i<array.length;i++){
halfDist += Cesium.Cartesian3.distance(array[i-1], array[i])/2;
}
let idx = 1;
while(halfDist > Cesium.Cartesian3.distance(array[idx-1], array[idx])){
halfDist -= Cesium.Cartesian3.distance(array[idx-1], array[idx]);
idx++;
}
let dir = Cesium.Cartesian3.subtract(array[idx], array[idx-1], new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(dir,dir);
let center = new Cesium.Cartesian3();
Cesium.Cartesian3.add(array[idx-1], Cesium.Cartesian3.multiplyByScalar(dir, halfDist, new Cesium.Cartesian3()), center);
return center;
}
function getSplitPoint(array, len) {
let totalDist = 0;
// 총 거리 계산
for (let i = 0; i < array.length - 1; i++) {
totalDist += Cesium.Cartesian3.distance(array[i], array[i + 1]);
}
// 각 포인트 간의 거리
let term = totalDist / (len - 1);
let splitPoints = [array[0]]; // 첫 번째 포인트 추가
let currentDist = 0;
for (let i = 0; i < array.length - 1; i++) {
let start = array[i];
let end = array[i + 1];
let segmentDist = Cesium.Cartesian3.distance(start, end);
while (currentDist + segmentDist >= term) {
let ratio = (term - currentDist) / segmentDist;
let newPoint = Cesium.Cartesian3.lerp(start, end, ratio, new Cesium.Cartesian3());
splitPoints.push(newPoint);
currentDist = 0; // 거리 초기화
if (splitPoints.length === len) {
return splitPoints;
}
}
currentDist += segmentDist;
}
// 필요한 포인트 수를 충족하지 않은 경우, 마지막 포인트 추가
if (splitPoints.length < len) {
splitPoints.push(array[array.length - 1]);
}
return splitPoints;
}
function splitLine(start, end, pointNum) {
let result = [];
const distance = Cesium.Cartesian3.distance(start, end);
const step = distance / (pointNum-1);
const direction = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(direction, direction);
let currentPoint = Cesium.Cartesian3.clone(start);
result.push(currentPoint);
for (let i = 1; i < pointNum-1; i++) {
const nextPoint = Cesium.Cartesian3.add(currentPoint, Cesium.Cartesian3.multiplyByScalar(direction, step * i, new Cesium.Cartesian3()), new Cesium.Cartesian3());
result.push(nextPoint);
}
result.push(end);
return result;
}
function getIntersectionPoint(start, end, rectLength){
let dir = Cesium.Cartesian3.subtract(end, start, new Cesium.Cartesian3());
let ray = new Cesium.Ray(start, dir);
let intersectionPoint = hmCesium.viewer.scene.pickFromRay(ray);
if(intersectionPoint && Cesium.Cartesian3.distance(start, intersectionPoint.position) < rectLength){
return intersectionPoint.position;
}
}
//array는 모두 이차배열로 만들어서 넣기
function convertCoord(array, from, to){
let result = [];
proj4.defs([
['EPSG:5185', '+proj=tmerc +lat_0=38 +lon_0=125 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
['EPSG:5186', '+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
['EPSG:5187', '+proj=tmerc +lat_0=38 +lon_0=129 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
['EPSG:5188', '+proj=tmerc +lat_0=38 +lon_0=131 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs'],
['EPSG:3124', '+proj=tmerc +lat_0=0 +lon_0=123 +k=0.99995 +x_0=500000 +y_0=0 +ellps=clrk66 +towgs84=-127.62,-67.24,-47.04,3.068,-4.903,-1.578,-1.06 +units=m +no_defs +type=crs'],
['EPSG:32242', '+proj=utm +zone=42 +ellps=WGS72 +towgs84=0,0,4.5,0,0,0.554,0.219 +units=m +no_defs +type=crs'],
['EPSG:32642', '+proj=utm +zone=42 +datum=WGS84 +units=m +no_defs +type=crs'],
['EPSG:4978', '+proj=geocent +datum=WGS84 +units=m +no_defs'],
['EPSG:4326', '+proj=longlat +datum=WGS84 +no_defs'],
])
array.forEach(coordi=>{
result.push(proj4(`EPSG:${from}`,`EPSG:${to}`,coordi));
});
return result;
}
function makeRectPoint2(center, left, right, length){
let leftDir = Cesium.Cartesian3.subtract(left, center, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(leftDir,leftDir);
let rightDir = Cesium.Cartesian3.subtract(right, center, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(rightDir, rightDir);
let left_car3 = Cesium.Cartesian3.add(center, Cesium.Cartesian3.multiplyByScalar(leftDir, length/2, new Cesium.Cartesian3()), new Cesium.Cartesian3());
let right_car3 = Cesium.Cartesian3.add(center, Cesium.Cartesian3.multiplyByScalar(rightDir, length/2, new Cesium.Cartesian3()), new Cesium.Cartesian3());
let left_up = new Cesium.Cartesian3();
Cesium.Cartesian3.normalize(left_car3, left_up);
let right_up = new Cesium.Cartesian3();
Cesium.Cartesian3.normalize(right_car3, right_up);
let front = Cesium.Cartesian3.cross(leftDir, left_up, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(front, front);
let back = Cesium.Cartesian3.cross(rightDir, right_up, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(back, back);
let center_carto = Cesium.Cartographic.fromCartesian(center);
let SS_car3 = Cesium.Cartesian3.add(left_car3, Cesium.Cartesian3.multiplyByScalar(back, length/2, new Cesium.Cartesian3()), new Cesium.Cartesian3());
let ES_car3 = Cesium.Cartesian3.add(left_car3, Cesium.Cartesian3.multiplyByScalar(front, length/2, new Cesium.Cartesian3()), new Cesium.Cartesian3());
let SE_car3 = Cesium.Cartesian3.add(right_car3, Cesium.Cartesian3.multiplyByScalar(back, length/2, new Cesium.Cartesian3()), new Cesium.Cartesian3());
let EE_car3 = Cesium.Cartesian3.add(right_car3, Cesium.Cartesian3.multiplyByScalar(front, length/2, new Cesium.Cartesian3()), new Cesium.Cartesian3());
let SS_carto = Cesium.Cartographic.fromCartesian(SS_car3);
let ES_carto = Cesium.Cartographic.fromCartesian(ES_car3);
let SE_carto = Cesium.Cartographic.fromCartesian(SE_car3);
let EE_carto = Cesium.Cartographic.fromCartesian(EE_car3);
let result = {
startS:Cesium.Cartesian3.fromRadians(SS_carto.longitude, SS_carto.latitude, center_carto.height),
startE:Cesium.Cartesian3.fromRadians(SE_carto.longitude, SE_carto.latitude, center_carto.height),
endS:Cesium.Cartesian3.fromRadians(ES_carto.longitude, ES_carto.latitude, center_carto.height),
endE:Cesium.Cartesian3.fromRadians(EE_carto.longitude, EE_carto.latitude, center_carto.height),
}
return result;
}
//무조건 2차...
function calculatePolynomial(points, degree = 2) {
const X = [];
const Y = [];
points.forEach(point => {
X.push(point[0]);
Y.push(point[1]);
});
const coefficients = excelQuadraticRegression(X, Y);
const splinePoints = [];
const numPoints = 100; // 원하는 점의 개수
const xMin = Math.min(...X);
const xMax = Math.max(...X);
const step = (xMax - xMin) / (numPoints - 1); // 각 점 사이의 간격을 계산
for (let i = 0; i < numPoints; i++) {
const x = xMin + i * step; // 현재 x 값을 계산
const y = coefficients.reduce((acc, coeff, index) =>
acc + coeff * Math.pow(x, index), 0
);
splinePoints.push([x,y,points[0][2]]);
}
return splinePoints;
}
function rotatePoint(point, angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return [
point[0] * cos - point[1] * sin,
point[0] * sin + point[1] * cos,
point[2] // Z축 값은 그대로 유지
];
}
function calculateDirection(points) {
const dx = points[points.length - 1][0] - points[0][0];
const dy = points[points.length - 1][1] - points[0][1];
return Math.atan2(dy, dx);
}
//calculatePolynomial => 첫점에서 마지막점으로 향하는 dir을 x축기준으로 변경
function calculatePolynomialWithRotation(points, degree = 2) {
const direction = calculateDirection(points);
const rotatedPoints = points.map(point => rotatePoint(point, -direction));
const X = rotatedPoints.map(point => point[0]);
const Y = rotatedPoints.map(point => point[1]);
const coefficients = excelQuadraticRegression(X, Y);
const splinePoints = [];
const numPoints = 100;
const xMin = Math.min(...X);
const xMax = Math.max(...X);
const step = (xMax - xMin) / (numPoints - 1);
for (let i = 0; i < numPoints; i++) {
const x = xMin + i * step;
const y = coefficients.reduce((acc, coeff, index) =>
acc + coeff * Math.pow(x, index), 0
);
const originalPoint = rotatePoint([x, y, points[0][2]], direction);
splinePoints.push(originalPoint);
}
return splinePoints;
}
function normalizeData(data) {
const mean = data.reduce((a, b) => a + b, 0) / data.length;
const std = Math.sqrt(data.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / data.length);
return {
normalizedData: data.map(x => (x - mean) / std),
mean,
std
};
}
function denormalizeCoefficients(coeffs, xStats) {
// y = a(x-μ)²/σ² + b(x-μ)/σ + c
// y = a'x² + b'x + c'
const a = coeffs.a / Math.pow(xStats.std, 2);
const b = (-2 * coeffs.a * xStats.mean) / Math.pow(xStats.std, 2) + coeffs.b / xStats.std;
const c = (coeffs.a * Math.pow(xStats.mean, 2)) / Math.pow(xStats.std, 2) -
(coeffs.b * xStats.mean) / xStats.std +
coeffs.c;
return { a, b, c };
}
function improvedMultipleLinearRegression(y, x_array) {
const n = y.length;
const k = x_array.length;
function matrixMultiply(a, b) {
const result = Array(a.length).fill().map(() => Array(b[0].length).fill(0));
return result.map((row, i) => {
return row.map((_, j) => {
return a[i].reduce((sum, elm, k) => sum + (elm * b[k][j]), 0)
})
})
}
function matrixTranspose(matrix) {
return matrix[0].map((_, i) => matrix.map(row => row[i]));
}
function luDecomposition(matrix) {
const n = matrix.length;
const lower = Array(n).fill().map(() => Array(n).fill(0));
const upper = Array(n).fill().map((_, i) => [...matrix[i]]);
for (let i = 0; i < n; i++) {
lower[i][i] = 1;
for (let j = i + 1; j < n; j++) {
const factor = upper[j][i] / upper[i][i];
lower[j][i] = factor;
for (let k = i; k < n; k++) {
upper[j][k] -= factor * upper[i][k];
}
}
}
return { lower, upper };
}
function solveLU(lower, upper, b) {
const n = lower.length;
const y = Array(n).fill(0);
const x = Array(n).fill(0);
// Forward substitution Ly = b
for (let i = 0; i < n; i++) {
y[i] = b[i];
for (let j = 0; j < i; j++) {
y[i] -= lower[i][j] * y[j];
}
}
// Back substitution Ux = y
for (let i = n - 1; i >= 0; i--) {
x[i] = y[i];
for (let j = i + 1; j < n; j++) {
x[i] -= upper[i][j] * x[j];
}
x[i] /= upper[i][i];
}
return x;
}
// 행렬 생성
const X = Array(n).fill().map(() => Array(k + 1).fill(1));
for (let i = 0; i < n; i++) {
for (let j = 0; j < k; j++) {
X[i][j + 1] = x_array[j][i];
}
}
const Xt = matrixTranspose(X);
const XtX = matrixMultiply(Xt, X);
const Xty = matrixMultiply(Xt, y.map(val => [val]));
// LU 분해를 사용한 값 구하기
const { lower, upper } = luDecomposition(XtX);
const coefficients = solveLU(lower, upper, Xty.map(row => row[0]));
// R² 계산
const y_mean = y.reduce((a, b) => a + b, 0) / n;
const y_pred = matrixMultiply(X, coefficients.map(c => [c])).map(row => row[0]);
const ss_tot = y.reduce((a, b) => a + Math.pow(b - y_mean, 2), 0);
const ss_res = y.reduce((a, b, i) => a + Math.pow(b - y_pred[i], 2), 0);
const r_squared = 1 - (ss_res / ss_tot);
return {
coefficients,
rSquared: r_squared
};
}
function excelQuadraticRegression(x, y) {
if (!Array.isArray(x) || !Array.isArray(y)) {
throw new Error('x와 y는 배열이어야 합니다');
}
if (x.length !== y.length) {
throw new Error('x와 y의 길이가 같아야 합니다');
}
if (x.length < 3) {
throw new Error('최소 3개의 데이터 포인트가 필요합니다');
}
// 데이터 정규화
const xStats = normalizeData(x);
const normalized_x = xStats.normalizedData;
// 정규화된 x²값 계산
const x_squared = normalized_x.map(val => val * val);
// 다중 선형 회귀 수행
const result = improvedMultipleLinearRegression(y, [normalized_x, x_squared]);
const [c, b, a] = result.coefficients; // 상수항, x계수, x²계수 순서
// 계수 역정규화
const denormalized = denormalizeCoefficients({a, b, c}, xStats);
return [denormalized.c, denormalized.b, denormalized.a];
}
// 위치 측정
function measureLoaction(){
console.log('위치');
let option = {
name : 'altitude',//이름
color : 'rgba(0, 0, 0, 0.0)',//포인트 색상
epsg : hmCesium.domManager.epsg,//표출 좌표계
altitude : {//=> 나중에 표출할 text 옵션
name : 'altitude',
style : 2, //fill : 0, outline:1, fillandoutline:2
backgroundColor : '#ffffff',//배경색 미입력시 배경없음
color : '#000000',//글자 색상
text : 'Altitude : @coordinate3', //coordinate1 = 변경된 1번째 좌표, coordinate2 = 변경된 2번째 좌표, coordinate3 = 변경된 3번째 좌표 변수 사용할것 앞에는 @표시
textShow : false,
},
callback : {
stopEditing : function(entity) {
stopEditing('location', entity);
}
}
}
hmCesium.getAltitude(option);
}
// 직선거리 측정
function measureStraightDistance() {
console.log('직선거리');
let option = {
name : 'distance',//이름
color : '#00ffff',//포인트 색상
aerial : { // 표출할 거리 = 표출 속성(text)
name : 'aerial distance',
style : 2, //fill : 0, outline:1, fillandoutline:2
backgroundColor : '#ffffff',//배경색 미입력시 배경없음
color : '#00ffff',//글자 색상
// lineColor:'#ff00ff',//aerial line color은 기본 color를 따라감.
text:`@value m`, //값 @value로 표시
textShow : false,
lineShow : true,
},
resultShow : false, //최종결과 show
callback : {
startEditing : function(entity) {
startEditing('straightDistance', entity);
},
onDrawing : function(entity) {
onDrawing('straightDistance', entity);
},
stopEditing : function(entity) {
stopEditing('straightDistance', entity);
}
}
}
hmCesium.getDistance(option);
}
// 수평거리 측정
function measureHorizontalDistance() {
console.log('수평거리');
let option = {
name : 'distance',//이름
type:'horizontal',
color : '#00ffff',//포인트 색상
aerial : { // 표출할 거리 = 표출 속성(text)
name : 'aerial distance',
style : 2, //fill : 0, outline:1, fillandoutline:2
backgroundColor : '#ffffff',//배경색 미입력시 배경없음
color : '#00ffff',//글자 색상
// lineColor:'#ff00ff',//aerial line color은 기본 color를 따라감.
text:`@value m`, //값 @value로 표시
textShow : false,
lineShow : false,
},
horizontal : { // 표출할 거리 = 표출 속성(text, line)
name : 'horizontal distance',
style : 2, //fill : 0, outline:1, fillandoutline:2
backgroundColor : '#ffffff',//배경색 미입력시 배경없음
color : '#00ff00',//글자 색상
lineColor:'#00ff00',//라인컬러
text:`@value m`,
textShow : false,
lineShow : true,
},
vertical : { // 표출할 거리 = 표출 속성(text, line)
name : 'vertical distance',
style : 2, //fill : 0, outline:1, fillandoutline:2
backgroundColor : '#ffffff',//배경색 미입력시 배경없음
color : '#000000',//글자 색상
lineColor:'#000000',//라인컬러
text:`@value m`,
textShow : false,
lineShow : true,
},
resultShow : false, //최종결과 show
callback : {
startEditing : function(entity) {
startEditing('horizontalDistance', entity);
},
onDrawing : function(entity) {
onDrawing('horizontalDistance', entity);
},
stopEditing : function(entity) {
stopEditing('horizontalDistance', entity);
}
}
}
hmCesium.getDistance(option);
}
// 수직거리 측정
function measureVerticalDistance() {
console.log('수직거리');
let option = {
name : 'distance',//이름
type:'vertical',
color : '#00ffff',//포인트 색상
aerial : { // 표출할 거리 = 표출 속성(text)
name : 'aerial distance',
style : 2, //fill : 0, outline:1, fillandoutline:2
backgroundColor : '#ffffff',//배경색 미입력시 배경없음
color : '#00ffff',//글자 색상
// lineColor:'#ff00ff',//aerial line color은 기본 color를 따라감.
text:`@value m`, //값 @value로 표시
textShow : false,
lineShow : false,
},
horizontal : { // 표출할 거리 = 표출 속성(text, line)
name : 'horizontal distance',
style : 2, //fill : 0, outline:1, fillandoutline:2
backgroundColor : '#ffffff',//배경색 미입력시 배경없음
color : '#00ff00',//글자 색상
lineColor:'#000000',//라인컬러
text:`@value m`,
textShow : false,
lineShow : true,
},
vertical : { // 표출할 거리 = 표출 속성(text, line)
name : 'vertical distance',
style : 2, //fill : 0, outline:1, fillandoutline:2
backgroundColor : '#ffffff',//배경색 미입력시 배경없음
color : '#0000ff',//글자 색상
lineColor:'#0000ff',//라인컬러
text:`@value m`,
textShow : false,
lineShow : true,
},
resultShow : false, //최종결과 show
callback : {
startEditing : function(entity) {
startEditing('verticalDistance', entity);
},
onDrawing : function(entity) {
onDrawing('verticalDistance', entity);
},
stopEditing : function(entity) {
stopEditing('verticalDistance', entity);
}
}
}
hmCesium.getDistance(option);
}
// 면적 측정
function measureArea() {
console.log('면적');
let option = {
name : 'area',
color : 'rgba(255, 255, 0, 0.5)', // 폴리곤 색상
area : {
name : 'area text',
text : `Area : @area ㎡\nPerimeter : @perimeter m`,
textShow : false,
style : 2,
backgroundColor : `#ffffff`,
color : `#000000`,
},
callback : {
startEditing : function(entity) {
startEditing('area', entity);
},
stopEditing : function(entity) {
stopEditing('area', entity);
}
}
}
hmCesium.getArea(option);
}
function startEditing(mode, entity) {
hmCesium.hmUtil.measureGeometryEntityArr.push(hmCesium.hmUtil.draw.drawList[hmCesium.hmUtil.draw.drawList.length-1]);
}
function onDrawing(mode, entity) {
let cartesian3, pixelPosition, text;
if (mode == 'straightDistance') {
cartesian3 = Cesium.Cartesian3.midpoint(
entity.polyline.positions._value[entity.polyline.positions._value.length-3],
entity.polyline.positions._value[entity.polyline.positions._value.length-2],
new Cesium.Cartesian3()
);
// text = `${addComma(entity.attribute.aerial[entity.attribute.aerial.length-1])}m`;
text = `<div class="point-title">
<h3>${addComma(entity.attribute.aerial[entity.attribute.aerial.length-1])}m</h3>
</div>`
hmCesium.hmUtil.measureGeometryEntityArr.push(hmCesium.hmUtil.draw.drawList[hmCesium.hmUtil.draw.drawList.length-1]);
}
if (mode == 'horizontalDistance') {
let firstCarto = Cesium.Cartographic.fromCartesian(entity.polyline.positions._value[entity.polyline.positions._value.length-3]);
let secondCarto = Cesium.Cartographic.fromCartesian(entity.polyline.positions._value[entity.polyline.positions._value.length-2]);
let midPoint = entity.attribute.midPointArr[entity.attribute.midPointArr.length-1];
if(firstCarto.height > secondCarto.height){
cartesian3 = Cesium.Cartesian3.midpoint(entity.polyline.positions._value[entity.polyline.positions._value.length-3], midPoint, new Cesium.Cartesian3());
}else{
cartesian3 = Cesium.Cartesian3.midpoint(midPoint, entity.polyline.positions._value[entity.polyline.positions._value.length-2], new Cesium.Cartesian3());
}
// text = `${addComma(entity.attribute.horizontal[entity.attribute.horizontal.length-1])}m`;
text = `<div class="point-title">
<h3>${addComma(entity.attribute.horizontal[entity.attribute.horizontal.length-1])}m</h3>
</div>`
for (let i = 5; i >= 1; i--) {
hmCesium.hmUtil.measureGeometryEntityArr.push(hmCesium.hmUtil.draw.drawList[hmCesium.hmUtil.draw.drawList.length-i]);
}
}
if (mode == 'verticalDistance') {
let firstCarto = Cesium.Cartographic.fromCartesian(entity.polyline.positions._value[entity.polyline.positions._value.length-3]);
let secondCarto = Cesium.Cartographic.fromCartesian(entity.polyline.positions._value[entity.polyline.positions._value.length-2]);
let midPoint = entity.attribute.midPointArr[entity.attribute.midPointArr.length-1];
if(firstCarto.height > secondCarto.height){
cartesian3 = Cesium.Cartesian3.midpoint(entity.polyline.positions._value[entity.polyline.positions._value.length-2], midPoint, new Cesium.Cartesian3());
}else{
cartesian3 = Cesium.Cartesian3.midpoint(midPoint, entity.polyline.positions._value[entity.polyline.positions._value.length-3], new Cesium.Cartesian3());
}
// text = `${addComma(entity.attribute.vertical[entity.attribute.vertical.length-1])}m`;
text = `<div class="point-title">
<h3>${addComma(entity.attribute.vertical[entity.attribute.vertical.length-1])}m</h3>
</div>`
for (let i = 5; i >= 1; i--) {
hmCesium.hmUtil.measureGeometryEntityArr.push(hmCesium.hmUtil.draw.drawList[hmCesium.hmUtil.draw.drawList.length-i]);
}
}
pixelPosition = Cesium.SceneTransforms.worldToWindowCoordinates(hmCesium.viewer.scene, cartesian3);
if (cartesian3) addMeasureLabelDiv(cartesian3, text, pixelPosition, hmCesium.hmUtil.measureLayer);
}
function stopEditing(mode, entity) {
let cartesian3, pixelPosition, text;
if (mode == 'location') {
cartesian3 = entity.attribute.position;
if(hmCesium.domManager.epsg == '4326'){
// text = `
// 위도 : ${addComma(entity.attribute.coordinate[1])}도 <br>
// 경도 : ${addComma(entity.attribute.coordinate[0])}도 <br>
// 높이 : ${addComma(entity.attribute.coordinate[2])}m
// `;
text = `
<div class="point-title">
<div class="point-wrap-fit">
<p>위도</p>
<h3>${addComma(entity.attribute.coordinate[1])}°</h3>
</div>
<div class="point-wrap-fit">
<p>경도</p>
<h3>${addComma(entity.attribute.coordinate[0])}°</h3>
</div>
<div class="point-wrap-fit">
<p>높이</p>
<h3>${addComma(entity.attribute.coordinate[2])}m</h3>
</div>
</div>
`
}else{
// text = `
// X : ${addComma(entity.attribute.coordinate[0])}m <br>
// Y : ${addComma(entity.attribute.coordinate[1])}m <br>
// Z : ${addComma(entity.attribute.coordinate[2])}m
// `;
let epsg = '중부';
switch(hmCesium.domManager.epsg){
case '5185': epsg = '서부'; break;
case '5186': epsg = '중부'; break;
case '5187': epsg = '동부'; break;
case '5188': epsg = '동해'; break;
}
text = `
<div class="point-title">
<h6>${epsg} 기준</h6>
<div class="point-wrap-fit">
<p>X</p>
<h3>${addComma(entity.attribute.coordinate[0])}m</h3>
</div>
<div class="point-wrap-fit">
<p>Y</p>
<h3>${addComma(entity.attribute.coordinate[1])}m</h3>
</div>
<div class="point-wrap-fit">
<p>Z</p>
<h3>${addComma(entity.attribute.coordinate[2])}m</h3>
</div>
</div>
`
}
}
if (mode == 'straightDistance') {
cartesian3 = entity.polyline.positions._value[entity.polyline.positions._value.length-1];
let totalAerial = 0;
for(let i = 0; i < entity.attribute.aerial.length; i++){
totalAerial += Number(entity.attribute.aerial[i].toFixed(4));
}
// text = `직선거리 합계 : ${addComma(totalAerial)}m`;
text = `
<div class="point-title">
<div class="point-wrap">
<p>직선거리 합계</p>
<h3>${addComma(totalAerial)}m</h3>
</div>
</div>
`
}
if (mode == 'horizontalDistance') {
let lastCarto = Cesium.Cartographic.fromCartesian(entity.polyline.positions._value[entity.polyline.positions._value.length-1]);
let lastMidPoint = Cesium.Cartographic.fromCartesian(entity.attribute.midPointArr[entity.attribute.midPointArr.length-1]);
let lon = Cesium.Math.toDegrees(lastCarto.longitude);
let lat = Cesium.Math.toDegrees(lastCarto.latitude);
let height = lastMidPoint.height;
cartesian3 = new Cesium.Cartesian3.fromDegrees(lon, lat, height);
let totalHorizontal = 0;
for(let i = 0; i < entity.attribute.horizontal.length; i++){
totalHorizontal += Number(entity.attribute.horizontal[i].toFixed(4));
}
// text = `수평거리 합계 : ${addComma(totalHorizontal)}m`;
text = `
<div class="point-title">
<div class="point-wrap">
<p>수평거리 합계</p>
<h3>${addComma(totalHorizontal)}m</h3>
</div>
</div>
`
}
if (mode == 'verticalDistance') {
let lastCarto = Cesium.Cartographic.fromCartesian(entity.polyline.positions._value[entity.polyline.positions._value.length-1]);
let lastMidPoint = Cesium.Cartographic.fromCartesian(entity.attribute.midPointArr[entity.attribute.midPointArr.length-1]);
let lon = Cesium.Math.toDegrees(lastCarto.longitude);
let lat = Cesium.Math.toDegrees(lastCarto.latitude);
let height = lastMidPoint.height;
cartesian3 = new Cesium.Cartesian3.fromDegrees(lon, lat, height);
let totalVertical = 0;
for(let i = 0; i < entity.attribute.vertical.length; i++){
totalVertical += Number(entity.attribute.vertical[i].toFixed(4));
}
// text = `수직거리 합계 : ${addComma(totalVertical)}m`;
text = `
<div class="point-title">
<div class="point-wrap">
<p>수직거리 합계</p>
<h3>${addComma(totalVertical)}m</h3>
</div>
</div>
`
}
// if (mode == 'area') {
// let positions = entity.polygon._hierarchy._value.positions.copyWithin();
// let labelPosition = hmCesium.hmUtil.measurement._getPolygonCenter(positions);
// let labelPositionCarto = Cesium.Cartographic.fromCartesian(labelPosition);
// let lon = Cesium.Math.toDegrees(labelPositionCarto.longitude);
// let lat = Cesium.Math.toDegrees(labelPositionCarto.latitude);
// let height = labelPositionCarto.height;
// cartesian3 = new Cesium.Cartesian3.fromDegrees(lon, lat, height);
// text = `
// 면적 : ${addComma(entity.attribute.areaInMeter)}m <br>
// 둘레 : ${addComma(entity.attribute.perimeter)}m
// `;
// }
pixelPosition = Cesium.SceneTransforms.worldToWindowCoordinates(hmCesium.viewer.scene, cartesian3);
if (cartesian3) addMeasureLabelDiv(cartesian3, text, pixelPosition, hmCesium.hmUtil.measureLayer);
hmCesium.hmUtil.measureLabelEntityArr = [];
hmCesium.hmUtil.measureGeometryEntityArr = [];
initCursorAndScreenEvent();
}
function measureEnd() {
// 마우스모드 초기화
hmCesium.endDraw();
if (hmCesium.hmUtil.measureLabelEntityArr.length != 0) {
hmCesium.hmUtil.measureLabelEntityArr.map(measureLabelEntity => {
// measureLayer에서 measureLabelEntityArr의 요소와 동일한 엔티티 삭제
hmCesium.hmUtil.measureLayer.entities._entities._array.map(entity => {
if (entity.id == measureLabelEntity.id) {
hmCesium.hmUtil.measureLayer.entities.removeById(entity.id);
}
})
// measureLabelArr에서 measureLabelEntityArr의 요소와 동일한 div 삭제
hmCesium.hmUtil.measureLabelDivArr.map(measureLabelDiv => {
if (measureLabelDiv == measureLabelEntity.div) {
hmCesium.hmUtil.measureLabelDivArr = hmCesium.hmUtil.measureLabelDivArr.filter((element) => element != measureLabelEntity.div);
}
})
// 등록되어 있는 measureLabel div중에서 measureLabelEntityArr의 요소와 동일한 div 삭제
let divs = document.querySelectorAll('.measureLabel');
divs.forEach(div=>{
if (div == measureLabelEntity.div) {
div.parentNode.removeChild(div);
}
});
})
}
if (hmCesium.hmUtil.measureGeometryEntityArr.length != 0) {
hmCesium.hmUtil.measureGeometryEntityArr.map(measureGeometryEntity => {
// hmCesium.hmUtil.draw.drawList에서 measureGeometryEntityArr의 요소와 동일한 엔티티 삭제
hmCesium.hmUtil.draw.drawList.map(draw => {
if (draw.id == measureGeometryEntity.id) {
hmCesium.viewer.entities.remove(draw);
hmCesium.hmUtil.draw.drawList = hmCesium.hmUtil.draw.drawList.filter((element) => element.id != draw.id);
}
})
})
}
hmCesium.viewer.scene.requestRender();
hmCesium.hmUtil.measureLabelEntityArr = [];
hmCesium.hmUtil.measureGeometryEntityArr = [];
}
// 측정 Clear 및 측정닫기
function measureClear() {
hmCesium.clearDraw();
// 마우스모드 초기화
hmCesium.endDraw();
// hmCesium.hmUtil.measureLayer에 포함된 엔티티 및 measureLayer에 모두 삭제
if (hmCesium.hmUtil.measureLayer) {
hmCesium.hmUtil.measureLayer.entities.removeAll();
delete hmCesium.hmUtil.measureLayer;
}
hmCesium.viewer.entities.removeAll();
hmCesium.viewer.scene.requestRender();
// 측정 라벨 div 전부 삭제
let divs = document.querySelectorAll('.measureLabel');
divs.forEach(div=>{
div.parentNode.removeChild(div);
});
hmCesium.hmUtil.measureLabelDivArr = [];
initCursorAndScreenEvent();
}
// 숫자 천 단위 콤마 추가, 소수점 넷 째 자리까지 표시
function addComma(val) {
let result = val.toFixed(4).toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
return result;
}
// 측정 라벨 div, 포인트 추가
function addMeasureLabelDiv(cartesian3, text, pixelPosition, layer){
let canvas = hmCesium.viewer.canvas.getBoundingClientRect();
let div = document.createElement('div');
div.cartesian3 = cartesian3;
div.innerHTML = text;
div.style.position = 'absolute';
div.style.left = canvas.left + pixelPosition.x + 'px';
div.style.top = canvas.top + pixelPosition.y + 'px';
div.classList.add('measureLabel');
document.body.appendChild(div);
hmCesium.hmUtil.measureLabelDivArr.push(div);
let label = layer.entities.add({
name: "",
position : cartesian3,
point: {
pixelSize: 4,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
clampToGround: false,
},
div: div
});
hmCesium.viewer.scene.requestRender();
hmCesium.hmUtil.measureLabelEntityArr.push(label);
}
//dataSource 가져오기
function getDataSource(pName, bMake) {
if(!hmCesium.viewer) return;
let layer = hmCesium.viewer.dataSources.getByName(pName);
if(layer.length > 0) return layer[0];
else{
if(!bMake) bMake = true;
if(bMake == true) {
layer = new Cesium.CustomDataSource(pName);
hmCesium.viewer.dataSources.add(layer);
return layer;
}
}
}
//라벨 생성
document.getElementById('func-label-btn').addEventListener('click',async (e)=>{
if(document.getElementById('func-label').style.display == 'none'){
resetAllMode();
document.getElementById('func-label').style.display = 'flex';
document.getElementById('func-label-bar').style.display = 'flex';
if(!document.getElementById('label-layer-btn').querySelector('input').checked){
document.getElementById('label-layer-btn').querySelector('input').checked = true;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('label-layer-btn').querySelector('input').dispatchEvent(event);
}
let ul = document.querySelector('#func-label ul');
ul.innerHTML = ``;
if(!hmCesium.labels){
hmCesium.labels = await readJSONFile(hmCesium.main.model[hmCesium.curModel].dLayer['label']);
}
for(let i = 0; i < hmCesium.labels.length; i++){
let li = document.createElement('li');
li.innerHTML = `<img class="icon" src="./svg/icon-label-dot-white.svg" alt="icon-label-dot-white">
<h4>${hmCesium.labels[i].text}</h4>`;
ul.appendChild(li);
li.addEventListener('click',()=>{
let option = {
location : [hmCesium.labels[i].longitude, hmCesium.labels[i].latitude, hmCesium.labels[i].height * 10, 0, -1.57],
duration : 1.5,
}
hmCesium.camera_flyTo(option);
})
}
//라벨 클릭 이벤트
hmCesium.viewer.screenSpaceEventHandler.setInputAction(labelClickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}else{
document.getElementById('func-label').style.display = 'none';
document.getElementById('func-label-bar').style.display = 'none';
initCursorAndScreenEvent();
}
document.getElementById('func-label-btn').classList.toggle('on');
})
//라벨 클릭 이벤트
function labelClickEvent(e){
const pickedObject = hmCesium.viewer.scene.pick(e.position,1,1);
if(pickedObject.primitive && pickedObject.primitive.info && pickedObject.primitive.info.id.includes('label')){
openLabelProperty(pickedObject.primitive.info);
}
}
//라벨 생성창 호출
document.getElementById('label-add-btn').addEventListener('click',()=>{
document.body.addEventListener('mousemove', changeCursor);
hmCesium.viewer.screenSpaceEventHandler.setInputAction((e)=>{
const cartesian = hmCesium.viewer.scene.pickPosition(e.position);
if(Cesium.defined(cartesian)){
//위치 전달, 생성창 열기
openLabelProperty(undefined, cartesian);
initCursorAndScreenEvent();
//라벨 클릭 이벤트 다시 달아주기
hmCesium.viewer.screenSpaceEventHandler.setInputAction(labelClickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
})
//라벨 속성창
async function openLabelProperty(label,position){
//속성창 오픈
document.getElementById('func-label-add').style.display = 'flex';
let text = document.getElementById('modal-label-text');//input
let size = document.getElementById('modal-label-size');//radio
let color = document.getElementById('modal-label-color');//radio
let alpha = document.getElementById('modal-label-alpha');//range
color.querySelector('#last-swatch').value = '#000000';
color.querySelector('#last-swatch').nextElementSibling.style.backgroundColor = '#000000';
if(label){//라벨 info가 있는경우 속성불러와서 표출
text.value = label.text;
size.querySelector(`input[value="${label.size}"]`).checked = true;
if(color.querySelector(`input[value="${label.bgColor}"]`)){
color.querySelector(`input[value="${label.bgColor}"]`).checked = true;
}else{
color.querySelector('#last-swatch').value = `${label.bgColor}`;
color.querySelector('#last-swatch').nextElementSibling.style.backgroundColor = `${label.bgColor}`;
color.querySelector('#last-swatch').checked = true;
}
alpha.value = (1-label.alpha);
alpha.style.background = `linear-gradient(to right, #fff ${(1-label.alpha)*100}%, #aaa ${(1-label.alpha)*100}%)`;
text.labelId = label.id;
text.latitude = label.latitude;
text.longitude = label.longitude;
text.labelHeight = label.height;
}else{//없는 경우 초기화
text.value = '';
size.querySelector('input[value="25"]').checked = true;
color.querySelector(`input[value="#000000"]`).checked = true;
alpha.value = 0;
alpha.style.background = `linear-gradient(to right, #fff ${0}%, #aaa ${0}%)`;
let carto = Cesium.Cartographic.fromCartesian(position);
text.latitude = Cesium.Math.toDegrees(carto.latitude);
text.longitude = Cesium.Math.toDegrees(carto.longitude);
text.labelHeight = carto.height;
text.labelId = undefined;
}
}
//라벨 속성창 닫기
document.getElementById('func-label-add').querySelector('img').addEventListener('click',()=>{
document.getElementById('func-label-add').style.display = 'none';
})
//라벨 속성창 삭제
document.getElementById('modal-label-delete').addEventListener('click',async ()=>{
let text = document.getElementById('modal-label-text');
let id = text.labelId;
let idx = findLabelIdx(id);
let label = hmCesium.labels[idx];
//삭제컨펌
if(!confirm(`라벨 "${(label)?label.text:document.getElementById('modal-label-text').value}" 정말 삭제합니까?`)) return;
if(idx != undefined){
hmCesium.labels.splice(idx,1);
}
await setLabel();
//라벨리스트 최신화
hmCesium.labels = await readJSONFile(hmCesium.main.model[hmCesium.curModel].dLayer['label']);
let ul = document.querySelector('#func-label ul');
ul.innerHTML = ``;
for(let i = 0; i < hmCesium.labels.length; i++){
let li = document.createElement('li');
li.innerHTML = `<img class="icon" src="./svg/icon-label-dot-white.svg" alt="icon-label-dot-white">
<h4>${hmCesium.labels[i].text}</h4>`;
ul.appendChild(li);
li.addEventListener('click',()=>{
let option = {
location : [hmCesium.labels[i].longitude, hmCesium.labels[i].latitude, hmCesium.labels[i].height * 10, 0, -1.57],
duration : 1.5,
}
hmCesium.camera_flyTo(option);
})
}
//지도창 라벨 다시 그리기
if(document.getElementById('label-layer-btn').querySelector('input').checked){
document.getElementById('label-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('label-layer-btn').querySelector('input').dispatchEvent(event);
}
document.getElementById('label-layer-btn').querySelector('input').checked = true;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('label-layer-btn').querySelector('input').dispatchEvent(event);
document.getElementById('func-label-add').style.display = 'none';
})
//라벨 속성창 적용
document.getElementById('modal-label-submit').addEventListener('click',async ()=>{
let text = document.getElementById('modal-label-text');
let id = text.labelId;
if(document.getElementById('modal-label-text').value == ''){
alert('텍스트를 꼭 입력해주세요.');
return;
}
let label = {
id:id,
text:document.getElementById('modal-label-text').value,
size:document.getElementById('modal-label-size').querySelector('input:checked').value,
bgColor:document.getElementById('modal-label-color').querySelector('input:checked').value,
alpha:1-document.getElementById('modal-label-alpha').value,
latitude:text.latitude,
longitude:text.longitude,
height:text.labelHeight,
projectId : hmCesium.main.projectCode,
modelId: hmCesium.curModel,
type:"label",
createDate:getToday()
}
if(id){
let idx = findLabelIdx(id);
hmCesium.labels[idx] = label;
}else{//신규
if(hmCesium.labels.length > 0)
label.id = `label`+(parseInt(hmCesium.labels[hmCesium.labels.length-1].id.split('label')[1])+1);
else
label.id = `label0`;
hmCesium.labels.push(label);
}
await setLabel();
//라벨리스트 최신화
hmCesium.labels = await readJSONFile(hmCesium.main.model[hmCesium.curModel].dLayer['label']);
let ul = document.querySelector('#func-label ul');
ul.innerHTML = ``;
for(let i = 0; i < hmCesium.labels.length; i++){
let li = document.createElement('li');
li.innerHTML = `<img class="icon" src="./svg/icon-label-dot-white.svg" alt="icon-label-dot-white">
<h4>${hmCesium.labels[i].text}</h4>`;
ul.appendChild(li);
li.addEventListener('click',()=>{
let option = {
location : [hmCesium.labels[i].longitude, hmCesium.labels[i].latitude, hmCesium.labels[i].height * 10, 0, -1.57],
duration : 1.5,
}
hmCesium.camera_flyTo(option);
})
}
//지도창 라벨 다시 그리기
if(document.getElementById('label-layer-btn').querySelector('input').checked){
document.getElementById('label-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('label-layer-btn').querySelector('input').dispatchEvent(event);
}
document.getElementById('label-layer-btn').querySelector('input').checked = true;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('label-layer-btn').querySelector('input').dispatchEvent(event);
document.getElementById('func-label-add').style.display = 'none';
})
//라벨 리스트에서 아이디로 라벨 idx찾기
function findLabelIdx(id){
if(hmCesium.labels.length < 1) return -1;
for(let i =0;i<hmCesium.labels.length;i++){
if(hmCesium.labels[i].id == id) return i;
}
}
//라벨 가져오기 호출
document.getElementById('label-get-btn').addEventListener('click',()=>{
//전체 보이고 현재 모델만 안보이게
document.getElementById('label-get-list').querySelectorAll('li').forEach(ele=>{
ele.style.display = 'block';
})
document.getElementById(`select-${hmCesium.curModel}`).style.display = 'none';
document.getElementById('label-get-selected').innerHTML = `라벨을 가져올 모델을 선택하세요.`;
document.getElementById('label-get-selected').selectedModel = undefined;
document.getElementById('label-get').style.display='flex';
})
//라벨 가져오기 닫기
document.querySelector('#label-get img.icon').addEventListener('click',()=>{
document.getElementById('label-get').style.display='none';
})
//라벨 가져오기 적용
document.getElementById('label-get-submit').addEventListener('click',async()=>{
let id = document.getElementById('label-get-selected').selectedModel;
console.log(id);
let labels = await readJSONFile(hmCesium.main.model[id].dLayer['label']);
for(let i = 0; i < labels.length; i++){
if(hmCesium.labels.length > 0){
labels[i].id = `label${(parseInt(hmCesium.labels[hmCesium.labels.length-1].id.split('label')[1])+i+1)}`;
}else{
labels[i].id = `label${i}`;
}
}
hmCesium.labels = [...hmCesium.labels,...labels];
await setLabel();
//라벨리스트 최신화
hmCesium.labels = await readJSONFile(hmCesium.main.model[hmCesium.curModel].dLayer['label']);
let ul = document.querySelector('#func-label ul');
ul.innerHTML = ``;
for(let i = 0; i < hmCesium.labels.length; i++){
let li = document.createElement('li');
li.innerHTML = `<img class="icon" src="./svg/icon-label-dot-white.svg" alt="icon-label-dot-white">
<h4>${hmCesium.labels[i].text}</h4>`;
ul.appendChild(li);
li.addEventListener('click',()=>{
let option = {
location : [hmCesium.labels[i].longitude, hmCesium.labels[i].latitude, hmCesium.labels[i].height * 10, 0, -1.57],
duration : 1.5,
}
hmCesium.camera_flyTo(option);
})
}
//지도창 라벨 다시 그리기
if(document.getElementById('label-layer-btn').querySelector('input').checked){
document.getElementById('label-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('label-layer-btn').querySelector('input').dispatchEvent(event);
}
document.getElementById('label-layer-btn').querySelector('input').checked = true;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('label-layer-btn').querySelector('input').dispatchEvent(event);
document.getElementById('label-get').style.display='none';
})
//라벨 전체삭제
document.getElementById('label-delete-btn').addEventListener('click',async ()=>{
if(confirm(`${hmCesium.curModel.split('__')[1]}에 등록된 전체 라벨을 삭제합니까?`)){
// hmCesium.deleteBillAll();
let bills = hmCesium.getBillsByType('label');
bills.forEach(bill=>{
hmCesium.deleteBill(bill);
});
document.querySelector('#func-label ul').replaceChildren();
hmCesium.labels = [];
await setLabel();
}
})
//라벨 저장
async function setLabel(){
let jsonData = hmCesium.labels;
let res = await axios.post(hmCesium.main.query.setLabel.replace(':curModel', hmCesium.curModel), jsonData);
if(res.status != 200){
alert('라벨 저장 중 Error 발생');
}
}
//이슈 생성
document.getElementById('func-issue-btn').addEventListener('click',async (e)=>{
if(document.getElementById('func-issue').style.display == 'none'){
resetAllMode();
document.getElementById('func-issue').style.display = 'flex';
document.getElementById('func-issue-bar').style.display = 'flex';
if(!document.getElementById('issue-layer-btn').querySelector('input').checked){
document.getElementById('issue-layer-btn').querySelector('input').checked = true;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('issue-layer-btn').querySelector('input').dispatchEvent(event);
}
let ul = document.querySelector('#func-issue ul');
ul.innerHTML = ``;
if(!hmCesium.issues){
hmCesium.issues = await readJSONFile(hmCesium.main.model[hmCesium.curModel].dLayer['issue']);
}
for(let i = 0; i < hmCesium.issues.length; i++){
let li = document.createElement('li');
li.innerHTML = `<img class="icon" src="./svg/icon-label-dot-white.svg" alt="icon-label-dot-white">
<h4>${hmCesium.issues[i].text}</h4>`;
ul.appendChild(li);
li.addEventListener('click',()=>{
let option = {
location : [hmCesium.issues[i].longitude, hmCesium.issues[i].latitude, hmCesium.issues[i].height * 10, 0, -1.57],
duration : 1.5,
}
hmCesium.camera_flyTo(option);
})
}
//이슈 클릭 이벤트
// hmCesium.viewer.screenSpaceEventHandler.setInputAction(issueClickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}else{
document.getElementById('func-issue-add').style.display = 'none';
document.getElementById('func-issue').style.display = 'none';
document.getElementById('func-issue-bar').style.display = 'none';
initCursorAndScreenEvent();
}
document.getElementById('func-issue-btn').classList.toggle('on');
})
//이슈 클릭 이벤트
function issueClickEvent(e){
if(document.querySelector('#issue-layer-btn input').checked){
const pickedObject = hmCesium.viewer.scene.pick(e.position,1,1);
if(pickedObject.primitive && pickedObject.primitive.info && pickedObject.primitive.info.id.includes('issue')){
if(document.getElementById('func-issue-bar').style.display !== 'none'){
openIssueProperty(pickedObject.primitive.info, undefined,true);
}else{
openIssueProperty(pickedObject.primitive.info);
}
}
}
}
//이슈 선택용 핸들러
let newHandler = new Cesium.ScreenSpaceEventHandler(hmCesium.viewer.scene.canvas);
newHandler.setInputAction(issueClickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
//이슈 생성창 호출
document.getElementById('issue-add-btn').addEventListener('click',()=>{
document.body.addEventListener('mousemove', changeCursor);
hmCesium.viewer.screenSpaceEventHandler.setInputAction((e)=>{
const cartesian = hmCesium.viewer.scene.pickPosition(e.position);
if(Cesium.defined(cartesian)){
//위치 전달, 생성창 열기
openIssueProperty(undefined, cartesian, true);
initCursorAndScreenEvent();
//이슈 클릭이벤트 다시 달아주기
hmCesium.viewer.screenSpaceEventHandler.setInputAction(issueClickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
})
function openIssueProperty(issue, position, edit){
if(edit){
document.getElementById('modal-issue-text').disabled = false;
document.getElementById('modal-issue-content').disabled = false;
document.querySelector('#func-issue-add .window-btn-wrap').style.display = '';
}else{
document.getElementById('modal-issue-text').disabled = true;
document.getElementById('modal-issue-content').disabled = true;
document.querySelector('#func-issue-add .window-btn-wrap').style.display = 'none';
}
let text = document.getElementById('modal-issue-text'); // 이후 추가부분
let details = document.getElementById('modal-issue-content');
let writer = document.getElementById('modal-issue-writer');
let date = document.getElementById('modal-issue-date');
// let files = document.getElementById('modal-issue-fileList'); //이후 추가부분
// files.style.display = 'none';
// files.innerHTML = ``;
//속성창 오픈 - 우선 파일첨부 보류
document.getElementById('func-issue-add').style.display = 'flex';
if(issue){//issue 가 있으면 속성표출
text.value = issue.text;
details.value = issue.details;
writer.innerHTML = issue.writer;
date.innerHTML = issue.createDate;
// if(issue.filePath && issue.filePath.length > 0){
// files.style.display = 'block';
// issue.filePath.forEach(file=>{
// let item= document.createElement('div');
// item.classList.add('file-item');
// item.innerHTML = `<p>${file}</p>
// <div class="button-div">
// <img src="/img/icons/issue-file-download.svg">
// <img src="/img/icons/issue-file-delete.svg">
// </div>`;
// files.appendChild(item);
// //다운로드, 삭제 이벤트 추가
// })
// }
writer.issueId = issue.id;
writer.latitude = issue.latitude;
writer.longitude = issue.longitude;
writer.issueHeight = issue.height;
}else{// 없으면 초기화
text.value = '';
details.value = ``;
writer.innerHTML = `${hmCesium.user.user_nm} ${hmCesium.user.position}`; // 추후 로그인 사용자
date.innerHTML = getToday();
// files.style.display = 'none';
let carto = Cesium.Cartographic.fromCartesian(position);
writer.latitude = Cesium.Math.toDegrees(carto.latitude);
writer.longitude = Cesium.Math.toDegrees(carto.longitude);
writer.issueHeight = carto.height;
writer.issueId = undefined;
}
}
//이슈 속성창 닫기
document.getElementById('func-issue-add').querySelector('img').addEventListener('click',()=>{
document.getElementById('func-issue-add').style.display = 'none';
})
//이슈 속성창 삭제
document.getElementById('modal-issue-cancel').addEventListener('click',async ()=>{
let writer = document.getElementById('modal-issue-writer');
let id = writer.issueId;
let idx = findIssueIdx(id);
let issue = hmCesium.issues[idx];
//삭제컨펌
if(!confirm(`이슈 "${(issue)?issue.text:document.getElementById('modal-issue-text').value}" 정말 삭제합니까?`)) return;
if(idx != undefined){
hmCesium.issues.splice(idx,1);
}
await setIssue();
//이슈 리스트 다시 만들기
hmCesium.issues = await readJSONFile(hmCesium.main.model[hmCesium.curModel].dLayer['issue']);
let ul = document.querySelector('#func-issue ul');
ul.innerHTML = ``;
for(let i = 0; i < hmCesium.issues.length; i++){
let li = document.createElement('li');
li.innerHTML = `<img class="icon" src="./svg/icon-label-dot-white.svg" alt="icon-label-dot-white">
<h4>${hmCesium.issues[i].text}</h4>`;
ul.appendChild(li);
li.addEventListener('click',()=>{
let option = {
location : [hmCesium.issues[i].longitude, hmCesium.issues[i].latitude, hmCesium.issues[i].height * 10, 0, -1.57],
duration : 1.5,
}
hmCesium.camera_flyTo(option);
})
}
//화면에 이슈 다시 그리기
if(document.getElementById('issue-layer-btn').querySelector('input').checked){
document.getElementById('issue-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('issue-layer-btn').querySelector('input').dispatchEvent(event);
}
document.getElementById('issue-layer-btn').querySelector('input').checked = true;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('issue-layer-btn').querySelector('input').dispatchEvent(event);
document.getElementById('func-issue-add').style.display = 'none';
})
//이슈 속성창 추가
document.getElementById('modal-issue-submit').addEventListener('click',async ()=>{
let writer = document.getElementById('modal-issue-writer');
let id = writer.issueId;
if(document.getElementById('modal-issue-content').value == '' || document.getElementById('modal-issue-text').value == ''){
alert('제목과 상세내용을 꼭 입력해주세요.');
return;
}
let issue = {
id:id,
text:document.getElementById('modal-issue-text').value, //이후 추가부분
latitude:writer.latitude,
longitude:writer.longitude,
height:writer.issueHeight,
projectId : hmCesium.main.projectCode,
modelId: hmCesium.curModel,
type:`issue-type1`,
icon : `./img/icons/issue-type1.svg`,
iconScale:1,
createDate:document.getElementById('modal-issue-date').innerText,
details:document.getElementById('modal-issue-content').value,
writer:document.getElementById('modal-issue-writer').innerHTML,
files : [],
}
if(id){
let idx = findIssueIdx(id);
hmCesium.issues[idx] = issue;
}else{//신규
if(hmCesium.issues.length > 0)
issue.id = `issue`+(parseInt(hmCesium.issues[hmCesium.issues.length-1].id.split('issue')[1])+1);
else
issue.id = `issue0`;
hmCesium.issues.push(issue);
}
await setIssue();
//이슈 리스트 다시 만들기
hmCesium.issues = await readJSONFile(hmCesium.main.model[hmCesium.curModel].dLayer['issue']);
let ul = document.querySelector('#func-issue ul');
ul.innerHTML = ``;
for(let i = 0; i < hmCesium.issues.length; i++){
let li = document.createElement('li');
li.innerHTML = `<img class="icon" src="./svg/icon-label-dot-white.svg" alt="icon-label-dot-white">
<h4>${hmCesium.issues[i].text}</h4>`;
ul.appendChild(li);
li.addEventListener('click',()=>{
let option = {
location : [hmCesium.issues[i].longitude, hmCesium.issues[i].latitude, hmCesium.issues[i].height * 10, 0, -1.57],
duration : 1.5,
}
hmCesium.camera_flyTo(option);
})
}
//화면에 이슈 다시 그리기
if(document.getElementById('issue-layer-btn').querySelector('input').checked){
document.getElementById('issue-layer-btn').querySelector('input').checked = false;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('issue-layer-btn').querySelector('input').dispatchEvent(event);
}
document.getElementById('issue-layer-btn').querySelector('input').checked = true;
let event = new Event('change', { bubbles: true, cancelable: true });
await document.getElementById('issue-layer-btn').querySelector('input').dispatchEvent(event);
document.getElementById('func-issue-add').style.display = 'none';
})
//이슈 리스트에서 아이디로 이슈 idx찾기
function findIssueIdx(id){
if(hmCesium.issues.length < 1) return -1;
for(let i =0;i<hmCesium.issues.length;i++){
if(hmCesium.issues[i].id == id) return i;
}
}
//이슈 전체삭제
document.getElementById('issue-delete-btn').addEventListener('click',async ()=>{
if(confirm(`${hmCesium.curModel.split('__')[1]}에 등록된 전체 이슈를 삭제합니까?`)){
// hmCesium.deleteBillAll();
let bills = hmCesium.getBillsByType('issue-type1');
bills.forEach(bill=>{
hmCesium.deleteBill(bill);
});
document.querySelector('#func-issue ul').replaceChildren();
hmCesium.issues = [];
await setIssue();
}
})
//이슈 저장
async function setIssue(){
let jsonData = hmCesium.issues;
let res = await axios.post(hmCesium.main.query.setIssue.replace(':curModel', hmCesium.curModel), jsonData);
if(res.status != 200){
alert('이슈 저장 중 Error 발생');
}
}
//이슈 파일추가
//이슈 파일삭제
//큐피드 ( 보류 )
//날짜 생성용
function getToday(){
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0'); // 월은 0부터 시작하므로 +1
const day = String(today.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////*** GSIM 기능 (중앙) END ***//////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////*** Layer (우측 하단) ***///////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//각 모델 선택 시 레이어 확인하여 레이어창 없애기
async function setLayer(dlayer){
//wpb - 선형중심선
//label - 라벨
//issue - 이슈
//plane - 계획평면
//siteLine - 용지라인
//이전 레이어 모두 off, 모두 disabled 처리후 있는것들만 풀기
let layer_btns = document.getElementById('layer-modal').querySelectorAll('.checkbox-label.layer');
for(let i = 0; i < layer_btns.length; i++){
layer_btns[i].checked = false;
if(!layer_btns[i].classList.contains('disabled'))
layer_btns[i].classList.add('disabled');
}
let keys = Object.keys(dlayer);
for(let i = 0; i < keys.length; i++){
document.getElementById(`${keys[i]}-layer-btn`).classList.remove('disabled');
// 전체 DLayer off로 시작
// if(keys[i] == 'label'||keys[i] == 'issue'){ //전체 dLayer 처음부터 보이기
// document.getElementById(`${keys[i]}-layer-btn`).querySelector('input').checked = true;
// const changeEvent = new Event('change',{
// bubbles : false,
// cancelable : false,
// composed : false
// });
// document.getElementById(`${keys[i]}-layer-btn`).querySelector('input').dispatchEvent(changeEvent);
// }
}
}
//체크박스 change 이벤트
document.querySelectorAll('#layer-modal input').forEach(ele=>{
ele.addEventListener('change', (e)=>{
if(e.target.closest('.checkbox-label.layer').classList.contains('disabled')){
return;
}
let type = e.target.closest('label').id.split('-layer-btn')[0];
let bOn = ele.checked;
switch(type){
case 'wpb' :
toggleWpb(bOn);
break;
case 'plane' :
togglePlane(bOn);
break;
case 'siteLine' :
toggleSiteLine(bOn);
break;
case 'label' :
toggleLabel(bOn);
break;
case 'issue' :
toggleIssue(bOn);
break;
}
})
})
//선형중심선
async function toggleWpb(bOn){
if(bOn){
hmCesium.loadRoadData(hmCesium.main.model[hmCesium.curModel].dLayer['wpb'], `EPSG:${hmCesium.main.model[hmCesium.curModel].projection}`);
}else{
hmCesium.removeRoadData();
}
}
//계획평면
let basePlanPolylineCollection = undefined;
async function togglePlane(bOn){
if(bOn){
togglePlanFunctionProgress(true);
let params = {
table : hmCesium.main.model[hmCesium.curModel].dLayer['plane']
}
let res = await axios.get(`${hmCesium.main.query['getPlaneData']}`,{params:params});
if (res.data.message == 'Get Plan Data Complete') {
if (res.data.queryResult.length != 0) setPlanPrimitives(res.data.queryResult, 'base');
}
}else{
if (basePlanPolylineCollection) hmCesium.viewer.scene.groundPrimitives.remove(basePlanPolylineCollection);
basePlanPolylineCollection = undefined;
hmCesium.viewer.scene.render();
}
}
//용지라인
let siteLinePlanPolylineCollection = undefined;
async function toggleSiteLine(bOn){
if(bOn){
togglePlanFunctionProgress(true);
let params = {
table : hmCesium.main.model[hmCesium.curModel].dLayer['siteLine']
}
let res = await axios.get(`${hmCesium.main.query['getPlaneData']}`,{params:params});
if (res.data.message == 'Get Plan Data Complete') {
if (res.data.queryResult.length != 0) setPlanPrimitives(res.data.queryResult, 'siteLine');
}
}else{
if (siteLinePlanPolylineCollection) hmCesium.viewer.scene.groundPrimitives.remove(siteLinePlanPolylineCollection);
siteLinePlanPolylineCollection = undefined;
hmCesium.viewer.scene.render();
}
}
//라벨
async function toggleLabel(bOn){
if(bOn){
hmCesium.labels = await readJSONFile(hmCesium.main.model[hmCesium.curModel].dLayer['label']);
hmCesium.addBills(hmCesium.labels);
hmCesium.viewer.scene.render();
}else{
let bills = hmCesium.getBillsByType('label');
bills.forEach(bill=>{
hmCesium.deleteBill(bill);
});
hmCesium.labels = undefined;
hmCesium.viewer.scene.render();
}
}
//이슈
async function toggleIssue(bOn){
if(bOn){
hmCesium.issues = await readJSONFile(hmCesium.main.model[hmCesium.curModel].dLayer['issue']);
hmCesium.addBills(hmCesium.issues);
hmCesium.viewer.scene.render();
// hmCesium.viewer.screenSpaceEventHandler.setInputAction(issueClickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}else{
let bills = hmCesium.getBillsByType('issue-type1');
bills.forEach(bill=>{
hmCesium.deleteBill(bill);
});
hmCesium.issues = undefined;
hmCesium.viewer.scene.render();
}
}
//json 파일 읽어오기
async function readJSONFile(fileUrl) {
try {
const response = await fetch(fileUrl);
if (response.ok) {
const geoJSONData = await response.json();
return geoJSONData;
} else {
throw new Error("파일을 불러오는 데 문제가 발생했습니다.");
}
} catch (error) {
console.error("오류:", error);
return null;
}
}
//대기창
let planFunctionProgressDiv = undefined;
function togglePlanFunctionProgress(bool) {
if (bool) {
planFunctionProgressDiv = document.createElement('div');
// planFunctionProgressDiv.cartesian3 = lastPickPos;
planFunctionProgressDiv.innerHTML = '<img src="./img/progress.gif">';
planFunctionProgressDiv.style.position = 'absolute';
planFunctionProgressDiv.style.display = 'flex';
planFunctionProgressDiv.style.width = '50px';
planFunctionProgressDiv.style.height = '50px';
planFunctionProgressDiv.style.left = '50%';
planFunctionProgressDiv.style.top = '50%';
planFunctionProgressDiv.style.transform = 'translate(-50%, -50%)';
planFunctionProgressDiv.style.background = 'rgba(255,255,255,0.5)';
planFunctionProgressDiv.style.borderRadius = '30px';
planFunctionProgressDiv.classList.add('plan-finction-progress');
document.body.appendChild(planFunctionProgressDiv);
} else {
if (!planFunctionProgressDiv) return;
document.body.removeChild(planFunctionProgressDiv);
planFunctionProgressDiv = undefined;
}
}
//plane 그리기함수
async function setPlanPrimitives(data, type, isRight) {
// collection 생성
let collection = new Cesium.PrimitiveCollection();
collection.id = `${type}PlanPolylineCollection`;
let geometryArray = [];
data.map(d => {
// 선 색깔 설정
let color = '#FF0000';
if (d.path) {
if (d.path.includes('Bline')) color = '#6699CC';
if (d.path.includes('Bplan')) color = '#0000FF';
if (d.path.includes('filling')) {
color = '#AABABA';
if (d.path.includes('j_filling')) color = '#CC9900';
if (d.path.includes('s_filling')) color = '#CD66CD';
}
if (d.path.includes('structure')) {
color = '#E8835E';
if (d.path.includes('structureT') || d.path.includes('structure_T')) color = '#00FF00';
}
if (d.path.includes('sichu')) color = '#000000';
if (d.path.includes('ascon')) color = '#848484';
}
if(d.text == 'Modified'){
if (d.path.toLowerCase().includes('blue')) color = '#0000ff';
if (d.path.toLowerCase().includes('brown')) color = '#e0a870';
if (d.path.toLowerCase().includes('cyan')) color = '#00ffff';
if (d.path.toLowerCase().includes('gray')) color = '#808080';
if (d.path.toLowerCase().includes('green')) color = '#00ff00';
if (d.path.toLowerCase().includes('red')) color = '#ff0000';
if (d.path.toLowerCase().includes('white')) color = '#ffffff';
if (d.path.toLowerCase().includes('yellow')) color = '#ffff00';
if (d.path.toLowerCase().includes('magenta')) color = '#ff00ff';
}
if(d.layer.includes('용지')) color = '#0BFA77';
Cesium.GeoJsonDataSource.load(d.geom).then((geojson) => {
geojson.entities.values.map(entity => {
let polylinePosition = entity.polyline.positions._value;
// 상세 도면 primitive 생성
let polylineGeometry = new Cesium.GeometryInstance({
geometry: new Cesium.GroundPolylineGeometry({
positions: polylinePosition,
width: 2,
arcType: Cesium.ArcType.GEODESIC
}),
attributes: {
color: new Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString(color)),
}
});
geometryArray.push(polylineGeometry);
})
})
})
let polylinePrimitive = new Cesium.GroundPolylinePrimitive({
geometryInstances: geometryArray,
asynchronous: false,
appearance: new Cesium.PolylineColorAppearance(),
releaseGeometryInstances: false,
allowPicking: false,
});
// collection에 primitive 추가
collection.add(polylinePrimitive);
// type에 맞는 전역변수에 collection 저장
if (type == 'base') {
basePlanPolylineCollection = collection;
}else if(type == 'siteLine'){
siteLinePlanPolylineCollection = collection;
} else {
// detailPlanPolylineCollection = collection;
}
// hmCesium.viewer.scene.groundPrimitives에 collection 추가 후 requestRender
hmCesium.viewer.scene.groundPrimitives.add(collection);
hmCesium.viewer.scene.requestRender();
// progress 삭제
// if(type == 'base' || type=='siteLine') setTimeout(togglePlanFunctionProgress(false),2000);
setTimeout(togglePlanFunctionProgress(false),2000);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////*** Layer (우측 하단) END ***//////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////*** footer (mouseInfo) ***///////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 토목좌표 설정
document.getElementById('gcs2').addEventListener('change', () => {
if(document.querySelector('input[name="pcs"]')) document.querySelector('input[name="pcs"]').checked = true;
document.querySelectorAll('input[name="pcs"]').forEach((radio) => {
radio.disabled = false;
let label = radio.closest('label');
label.style.opacity = '100%';
label.style.cursor = 'pointer';
});
hmCesium.change_coordiUnit(document.querySelector('input[name="pcs"]:checked').id);
});
// 좌표 변환 설정
document.querySelectorAll('input[name="pcs"]').forEach((radio) => {
radio.addEventListener('change', () => {
hmCesium.change_coordiUnit(radio.id);
});
});
// 위경도 설정
document.getElementById('gcs1').addEventListener('change', (e) => {
if (e.target.checked) {
hmCesium.change_coordiUnit('4326');
}
document.querySelectorAll('input[name="pcs"]').forEach((radio) => {
radio.disabled = true;
radio.checked = false;
let label = radio.closest('label');
label.style.opacity = '25%';
label.style.cursor = 'not-allowed';
});
});
// 모달열기
document.querySelector('.coordinate p').addEventListener('click', () => {
if(document.getElementById('select-coordi').style.display == 'none'){
document.getElementById('select-coordi').style.display = 'block';
}else{
document.getElementById('select-coordi').style.display = 'none';
}
});
// 모달닫기
document.getElementById('select-coordi-close').addEventListener('click', () => {
document.getElementById('select-coordi').style.display = 'none';
});
let keys = Object.keys(hmCesium.main.model);
let lastKey = keys[keys.length-1];
//모델 설정 후 좌표 바꾸기
if(hmCesium.main.model[lastKey].projection != '4326'){
document.getElementById('gcs2').click();
if(document.getElementById(hmCesium.main.model[lastKey].projection)){
document.getElementById(hmCesium.main.model[lastKey].projection).click();
}else{
document.getElementById('gcs1').click();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////*** footer (mouseInfo) END ***//////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////vietnam용 bill
async function makeVietnamBill(pLabels){
let labels = (!pLabels)?hmCesium.main.label:pLabels;
let layer = changeLayer('labels', true);
let keys = Object.keys(labels);
for(let i = 0; i < keys.length; i++){
let bill = {
html : `<div class="point" style="text-align: center; pointer-events: all; height: fit-content; background-color: white; border-radius: 4px;">
<div class="point-title" style=" display: flex; gap: 8px; border: 1px solid #111; padding: 8px 16px 8px 8px; border-radius: 4px; width: max-content; position: relative; box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.2);">
<h3 style="font-size: 12px; font-weight: 600; line-height: 20px; margin:0;">
${keys[i]}</h3>
<div style="position: absolute; bottom: -15px; left: 50%; transform: translateX(-50%); width: 20px; height: 14px; background: black; clip-path: polygon(50% 100%, 0 0, 100% 0); z-index: 0;"></div>
<div style="position: absolute; bottom: -14px; left: 50%; transform: translateX(-50%); width: 18px; height: 14px; background: white; clip-path: polygon(50% 100%, 0 0, 100% 0); z-index: 1;"></div>
</div>
</div>`,
position : [labels[keys[i]][0], labels[keys[i]][1]],
name : keys[i],
path : ` `,
}
await loadBillboardForVietnam(bill, layer);
}
}
async function loadBillboardForVietnam(json, layer) {
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.left = '-9999px';
container.style.top = '-9999px';
container.style.visibility = 'hidden';
container.innerHTML = json.html;
document.body.appendChild(container);
await new Promise(resolve => setTimeout(resolve, 100));
const elementRect = container.getBoundingClientRect();
const width = Math.ceil(elementRect.width) + 12+6;
const height = Math.ceil(elementRect.height) + 20 + 16;
// SVG 데이터를 Base64로 인코딩하여 사용
const svgData = `
<svg xmlns="http://www.w3.org/2000/svg"
width="${width}" height="${height}"
viewBox="0 0 ${width-12} ${height}"
style="background: transparent;">
<foreignObject x="8" y="8" width="${width-16}" height="${height-16}">
<div xmlns="http://www.w3.org/1999/xhtml"
style="font-family: Arial, sans-serif;
width: 100%;
height: 100%;
box-sizing: border-box;">
${container.innerHTML}
</div>
</foreignObject>
</svg>
`;
// Base64 인코딩으로 변경
const encodedSvg = btoa(unescape(encodeURIComponent(svgData)));
const dataUrl = `data:image/svg+xml;base64,${encodedSvg}`;
const position = Cesium.Cartesian3.fromDegrees(json.position[0], json.position[1], 0);
// 이미지 로드 확인을 위한 테스트
const testImg = new Image();
testImg.onload = function() {
console.log('SVG 이미지 로드 성공:', width, 'x', height);
const label = layer.entities.add({
position: position,
name: json.name,
billboard: {
image: dataUrl,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
width: width,
height: height,
pixelOffset: new Cesium.Cartesian2(-4, -45),
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
// distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 250_000),
show: true // 명시적으로 표시 설정
}
});
const bill = layer.entities.add({
position: position,
name: json.name,
billboard: {
image: `./img/location.svg`,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
show: true
}
});
bill.__label = label;
document.body.removeChild(container);
};
testImg.onerror = function() {
console.error('SVG 이미지 로드 실패');
console.log('SVG 데이터:', svgData);
document.body.removeChild(container);
};
testImg.src = dataUrl;
}
function changeLayer(name, bMake = false){
let viewer = hmCesium.viewer;
let layer = viewer.dataSources.getByName(name);
if(layer.length == 0){
if(bMake){
layer = new Cesium.CustomDataSource(name);
viewer.dataSources.add(layer);
}else{
return undefined;
}
}else{
layer = layer[0];
}
return layer;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////*** modelFollowingArrow ***///////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//화살표 만들기
hmCesium.viewer.scene.preRender.addEventListener(()=>{
let tileset = getBiggestTileset(hmCesium);
if(tileset){
if(!checkTilesetInScreen(tileset,hmCesium)){
if(!hmCesium.tilesetArrow){
loadArrowGlb(hmCesium);
// createArrowPrimitive();
}
//카메라 10m앞에 1.7m 아래위치
let arrowPos = hmCesium.viewer.camera.position.clone();
Cesium.Cartesian3.add(arrowPos, Cesium.Cartesian3.multiplyByScalar(hmCesium.viewer.camera.direction, 10, new Cesium.Cartesian3()), arrowPos);
Cesium.Cartesian3.add(arrowPos, Cesium.Cartesian3.multiplyByScalar(hmCesium.viewer.camera.up, -1.7, new Cesium.Cartesian3()), arrowPos);
let target = tileset.boundingSphere.center.clone();
let dir = Cesium.Cartesian3.subtract(target, arrowPos, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(dir,dir);
if(hmCesium.tilesetArrow){
hmCesium.tilesetArrow.modelMatrix = getTransform(arrowPos, dir, 0.6);
}
}else{
if(hmCesium.tilesetArrow){
hmCesium.tilesetArrow.modelMatrix = Cesium.Matrix4.IDENTITY;
}
}
}
});
//타일셋위치 화살표
async function loadArrowGlb(cesium){
cesium.tilesetArrow = await Cesium.Model.fromGltfAsync({
url: `/libs/gsimViewer/img/arrow_unlit_unweld.glb`,
modelMatrix: Cesium.Matrix4.IDENTITY,
})
cesium.viewer.scene.primitives.add(cesium.tilesetArrow);
cesium.tilesetArrow.color = Cesium.Color.RED.withAlpha(0.5);
}
function getBiggestTileset(cesium){
let keys = Object.keys(cesium.projectManager.tileset.main);
let biggest = undefined;
for(let i =0 ; i < keys.length; i++){
if(cesium.projectManager.tileset.main[keys[i]] && cesium.projectManager.tileset.main[keys[i]].show &&!(cesium.projectManager.tileset.main[keys[i]] instanceof Array)){
if(!biggest) biggest = cesium.projectManager.tileset.main[keys[i]];
if(biggest.boundingSphere.radius <= cesium.projectManager.tileset.main[keys[i]].boundingSphere.radius){
biggest = cesium.projectManager.tileset.main[keys[i]];
}
}
}
return biggest;
}
function checkTilesetInScreen(tileset, cesium){
let boundingSphere = tileset.root.boundingSphere;
let cullingVolume = cesium.viewer.camera.frustum.computeCullingVolume(
cesium.viewer.camera.position,
cesium.viewer.camera.direction,
cesium.viewer.camera.up
);
let flagRadius = boundingSphere.radius;
let visibility = cullingVolume.computeVisibility(boundingSphere);
if(visibility >= 0){
let camPosi = cesium.viewer.camera.position.clone();
let distance = Cesium.Cartesian3.distance(camPosi, tileset.boundingSphere.center);
if(distance>= flagRadius * 10){
visibility = -1;
}
}
return visibility != Cesium.Intersect.OUTSIDE;
}
function getTransform(origin, dir, scale){
let up = Cesium.Cartesian3.normalize(origin, new Cesium.Cartesian3());
let right = Cesium.Cartesian3.cross(dir, up, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(right, right);
let newUp = Cesium.Cartesian3.cross(right, dir, new Cesium.Cartesian3());
Cesium.Cartesian3.normalize(newUp, newUp);
// 회전 행렬 생성
let rotation = new Cesium.Matrix3();
Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
Cesium.Matrix3.setColumn(rotation, 1, newUp, rotation);
Cesium.Matrix3.setColumn(rotation, 2, dir, rotation);
// X축 기준으로 90도 회전 행렬 생성 (화살표를 y축기준으로 만들었음)
const angle = Math.PI / 2; // 90도
const xRotation = Cesium.Matrix3.fromRotationX(angle);
// 기존 회전 행렬에 X축 회전 행렬 곱하기
let finalRotation = Cesium.Matrix3.multiply(rotation, xRotation, new Cesium.Matrix3());
//모델은 z도 돌려줘야함
const zRotation = Cesium.Matrix3.fromRotationZ(-angle);
Cesium.Matrix3.multiply(finalRotation, zRotation, finalRotation);
let matrix = Cesium.Matrix4.multiplyByMatrix3(
Cesium.Matrix4.fromTranslation(origin),
finalRotation,
new Cesium.Matrix4()
);
let scaling = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(scale, scale, scale));
Cesium.Matrix4.multiply(matrix, scaling, matrix);
return matrix;
}