let _size = [48, 48, 48, 48, 40, 32, 24, 22, 20, 18, 18,], _Map = null, _SelectedObj = null, _CctvArray = [], _CctvFlag = false, _CctvListFlag = false, _IntersectionArray = [], _IntersectionFlag = false, _IntersectionListFlag = false, _IntersectionCameraArray = [], _AtrdArray = [], _AtrdData = [], _AtrdMap = new Map(), _VmsArray = [], _VmsFlag = false, _VmsListFlag = false, _IncidentArray = [], _IncidentFlag = false, _IncidentListFlag = false, _TrafficArray = [], _TrafficFlag = false, _TrafficListFlag = false, selectIncidentId = null const g_color = new Map(); g_color.set('LTC0', '#888888;'); g_color.set('LTC1', '#2fba2c;'); g_color.set('LTC2', '#ffc500;'); g_color.set('LTC3', '#ee0000;'); $(()=>{ /** * 카카오 맵 생성 * @type {HTMLElement} */ const container = document.getElementById('map'); //지도를 담을 영역의 DOM 레퍼런스 const options = { //지도를 생성할 때 필요한 기본 옵션 center: new kakao.maps.LatLng(36.0191816, 129.3432983), //지도의 중심좌표. level: 6, maxLevel: 9, minLevel: 1, disableDoubleClickZoom: true }; _Map = new kakao.maps.Map(container, options); const mapTypeControl = new kakao.maps.MapTypeControl(); _Map.addControl(mapTypeControl, kakao.maps.ControlPosition.TOPRIGHT); const zoomControl = new kakao.maps.ZoomControl(); _Map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHT); /** * Map Zoom Level Change 이벤트 */ kakao.maps.event.addListener(_Map, 'zoom_changed', function () { markerSizeChangeWithZoomLevel(_CctvArray, _CctvFlag); markerSizeChangeWithZoomLevel(_VmsArray, _VmsFlag); intersectionMarkerChangeWithZoomLevel(); intersectionCameraChangeWidthZoomLevel(); markerSizeChangeWithZoomLevel(_IncidentArray, _IncidentFlag); if (_AtrdArray.length > 0) { atrdMarkerResize(); } getVertex(); }); kakao.maps.event.addListener(_Map, 'dragend', function() { getVertex(); }) function atrdMarkerResize() { let size = 48; let level = _Map.getLevel(); if (level >= 7) { size = 24; } _AtrdArray.forEach((atrd)=>{ const imageSize = new kakao.maps.Size(size, size); const imageSrc = '/images/icon/atrd' + atrd.obj.drct_cd +'.png'; const imageOption = { offset: new kakao.maps.Point(size/2, size), }; const markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imageOption); atrd.marker.setImage(markerImage); }) if (level === 3) { level = 4; } if (level >= 8) { level = 7; } else if (level >= 6) { level = 6; } const atrdInfo = _AtrdMap.get(level.toString()); if (atrdInfo) { _AtrdData = []; let upHillArr = []; let downHillArr = []; const $selectedLi = $(".left-list-area .list-content.list > li.click"); let id; let idArr; if ($selectedLi[0]) { id = $selectedLi.attr('id'); } if (id) { idArr = id.split("_"); } atrdInfo.forEach((atrd)=>{ if (atrd.atrd_id === idArr[1] || atrd.atrd_id == idArr[2]) { _AtrdData.push(atrd); if (atrd.atrd_id === idArr[1]) { upHillArr.push(atrd); } else { downHillArr.push(atrd); } } }) if (upHillArr[0]) { let upHillX = upHillArr[0].x_crdn_arr.split(',')[0]; let upHillY = upHillArr[0].y_crdn_arr.split(',')[0]; _AtrdArray[0].marker.setPosition(new kakao.maps.LatLng(upHillY, upHillX)); } if (downHillArr[0]) { let downHillX = downHillArr[0].x_crdn_arr.split(',')[0]; let downHillY = downHillArr[0].y_crdn_arr.split(',')[0]; _AtrdArray[1].marker.setPosition(new kakao.maps.LatLng(downHillY, downHillX)); } } } /** * 스마트교차로 카메라 레벨별 이미지 보이기 감추기 */ function intersectionCameraChangeWidthZoomLevel() { const level = _Map.getLevel(); const size = [32, 32, 32] const levelSize = size[level];; if (_IntersectionCameraArray.length > 0) { _IntersectionCameraArray.forEach((obj) => { const content = $('
'); content.css( { width: levelSize+ 'px', height: levelSize+ 'px', backgroundImage:'url(/images/icon/intersection-cctv.png)', backgroundSize : levelSize+'px '+levelSize+'px', backgroundRepeat: 'no-repeat', backgroundPosition : 'center', transform : 'rotate(' + obj.obj.cmra_angn +'deg)', cursor : 'pointer' }); obj.marker.setContent(content[0]); content.on('click', ()=> { obj.click(); }); if (level <= 2 && _IntersectionFlag) { obj.marker.setMap(_Map); obj.polyline.setMap(_Map); } else { obj.marker.setMap(null); obj.polyline.setMap(null); } }) } } /** * 스마트교차로 레벨별 이미지 보이기 감추기 */ function intersectionMarkerChangeWithZoomLevel() { const level = _Map.getLevel(); if (_IntersectionArray.length > 0) { _IntersectionArray.forEach((obj)=>{ setMarkerImage(obj, ""); if (level > 2 && _IntersectionFlag) { obj.marker.setVisible(true); } else { obj.marker.setVisible(false); } }) } } /** * 맵 레벨별 이미지 변환 * @param array */ function markerSizeChangeWithZoomLevel(array) { if (array && array.length > 0) { array.forEach((obj)=>{ if (obj.isClick) { setMarkerImage(obj, 2) } else { setMarkerImage(obj, 1); } }); } } /** * 범례 클릭 색 변환 * */ $('.legend-bottom button').on('click', function (){ const target = $(this); const legendIcon = target.children().eq(0); const legendText = target.children().eq(1).text(); const isShow = !legendIcon.hasClass('active'); switch (legendText) { case 'CCTV' : onOffFacility(_CctvArray, isShow, legendIcon, target, null); _CctvFlag = isShow; break; case '스마트교차로CCTV': onOffFacility(_IntersectionArray, isShow, legendIcon, target, _IntersectionCameraArray); _IntersectionFlag = isShow; break; case '도로전광판': onOffFacility(_VmsArray, isShow, legendIcon, target, null); _VmsFlag = isShow; break; case '돌발정보': onOffFacility(_IncidentArray, isShow, legendIcon, target, null); _IncidentFlag = isShow; break; case '소통정보': onOffFacility(_TrafficArray, isShow, legendIcon, target, _AtrdArray); _TrafficFlag = isShow; break; } }) /** * 범례 호버 색 변환 * */ $('.legend-bottom ul li').hover((event)=>{ const iconDiv = $(event.currentTarget).children().eq(0); if (!iconDiv.hasClass('active')) { iconDiv.children().eq(0).addClass('hover'); } else { if (iconDiv.children().eq(0).hasClass('hover')) { iconDiv.children().eq(0).removeClass('hover'); } } }, (event)=>{ const iconDiv = $(event.currentTarget).children().eq(0); if (!iconDiv.hasClass('active')) { iconDiv.children().eq(0).removeClass('hover'); } else { if (iconDiv.children().eq(0).hasClass('hover')) { iconDiv.children().eq(0).removeClass('hover'); } } }); /** * 탭 호버 색 변환 * */ $('.left-list-area .list-tab > li').hover((event)=>{ const iconDiv = $(event.currentTarget); if (!iconDiv.hasClass('active')) { iconDiv.children().eq(0).addClass('hover'); } else { if (iconDiv.children().eq(0).hasClass('hover')) { iconDiv.children().eq(0).removeClass('hover'); } } }, (event)=>{ const iconDiv = $(event.currentTarget); if (!iconDiv.hasClass('active')) { iconDiv.children().eq(0).removeClass('hover'); } else { if (iconDiv.children().eq(0).hasClass('hover')) { iconDiv.children().eq(0).removeClass('hover'); } } }); initFaicilityData(); getVertex(); let _MakerArr = []; $('.tab-title > div').on('click', function(){ let searchType = $('.tab-title > div.active').text(); if (searchType === $(this).text()) return; $('.tab-title > div').toggleClass("active"); searchType = $(this).text(); let list; let spot; if (searchType === '지점 검색') { list = 'none'; spot = 'block'; $('.list-content.spot').html(""); } else { list = 'block'; spot = 'none'; $('.list-content.list > li').css('display', 'block'); } $('#search-box').val(""); $('.list-content.list').css('display', list); $('.list-content.spot').css('display', spot); if (_MakerArr.length > 0) { _MakerArr.forEach((obj)=>{ obj.setMap(null); }); _MakerArr = []; } }) $('#search-box').on('keyup', function () { const searchType = $('.tab-title > div.active').text(); const searchText = $(this).val(); if (searchType === '리스트') { if (!$('.list-content.list > li').length) return; for (let ii = 0; ii < $('.list-content.list > li').length; ii++) { const li = $('.list-content.list > li').eq(ii); if ($(this).val().length === 0) { li.css('display', 'block'); } else { let text = $('.list-content.list > li').eq(ii).text().toLowerCase(); if (text.includes(searchText.toLowerCase())) { li.css('display', 'block'); } else { li.css('display', 'none'); } } } } else { if (_MakerArr.length > 0) { _MakerArr.forEach((obj)=>{ obj.setMap(null); }); _MakerArr = []; } $('.list-content.spot').html(""); $.ajax({ url: 'https://dapi.kakao.com/v2/local/search/address.json', headers: { 'Authorization': 'KakaoAK 4896b94398b96949d349881d004835cf'}, data :{ query : searchText, }, type: 'GET' }).done(function(data) { if (data && data.documents.length > 0) { let str = ""; const setMap = new Map(); const bounds = new daum.maps.LatLngBounds(); let minX; let minY; let maxX; let maxY; let idx = 0; data.documents.forEach((obj)=>{ if (!setMap.get(obj.address_name) && obj.address_name.includes("경북 포항시")) { idx++; let addr = obj.address_name; if (obj.x && obj.y) { const position = new kakao.maps.LatLng(obj.y, obj.x); let marker = new kakao.maps.Marker({ position : position, title : addr , content : addr }); new kakao.maps.event.addListener(marker, 'click', function() { moveLocation(obj.x, obj.y, idx); }) if (!minX) { minX = obj.x; } if (!maxX) { maxX = obj.x; } if (!minY) { minY = obj.y; } if (!maxY) { maxY = obj.y; } minX = minX <= obj.x ? minX : obj.x; maxX = maxX >= obj.x ? maxX : obj.x; minY = minY <= obj.y ? minY : obj.y; maxY = maxY >= obj.y ? maxY : obj.y; marker.setMap(_Map); _MakerArr.push(marker); setMap.set(addr, obj); str+= `
  • ${obj.address_name}
  • `; } } }); if (minX && minY && maxX && maxY) { bounds.extend(new daum.maps.LatLng(minY, minX)); bounds.extend(new daum.maps.LatLng(maxY, maxX)); _Map.setBounds(bounds); $('.list-content.spot').html(str); getVertex(); } } }); } }) }); function moveLocation(xCoordinate, yCoordinate, listIndex) { const position = new kakao.maps.LatLng(yCoordinate, xCoordinate); _Map.setCenter(position); _Map.setLevel(3); $('.list-content.spot > li.click').removeClass('click'); $('#spot-' +listIndex).addClass('click'); getVertex(); } function getVertex() { if (_TrafficArray.length > 0) { _TrafficArray.forEach((obj) => { obj.setVisibleMarker(false); if (obj.infoWindow) { obj.infoWindow.setMap(null); } }) _TrafficArray = []; } let level = _Map.getLevel(); const bounds = _Map.getBounds(); const swLatLng = bounds.getSouthWest(); const neLatLng = bounds.getNorthEast(); const data = { levl : level, swLat : swLatLng.getLat(), neLat : neLatLng.getLat(), swLng : swLatLng.getLng(), neLng : neLatLng.getLng(), } console.log(level); getDataAsync('/api/traffic/vertex-list', 'POST', data, null, (jsonData)=>{ if (jsonData && jsonData.length > 0) { jsonData.forEach((obj)=>{ const trafficObj = new TrafficObj(obj); if (_AtrdData.length > 0) { _AtrdData.forEach((atrd)=>{ if (atrd.road_id == trafficObj.ID) { trafficObj.polyBackLine.setOptions({ strokeColor : 'black', }); } }) } _TrafficArray.push(trafficObj); }) } }) } function initFaicilityData() { getDataAsync('/api/traffic/cctv-list', 'POST', null, null, (jsonData)=>{ if (jsonData && jsonData.length > 0) { _CctvArray = receiveFacilityData(jsonData, _CctvArray, TbCCtvObj, _CctvListFlag, 'cctv'); } }, null); getDataAsync('/api/traffic/vms-list', 'POST', null, null, (jsonData)=>{ if (jsonData && jsonData.length > 0) { jsonData.sort((a, b)=>{ return a.vms_nm > b.vms_nm ? 1 : a.vms_nm < b.vms_nm ? -1 : 0; }) _VmsArray = receiveFacilityData(jsonData, _VmsArray, TbVmsObj, _VmsListFlag, 'vms'); } }, null); getDataAsync('/api/traffic/incd-list', 'POST', null, null, (jsonData)=>{ if (jsonData && jsonData.length > 0) { _IncidentArray = receiveFacilityData(jsonData, _IncidentArray, TbIncdObj, _IncidentListFlag, 'incident'); if (selectIncidentId) { _IncidentArray.forEach((incd)=>{ if (incd.ID === selectIncidentId) { incd.click(); } }) } } }, null); getDataAsync('/api/itcs/list', 'POST', null, null, (jsonData)=>{ if (jsonData && jsonData.length > 0) { jsonData.sort((a, b)=>{ return a.ixr_nm > b.ixr_nm ? 1 : a.ixr_nm < b.ixr_nm ? -1 : 0; }); let data = []; jsonData.forEach((obj)=>{ if(obj.detail.length > 0) { data.push(obj); } }) _IntersectionArray = receiveFacilityData(data, _IntersectionArray, IntersectionObj, _IntersectionListFlag, 'intersection'); } }, null); if (_TrafficListFlag) { getDataAsync("/api/traffic/atrd-vertex-all", "POST", null, null, (jsonData)=>{ if (jsonData && jsonData.length > 0) { _AtrdMap = new Map(); jsonData.forEach((obj)=>{ _AtrdMap.set(obj.level, obj.list); }); } }, null); getDataAsync('/api/traffic/atrd-list', 'POST', null, null, (jsonData)=>{ if (jsonData) { let listStr = ""; let mobileStr = ""; let atrdData = []; for (let key in jsonData){ atrdData.push({ name : key, list : jsonData[key] }); } atrdData.sort((a,b)=>{ return a.name > b.name ? 1 : a.name < b.name ? -1 : 0; }); atrdData.forEach((obj)=>{ let upHillId = null; let downHillId = null; const list = obj.list; if (list && list.length === 2) { upHillId = list[0].atrd_id; downHillId = list[1].atrd_id; if (list[0].drct_cd === "1") { upHillId = list[1].atrd_id; downHillId = list[0].atrd_id; } } listStr += `
  • ${obj.name}
  • ` mobileStr += `` }); const $mobileSelect = $('.mobile-select'); $mobileSelect.append($(mobileStr)); $mobileSelect.on('change', function(){ const ids = $(this).val(); if (ids && ids !== '-') { const idArr = ids.split('_'); atrdClickEvent(idArr[0], idArr[1]); } else { if (_AtrdArray.length > 0) { _AtrdArray.forEach((atrd)=>{ atrd.close(); }) _AtrdData = []; getVertex(); } } }) const listSection = $('.left-list-area .list-content.list'); listSection.empty(); listSection.html(listStr); } }); } } /** * 간선도로 클릭 이벤트 * @param AupHillId 상행 ID * @param AdownHillId 하행 ID */ function atrdClickEvent(AupHillId, AdownHillId) { if (_AtrdArray.length > 0) { _AtrdArray.forEach((atrd)=>{ atrd.close(); }); _AtrdArray = []; } _AtrdData = []; const $selectedLi = $('.left-list-area .list-content.list > li.click'); if ($selectedLi) { $selectedLi.removeClass('click'); } const $selectLi = $('#atrd_' + AupHillId + "_" + AdownHillId); $selectLi.addClass('click'); $selectLi.focus(); //상행, 하행 ID 가 있다면 실행 if (AupHillId && AdownHillId) { const upHillArr = []; const downHillArr = []; const bounds = new daum.maps.LatLngBounds(); //전체 간선도로 목록을 담은 Map 에서 기준값을 5레벨로 잡아 포커스 좌표를 설정 if (_AtrdMap.get("5")) { const basicAtrd = _AtrdMap.get("5"); for (let idx in basicAtrd) { const obj = basicAtrd[idx]; if(obj.atrd_id == AupHillId || obj.atrd_id == AdownHillId) { bounds.extend(new daum.maps.LatLng(obj.y_crdn_min, obj.x_crdn_min)); bounds.extend(new daum.maps.LatLng(obj.y_crdn_max, obj.x_crdn_max)); } } _Map.setBounds(bounds); //전체 레벨이 다있는게 아니므로 로드 레벨은 재설정 let level = _Map.getLevel(); if (level >= 8) { level = 7; } else if (level >= 6) { level = 6; } /** * 범위를 포커스 하게 되면 레벨이 변경되므로 변경 된 레벨의 간선도로 목록에서 * 상행, 하행 ID가 포함된 데이터 갖고오며 상행, 하행 데이터 따로 구분 */ const atrdInfo = _AtrdMap.get(level.toString()); if (atrdInfo) { atrdInfo.forEach((atrd)=>{ if (atrd.atrd_id === AupHillId || atrd.atrd_id == AdownHillId) { _AtrdData.push(atrd); if (atrd.atrd_id === AupHillId) { upHillArr.push(atrd); } else { downHillArr.push(atrd); } } }) } //레벨이 변경됐으므로 다시한번 버텍스를 초기화해준다 getVertex(); //각 상행 하행의 첫번째 좌표를 가지고 상행, 하행 아이콘을 생성 하며 객체는 array에 보관해둠 if (upHillArr[0]) { const upHillObj = new TbAtrdObj(upHillArr[0]); _AtrdArray.push(upHillObj); } if (downHillArr[0]) { const downHillObj = new TbAtrdObj(downHillArr[0]); _AtrdArray.push(downHillObj); } } } } /** * 간선도로 상,하행 마커 그리기 * @param src 상, 하행 코드 * @param lat X 좌표 * @param lng Y 좌표 * @param name 명칭 * @returns {daum.maps.Marker} 생성 마커 */ function drawAtrdMakrer(src, lng, lat, name) { let imageSize; let imageOption; let imageSrc = '/images/icon/atrd' + src + '.png'; //레벨별 사이즈를 다르게 표출 const level = _Map.getLevel(); let size = 48; if (level >= 7) { size = 24; } imageSize = new daum.maps.Size(size, size); imageOption = { offset: new daum.maps.Point(size/2, size), }; let markerImage = new daum.maps.MarkerImage(imageSrc, imageSize, imageOption); let markerPosition = new daum.maps.LatLng(lng, lat); let atrdSideMarker = new daum.maps.Marker({ position: markerPosition, image: markerImage, zIndex: 5, title: name, }); return atrdSideMarker; } /** * 소통정보 Object */ class TrafficObj { constructor(obj) { this.ID = obj.roadway_id; this.NAME = obj.roadway_nm; this.obj = obj; this.infoWindow = null; this.polyLine = null; this.polyBackLine = null; this.init(); } init() { const _self = this; const trafficObj = this.obj; const xArray = trafficObj.x_crdn.split(","); const yArray = trafficObj.y_crdn.split(","); const linePath = []; for (let ii = 0; ii < xArray.length; ii++) { const x_crdn = Number(xArray[ii]); const y_crdn = Number(yArray[ii]); const coordinates = new kakao.maps.LatLng(y_crdn, x_crdn); linePath.push(coordinates); } let strokeWeight = 5; let strokeWeightBack = 7; const level = _Map.getLevel(); if (level == 3) { strokeWeightBack = 6; } else if (level == 5 || level == 4) { strokeWeight = 3; strokeWeightBack = 5; } else if (level >= 6) { strokeWeight = 2; strokeWeightBack = 4; } this.polyBackLine = new daum.maps.Polyline({ path: linePath, // 선을 구성하는 좌표배열 입니다 strokeWeight: strokeWeightBack, // 선의 두께 입니다 strokeColor: '#eeeeee', // 선의 색깔입니다 strokeOpacity: 1, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다 strokeStyle: 'solid', // 선의 스타일입니다 zIndex: 1 }); this.polyLine = new daum.maps.Polyline({ path: linePath, // 선을 구성하는 좌표배열 입니다 strokeWeight: strokeWeight, // 선의 두께 입니다 strokeColor: g_color.get(trafficObj.cmtr_grad_cd), // 선의 색깔입니다 strokeOpacity: 1, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다 strokeStyle: 'solid', // 선의 스타일입니다 zIndex: 2 }); this.setVisibleMarker(_TrafficFlag); daum.maps.event.addListener(this.polyLine, 'mouseover', function (event) { this.setOptions({strokeColor: '#0000FF'}); const iwContent = `
    ${_self.NAME} ${trafficObj.grad_nm}
    ${trafficObj.strt_nm_node} → ${trafficObj.end_nm_node}
    소요시간 : 약 ${textFormat(trafficObj.trvl_hh)}분   속도 : 약 ${textFormat(trafficObj.sped)}km/h
    `; _self.infoWindow = new daum.maps.CustomOverlay({ map: _Map, clickable: true, position: event.latLng, content: iwContent, xAnchor: -0.1, yAnchor: 1.1, zIndex: 4 }); }); daum.maps.event.addListener(this.polyLine, 'mouseout', function () { this.setOptions({strokeColor: g_color.get(trafficObj.cmtr_grad_cd)}); if (_self.infoWindow != null) _self.infoWindow.setMap(null); }); } setVisibleMarker(isVisible) { const isShow = isVisible ? _Map : null; this.polyBackLine.setMap(isShow); this.polyLine.setMap(isShow); } } /** * 간선도로 Object */ class TbAtrdObj { constructor(obj) { this.ID = obj.atrd_id; this.NAME = obj.atrd_nm; this.X_CRDN = null; this.Y_CRDN = null; this.obj = obj; this.marker = null; this.imgSrc = '/images/icon/atrd'; this.isClick = false; this.init(); } init() { if (this.obj.x_crdn_arr) { this.X_CRDN = this.obj.x_crdn_arr.split(",")[0]; } if (this.obj.y_crdn_arr) { this.Y_CRDN = this.obj.y_crdn_arr.split(",")[0]; } const name = this.NAME + " [" + this.obj.drct_nm + "]"; this.marker = drawAtrdMakrer(this.obj.drct_cd, this.Y_CRDN, this.X_CRDN, name); this.click(); } click() { this.setVisibleMarker(true); } setVisibleMarker(isVisible) { let visible = isVisible ? _Map : null; this.marker.setMap(visible); this.isClick = isVisible; } close() { this.setVisibleMarker(false); } } /** * CCTV Object */ class TbCCtvObj { constructor(obj) { this.ID = obj.cctv_mngm_nmbr; this.NAME = obj.istl_lctn_nm; this.X_CRDN = obj.x_crdn; this.Y_CRDN = obj.y_crdn; this.obj = obj; this.marker = null; this.infoWindow = null; this.isClick = false; this.imgSrc = '/images/icon/cctv'; this.timer = null; } init() { this.marker = markerInit(this, _CctvFlag); } setVisibleMarker(isVisible) { this.marker.setVisible(isVisible); } click() { const coordinates = new kakao.maps.LatLng(Number(this.Y_CRDN), Number(this.X_CRDN)); if (_SelectedObj != null) { if (_SelectedObj === this) { return; } _SelectedObj.close(); } _SelectedObj = this; const selectedLi = $('#cctv-' + this.ID); selectedLi.addClass('click'); selectedLi.focus(); _Map.setCenter(coordinates); const iwContent = `
    ${this.NAME}
    X
    ※ CCTV영상은 30초간 제공됩니다.
    계속재생
    `; $('body').append($(iwContent)); this.infoWindow = $('.cctv-info-window'); let position = getInfoWidowPosition(this.infoWindow); let top = position[0]; let left = position[1]; this.infoWindow.css({ top : top + 'px', left : left + 'px', position : 'absolute' }); this.infoWindow.draggable({containment : 'body', handle: '.cctv-name-'+this.ID}); // this.infoWindow = new kakao.maps.CustomOverlay({position: coordinates, content: iwContent, zindex: 15, yAnchor: 1.1}); // this.infoWindow.setMap(_Map); let video = videojs("video-" + this.ID, { sources: [ { src: this.obj.strm_http_addr, type: "application/x-mpegURL", crossorigin: "anonymous", }, ], responsive: false, autoplay: true, muted: true, preload: "metadata", }); video.on('error', ()=>{ if (video.error().code === 4) { video.pause(); video.dispose(); if (this.timer) { clearTimeout(this.timer); } $('.content > div:nth-child(1)').append($('')); $('.content > div:nth-child(1)').css({ display : 'flex', alignItems : 'center', justifyContent : 'center', }); video = null; } }) this.timer = setTimeout(()=>{ if (video) { video.pause(); } }, 30000); let cctvObj = this; $('.continue-play').on('click', ()=>{ if (cctvObj.timer) { setTimeout(cctvObj.timer); } if (video) { video.play(); cctvObj.timer = setTimeout(()=>{ video.pause(); }, 30000); } }); setMarkerImage(this, 2); this.isClick = true; moveToScroll(_CctvListFlag, _CctvArray, this.ID); } close() { this.isClick = false; _SelectedObj = null; setMarkerImage(this, 1); let oldPlayer = document.getElementById("video-" + this.ID); let selectedLi = $("#cctv-" + this.ID); if (selectedLi.hasClass('click')) { selectedLi.removeClass('click'); } if (_CctvListFlag) { $('.mobile-select').val("-"); } if (oldPlayer) { videojs(oldPlayer).dispose(); } if (this.infoWindow) { this.infoWindow.remove(); // this.infoWindow.setMap(null); this.infoWindow = null; } if (this.timer) { clearTimeout(this.timer); } } } /** * VMS Object */ class TbVmsObj { constructor(obj) { this.ID = obj.vms_ctlr_nmbr; this.NAME = obj.vms_nm; this.X_CRDN = obj.x_crdn; this.Y_CRDN = obj.y_crdn; this.obj = obj; this.marker = null; this.infoWindow = null; this.isClick = false; this.imgSrc = '/images/icon/vms'; this.timer = null; } init() { this.marker = markerInit(this, _VmsFlag); } setVisibleMarker(isVisible) { this.marker.setVisible(isVisible); } click() { if (_SelectedObj != null) { if (_SelectedObj === this) { return; } _SelectedObj.close(); } _SelectedObj = this; const selectedLi = $('#vms-' + this.ID); selectedLi.addClass('click'); selectedLi.focus(); const coordinates = new kakao.maps.LatLng(Number(this.Y_CRDN), Number(this.X_CRDN)); _Map.setCenter(coordinates); let phaseArray = []; let width; let height; let isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); switch (this.obj.vms_type_cd) { case 'VMC1': width = 400; height = 64; break; case 'VMC2': width = 384; height = 64; break; case 'VMC3': width = 288; height = 160; break; case 'VMC4': width = 256; height = 192; break; } const windowHeight = $(window).height(); if (isMobile || windowHeight < 900) { width = width/2; height = height/2; } width = width +'px'; height = height + 'px'; const msg = this.obj.msg; if (!msg || msg.length === 0) { width = '256px'; height = '50px'; } let iwContent = `
    ${this.NAME}
    X
    `; if (msg && msg.length > 0) { for (let idx in msg) { let msgObj = msg[idx]; let className = ''; if (idx === "0") { className = 'active' } iwContent += ``; phaseArray.push(msgObj.phase); } } else { iwContent += '표출 이미지 데이터가 없습니다.'; } iwContent += `
    `; // this.infoWindow = new kakao.maps.CustomOverlay({position: coordinates, content: iwContent, zindex: 15, yAnchor: 1.2}); $('body').append($(iwContent)); this.infoWindow = $('.vms-info-window'); let position = getInfoWidowPosition(this.infoWindow); let top = position[0]; let left = position[1]; this.infoWindow.css({ top : top + 'px', left : left + 'px', position: 'absolute', zIndex : '999', }) this.infoWindow.draggable({containment : 'body', handle: '.vms-name-'+this.ID}); // this.infoWindow.setMap(_Map); setMarkerImage(this, 2) this.isClick = true; moveToScroll(_VmsListFlag, _VmsArray, this.ID); let cnt = 1; this.timer = setInterval(()=>{ if (phaseArray.length > 0) { if (cnt === phaseArray.length) { cnt = 0; } const activeImage = $('.vms-info-window .content img.active'); if (activeImage[0]) { activeImage.removeClass('active'); } $("#phase-" + phaseArray[cnt]).addClass('active'); cnt++; } }, 4000); } close() { this.isClick = false; _SelectedObj = null; setMarkerImage(this, 1); let selectedLi = $("#vms-" + this.ID); if (selectedLi.hasClass('click')) { selectedLi.removeClass('click'); } if (_VmsListFlag) { $('.mobile-select').val("-"); } if (this.infoWindow) { // this.infoWindow.setMap(null); this.infoWindow.remove(); this.infoWindow = null; } if (this.timer) { clearTimeout(this.timer); } } } /** * 돌발정보 Object */ class TbIncdObj { constructor(obj) { this.ID = obj.incd_ocrr_id; this.NAME = obj.incd_titl; this.X_CRDN = obj.x_crdn; this.Y_CRDN = obj.y_crdn; this.obj = obj; this.marker = null; this.infoWindow = null; this.isClick = false; this.imgSrc = '/images/icon/incd'; } init() { this.marker = markerInit(this, _IncidentFlag); } setVisibleMarker(isVisible) { this.marker.setVisible(isVisible); } click() { if (_SelectedObj != null) { if (_SelectedObj === this) { return; } _SelectedObj.close(); } _SelectedObj = this; const selectedLi = $('#incident-' + this.ID); selectedLi.addClass('click'); selectedLi.focus(); const coordinates = new kakao.maps.LatLng(Number(this.Y_CRDN), Number(this.X_CRDN)); _Map.setCenter(coordinates); let iwContent = `
    ${this.NAME}
    X
    위치 : ${this.obj.road_nm}
    설명 : ${this.obj.incd_expl}
    기간 : ${this.obj.incd_strt_dt} ~ ${this.obj.incd_end_prar_dt}
    `; // this.infoWindow = new kakao.maps.CustomOverlay({position: coordinates, content: iwContent, zindex: 15, yAnchor: 1.2}); // this.infoWindow.setMap(_Map); $('body').append($(iwContent)); this.infoWindow = $('.incident-info-window'); let position = getInfoWidowPosition(this.infoWindow); let top = position[0]; let left = position[1]; this.infoWindow.css({ top : top + 'px', left : left + 'px', position: 'absolute', zIndex : '999', }) this.infoWindow.draggable({containment : 'body', handle: '.incident-name-'+ this.ID}); setMarkerImage(this, 2); this.isClick = true; moveToScroll(_IncidentListFlag, _IncidentArray, this.ID); } close() { this.isClick = false; _SelectedObj = null; setMarkerImage(this, 1) let selectedLi = $("#incident-" + this.ID); if (selectedLi.hasClass('click')) { selectedLi.removeClass('click'); } if (_IncidentListFlag) { $('.mobile-select').val("-"); } if (this.infoWindow) { // this.infoWindow.setMap(null); this.infoWindow.remove(); this.infoWindow = null; } } } /** * 스마트 교차로 Object */ class IntersectionObj { constructor(obj) { this.ID = obj.ixr_id; this.NAME = obj.ixr_nm; this.X_CRDN = obj.x_crdn; this.Y_CRDN = obj.y_crdn; this.obj = obj; this.marker = null; this.infoWindow = null; this.isClick = false; this.imgSrc = '/images/icon/intersection'; this.detail = []; } init() { let imageSrc = this.imgSrc + '.png', // 마커이미지의 주소입니다 size = _size[_Map.getLevel()], imageSize = new kakao.maps.Size(size, size), // 마커이미지의 크기입니다 imageOption = { offset: new kakao.maps.Point(size/2, size/2), alt: this.NAME, }; // 마커의 이미지정보를 가지고 있는 마커이미지를 생성합니다 let markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imageOption), markerPosition = new kakao.maps.LatLng(this.Y_CRDN, this.X_CRDN); // 마커가 표시될 위치입니다 this.marker = new kakao.maps.Marker( {map: _Map, position: markerPosition, image: markerImage, zindex: 10, clickable: true, title : this.NAME} ); const level = _Map.getLevel(); const isVisible = (level > 2 && _IntersectionFlag); this.marker.setVisible(isVisible); let _self = this; kakao.maps.event.addListener(this.marker, 'click', ()=> _self.click()); } setVisibleMarker(isVisible) { if (_Map.getLevel() <= 2) isVisible = false; this.marker.setVisible(isVisible); } click() { if (_SelectedObj != null) { // if (_SelectedObj === this) { // return; // } _SelectedObj.close(); } _SelectedObj = this; const selectedLi = $('#intersection-' + this.ID); selectedLi.addClass('click'); selectedLi.focus(); const coordinates = new kakao.maps.LatLng(Number(this.Y_CRDN), Number(this.X_CRDN)); _Map.setCenter(coordinates); this.isClick = true; moveToScroll(_IntersectionListFlag, _IntersectionArray, this.ID); _Map.setLevel(2); } close() { this.isClick = false; _SelectedObj = null; if (_IntersectionListFlag) { $('.mobile-select').val("-"); } let selectedLi = $("#intersection-" + this.ID); if (selectedLi.hasClass('click')) { selectedLi.removeClass('click'); } } } /** * 스마트교차로 카메라 Object */ class IntersectionCameraObj { constructor(obj) { this.ID = obj.cmra_id + '_' + obj.drct_dvsn_cd; this.NAME = obj.drct_lctn; this.X_CRDN = obj.cmra_x_crdn; this.Y_CRDN = obj.cmra_y_crdn; this.obj = obj; this.marker = null; this.infoWindow = null; this.isClick = false; this.timer = null; this.polyline = null; } init() { this.marker = intersectionCameraMarkerInit(this); } setVisibleMarker(isVisible) { const visible = isVisible && _Map.getLevel() <= 2 ? _Map : null; this.marker.setMap(visible); this.polyline.setMap(visible); } click() { const coordinates = new kakao.maps.LatLng(Number(this.Y_CRDN), Number(this.X_CRDN)); if (_SelectedObj != null) { if (_SelectedObj === this) { return; } _SelectedObj.close(); } _SelectedObj = this; _Map.setCenter(coordinates); const iwContent = `
    ${this.NAME}
    X
    ※ CCTV영상은 30초간 제공됩니다.
    계속재생
    `; $('body').append($(iwContent)); this.infoWindow = $('.cctv-info-window'); // const headerH = $('header').height(); // const wrapH = $('.trafficWrap').innerHeight(); // let top = headerH + (wrapH/2) - this.infoWindow.innerHeight() + 10; // let left = (window.innerWidth / 2) - (this.infoWindow.width() / 2); let position = getInfoWidowPosition(this.infoWindow); let top = position[0]; let left = position[1]; this.infoWindow.css({ top : top + 'px', left : left + 'px', position: 'absolute', zIndex : '999', }); this.infoWindow.draggable({containment : 'body', handle : '.ixr-name-' + this.ID}); // this.infoWindow = new kakao.maps.CustomOverlay({ // position: coordinates, // content: iwContent, // zindex: 15, // yAnchor: 1.1 // }); // this.infoWindow.setMap(_Map); let video = videojs("video-" + this.ID, { sources: [ { src: this.obj.hmpg_cmra_url, type: "application/x-mpegURL", crossorigin: "anonymous", }, ], responsive: false, autoplay: true, muted: true, preload: "metadata", }); video.on('error', ()=>{ if (video.error().code === 4) { video.pause(); video.dispose(); $('.content > div:nth-child(1)').append($('')); $('.content > div:nth-child(1)').css({ display : 'flex', alignItems : 'center', justifyContent : 'center', }); video = null; } }); this.timer = setTimeout(() => { if (video) { video.pause(); } }, 30000); let _self = this; $('.continue-play').on('click', () => { if (_self.timer) { setTimeout(_self.timer); } if (video) { video.play(); _self.timer = setTimeout(() => { video.pause(); }, 30000); } }); this.isClick = true; $('.close-window').on('click', (event)=> { event.preventDefault(); this.close(); }); } close() { this.isClick = false; _SelectedObj = null; let oldPlayer = document.getElementById("video-" + this.ID); if (oldPlayer) { videojs(oldPlayer).dispose(); } if (this.infoWindow) { // this.infoWindow.setMap(null); this.infoWindow.remove(); this.infoWindow = null; } if (this.timer) { clearTimeout(this.timer); } } } /** * 인포윈도우 이벤트 * @param type 시설물 유형 * @param id 요소 ID * @param event 이벤트 종류 (click, close) */ function infoWindowEvent(type, id, event) { window.event.preventDefault(); let array = []; switch (type) { case 'cctv': array = _CctvArray; break; case 'vms': array = _VmsArray; break; case 'incident': array = _IncidentArray; break; case 'intersection': array = _IntersectionArray; break; } if (array.length > 0) { let idx = array.findIndex(markerObj => markerObj.ID == id); if (event === 'click') { array[idx].click(); if (_TrafficFlag) { getVertex(); } } else if (event === 'close') { array[idx].close(); } else {} } } /** * 범례 클릭 시설물 아이콘 보이기 / 감추기 * @param array 시설물 리스트 * @param isShow 보이기/감추기(true / false) * @param icon 범례 아이콘 * @param target 범례 요소 */ function onOffFacility(array, isShow, icon, target, subArray) { if (isShow === false) { icon.removeClass('active'); target.removeClass('active'); array.forEach((obj)=>{ obj.setVisibleMarker(false); if (obj.isClick === true) { obj.close(); } }); if (subArray) { subArray.forEach((obj)=>{ obj.setVisibleMarker(false); if (obj.isClick === true) { obj.close(); } }) } } else { icon.addClass('active') target.addClass('active'); array.forEach((obj)=>{ obj.setVisibleMarker(true); }); if (subArray) { subArray.forEach((obj)=>{ obj.setVisibleMarker(true); }) } } } let isHide = false /** * 좌측 목록 토글 이벤트 */ function toggleEvent() { const $listArea = $('.left-list-area'); const $toggleButton = $('.toggle-button'); if (!isHide) { $toggleButton.animate({ left: 0 }, 'slow'); $listArea .animate({ left: -$listArea.width() }, 'slow'); $toggleButton.text('>'); } else { $toggleButton.animate({ left: $listArea.width() }, 'slow'); $listArea .animate({ left: 0 }, 'slow'); $toggleButton.text('<'); } isHide = !isHide; } window.addEventListener('resize', function(event) { if ($(this).width() > 450) { const $toggleButton = $('.toggle-button'); const $listArea = $('.left-list-area'); const left = $toggleButton.offset().left; const listLeft = $listArea.offset().left; if ($(this).width() >= 920) { if (left > 0 && left < 400) { $toggleButton.css('left', 400); } if (listLeft > -400 && listLeft < 0) { $listArea.css('left', -400); } } else { if (left > 0 && left > 275) { $toggleButton.css('left', 275); } if (listLeft < -273) { $listArea.css('left', -273); } } } }) /** * 시설물 마커 초기화 * @param obj 시설물 객체 * @returns {kakao.maps.Marker} */ function markerInit(obj, flag) { let imageSrc = obj.imgSrc + '1.png', // 마커이미지의 주소입니다 size = _size[_Map.getLevel()], imageSize = new kakao.maps.Size(size, size), // 마커이미지의 크기입니다 imageOption = { offset: new kakao.maps.Point(size/2, size/2), alt: obj.NAME, }; // 마커의 이미지정보를 가지고 있는 마커이미지를 생성합니다 let markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imageOption), markerPosition = new kakao.maps.LatLng(obj.Y_CRDN, obj.X_CRDN); // 마커가 표시될 위치입니다 let marker = new kakao.maps.Marker( {map: _Map, position: markerPosition, image: markerImage, zindex: 10, clickable: true, title : obj.NAME} ); marker.setVisible(flag); kakao.maps.event.addListener(marker, 'mouseover', function () { if (!obj.isClick) { setMarkerImage(obj, 2); } }); kakao.maps.event.addListener(marker, 'mouseout', function () { if (!obj.isClick) { setMarkerImage(obj, 1) } }); kakao.maps.event.addListener(marker, 'click', ()=> obj.click()); return marker; } /** * 스마트 교차로 카메라 마커 초기화 * @param obj 스마트 교차로 카메라 객체 * @returns {kakao.maps.Marker} */ function intersectionCameraMarkerInit(obj) { const position = new kakao.maps.LatLng(obj.Y_CRDN, obj.X_CRDN); const content = $('
    '); const angle = Number(obj.obj.cmra_angn); content.css( { width: '38.45px', height: '38.45px', backgroundImage:'url(/images/icon/intersection-cctv.png)', backgroundSize : '38.45px 38.45px', backgroundRepeat: 'no-repeat', backgroundPosition : 'center', transform : 'rotate(' + angle +'deg)' }); const marker = new daum.maps.CustomOverlay({ content: content[0], position: position, zindex: 15, }); const linePath1 = [obj.obj.start_x, obj.obj.start_y] const linePath2 = [obj.obj.end_x, obj.obj.end_y]; const color = ['#888888', '#15B337', '#15B337', '#15B337', '#FFAA00', '#FFAA00', '#EB260C', '#EB260C', '#EB260C']; obj.polyline = new kakao.maps.Polyline({ path: [ new kakao.maps.LatLng(linePath1[1], linePath1[0]), new kakao.maps.LatLng(linePath2[1], linePath2[0]), ], strokeWeight: 10, strokeColor: color[obj.obj.acrd_los], strokeOpacity: 1, strokeStyle: 'solid', endArrow: true, name : obj.NAME, }); if (_Map.getLevel() <= 2 && _IntersectionFlag){ marker.setMap(_Map); obj.polyline.setMap(_Map); } content.on('click', ()=> { obj.click(); }); return marker; } /** * 시설물 이미지 유형 변경 이벤트 * @param obj 시설물 객체 * @param type 시설물 이미지 유형 */ function setMarkerImage(obj, type) { const size = _size[_Map.getLevel()]; const point = size/2; const imageSize = new kakao.maps.Size(size, size); const imageSrc = obj.imgSrc + type +'.png'; const imageOption = { offset: new kakao.maps.Point(point, point), alt: obj.NAME }; const markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imageOption); obj.marker.setImage(markerImage); } /** * 선택 리스트 스크롤 높이 반환 이벤트 * @param selectIndex 선택 인덱스 * @returns {number} 스크롤 높이 */ function getScrollTop(selectIndex) { let scrollTop = 0; for (let ii=0; ii < selectIndex; ii++) { let height = $('.left-list-area .list-content.list').children().eq(ii).css('height'); if (height) { height = Number(height.replace('px', '')); if (!isNaN(height)) { scrollTop += height; } } } return scrollTop; } /** * 좌측 리스트 선택 시 스크롤 변경 이벤트 * @param flag * @param array * @param id */ function moveToScroll(flag, array, id) { if (flag) { let selectIndex = array.findIndex((obj)=> obj.ID == id); let scrollTop = getScrollTop(selectIndex); $('.left-list-area .list-content.list').animate({ scrollTop : scrollTop + 'px' }); $('.mobile-select').val(id); } } /** * 시설물 데이터 처리 메서드 * @param jsonData 수신 데이터 * @param array 시설물 Object Array List * @param facilityClass 시설물 Object Class * @param listFlag 시설물 리스트 목록 표출 플래그 * @param type 시설물 유형 * @returns {*[]} 시설물 Object Array List */ function receiveFacilityData(jsonData, array, facilityClass, listFlag, type) { if (array.length > 0) { array.forEach((obj)=>{ obj.marker.setVisible(false); }) } if (type === 'intersection') { if (_IntersectionCameraArray.length > 0) { _IntersectionCameraArray.forEach((obj) => { obj.marker.setMap(null); obj.polyline.setMap(null); }) } } array = []; let listStr = ""; let mobileStr = ""; jsonData.forEach((obj)=>{ const markerObj = new facilityClass(obj); if (type === 'intersection') { if (obj.detail && obj.detail.length > 0) { obj.detail.forEach((cameraObj)=>{ const camera = new IntersectionCameraObj(cameraObj); camera.init(); _IntersectionCameraArray.push(camera); }) } } markerObj.init(); listStr += `
  • ${markerObj.NAME}
  • ` mobileStr += ``; array.push(markerObj); }); if (listFlag === true) { const listSection = $('.left-list-area .list-content.list'); listSection.empty(); listSection.html(listStr); const $mobileSelect = $('.mobile-select'); $mobileSelect.append($(mobileStr)); $mobileSelect.on('change', function(){ const id = $(this).val(); if (id && id !== "-") { infoWindowEvent(type, id , 'click'); } }) } return array; } //https://map.kakao.com/link/search/카카오 function getInfoWidowPosition(infoWindow) { const map = $('#map'); let mapHalfW = map.innerWidth() / 2; let mapHalfH = map.innerHeight() / 2; let mapTop = map.offset().top; let halfW = infoWindow.innerWidth() / 2; let height = infoWindow.innerHeight(); let left = mapHalfW - halfW; let iconH = _size[_Map.getLevel()]; let top = mapTop + mapHalfH - height - iconH; return [top, left]; }