// 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 = `

${keys[i].split('__')[0]}

${keys[i].split('__')[1]}

`; 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 = `
`; 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 = `
`; 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 = `
`; 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 = `
`; 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 = ` ${new XMLSerializer().serializeToString(element)} `; // 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 += `
${road.RoadName}
`; list.innerHTML += `
  • icon-label-dot-white

    ${road.RoadName}

  • `; //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 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 = `
    측정 경사 1 : ${m_slope.toFixed(2)}
    //
    최단 경사 평균 ${(hmCesium.slopeResult.length>0)?('1 : '+calcAvg(hmCesium.slopeResult).toFixed(2)):' - 최단 경사 측정 실패'}`; let text = `

    측정경사

    1 : ${m_slope.toFixed(2)}

    최단 경사 평균

    ${(hmCesium.slopeResult.length>0)?('1 : '+calcAvg(hmCesium.slopeResult).toFixed(2)):' - 최단 경사 측정 실패'}

    `; 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 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 = `

    ${addComma(entity.attribute.aerial[entity.attribute.aerial.length-1])}m

    ` 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 = `

    ${addComma(entity.attribute.horizontal[entity.attribute.horizontal.length-1])}m

    ` 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 = `

    ${addComma(entity.attribute.vertical[entity.attribute.vertical.length-1])}m

    ` 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])}도
    // 경도 : ${addComma(entity.attribute.coordinate[0])}도
    // 높이 : ${addComma(entity.attribute.coordinate[2])}m // `; text = `

    위도

    ${addComma(entity.attribute.coordinate[1])}°

    경도

    ${addComma(entity.attribute.coordinate[0])}°

    높이

    ${addComma(entity.attribute.coordinate[2])}m

    ` }else{ // text = ` // X : ${addComma(entity.attribute.coordinate[0])}m
    // Y : ${addComma(entity.attribute.coordinate[1])}m
    // 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 = `
    ${epsg} 기준

    X

    ${addComma(entity.attribute.coordinate[0])}m

    Y

    ${addComma(entity.attribute.coordinate[1])}m

    Z

    ${addComma(entity.attribute.coordinate[2])}m

    ` } } 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 = `

    직선거리 합계

    ${addComma(totalAerial)}m

    ` } 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 = `

    수평거리 합계

    ${addComma(totalHorizontal)}m

    ` } 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 = `

    수직거리 합계

    ${addComma(totalVertical)}m

    ` } // 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
    // 둘레 : ${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(? 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 = `icon-label-dot-white

    ${hmCesium.labels[i].text}

    `; 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 = `icon-label-dot-white

    ${hmCesium.labels[i].text}

    `; 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 = `icon-label-dot-white

    ${hmCesium.labels[i].text}

    `; 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{ //전체 보이고 현재 모델만 안보이게 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 = `icon-label-dot-white

    ${hmCesium.labels[i].text}

    `; 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 = `icon-label-dot-white

    ${hmCesium.issues[i].text}

    `; 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 = `

    ${file}

    //
    // // //
    `; // 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 = `icon-label-dot-white

    ${hmCesium.issues[i].text}

    `; 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 = `icon-label-dot-white

    ${hmCesium.issues[i].text}

    `; 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{ 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 = ''; 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 : `

    ${keys[i]}

    `, 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 = `
    ${container.innerHTML}
    `; // 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; }