let _size = [48, 48, 48, 48, 40, 32, 24, 22, 20, 18, 18];
let _AtrdData = [];
let _AtrdMap = new Map();
let selectIncidentId = null;
//시설물 유형
const _FacilityArray = ['cctv', 'vms', 'intersection', 'incident', 'traffic', 'parking', 'intersectionCamera'];
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;');
const CCTV_DISPLAY_TIME = 30 * 1000; // 영상 표출 시간
const VMS_DISPLAY_TIME = 4 * 1000; // VMS 이미지 표출 시간
let _MapHandler; //카카오 맵 핸들러
let _Level = 6; //현재 줌레벨
let _methodMap = new Map();
_methodMap.set('cctv', getCctv);
_methodMap.set('vms', getVms);
_methodMap.set('incident', getIncident);
_methodMap.set('intersection', getIntersection);
_methodMap.set('traffic', getAtrd);
_methodMap.set('parking', getParking);
$(()=>{
// Map 요소 ID로 생성
_MapHandler = new MapHandler('map');
_MapHandler.init();
// 시설물 유형 정보로 flag 값 세팅
if (_type) {
_MapHandler[_type + 'Flag'] = true;
_MapHandler[_type + 'ListFlag'] = true;
_methodMap.get(_type)();
}
/**
* 범례 클릭 이벤트 및 색 변환
* */
$('.legend-bottom button').on('click', function (){
const target = $(this);
const legendIcon = target.children().eq(0);
const isShow = !legendIcon.hasClass('active');
const id = target.attr('id');
const type = id.replace('-legend', '');
let subType = null;
if (type === 'intersection') {
subType = type + 'Camera';
}
if (type === 'traffic') {
subType = 'atrd';
getVertex();
}
else {
if (_MapHandler[type] && _MapHandler[type].length <= 0) {
_methodMap.get(type)();
}
}
let toggle = 'hide';
let classToggle = 'removeClass';
if (isShow === true) {
toggle = 'show';
classToggle = 'addClass';
}
legendIcon[classToggle]('active');
target[classToggle]('active');
_MapHandler[toggle](type);
if (subType) {
_MapHandler[toggle](subType);
}
})
/**
* 범례 호버 색 변환
* */
$('.legend-bottom ul li').hover((event)=>{
const iconDiv = $(event.currentTarget).children().eq(0);
const childDiv = iconDiv.children().eq(0);
if (!iconDiv.hasClass('active')) {
childDiv.addClass('hover');
}
else {
if (childDiv.hasClass('hover')) {
childDiv.removeClass('hover');
}
}
},
(event)=>{
const iconDiv = $(event.currentTarget).children().eq(0);
const childDiv = iconDiv.children().eq(0);
if (!iconDiv.hasClass('active')) {
childDiv.removeClass('hover');
}
else {
if (childDiv.hasClass('hover')) {
childDiv.removeClass('hover');
}
}
});
/**
* 탭 호버 색 변환
* */
$('.left-list-area .list-tab > li').hover((event)=>{
const iconDiv = $(event.currentTarget);
const childDiv = iconDiv.children().eq(0);
if (!iconDiv.hasClass('active')) {
childDiv.addClass('hover');
}
else {
if (childDiv.hasClass('hover')) {
childDiv.removeClass('hover');
}
}
},
(event)=>{
const iconDiv = $(event.currentTarget);
const childDiv = iconDiv.children().eq(0);
if (!iconDiv.hasClass('active')) {
childDiv.removeClass('hover');
}
else {
if (childDiv.hasClass('hover')) {
childDiv.removeClass('hover');
}
}
});
let _MarkerArr = [];
$('.tab-title > div').on('click', function(){
const $spot = $('.list-content.spot');
const $list = $('.list-content.list');
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';
$spot.html("");
}
else {
list = 'block';
spot = 'none';
$('.list-content.list > li').css('display', 'block');
}
$('#search-box').val("");
$list.css('display', list);
$spot.css('display', spot);
if (_MarkerArr.length > 0) {
_MarkerArr.forEach((obj)=>{
obj.setMap(null);
});
_MarkerArr = [];
}
})
$('#search-box').on('keyup', function () {
const searchType = $('.tab-title > div.active').text();
const searchText = $(this).val();
if (searchType === '리스트') {
const $list = $('.list-content.list > li');
if (!$list.length) return;
for (let ii = 0; ii < $list.length; ii++) {
const li = $list.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 (_MarkerArr.length > 0) {
_MarkerArr.forEach((obj)=>{
obj.setMap(null);
});
_MarkerArr = [];
}
$('.list-content.spot').html("");
if (!searchText || !searchText.trim()) return;
$.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 kakao.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 = getKakaoPosition(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(_MapHandler.map);
_MarkerArr.push(marker);
setMap.set(addr, obj);
str+= `
');
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', ()=> {
_MapHandler.click(type, obj.ID);
});
if (level <= 2 && _MapHandler['intersectionFlag']) {
obj.marker.setMap(map);
obj.polyline.setMap(map);
} else {
obj.marker.setMap(null);
obj.polyline.setMap(null);
}
})
}
}
/**
* 스마트교차로 레벨별 이미지 보이기 감추기
*/
function intersectionMarkerChangeWithZoomLevel() {
const level = _Level;
if (_MapHandler['intersection'].length > 0) {
_MapHandler['intersection'].forEach((obj)=>{
setMarkerImage(obj, "", false);
if (level > 2 && _MapHandler['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, false)
}
else {
setMarkerImage(obj, 1, false);
}
});
}
}
/**
* 지점 클릭 위치 이동
* @param xCoordinate x좌표
* @param yCoordinate y좌표
* @param listIndex 지점 선택 인덱스
*/
function moveLocation(xCoordinate, yCoordinate, listIndex) {
const position = getKakaoPosition(yCoordinate, xCoordinate);
_MapHandler.map.setCenter(position);
_MapHandler.map.setLevel(3);
$('.list-content.spot > li.click').removeClass('click');
$('#spot-' +listIndex).addClass('click');
getVertex();
}
/**
* cctv 데이터 가져오기
*/
function getCctv() {
getDataAsync('/api/traffic/cctv-list', 'POST', null, null, (jsonData)=>{
if (jsonData && jsonData.length > 0) {
receiveFacilityData(jsonData, TbCCtvObj,'cctv');
}
}, null);
}
/**
* vms 데이터 가져오기
*/
function getVms() {
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;
})
receiveFacilityData(jsonData, TbVmsObj,'vms');
}
}, null);
}
/**
* 돌발정보
*/
function getIncident () {
getDataAsync('/api/traffic/incident-list', 'POST', null, null, (jsonData)=>{
if (jsonData && jsonData.length > 0) {
receiveFacilityData(jsonData, TbIncdObj, 'incident');
if (selectIncidentId) {
_MapHandler.click('incident', selectIncidentId);
}
}
}, null);
}
/**
* 스마트교차로
*/
function getIntersection() {
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);
}
})
receiveFacilityData(data, IntersectionObj, 'intersection');
}
}, null);
}
/**
* 주차정보
*/
function getParking() {
getDataAsync('/api/traffic/parking-list', 'POST', null, null, (jsonData)=>{
if (jsonData && jsonData.length > 0) {
receiveFacilityData(jsonData, TbParkingObj, 'parking');
}
}, null);
}
/**
* 간선도로 정보
*/
function getAtrd() {
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 {
const array = _MapHandler['atrd'];
if (array.length > 0) {
array.forEach((atrd)=>{
atrd.close();
})
_MapHandler['atrd'] = [];
getVertex();
}
}
})
const listSection = $('.left-list-area .list-content.list');
listSection.empty();
listSection.html(listStr);
}
});
getVertex();
}
/**
* 소통정보 가져오기
*/
function getVertex() {
const array = _MapHandler['traffic'];
if (array.length > 0) {
array.forEach((obj) => {
obj.setVisibleMarker(false);
if (obj.infoWindow) {
obj.infoWindow.setMap(null);
}
})
_MapHandler['traffic'] = [];
}
let level = _MapHandler.map.getLevel();
const bounds = _MapHandler.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(),
}
if (_MapHandler.atrd && _MapHandler.atrd.length > 0) {
_MapHandler.atrd.forEach((atrd)=>{
atrd.cnt = 0;
atrd.trvl_hh = 0;
atrd.sped = 0;
})
}
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.toString() === trafficObj.ID.toString()) {
let sped = Number(obj.sped);
let trvl_hh = Number(obj.trvl_hh);
const atrdObj = _MapHandler.getSelectObj(_MapHandler['atrd'], atrd.atrd_id);
if (!isNaN(sped)) {
atrdObj.sped += sped;
}
if (!isNaN(trvl_hh)) {
atrdObj.trvl_hh += trvl_hh;
}
atrdObj.cnt += 1;
trafficObj.polyBackLine.setOptions({
strokeColor : 'black',
});
}
})
}
_MapHandler['traffic'].push(trafficObj);
})
}
});
}
/**
* 간선도로 클릭 이벤트
* @param AupHillId 상행 ID
* @param AdownHillId 하행 ID
*/
function atrdClickEvent(AupHillId, AdownHillId) {
let atrdArr = _MapHandler['atrd'];
if (atrdArr.length > 0) {
atrdArr.forEach((atrd)=>{
atrd.close();
});
_MapHandler['atrd'] = [];
}
const selectedObj = _MapHandler.selectedObj;
if (selectedObj) {
_MapHandler.close(selectedObj.type, selectedObj.ID);
}
_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 kakao.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.toString() === AupHillId || obj.atrd_id.toString() === AdownHillId) {
bounds.extend(new kakao.maps.LatLng(obj.y_crdn_min, obj.x_crdn_min));
bounds.extend(new kakao.maps.LatLng(obj.y_crdn_max, obj.x_crdn_max));
}
}
_MapHandler.map.setBounds(bounds);
//전체 레벨이 다있는게 아니므로 로드 레벨은 재설정
let level = _Level;
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.toString() === AupHillId.toString() || atrd.atrd_id.toString() === AdownHillId.toString()) {
_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]);
_MapHandler['atrd'].push(upHillObj);
}
if (downHillArr[0]) {
const downHillObj = new TbAtrdObj(downHillArr[0]);
_MapHandler['atrd'].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 = _Level;
let size = 48;
if (level >= 7) {
size = 24;
}
imageSize = new kakao.maps.Size(size, size);
imageOption = {
offset: new kakao.maps.Point(size/2, size),
};
let markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imageOption);
let markerPosition = new kakao.maps.LatLng(lng, lat);
let atrdSideMarker = new kakao.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 = getKakaoPosition(y_crdn, x_crdn);
linePath.push(coordinates);
}
let strokeWeight = 5;
let strokeWeightBack = 7;
const level = _Level;
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 kakao.maps.Polyline({
path: linePath, // 선을 구성하는 좌표배열 입니다
strokeWeight: strokeWeightBack, // 선의 두께 입니다
strokeColor: '#eeeeee', // 선의 색깔입니다
strokeOpacity: 1, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다
strokeStyle: 'solid', // 선의 스타일입니다
zIndex: 1
});
this.polyLine = new kakao.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(_MapHandler['trafficFlag']);
new kakao.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 kakao.maps.CustomOverlay({
map: _MapHandler.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 ? _MapHandler.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.sped = 0;
this.trvl_hh = 0;
this.cnt = 0;
this.infoWindow = null;
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();
// let _self = this;
// new kakao.maps.event.addListener(this.marker, 'mouseover', function (event) {
// let position = getKakaoPosition(_self.Y_CRDN, _self.X_CRDN);
// const iwContent =
// `
//
// ${_self.NAME}
// [${_self.obj.drct_nm}]
//
//
// 평균속도 : ${Math.round(_self.sped/_self.cnt)} km/h
// 통행시간 : ${textFormat(Math.round(_self.trvl_hh/_self.trvl_hh))}분
//
//
`;
// _self.infoWindow = new kakao.maps.CustomOverlay({
// map: _MapHandler.map,
// clickable: true,
// position: position,
// content: iwContent,
// xAnchor: -0.1,
// yAnchor: 1.1,
// zIndex: 4
// });
// });
// new kakao.maps.event.addListener(this.marker, 'mouseout', function (event) {
// _self.infoWindow.setMap(null);
// })
}
click() {
this.setVisibleMarker(true);
}
setVisibleMarker(isVisible) {
let visible = isVisible ? _MapHandler.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.URL = obj.strm_http_addr;
this.type = 'cctv';
this.obj = obj;
this.marker = null;
this.infoWindow = null;
this.iwContent = null;
this.isClick = false;
this.imgSrc = '/images/icon/cctv';
this.timer = null;
}
init() {
this.marker = createMarker(this, 'cctv');
this.iwContent = `
※ CCTV영상은 30초간 제공됩니다.
계속재생
`;
}
setVisibleMarker(isVisible) {
this.marker.setVisible(isVisible);
}
}
/**
* Parking Object
*/
class TbParkingObj {
constructor(obj) {
this.ID = obj.parking_id;
this.NAME = obj.parking_nm;
this.X_CRDN = obj.x_crdn;
this.Y_CRDN = obj.y_crdn;
this.obj = obj;
this.marker = null;
this.infoWindow = null;
this.iwContent = null;
this.isClick = false;
this.imgSrc = '/images/icon/parking';
this.timer = null;
this.type = 'parking';
}
init() {
this.marker = createMarker(this, 'parking');
this.iwContent = `
주차면수
${this.obj.parking_num} 대
구분
${this.obj.parking_type_desc}
기본요금
${this.obj.parking_fee_type_desc}
주소
${this.obj.parking_addr}
`;
}
setVisibleMarker(isVisible) {
this.marker.setVisible(isVisible);
}
}
/**
* 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.iwContent = null;
this.isClick = false;
this.imgSrc = '/images/icon/vms';
this.timer = null;
this.phaseArray = [];
this.type = 'vms';
}
init() {
this.marker = createMarker(this, 'vms');
this.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 =
`
`;
if (msg && msg.length > 0) {
for (let idx in msg) {
let msgObj = msg[idx];
let className = '';
if (idx === "0") {
className = 'active'
}
iwContent += `

`;
this.phaseArray.push(msgObj.phase);
}
}
else {
iwContent += '표출 이미지 데이터가 없습니다.';
}
iwContent += `
`;
this.iwContent = iwContent;
}
setVisibleMarker(isVisible) {
this.marker.setVisible(isVisible);
}
}
/**
* 돌발정보 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';
this.iwContent = null;
this.type = 'incident';
}
init() {
this.marker = createMarker(this, 'incident');
this.iwContent = `
위치 : ${this.obj.road_nm}
설명 : ${this.obj.incd_expl}
기간 : ${this.obj.incd_strt_dt} ~ ${this.obj.incd_end_prar_dt}
`;
}
setVisibleMarker(isVisible) {
this.marker.setVisible(isVisible);
}
}
/**
* 스마트 교차로 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 = [];
this.type = 'intersection';
}
init() {
let imageSrc = this.imgSrc + '.png', // 마커이미지의 주소입니다
size = _size[_Level],
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 = getKakaoPosition(this.Y_CRDN, this.X_CRDN); // 마커가 표시될 위치입니다
this.marker = new kakao.maps.Marker(
{map: _MapHandler.map, position: markerPosition, image: markerImage, zindex: 10, clickable: true, title : this.NAME}
);
const level = _Level;
const isVisible = (level > 2 && _MapHandler['intersectionFlag']);
this.marker.setVisible(isVisible);
let _self = this;
new kakao.maps.event.addListener(this.marker, 'click', ()=> _MapHandler.click('intersection', _self.ID));
}
setVisibleMarker(isVisible) {
if (_Level <= 2) isVisible = false;
this.marker.setVisible(isVisible);
}
}
/**
* 스마트교차로 카메라 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.URL = obj.hmpg_cmra_url;
this.obj = obj;
this.marker = null;
this.infoWindow = null;
this.iwContent = null;
this.isClick = false;
this.timer = null;
this.polyline = null;
this.type = 'intersectionCamera';
}
init() {
this.marker = createIntersectionCameraMarker(this);
this.iwContent =
`
※ CCTV영상은 30초간 제공됩니다.
계속재생
`;
}
setVisibleMarker(isVisible) {
const visible = isVisible && _Level <= 2 ? _MapHandler.map : null;
this.marker.setMap(visible);
this.polyline.setMap(visible);
}
}
/**
* 인포윈도우 이벤트
* @param type 시설물 유형
* @param id 요소 ID
* @param event 이벤트 종류 (click, close)
*/
function infoWindowEvent(type, id, event) {
_MapHandler[event](type, id);
}
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 createMarker(obj, type) {
let map = _MapHandler.map;
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 = getKakaoPosition(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}
);
let flag = _MapHandler[type+'Flag'];
marker.setVisible(flag);
kakao.maps.event.addListener(marker, 'mouseover', function () {
if (!obj.isClick) {
setMarkerImage(obj, 2, false);
}
});
kakao.maps.event.addListener(marker, 'mouseout', function () {
if (!obj.isClick) {
setMarkerImage(obj, 1, false)
}
});
kakao.maps.event.addListener(marker, 'click', ()=> _MapHandler.click(type, obj.ID));
return marker;
}
/**
* 스마트 교차로 카메라 마커 초기화
* @param obj 스마트 교차로 카메라 객체
* @returns {kakao.maps.Marker}
*/
function createIntersectionCameraMarker(obj) {
const position = getKakaoPosition(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 kakao.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: [
getKakaoPosition(linePath1[1], linePath1[0]),
getKakaoPosition(linePath2[1], linePath2[0]),
],
strokeWeight: 10,
strokeColor: color[obj.obj.acrd_los],
strokeOpacity: 1,
strokeStyle: 'solid',
endArrow: true,
name : obj.NAME,
});
if (_Level <= 2 && _MapHandler['intersectionFlag']){
marker.setMap(_MapHandler.map);
obj.polyline.setMap(_MapHandler.map);
}
content.on('click', ()=> {
obj.click();
});
return marker;
}
/**
* 시설물 이미지 유형 변경 이벤트
* @param obj 시설물 객체
* @param type 시설물 이미지 유형
*/
function setMarkerImage(obj, type, isAtrd) {
const currentLevel = _Level;
let size = _size[currentLevel];
let point1 = size/2;
let point2 = size/2;
if (isAtrd) {
size = ( currentLevel >= 7 ) ? 24 : 48;
point1 = size/2;
point2 = size;
}
const imageSize = new kakao.maps.Size(size, size);
const imageSrc = obj.imgSrc + type +'.png';
const imageOption = {
offset: new kakao.maps.Point(point1, point2),
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, facilityClass, type) {
if (_MapHandler[type].length > 0) {
_MapHandler[type].forEach((obj)=>{
if (obj.marker) {
obj.marker.setMap(null);
}
if (obj.polyline) {
obj.polyline.setMap(null);
}
});
_MapHandler[type] = [];
}
let listStr = "";
let mobileStr = "";
if (jsonData && jsonData.length > 0) {
jsonData.forEach((obj)=>{
const marker = new facilityClass(obj);
marker.init();
if (type === 'intersection') {
if (obj.detail && obj.detail.length > 0) {
obj.detail.forEach((cameraObj)=>{
const camera = new IntersectionCameraObj(cameraObj);
camera.init();
_MapHandler[type + 'Camera'].push(camera);
})
}
}
listStr += `
${marker.NAME}`
mobileStr += `
`;
_MapHandler[type].push(marker);
});
if (_MapHandler[type + 'Flag']) {
_MapHandler.show(type);
}
}
if (_MapHandler[type + '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');
}
})
}
}
/**
* 맵 중앙 아이콘 위 위치 좌표
* @param infoWindow
* @returns {number[]}
*/
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[_Level];
let top = mapTop + mapHalfH - height - iconH;
return [top, left];
}
/**
* 맵 드래그 적용
*/
function setInfoWindowPositionWidthDraggable(markerObj, type) {
const {infoWindow, ID} = markerObj;
const position = getInfoWidowPosition(infoWindow);
let top = position[0];
let left = position[1];
infoWindow.css({
top : top + 'px',
left : left + 'px',
position : 'absolute',
zIndex : 999,
});
infoWindow.draggable({containment : 'body', handle: '.'+ type + '-name-' + ID});
}
/**
* 카카오 포지션 지정
*/
function getKakaoPosition(yCoordinate, xCoordinate) {
return new kakao.maps.LatLng(Number(yCoordinate), Number(xCoordinate));
}
/**
* videoJs 객체 생성
*/
function createVideoJs(id, url) {
let video = videojs("video-" + id, {
sources: [
{
src: 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();
const $errorBox = $('.content > div:nth-child(1)');
$errorBox.append($('

'));
$errorBox.css({
display : 'flex',
alignItems : 'center',
justifyContent : 'center',
});
video = null;
}
});
return video;
}
class MapHandler {
constructor(id) {
this.selectedObj = null;
this.map = null;
this.mapElement = id;
this.atrd = [];
}
init () {
//시설물별 배열, 토글 플래그, 리스트 플래그 생성
_FacilityArray.forEach((type)=>{
this[type] = [];
this[type + 'Flag'] = false;
this[type + 'ListFlag'] = false;
});
const container = document.getElementById(this.mapElement); //지도를 담을 영역의 DOM 레퍼런스
const options = { //지도를 생성할 때 필요한 기본 옵션
center: getKakaoPosition(36.0191816, 129.3432983), //지도의 중심좌표.
level: _Level,
maxLevel: 9,
minLevel: 1,
disableDoubleClickZoom: true
};
this.map = new kakao.maps.Map(container, options);
const mapTypeControl = new kakao.maps.MapTypeControl();
this.map.addControl(mapTypeControl, kakao.maps.ControlPosition.TOPRIGHT);
const zoomControl = new kakao.maps.ZoomControl();
this.map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHT);
const handler = this;
/**
* Map Zoom Level Change 이벤트 (이미지 사이즈, 스마트 교차로 이미지 토글, 소통정보 정보 변경)
*/
kakao.maps.event.addListener(this.map, 'zoom_changed', function () {
_Level = this.getLevel();
const zoomChangeArray = ['cctv', 'vms', 'parking', 'incident'];
zoomChangeArray.forEach((type)=>{
const markerArr = handler[type];
const markerFlag = handler[type + 'Flag'];
markerSizeChangeWithZoomLevel(markerArr, markerFlag);
});
intersectionMarkerChangeWithZoomLevel();
intersectionCameraChangeWidthZoomLevel();
if (handler['atrd'].length > 0) {
atrdMarkerResize();
}
getVertex();
});
/**
* 좌표 데이터가의 양이 많으므로 이동 할때마다 영역별 소통정보를 새로 그려줌
*/
kakao.maps.event.addListener(this.map, 'dragend', function() {
getVertex();
});
}
//마커 클릭 이벤트
click(type, id) {
const markerArr = this[type]; // 시설물 배열
const markerFlag = this[ type + 'Flag']; // 시설물 토글 플래그
const markerListFlag = this[ type + 'ListFlag']; // 시설물 토글 플래그
const atrdArr = this['atrd'];
if (atrdArr && atrdArr.length > 0) {
atrdArr.forEach((atrd)=>{
atrd.close();
});
this['atrd'] = [];
_AtrdData = [];
const $selectedLi = $('.left-list-area .list-content.list > li.click');
if ($selectedLi) {
$selectedLi.removeClass('click');
}
}
let selectObj = this.selectedObj; // 이전 클릭 객체
let clickObj = this.getSelectObj(markerArr, id); // 현재 클릭 객체
if (selectObj) {
if (selectObj === clickObj && type !== 'intersection') {
return;
}
_MapHandler.close(selectObj.type, selectObj.ID);
}
const coordinates = getKakaoPosition(clickObj.Y_CRDN, clickObj.X_CRDN);
this.selectedObj = clickObj;
const selectedLi = $('#'+type+'-' + clickObj.ID);
selectedLi.addClass('click');
selectedLi.focus();
this.map.setCenter(coordinates);
if (this['trafficFlag']) {
getVertex();
}
if (clickObj.iwContent) { // 인포 윈도우가 있을때만 실행
clickObj.infoWindow = $(clickObj.iwContent);
$('body').append(clickObj.infoWindow);
setInfoWindowPositionWidthDraggable(clickObj, type);
}
if (clickObj.URL) { // Url 유무로 영상 이벤트 실행
this.videoEvent(clickObj);
}
if (type === 'vms') { // vms 일 경우 이미지 표출실행
this.vmsEvent(clickObj);
}
if (type === 'intersection') { // 스마트 교차로는 줌레벨 2로 변경
this.map.setLevel(2);
}
else if (clickObj.imgSrc) { // img src 값이 있는 객체만 클릭 이미지 변경
setMarkerImage(clickObj, 2, false);
}
clickObj.isClick = true;
if (markerListFlag) {
moveToScroll(markerFlag, markerArr, clickObj.ID); // 클릭 시 리스트 이동
}
}
//마커 이벤트 종료
close(type, id) {
const markerArr = this[type];
const listFlag = this[type + 'ListFlag'];
const closeObj = this.getSelectObj(markerArr, id);
if (closeObj) {
closeObj.isClick = false;
this.selectedObj = null;
if (!type.includes('intersection')) { // 스마트교차로 아닌 화면은 이미지를 원래대로 돌린다.
setMarkerImage(closeObj, 1, false);
}
let oldPlayer = document.getElementById("video-" + closeObj.ID);
let selectedLi = $("#" + type + "-" + closeObj.ID);
if (selectedLi.hasClass('click')) { // 선택 리스트가 있는지 여부
selectedLi.removeClass('click');
}
if (listFlag) { // 리스트 플래그 있는지 여부
$('.mobile-select').val("-");
}
if (oldPlayer) { // 켜져있던 영상이 있는지 여부
videojs(oldPlayer).dispose();
}
if (closeObj.infoWindow) { // 인포 윈도우를 화면에 올려놨는지
closeObj.infoWindow.remove();
closeObj.infoWindow = null;
}
if (closeObj.timer) { // 인터벌 된 객체가 있는지
clearTimeout(closeObj.timer);
}
}
}
//선택 객체 찾기
getSelectObj(array, id) {
let idx = array.findIndex(obj => obj.ID.toString() === id.toString());
return array[idx];
}
// 마커 보이기
show(type) {
this[type].forEach(obj => obj.setVisibleMarker(true));
this[type + 'Flag'] = true;
}
// 마커 숨기기
hide(type) {
this[type].forEach(obj => {
obj.setVisibleMarker(false);
if (obj.isClick === true) {
_MapHandler.close(type, obj.ID);
}
});
this[type + 'Flag'] = false;
}
//영상 이벤트
videoEvent(obj) {
let video = createVideoJs(obj.ID, obj.URL);
obj.timer = setTimeout(()=>{
if (video) {
video.pause();
}
}, CCTV_DISPLAY_TIME);
$('.continue-play').on('click', ()=>{
if (obj.timer) {
setTimeout(obj.timer);
}
if (video) {
video.play();
obj.timer = setTimeout(()=>{
video.pause();
}, CCTV_DISPLAY_TIME);
}
});
}
//vms 이미지 표출 이벤트
vmsEvent(obj) {
let cnt = 1;
if (obj.phaseArray.length > 0) {
obj.timer = setInterval(()=>{
if (cnt === obj.phaseArray.length) {
cnt = 0;
}
const activeImage = $('.vms-info-window .content img.active');
if (activeImage[0]) {
activeImage.removeClass('active');
}
$("#phase-" + obj.phaseArray[cnt]).addClass('active');
cnt++;
}, VMS_DISPLAY_TIME);
}
}
}