info.jsp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170
  1. <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
  2. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
  3. <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
  4. <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
  5. <%@ page import="java.text.NumberFormat" %>
  6. <section class="main-container">
  7. <!-- 좌측목록 지도 영역 -->
  8. <article class="left-Area">
  9. <ul class="sub-Tab">
  10. <li class="on"><a href="#">소통통계</a></li>
  11. <%-- <li><a href="#">교차로교통량</a></li>--%>
  12. </ul>
  13. <div class="left-list">
  14. <div class="condition-box">
  15. <div class="search-box">
  16. <input type="text" autocomplete="off" placeholder="검색어를 입력해주세요" id="searchText" alt="교차로 검색" onkeyup="searchEvent(event)">
  17. <div id="searchBtn" onclick="searchEvent()">조회</div>
  18. </div>
  19. <div class="condition">
  20. <div class="list-box">
  21. <c:if test="${atrdNmList.size() > 0}">
  22. <c:forEach var="item" items="${atrdNmList}" varStatus="status">
  23. <input type="checkbox" value="${item.atrd_id}" id="atrd_${item.atrd_id}" style="display: none;" title="${item.atrd_nm}">
  24. <label for="atrd_${item.atrd_id}" ondblclick="moveEvent('selected-atrd', 'list-box')" onclick="atrdTrfInfo('${item.atrd_id}')" title="${item.atrd_nm}">${item.atrd_nm}</label>
  25. </c:forEach>
  26. </c:if>
  27. </div>
  28. <div class="list-select">
  29. <select name="infoList" id="infoList" onchange="optionChange(this)">
  30. <option>도로목록</option>
  31. <c:if test="${atrdNmList.size() > 0}">
  32. <c:forEach var="item" items="${atrdNmList}" varStatus="status">
  33. <option value="${item.atrd_id}">${item.atrd_nm}</option>
  34. </c:forEach>
  35. </c:if>
  36. </select>
  37. <label for="infoList" style="display: none;"></label>
  38. </div>
  39. </div>
  40. <div class="button-box">
  41. <div>
  42. <div onclick="moveEvent('selected-atrd', 'list-box')">▼</div>
  43. <div onclick="moveEvent('list-box', 'selected-atrd')">▲</div>
  44. </div>
  45. <div>
  46. <div onclick="totMoveEvent('selected-atrd', 'list-box')">전체선택</div>
  47. <div onclick="totMoveEvent('list-box', 'selected-atrd')">전체해제</div>
  48. </div>
  49. </div>
  50. <div class="condition selected-atrd">
  51. <div>
  52. <c:if test="${atrdNmList.size() > 0}">
  53. <c:forEach var="item" items="${atrdNmList}" varStatus="status">
  54. <input type="checkbox" value="${item.atrd_id}" id="atrd_${item.atrd_id}_selected" style="display: none;" title="${item.atrd_nm}">
  55. <label for="atrd_${item.atrd_id}_selected" style="display: none;" ondblclick="moveEvent('list-box', 'selected-atrd')" onclick="atrdTrfInfo('${item.atrd_id}')" title="${item.atrd_nm}">${item.atrd_nm}</label>
  56. </c:forEach>
  57. </c:if>
  58. </div>
  59. </div>
  60. <div class="condition statisticList">
  61. <select name="sTimeType" id="sTimeType">
  62. <option value="sTime">시통계</option>
  63. <option value="sDay">일통계</option>
  64. <option value="sMonth">월통계</option>
  65. <option value="sYear">년통계</option>
  66. </select>
  67. <label for="sTimeType"></label>
  68. </div>
  69. <div class="condition2 statisticList">
  70. <label>검색선택</label>
  71. <span>
  72. <select id="infoYear" name="year" class="statis-search-select" onchange="selectDateChanger(true, false)" style="width: 70px;">
  73. <c:forEach begin="2022" end="${thisYear}" varStatus="status">
  74. <option value="${status.current}" <c:if test="${status.current == thisYear}">selected</c:if>>${status.current}</option>
  75. </c:forEach>
  76. </select>
  77. <label for="infoYear">년</label>
  78. <select id="infoMonth" name="month" class="statis-search-select" style="width: 50px;" onchange="selectDateChanger(false, false)">
  79. <c:forEach begin="1" end="${thisMonth}" varStatus="status">
  80. <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == thisMonth}">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
  81. </c:forEach>
  82. </select>
  83. <label for="infoMonth" class="infoMonth">월</label>
  84. <select id="infoDate" name="date" class="statis-search-select" style="width: 50px;">
  85. <c:forEach begin="1" end="${thisDate}" varStatus="status">
  86. <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == (thisDate - 1)}">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
  87. </c:forEach>
  88. </select>
  89. <label for="infoDate" class="infoDate">일</label>
  90. <select id="infoHour" name="hour" class="statis-search-select" style="width: 50px;">
  91. <c:forEach begin="0" end="23" varStatus="status">
  92. <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == 0}">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
  93. </c:forEach>
  94. </select>
  95. <label for="infoHour" class="infoHour">시</label>
  96. </span>
  97. <span>~</span>
  98. <span>
  99. <select id="infoYear2" name='year2' class="statis-search-select" style="width: 70px;" onchange="selectDateChanger(true, true)">
  100. <c:forEach begin="2022" end="${thisYear}" varStatus="status">
  101. <option value="${status.current}" <c:if test="${status.current == thisYear}">selected</c:if>>${status.current}</option>
  102. </c:forEach>
  103. </select>
  104. <label for="infoYear2">년</label>
  105. <select id="infoMonth2" name='month2' class="statis-search-select" style="width: 50px;" onchange="selectDateChanger(false, true)">
  106. <c:forEach begin="1" end="${thisMonth}" varStatus="status">
  107. <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == thisMonth}">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
  108. </c:forEach>
  109. </select>
  110. <label for="infoMonth2" class="infoMonth2">월</label>
  111. <select id="infoDate2" name='date2' class="statis-search-select" style="width: 50px;">
  112. <c:forEach begin="1" end="${thisDate}" varStatus="status">
  113. <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == (thisDate - 1)}">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
  114. </c:forEach>
  115. </select>
  116. <label for="infoDate2" class="infoDate2">일</label>
  117. <select id="infoHour2" name='hour2' class="statis-search-select" style="width: 50px;">
  118. <c:forEach begin="0" end="23" varStatus="status">
  119. <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == 23 }">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
  120. </c:forEach>
  121. </select>
  122. <label for="infoHour2" class="infoHour2">시</label>
  123. </span>
  124. </div>
  125. <div class="btn-box statisticList">
  126. <button type="button" value="통계조회" class="btn-main search-btn" onclick="selectData()">통계 조회</button>
  127. </div>
  128. <div class="btn-box statisticList">
  129. <button type="button" value="엑셀" class="btn-sub excel-btn" onclick="excelDown()">엑셀 저장</button>
  130. <button type="button" value="엑셀" class="btn-sub csv-btn" onclick="csvDown()">텍스트 저장</button>
  131. <a class='csv-a' style="display:none;"></a>
  132. </div>
  133. <div style="display: flex; gap: 10px; align-items: center; justify-content: flex-end; margin-top: 5px; padding: 2px; margin-left: auto;">
  134. <label for="limit" style="font-size: 16px;">조회된 라인 수</label><input style="width: 150px; box-sizing: border-box; padding-right: 10px; text-align: right;" id="limit" disabled type="text" name="limit" value="0" title="조회된 라인 수">
  135. </div>
  136. </div>
  137. </div>
  138. </article>
  139. <!-- // 좌측목록 지도 영역 -->
  140. <!-- 서브 지도 영역 -->
  141. <article class="mapArea statistics">
  142. <div id="statistics_map">
  143. </div>
  144. <div class="move-up" onclick="move(this)"></div>
  145. <div class="mapContent statistics">
  146. <div class="check-text">※ 속도와 통행시간 확인 후 이용하시기 바랍니다.</div>
  147. <div class="road_info-box sub-road_info">
  148. <img alt="loading" src="/images/ajax_loading.gif" id="loading" style="display:none;position:absolute;left:calc(50% - 25px);top:calc(50%);z-index:10; width: 50px; height: 50px;"/>
  149. <table id ="info-road-list" class="info-road-list">
  150. <colgroup>
  151. <col width="20%">
  152. <col width="30%">
  153. <col width="20%">
  154. <col width="15%">
  155. <col width="15%">
  156. </colgroup>
  157. <thead>
  158. <tr>
  159. <th scope="col">도로명</th>
  160. <th scope="col">구간</th>
  161. <th scope="col">통계시각</th>
  162. <th scope="col">속도(km/h)</th>
  163. <th scope="col">통행시간(sec)</th>
  164. </tr>
  165. </thead>
  166. <tbody class="content">
  167. <tr>
  168. <td colspan="5" class="nodata" style="height:155px; text-align: center;">표출할 정보가 없습니다.</td>
  169. </tr>
  170. </tbody>
  171. </table>
  172. <div class="pagination"></div>
  173. </div>
  174. </div>
  175. </article>
  176. <!-- // 서브 지도 영역 -->
  177. </section>
  178. <script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=${key}"></script>
  179. <script src="/js/xlsx-0.18.12.min.js"></script>
  180. <script src="/js/fileSaver-1.3.8.min.js"></script>
  181. <script type="text/javascript">
  182. let _statLimit = ${statLimit};
  183. let atrdList = '${atrdNmList}';
  184. let _AtrdSideMarkerArr = [];
  185. let _FwdLineArr = [];
  186. let _BwdLineArr = [];
  187. let _prevLevel = 0;
  188. let _atrdMap = new Map();
  189. let _atrdMarkerArr = [];
  190. let _RoadPolyArr = [];
  191. let _RoadPolyBackArr = [];
  192. let _beforeLevel;
  193. let _seletData = [];
  194. let _type = null;
  195. const regex = /atrd_id=([\w,]+).*?road_count=(\w+)/g;
  196. const idToRoadCountMap = new Map();
  197. for (const match of atrdList.matchAll(regex)) {
  198. let ids = match[1].slice(0, -1);
  199. let roadCount = Number(match[2]);
  200. if (ids.split(",").length > 2) {
  201. roadCount = (Number(match[2])/ids.split(",").length) * 2;
  202. }
  203. idToRoadCountMap.set(ids, roadCount);
  204. }
  205. const mapContainer = document.getElementById('statistics_map'), // 지도를 표시할 div
  206. mapOption = {
  207. center: new kakao.maps.LatLng('37.01423130393533', '126.93995173968553'), // 평택지도의 중심좌표
  208. maxLevel: 9,
  209. minLevel: 6,
  210. level: 10, // 지도의 확대 레벨
  211. disableDoubleClickZoom: true
  212. };
  213. function move(el) {
  214. $('.mapContent.statistics').toggleClass('on');
  215. $('#statistics_map').toggleClass('on');
  216. $(el).toggleClass('on');
  217. }
  218. function optionChange(el) {
  219. const value = $(el).val();
  220. totMoveEvent('list-box', 'selected-ixr');
  221. if (value) {
  222. $('label[for="ixr_'+value+'"]').click();
  223. $('label[for="ixr_'+value+'"]').dblclick();
  224. }
  225. }
  226. /**
  227. * 좌측 상단 도로명 위, 아래 이동 이벤트
  228. * @param show 보여주는 박스
  229. * @param hide 가리는 박스
  230. */
  231. function moveEvent(show, hide) {
  232. const hideEl = $("." + hide + ' input[type="checkbox"]:checked');
  233. const hideLabel = hideEl.next();
  234. const showEl = $('.' + show + ' input[value="'+hideEl.val()+'"]');
  235. const showLabel = showEl.next();
  236. hideLabel.css('display', 'none');
  237. showLabel.css('display', 'inline-block');
  238. showEl.prop('checked', true);
  239. }
  240. /**
  241. * 좌측 상단 도로명 전체 선택, 해제 이벤트
  242. * @param show 보여주는 박스
  243. * @param hide 가리는 박스
  244. */
  245. function totMoveEvent(show, hide) {
  246. const showEl = $("." + show + ' input[type="checkbox"]');
  247. const showLabel = showEl.next();
  248. const hideEl = $("." + hide + ' input[type="checkbox"]');
  249. const hideLabel = hideEl.next();
  250. hideLabel.css('display', 'none');
  251. showLabel.css('display', 'inline-block');
  252. const checked = $('.' + hide + ' input[type="checkbox"]:checked');
  253. if (checked.length) {
  254. $('.' + show + ' input[value="'+checked.val()+'"]').prop('checked', true);
  255. }
  256. }
  257. _Map = new kakao.maps.Map(mapContainer, mapOption);
  258. const mapTypeControl = new kakao.maps.MapTypeControl();
  259. const zoomControl = new kakao.maps.ZoomControl();
  260. _Map.addControl(mapTypeControl, kakao.maps.ControlPosition.RIGHT);
  261. _Map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHT);
  262. kakao.maps.event.addListener(_Map, 'zoom_changed', function () {
  263. // 지도의 현재 레벨을 얻어옵니다
  264. _Level = _Map.getLevel();
  265. drawRoadMap();
  266. });
  267. kakao.maps.event.addListener(_Map, 'dragend', function () {
  268. drawRoadMap();
  269. });
  270. getAtrdInfo();
  271. /**
  272. * 도로 정보
  273. */
  274. function getAtrdInfo() {
  275. $.ajax({
  276. type: "POST",
  277. contentType:"application/x-www-form-urlencoded;charset=utf-8",
  278. url: '/common/getRoadVertexArr.do',
  279. success: function (data) {
  280. if (data && data.length) {
  281. data.forEach((obj)=>{
  282. if (!_atrdMap.get(obj.levl)) {
  283. _atrdMap.set(obj.levl, new Map());
  284. _atrdMap.get(obj.levl).set("markerObj", []);
  285. }
  286. const polyObj = new PolyObj(obj);
  287. _atrdMap.get(obj.levl).get("markerObj").push(polyObj);
  288. if (obj.atrd_id) {
  289. if (!_atrdMap.get(obj.levl).get(obj.atrd_id)) {
  290. _atrdMap.get(obj.levl).set(obj.atrd_id, []);
  291. }
  292. _atrdMap.get(obj.levl).get(obj.atrd_id).push(polyObj);
  293. }
  294. });
  295. }
  296. },
  297. complete: function () {
  298. drawRoadMap()
  299. },
  300. error: function (request, status, error) {
  301. },
  302. });
  303. }
  304. function getAtrdLevel() {
  305. let level = _Level;
  306. if (level <= 7) {
  307. level = 5;
  308. }
  309. else {
  310. level = 6;
  311. }
  312. return level;
  313. }
  314. /**
  315. * 도로 표출 Object
  316. */
  317. class PolyObj {
  318. constructor(obj) {
  319. this.atrd_id = obj.atrd_id;
  320. this.road_nm = obj.road_nm;
  321. this.strt_nm_node = obj.strt_nm_node;
  322. this.end_nm_node = obj.end_nm_node;
  323. this.sect_lngt = obj.sect_lngt;
  324. this.levl = obj.levl;
  325. this.x_crdn = obj.x_crdn;
  326. this.y_crdn = obj.y_crdn;
  327. this.y_crdn_min = obj.y_crdn_min;
  328. this.y_crdn_max = obj.y_crdn_max;
  329. this.x_crdn_min = obj.x_crdn_min;
  330. this.x_crdn_max = obj.x_crdn_max;
  331. this.road_ord = obj.road_ord;
  332. this.obj = obj;
  333. this.polyline = null;
  334. this.polylineBack = null;
  335. this.content = null;
  336. this.marker = null;
  337. this.makeMarkers();
  338. this.isClick = false;
  339. }
  340. makeMarkers() {
  341. let xCrdnArr = this.x_crdn.split(',');
  342. let yCrdnArr = this.y_crdn.split(',');
  343. let linePath = [];
  344. for (let ii = 0; ii < xCrdnArr.length; ii++) {
  345. linePath.push(new daum.maps.LatLng(yCrdnArr[ii], xCrdnArr[ii]));
  346. }
  347. const cmtrGradColor = '#00981e';
  348. let strokeWeight = 3;
  349. let strokeWeightBack = 5;
  350. let size = 48;
  351. let imageName = this.obj.drct_cd === '상행' ? 'S' : 'E';
  352. if (this.levl > 6) {
  353. strokeWeight = 2;
  354. strokeWeightBack = 4;
  355. size = 24;
  356. }
  357. this.polyline = new daum.maps.Polyline({
  358. path: linePath,
  359. strokeWeight: strokeWeight,
  360. strokeColor: cmtrGradColor,
  361. strokeOpacity: 1,
  362. strokeStyle: 'solid',
  363. zIndex: 2,
  364. });
  365. this.polylineBack = new daum.maps.Polyline({
  366. path: linePath,
  367. strokeWeight: strokeWeightBack,
  368. strokeColor: '#FFFFFF',
  369. strokeOpacity: 1,
  370. strokeStyle: 'solid',
  371. zIndex: 1,
  372. });
  373. if (this.road_ord === 1) {
  374. const imageSrc = '/images/traffic/map-marker' + imageName + '.png';
  375. const imageSize = new daum.maps.Size(size, size);
  376. const imageOption = {
  377. offset: new daum.maps.Point(size/2, size)
  378. };
  379. const markerImage = new daum.maps.MarkerImage(imageSrc, imageSize, imageOption);
  380. const markerPosition = new daum.maps.LatLng(yCrdnArr[0], xCrdnArr[0]); // 마커가 표시될 위치입니다
  381. this.marker = new daum.maps.Marker({position: markerPosition, image: markerImage, zIndex: 5});
  382. }
  383. const obj = this;
  384. let sectLngt = obj.sect_lngt;
  385. if (sectLngt && !isNaN(Number(sectLngt))) {
  386. sectLngt = sectLngt.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
  387. }
  388. this.content = '<div class="mapPop traffic_LTC0">'
  389. + '<div class="head"><span class="title">' + obj.road_nm + '</span></div>'
  390. + '<div class="cont"><dl>'
  391. + '<dt>[ ' + obj.strt_nm_node + ' ] 부터<br/>[ ' + obj.end_nm_node + ' ] 까지</dt>'
  392. + '<dd>구간거리 : ' + sectLngt + ' m</dd>'
  393. + '</dl></div>';
  394. daum.maps.event.addListener(this.polyline, 'mouseover', function (mouseEvent) {
  395. const beforeOver = $('.mapPop.traffic_LTC0')
  396. if (beforeOver.length) beforeOver.remove();
  397. this.setOptions({strokeColor: 'blue'});
  398. const overlay = $(obj.content);
  399. $('.main-container').append(overlay);
  400. daum.maps.event.addListener(obj.polyline, 'mouseout', function (mouseEvent) {
  401. if (overlay) {
  402. overlay.remove();
  403. }
  404. this.setOptions({strokeColor: cmtrGradColor});
  405. });
  406. });
  407. }
  408. /**
  409. * 지도 표출 선 색상 변경(선택)
  410. */
  411. setSelectStyle() {
  412. if (this.polylineBack) {
  413. this.polylineBack.setOptions({
  414. zIndex : 3,
  415. strokeColor : '#000000',
  416. })
  417. }
  418. if (this.polyline) {
  419. this.polyline.setOptions({zIndex : 4})
  420. }
  421. if (this.marker) {
  422. this.marker.setMap(_Map);
  423. this.isClick = true;
  424. }
  425. }
  426. /**
  427. * 지도 표출 선 색상 변경(기본)
  428. */
  429. setDefaultStyle() {
  430. if (this.polylineBack) {
  431. this.polylineBack.setOptions({
  432. zIndex : 1,
  433. strokeColor : '#FFFFFF',
  434. })
  435. }
  436. if (this.polyline) {
  437. this.polyline.setOptions({zIndex : 2})
  438. }
  439. if (this.marker) {
  440. this.marker.setMap(null);
  441. this.isClick = false;
  442. }
  443. }
  444. /**
  445. * 보이기/숨기기
  446. * @param isShow : _Map or null
  447. */
  448. showHide(isShow) {
  449. if (this.polylineBack) {
  450. this.polylineBack.setMap(isShow);
  451. }
  452. if (this.polyline) {
  453. this.polyline.setMap(isShow);
  454. }
  455. if (this.marker && this.isClick) {
  456. this.marker.setMap(isShow);
  457. }
  458. }
  459. }
  460. /**
  461. * 지도에 도로 표출
  462. */
  463. function drawRoadMap() {
  464. let level = getAtrdLevel();
  465. if (level === _beforeLevel) return; //이전 레벨과 레벨이 같다면 안그림
  466. if (_beforeLevel) { // 기존께 있다면 지움
  467. const markerArr = _atrdMap.get(_beforeLevel).get("markerObj");
  468. markerArr.forEach((marker)=>{
  469. marker.showHide(null);
  470. });
  471. }
  472. _beforeLevel = level;
  473. const currentMarkerArr = _atrdMap.get(level).get("markerObj");
  474. currentMarkerArr.forEach((marker)=>{
  475. marker.showHide(_Map);
  476. });
  477. }
  478. /**
  479. * 선택 해제(기본 스타일로 변경)
  480. * @param atrdId 선택 해제 간선도로 ID
  481. */
  482. function removeLines(atrdId) {
  483. if (_AtrdId && _AtrdId.length) {
  484. const atrdArr = atrdId.split(",");
  485. if (atrdArr[0] !== _AtrdId[0]) {
  486. _AtrdId.forEach((atrd)=>{
  487. setStyle(_atrdMap.get(5).get(atrd), "setDefaultStyle");
  488. setStyle(_atrdMap.get(6).get(atrd), "setDefaultStyle");
  489. });
  490. }
  491. }
  492. }
  493. function setStyle(arr, method) {
  494. if (arr && arr.length) {
  495. arr.forEach((line)=>{
  496. line[method]();
  497. })
  498. }
  499. }
  500. function atrdTrfInfo(atrdId) {
  501. if (!atrdId && !atrdId.includes(",")) return;
  502. if ($(document.getElementById(atrdId)).is(':checked') || $(document.getElementById(atrdId + '_selected')).is(':checked')) return;
  503. $('.condition input[type="checkbox"]').prop("checked", false);
  504. removeLines(atrdId);
  505. _AtrdId = atrdId.split(',');
  506. if (_AtrdId.length < 2) return;
  507. const bounds = new daum.maps.LatLngBounds();
  508. for (let ii = 0; ii < 2; ii++) {
  509. const atrd = _AtrdId[ii];
  510. const atrd5 = _atrdMap.get(5).get(atrd);
  511. const atrd6 = _atrdMap.get(6).get(atrd);
  512. for (let jj = 0; jj < atrd5.length; jj++) {
  513. const polyObj = atrd5[jj];
  514. bounds.extend(new daum.maps.LatLng(polyObj.y_crdn_min, polyObj.x_crdn_min));
  515. bounds.extend(new daum.maps.LatLng(polyObj.y_crdn_max, polyObj.x_crdn_max));
  516. }
  517. setStyle(atrd5, "setSelectStyle");
  518. setStyle(atrd6, "setSelectStyle");
  519. }
  520. _Map.setBounds(bounds);
  521. }
  522. function excelDown(){
  523. if (!_seletData || !_seletData.list || !_seletData.list.length) {
  524. return alert('조회된 교차로 교통량 정보가 없습니다.');
  525. }
  526. exportExcel();
  527. }
  528. function exportExcel() {
  529. const fileName = '소통통계 - '+ $('option[value="'+_type+'"]').text() + '_' + nowTime() + '.xlsx';
  530. const link = document.createElement('a');
  531. const blob = base64ToBlob(_seletData.excel, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
  532. const url = URL.createObjectURL(blob);
  533. link.href = url;
  534. link.download = fileName; // 다운로드할 파일 이름 설정
  535. link.click();
  536. URL.revokeObjectURL(url); // URL 객체 해제
  537. }
  538. function base64ToBlob(base64, mimeType) {
  539. const byteCharacters = atob(base64); // Base64를 디코딩
  540. const byteArrays = [];
  541. for (let offset = 0; offset < byteCharacters.length; offset += 1024) {
  542. const slice = byteCharacters.slice(offset, offset + 1024);
  543. const byteNumbers = new Array(slice.length);
  544. for (let i = 0; i < slice.length; i++) {
  545. byteNumbers[i] = slice.charCodeAt(i);
  546. }
  547. const byteArray = new Uint8Array(byteNumbers);
  548. byteArrays.push(byteArray);
  549. }
  550. return new Blob(byteArrays, { type: mimeType });
  551. }
  552. function csvDown(){
  553. if (!_seletData || !_seletData.list || !_seletData.list.length) {
  554. return alert('조회된 교차로 교통량 정보가 없습니다.');
  555. }
  556. const csv = [];
  557. const row = [];
  558. //1열에는 컬럼명
  559. row.push(
  560. "도로명",
  561. "구간",
  562. "통계시각",
  563. "속도(km/h)",
  564. "통행시간(sec)"
  565. );
  566. csv.push(row.join(","));
  567. for (let obj of _seletData.list) {
  568. const statDt = obj.stat_dt;
  569. let prcnDt = '-';
  570. if (statDt) {
  571. const yyyy = statDt.substring(0,4);
  572. const mm = statDt.substring(4,6);
  573. const dd = statDt.substring(6,8);
  574. const hh = statDt.substring(8,10);
  575. const mi = statDt.substring(10,12);
  576. const ss = statDt.substring(12,14);
  577. if (_type === 'sYear') {
  578. prcnDt = yyyy + '년';
  579. }
  580. else if (_type === 'sMonth') {
  581. prcnDt = yyyy + '년 ' + mm + '월';
  582. }
  583. else if (_type === 'sDay') {
  584. prcnDt = yyyy + '/' + mm + '/' + dd;
  585. }
  586. else {
  587. prcnDt = yyyy + '/' + mm + '/' + dd + ' ' + hh + ':' + mi + ':' + ss;
  588. }
  589. }
  590. const row = [
  591. obj.road_nm + "," +
  592. obj.strt_nm + " - " + obj.end_nm + "," +
  593. prcnDt + "," +
  594. obj.sped + "," +
  595. obj.trvl_hh
  596. ]
  597. csv.push(row);
  598. }
  599. csv = csv.join("\n");
  600. const filename = '소통통계 - ' + $('option[value="'+_type+'"]').text() + '_' + nowTime() + '.csv';
  601. //한글 처리를 해주기 위해 BOM 추가하기
  602. const BOM = "\uFEFF";
  603. csv = BOM + csv;
  604. const csvFile = new Blob([csv], { type: "text/csv" });
  605. const downloadLink = document.querySelector(".csv-a");
  606. downloadLink.download = filename;
  607. downloadLink.href = window.URL.createObjectURL(csvFile);
  608. downloadLink.style.display = "none";
  609. document.body.appendChild(downloadLink);
  610. downloadLink.click();
  611. downloadLink.remove();
  612. }
  613. function nowTime() {
  614. let now = new Date();
  615. let year = now.getFullYear().toString();
  616. let month = timeFormmater(now.getMonth() + 1).toString();
  617. let date = timeFormmater(now.getDate()).toString();
  618. let hour = timeFormmater(now.getHours()).toString();
  619. let minutes = timeFormmater(now.getMinutes()).toString();
  620. let seconds = timeFormmater(now.getSeconds()).toString();
  621. return year + month + date + hour + minutes + seconds;
  622. }
  623. //시간 한자리 경우 2자리로 세팅
  624. function timeFormmater(time) {
  625. let result = time < 10 ? "0" + time.toString() : time.toString();
  626. return result;
  627. }
  628. /**
  629. * 통계 타입에 따른 보이기 감추기
  630. * @param visible 보이는 목록
  631. * @param hide 가리는 목록
  632. */
  633. function inputVisible(visible, hide) {
  634. if (visible && visible.length) {
  635. visible.forEach((el)=>{
  636. $('#' + el).show();
  637. $('.' + el).show();
  638. });
  639. }
  640. if (hide && hide.length) {
  641. hide.forEach((el)=>{
  642. $('#' + el).hide();
  643. $('.' + el).hide();
  644. });
  645. }
  646. }
  647. $('#sTimeType').change(function(){
  648. let sTimeTypeVal = $('#sTimeType option:selected').val();
  649. if (sTimeTypeVal === 'sDay') {
  650. inputVisible(['infoMonth', 'infoDate', 'infoMonth2', 'infoDate2'], ['infoHour', 'infoHour2']);
  651. }
  652. else if (sTimeTypeVal === 'sMonth') {
  653. inputVisible(['infoMonth', 'infoMonth2'], ['infoDate', 'infoDate2', 'infoHour', 'infoHour2']);
  654. }
  655. else if (sTimeTypeVal === 'sYear') {
  656. inputVisible([], ['infoMonth', 'infoMonth2', 'infoDate', 'infoDate2', 'infoHour', 'infoHour2']);
  657. }
  658. else {
  659. inputVisible(['infoMonth', 'infoMonth2', 'infoDate', 'infoDate2', 'infoHour', 'infoHour2'], []);
  660. }
  661. });
  662. // let sTimeTypeVal = 'sTime';
  663. let _AtrdNm= ''
  664. function btnActiveState(stts){
  665. const searchBtn = $('.btn-main.search-btn');
  666. const excelBtn = $('.btn-sub.excel-btn');
  667. const csvBtn = $('.btn-sub.csv-btn');
  668. searchBtn.prop('disabled', stts);
  669. excelBtn.prop('disabled', stts);
  670. csvBtn.prop('disabled', stts);
  671. }
  672. function selectData(){
  673. const type = $('#sTimeType').val();
  674. const selectList = $('.list-select');
  675. // let ids = "";
  676. let ids = [];
  677. let checkIds = [];
  678. if (selectList.css('display') === 'none') {
  679. const checkArr = $('.selected-atrd input[type="checkbox"]');
  680. for (let ii = 0; ii < checkArr.length; ii++) {
  681. const check = checkArr.eq(ii);
  682. if (check.next().css('display') !== 'none') {
  683. // console.log(check.val());
  684. const atrdIds = check.val();
  685. checkIds.push(atrdIds);
  686. if (atrdIds.includes(',')) {
  687. const arr = atrdIds.split(',');
  688. ids.push(arr[0], arr[1]);
  689. }
  690. }
  691. }
  692. }
  693. else {
  694. const infoList = $('#infoList');
  695. if (infoList.val().includes(',')) {
  696. const atrdIds = infoList.val().split(',');
  697. checkIds.push(infoList.val());
  698. ids.push(atrdIds[0], atrdIds[1]);
  699. }
  700. }
  701. const selectSize = ids.length;
  702. if (selectSize === 0) return alert('도로목록을 선택해주세요.');// 선택 도로목록 확인
  703. const year2El = $("#infoYear2");
  704. const month2El = $("#infoMonth2");
  705. const date2El = $("#infoDate2");
  706. const hour2El = $("#infoHour2");
  707. let year = $("#infoYear").val();
  708. let month = $("#infoMonth").val();
  709. let date = $("#infoDate").val();
  710. let hour = $("#infoHour").val();
  711. let year2 = year2El.val();
  712. let month2 = month2El.val();
  713. let date2 = date2El.val();
  714. let hour2 = hour2El.val();
  715. let from, to, fromDate, toDate, fromDateWord;
  716. if (type === 'sTime') {
  717. from = year+month+date+hour + '0000';
  718. to = year2+month2+date2+hour2 + '5959';
  719. toDate = new Date(year2 + "-" +month2 + "-" + date2 + " " + hour2 + ":59:59");
  720. fromDateWord = year + "-" +month + "-" + date + " " + hour + ":00:00";
  721. fromDate = new Date(fromDateWord);
  722. fromDate.setDate(new Date(fromDate).getDate() - 1);
  723. fromDate.setSeconds(new Date(fromDate).getSeconds() - 1);
  724. }
  725. else if (type === 'sDay') {
  726. from = year+month+date + '000000';
  727. to = year2+month2+date2 + '235959';
  728. toDate = new Date(year2 + "-" +month2 + "-" + date2 + " 23:59:59");
  729. fromDateWord = year + "-" +month + "-" + date + " 00:00:00";
  730. fromDate = new Date(fromDateWord);
  731. fromDate.setDate(new Date(fromDate).getDate() - 1);
  732. fromDate.setSeconds(new Date(fromDate).getSeconds() - 1);
  733. }
  734. else if (type === 'sMonth') {
  735. const lastDate = new Date(year2, month2, 0);
  736. const last = lastDate.getDate();
  737. from = year+month + '01000000';
  738. to = year2+month2 + last + '235959';
  739. toDate = new Date(year2 + "-" +month2 + "-" + last + " 23:59:59");
  740. fromDateWord = year + "-" +month + "-01 00:00:00";
  741. fromDate = new Date(fromDateWord);
  742. fromDate.setDate(new Date(fromDate).getDate() - 1);
  743. fromDate.setSeconds(new Date(fromDate).getSeconds() - 1);
  744. }
  745. else if (type === 'sYear') {
  746. from = year + '0101000000';
  747. to = year2 + '1231235959';
  748. }
  749. if (from > to) {
  750. return alert("검색 시작 일이 종료 일보다 큽니다.");
  751. }
  752. const limitDate = getLimitDate(selectSize, type, fromDate, checkIds);
  753. if (type !== 'sYear' && toDate > limitDate) {
  754. const yyyy = limitDate.getFullYear();
  755. const MM = String(limitDate.getMonth() + 1).padStart(2, '0');
  756. const dd = String(limitDate.getDate()).padStart(2, '0');
  757. const HH = String(limitDate.getHours()).padStart(2, '0');
  758. const mm = String(limitDate.getMinutes()).padStart(2, '0');
  759. const ss = String(limitDate.getSeconds()).padStart(2, '0');
  760. const formattedDate = `\${yyyy}-\${MM}-\${dd} \${HH}:\${mm}:\${ss}`;
  761. if (confirm("현재 조회 기간이 데이터 조회 최대 기간을 초과하였습니다. \n조회 기간을 변경하여 데이터를 조회" +
  762. "하시겠습니까?\n검색 기간 : " + fromDateWord + " ~ " + formattedDate)) {
  763. to = yyyy + MM + dd + HH + mm + ss;
  764. year2El.val(yyyy).change();
  765. month2El.val(MM).change();
  766. date2El.val(dd);
  767. hour2El.val(HH);
  768. }
  769. else {
  770. return;
  771. }
  772. }
  773. const params = {
  774. atrdIds : ids,
  775. from : from,
  776. to : to,
  777. type : type,
  778. }
  779. const tbody = $('.content');
  780. const pagination = $('.pagination');
  781. pagination.empty();
  782. tbody.empty();
  783. $('#limit').val(0);
  784. _seletData = null;
  785. $.ajax({
  786. type:'POST',
  787. url: "/statistics/getAtrdStat.do",
  788. traditional : true,
  789. data: params,
  790. success: function(data){
  791. _seletData = data;
  792. _type = type;
  793. const cnt = _seletData.list.length.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
  794. alert("데이터 " + cnt + "건이 조회되었습니다.");
  795. $('#limit').val(cnt);
  796. // let vertexData = data;
  797. },
  798. error : (error)=> {
  799. console.log(error);
  800. alert("데이터 조회중 오류가 발생했습니다.\n" + error.responseJSON.message);
  801. },
  802. beforeSend : function(){
  803. $('#loading').show();
  804. btnActiveState(true);
  805. },
  806. complete : function(){
  807. $('#loading').hide();
  808. btnActiveState(false);
  809. drawTable(1);
  810. // loadPage(1);
  811. }
  812. });
  813. }
  814. function appendDataToTable(startIndex, endIndex) {
  815. let str = '';
  816. const tbody = $('.content');
  817. const table = $('.info-road-list');
  818. if (_seletData.list.length === 0) {
  819. table.css('height', '100%');
  820. tbody.append($('<tr><td colspan="5" class="nodata" style="height:155px">표출할 정보가 없습니다.</td></tr>'));
  821. }
  822. else {
  823. table.css('height', 'auto');
  824. const paginatedData = _seletData.list.slice(startIndex, endIndex);
  825. paginatedData.forEach(item => {
  826. const row = document.createElement('tr');
  827. const statDt = item.stat_dt;
  828. const yyyy = statDt.substring(0,4);
  829. const mm = statDt.substring(4,6);
  830. const dd = statDt.substring(6,8);
  831. const hh = statDt.substring(8,10);
  832. const mi = statDt.substring(10,12);
  833. const ss = statDt.substring(12,14);
  834. let prcnDt;
  835. if (_type === 'sYear') {
  836. prcnDt = yyyy + '년';
  837. }
  838. else if (_type === 'sMonth') {
  839. prcnDt = yyyy + '년 ' + mm + '월';
  840. }
  841. else if (_type === 'sDay') {
  842. prcnDt = yyyy + '/' + mm + '/' + dd;
  843. }
  844. else {
  845. prcnDt = yyyy + '/' + mm + '/' + dd + ' ' + hh + ':' + mi + ':' + ss;
  846. }
  847. row.innerHTML = `<td>\${item.road_nm}</td>
  848. <td>\${item.strt_nm} - \${item.end_nm}</td>
  849. <td>\${prcnDt}</td>
  850. <td>\${item.sped} km/h</td>
  851. <td>\${item.trvl_hh}</td>`;
  852. tbody.append(row);
  853. });
  854. }
  855. }
  856. function loadPage(page) {
  857. const rowsPerPage = 10;
  858. const startIndex = (page - 1) * rowsPerPage;
  859. const endIndex = page * rowsPerPage;
  860. appendDataToTable(startIndex, endIndex);
  861. }
  862. //
  863. // let currentPage = 1;
  864. // const tableContainer = $('.road_info-box.sub-road_info');
  865. // tableContainer.on('scroll', ()=>{
  866. // if ($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight - 5) {
  867. // currentPage++;
  868. // loadPage(currentPage);
  869. // }
  870. // })
  871. function getLimitDate(selectSize, type, startDate, ids) {
  872. let tot = 0;
  873. for(let id of ids) {
  874. tot += idToRoadCountMap.get(id) || 0;
  875. }
  876. const limitRate = Math.floor(_statLimit / tot);
  877. const date = new Date(startDate);
  878. let setMethod = "Hours";
  879. if (type === 'sDay') {
  880. setMethod = "Date";
  881. }
  882. else if (type === 'sMonth') {
  883. setMethod = "Month";
  884. }
  885. return new Date(date["set" + setMethod](date["get" + setMethod]() + limitRate));
  886. }
  887. function drawTable(idx) {
  888. let str = '';
  889. const tbody = $('.content');
  890. const table = $('.info-road-list');
  891. const pagination = $('.pagination');
  892. pagination.hide();
  893. const rowsPerPage = 14;
  894. const pagesPerGroup = 10;
  895. if (!_seletData || !_seletData.list || _seletData.list.length === 0) {
  896. table.css('height', '100%');
  897. str = $('<tr><td colspan="5" class="nodata" style="height:155px">표출할 정보가 없습니다.</td></tr>');
  898. }
  899. else {
  900. table.css('height', 'auto');
  901. let start = (idx - 1) * rowsPerPage;
  902. let end = start + rowsPerPage;
  903. let max = _seletData.list.length;
  904. if (max < end) end = max;
  905. let pageStr = "";
  906. if (max > rowsPerPage) {
  907. const totalPage = Math.ceil(max / rowsPerPage);
  908. const currentGroup = Math.ceil(idx / pagesPerGroup);
  909. const startPage = (currentGroup - 1) * pagesPerGroup + 1;
  910. const endPage = Math.min(startPage + pagesPerGroup - 1, totalPage);
  911. pagination.css('display', 'flex');
  912. if (startPage > 10) {
  913. pageStr += "<div onclick='drawTable("+(startPage - 1)+")'>[PREV]</div>";
  914. }
  915. for (let ii = startPage; ii <= endPage; ii++) {
  916. let className = "";
  917. if (ii === idx) {
  918. className = "on";
  919. }
  920. pageStr += "<div class='"+className+"' onclick='drawTable("+ ii +")'>["+ii+"]</div>";
  921. }
  922. if (totalPage > endPage) {
  923. pageStr += "<div onclick='drawTable("+(endPage + 1)+")'>[NEXT]</div>";
  924. }
  925. pagination.html(pageStr);
  926. }
  927. for (let ii = start; ii < end; ii++) {
  928. const obj = _seletData.list[ii];
  929. const statDt = obj.stat_dt;
  930. let prcnDt = '-';
  931. if (statDt) {
  932. const yyyy = statDt.substring(0,4);
  933. const mm = statDt.substring(4,6);
  934. const dd = statDt.substring(6,8);
  935. const hh = statDt.substring(8,10);
  936. const mi = statDt.substring(10,12);
  937. const ss = statDt.substring(12,14);
  938. if (_type === 'sYear') {
  939. prcnDt = yyyy + '년';
  940. }
  941. else if (_type === 'sMonth') {
  942. prcnDt = yyyy + '년 ' + mm + '월';
  943. }
  944. else if (_type === 'sDay') {
  945. prcnDt = yyyy + '/' + mm + '/' + dd;
  946. }
  947. else {
  948. prcnDt = yyyy + '/' + mm + '/' + dd + ' ' + hh + ':' + mi + ':' + ss;
  949. }
  950. }
  951. str += '<tr>';
  952. str += '<td>'+obj.road_nm+'</td>';
  953. str += '<td>'+obj.strt_nm+' - '+obj.end_nm+'</td>';
  954. str += '<td>'+prcnDt+'</td>';
  955. str += '<td>'+obj.sped+'</td>';
  956. str += '<td>'+obj.trvl_hh+'</td>';
  957. str += '</tr>';
  958. }
  959. }
  960. tbody.html(str);
  961. }
  962. /**
  963. * 년도 날짜에 따른 월, 일 범위값 설정
  964. * @param(boolean) isSetMonth 일만 변경할건지
  965. */
  966. function selectDateChanger(isSetMonth, isTo) {
  967. const year = $("#infoYear");
  968. const month = $("#infoMonth");
  969. const day = $("#infoDate");
  970. const toYear = $("#infoYear2");
  971. const toMonth = $("#infoMonth2");
  972. const toDay = $("#infoDate2");
  973. let now = new Date();
  974. let nowDay = now.getDate();
  975. let nowYear = now.getFullYear();
  976. let nowMonth = now.getMonth() + 1;
  977. let dayVal = day.val();
  978. let monthVal = month.val();
  979. let yearVal = year.val();
  980. let monthEl = month;
  981. let dayEl = day;
  982. if (isTo) {
  983. dayVal = toDay.val();
  984. monthVal = toMonth.val();
  985. yearVal = toYear.val();
  986. monthEl = toMonth;
  987. dayEl = toDay;
  988. }
  989. let lastDate = new Date(yearVal, monthVal, 0);
  990. let minDay = lastDate.getDate();
  991. let minMonth = 12;
  992. if (nowYear.toString() === yearVal) {
  993. minMonth = nowMonth;
  994. if (nowMonth === Number(monthVal)) minDay = nowDay;
  995. }
  996. if (isSetMonth) {
  997. setSelectOption(minMonth, monthVal, monthEl);
  998. }
  999. setSelectOption(minDay, dayVal, dayEl);
  1000. }
  1001. /**
  1002. * 날짜 Select Box option Setting
  1003. * @param min 마지막 값
  1004. * @param value 현재 값
  1005. * @param target 요소
  1006. * @param word 날짜 글자
  1007. */
  1008. function setSelectOption(min, value, target) {
  1009. let option = getOption(min);
  1010. target.html(option);
  1011. if (min < Number(value)) {
  1012. target.val('01');
  1013. }
  1014. else {
  1015. target.val(value);
  1016. }
  1017. }
  1018. /**
  1019. * 날짜 Select option for문
  1020. * @param rate 종료 값
  1021. * @param word 날짜 글짜
  1022. * @returns {string}
  1023. */
  1024. function getOption(rate) {
  1025. let option = ''
  1026. for ( let ii = 1; ii <= rate; ii++) {
  1027. let value = ii;
  1028. if (ii < 10) {
  1029. value = ('0'+ii);
  1030. }
  1031. option += '<option value="'+value+'">'+value + '</option>';
  1032. }
  1033. return option;
  1034. }
  1035. /**
  1036. * 교차로 검색 이벤트
  1037. * @param el
  1038. */
  1039. function searchEvent(ev) {
  1040. if (!ev || ev.key === 'Enter') {
  1041. const searchText = $('#searchText').val();
  1042. if (searchText === "") {
  1043. return alert('검색어를 입력해주세요');
  1044. }
  1045. const labelArr = $('.list-box label');
  1046. // totMoveEvent('list-box', 'selected-atrd');
  1047. for (let ii = 0; ii < labelArr.length; ii++) {
  1048. const label = labelArr.eq(ii);
  1049. if (label.text().includes(searchText)) {
  1050. label.click();
  1051. label.dblclick();
  1052. }
  1053. }
  1054. }
  1055. }
  1056. </script>