2025๋…„ 8์›” 1์ผ ๊ธˆ์š”์ผ

anyangcheon_poi

<!DOCTYPE html>


<html lang="ko">


<head>


  <meta charset="utf-8" />


  <title>๐Ÿš“ ๊ฐ€๋กœ๋“ฑ ์œ„์น˜ ๊ธฐ๋ฐ˜ ํ˜„์žฅ ์ถœ๋™ ์‹œ์Šคํ…œ (์ˆ˜๋™ ์ˆœ์ฐฐ ๋ฒ„์ „)</title>


  <meta name="viewport" content="width=device-width, initial-scale=1" />


  <style>


    body { font-family: Arial, sans-serif; margin: 0; padding: 0; background: #f9f9f9; }


    h3 { text-align: center; background: #007bff; color: white; margin: 0; padding: 10px; }


    #panel { padding: 10px; text-align: center; max-width: 400px; margin: 10px auto; }


    #panel input, #panel button {


      width: 90%; max-width: 350px; padding: 10px; margin: 5px auto;


      font-size: 16px; display: block; border: 1px solid #ccc; border-radius: 5px;


    }


    #map { height: 70vh; width: 100%; }


    #clickedCoords {


      width: 90%; max-width: 350px; padding: 8px; margin: 5px auto;


      border: 1px solid #007bff; background: #eef6ff; font-size: 14px;


      border-radius: 5px; text-align: center;


    }


    #copyBtn {


      width: 90%; max-width: 350px; background: #ff9800; color: white;


      border: none; padding: 8px; border-radius: 5px; cursor: pointer;


    }


    #completeBtn {


      background: #28a745; color: white; border: none;


      padding: 10px; border-radius: 5px; font-size: 16px;


      cursor: pointer; width: 90%; max-width: 350px; margin: 10px auto; display: block;


    }


    #upcomingPanel {


      position: fixed; top: 10px; right: 10px; width: 250px; max-height: 60vh;


      overflow-y: auto; background: rgba(255,255,255,0.95);


      border: 1px solid #007bff; border-radius: 5px; padding: 10px;


      font-size: 14px; z-index: 9999; box-shadow: 2px 2px 5px rgba(0,0,0,0.2);


      transition: all 0.3s ease; cursor: pointer;


    }


    #upcomingPanel h4 { margin: 0 0 5px; font-size: 16px; color: #007bff; }


    #upcomingList { list-style: none; padding: 0; margin: 0; }


    #upcomingList li { padding: 5px 8px; border-bottom: 1px solid #ddd; cursor: pointer; border-radius: 3px; user-select: none; }


    #upcomingList li:hover { background: #eef6ff; }


    #upcomingPanel.hidden { width: 50px; height: 40px; padding: 5px; overflow: hidden; cursor: pointer; }


    #upcomingPanel.hidden h4, #upcomingPanel.hidden ul { display: none; }


    #upcomingPanel.hidden::after { content: "⏰"; position: absolute; top: 10px; left: 15px; font-size: 20px; color: #007bff; }


  </style>


  <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyA5qR9rGB6Qv43IYSMoHKhArm5gJdapXGk&libraries=places"></script>


</head>


<body>


  <h3>๐Ÿš“ ๊ฐ€๋กœ๋“ฑ ์œ„์น˜ ๊ธฐ๋ฐ˜ ์ถœ๋™ ์‹œ์Šคํ…œ (์ˆ˜๋™ ์ˆœ์ฐฐ)</h3>




  <div id="panel">


    <input type="text" id="keyword" placeholder="ํ‚ค์›Œ๋“œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š” (์˜ˆ: L0, L003, ๋ฒ”์ž, ๋ฒ” ํ”ผ, ์ „ํ™”๋ฒˆํ˜ธ ์ผ๋ถ€)" />


    <button onclick="searchAndShow()">๊ฒ€์ƒ‰</button>


    <div id="clickedCoords">๐Ÿ“ ์ง€๋„์—์„œ ์œ„์น˜๋ฅผ ํด๋ฆญํ•˜๋ฉด ์ขŒํ‘œ๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</div>


    <button id="copyBtn" onclick="copyCoords()">์ขŒํ‘œ ๋ณต์‚ฌ</button>


    <button id="completeBtn" onclick="completePatrol()">✅ ์ˆœ์ฐฐ ์™„๋ฃŒ</button>


  </div>




  <div id="upcomingPanel">


    <h4>⏰ ์ˆœ์ฐฐ ํ›„ ์ˆœ์ฐฐ์™„๋ฃŒ ์š”๋ง</h4>


    <ul id="upcomingList"></ul>


  </div>




  <div id="map"></div>




<script>


let map, markers = [], infoWindow;


let lastClickedCoord = "";


let activePatrol = null;


let completedPatrols = new Set();




const lampData = {


  "L001": { ๋ถ„๋ฅ˜:"๊ฐ€๋กœ๋“ฑ", ๊ณ ์œ ์ด๋ฆ„:"L001", ์ „ํ™”๋ฒˆํ˜ธ:"02-2345-4444", ์ฃผ์†Œ:"์„œ์šธ ์–‘์ฒœ๊ตฌ ํ•ฉ์ •๋™", ํŠน์ด์‚ฌํ•ญ:"์ค‘์š”", ์ขŒํ‘œ:"37.385719,126.658645" },

  "L002": { ๋ถ„๋ฅ˜:"๊ฐ€๋กœ๋“ฑ", ๊ณ ์œ ์ด๋ฆ„:"L002", ์ „ํ™”๋ฒˆํ˜ธ:"02-2345-7744", ์ฃผ์†Œ:"์„œ์šธ ์–‘์ฒœ๊ตฌ ํ•ฉ์ •๋™", ํŠน์ด์‚ฌํ•ญ:"์ ๊ฒ€ ํ•„์š”", ์ขŒํ‘œ:"37.386500,126.660000" },

  "L003": { ๋ถ„๋ฅ˜:"๊ฐ€๋กœ๋“ฑ", ๊ณ ์œ ์ด๋ฆ„:"L003", ์ „ํ™”๋ฒˆํ˜ธ:"02-2345-4488", ์ฃผ์†Œ:"", ํŠน์ด์‚ฌํ•ญ:"์ค‘์š”", ์ขŒํ‘œ:"37.387000,126.661000" },

  "CV1": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV1", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"์˜ค๋ชฉ๋กœ37๊ธธ 11(๊ทธ๋ฆฐ๋นŒ๋ผ 102ํ˜ธ)", ํŠน์ด์‚ฌํ•ญ:"25.5.16~25.8.26. 22:00~23:00, 42ํ˜ธ)", ์ขŒํ‘œ:"37.526140,126.856032" },

  "CV2": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV2", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"์˜ค๋ชฉ๋กœ24๊ธธ 19-22 2์ธต", ํŠน์ด์‚ฌํ•ญ:"7.5~8.4(๋งค์ผ 22~23์‹œ)", ์ขŒํ‘œ:"37.522740,126.854003" },

  "CV3": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV3", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"์˜ค๋ชฉ๋กœ34๊ธธ7, 302ํ˜ธ", ํŠน์ด์‚ฌํ•ญ:"7.8~9.7(๋งค์ผ 22~23์‹œ)", ์ขŒํ‘œ:"37.524296,126.855753" },

  "CV4": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV4", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"์‹ ๋ชฉ๋กœ5 ๋ชฉ๋™์‚ผ์„ฑ์•„ํŒŒํŠธ 105๋™ 107ํ˜ธ", ํŠน์ด์‚ฌํ•ญ:"5.22~8.21(๋งค์ผ 13~14, 22~23)", ์ขŒํ‘œ:"37.517046,126.876516" },

  "CV5": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV5", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"๋ชฉ๋Œ๋กœ23๊ธธ35 B๋™ ์ง€์ธต", ํŠน์ด์‚ฌํ•ญ:"4.15~8.14(๋งค์ผ 12~13์‹œ)", ์ขŒํ‘œ:"37.527701,126.859364" },

  "CV6": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV6", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"์€ํ–‰์ •๋กœ42 ์‹ ์„œ๊ณ ๋“ฑํ•™๊ต", ํŠน์ด์‚ฌํ•ญ:"6.8~9.7(์›”~๊ธˆ 8~9, 22~23์‹œ)", ์ขŒํ‘œ:"37.523921,126.860085" },

  "CV7": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV7", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"๋ชฉ๋™๋กœ11๊ธธ 33 401(ํ‚ค์ฆˆ๋‰ด๋นŒ๋ฆฌ์ง€)", ํŠน์ด์‚ฌํ•ญ:"5.23~8.22(๋งค์ผ 23~24์‹œ)", ์ขŒํ‘œ:"37.522949,126.861280" },

  "CV8": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV8", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"์ค‘์•™๋กœ52๊ธธ 50 201ํ˜ธ", ํŠน์ด์‚ฌํ•ญ:"7.15~8.14(๋งค์ผ 9~10, 21~22)", ์ขŒํ‘œ:"37.525925,126.855922" },

  "CV9": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV9", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"๋ชฉ๋™๋กœ19๊ธธ 31 ๊ฑดํ–ฅํŒŒํฌ๋นŒ 402ํ˜ธ", ํŠน์ด์‚ฌํ•ญ:"7.8~8.7(์ˆœ์ฐฐ๋ฏธ์‹ค์‹œ)", ์ขŒํ‘œ:"37.524588,126.861030" },

  "CV10": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV10", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"๋ชฉ๋™๋กœ 193 5์ธต(์•„๋œฐ๋ฆฌ์— ํ•™์›)", ํŠน์ด์‚ฌํ•ญ:"7.19~8.17(11~12, 21~22))", ์ขŒํ‘œ:"37.525567,126.858867" },

  "CV11": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV11", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"์‹ ๋ชฉ๋กœ5๊ธธ 15 1์ธต(ํ™”์ดํŠธ์Šค์™„)", ํŠน์ด์‚ฌํ•ญ:"7.19~8.18(13~14์‹œ)", ์ขŒํ‘œ:"37.517567,126.872267" },

  "CV12": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV12", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"์˜ค๋ชฉ๋กœ354 101๋™ 1002ํ˜ธ", ํŠน์ด์‚ฌํ•ญ:"7.18~8.17(15~16, 22~23)", ์ขŒํ‘œ:"37.523919,126.876851" },

  "CV13": { ๋ถ„๋ฅ˜:"๋ฒ”์ฃ„ํ”ผํ•ด์ž", ๊ณ ์œ ์ด๋ฆ„:"CV13", ์ „ํ™”๋ฒˆํ˜ธ:"", ์ฃผ์†Œ:"์˜ค๋ชฉ๋กœ300 204๋™ 701", ํŠน์ด์‚ฌํ•ญ:"7.31~8.30(05~06, 19~20)", ์ขŒํ‘œ:"37.524183,126.870829" },


  "PP1": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP1", ์ „ํ™”๋ฒˆํ˜ธ:"02-2696-2896", ์ฃผ์†Œ:"๋ชฉ๋™๋กœ 189", ํŠน์ด์‚ฌํ•ญ:"์ œ์ผ์€ํ–‰ ์•ž", ์ขŒํ‘œ:"37.525392,126.864424" },

  "PP2": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP2", ์ „ํ™”๋ฒˆํ˜ธ:"02-2065-9837", ์ฃผ์†Œ:"์‹ ์ •์ค‘์•™๋กœ 81", ํŠน์ด์‚ฌํ•ญ:"ํ˜„๋Œ€๊ณต์—…์‚ฌ, ์ ์‹ฌ๋ฐฅ์ƒ ์•ž", ์ขŒํ‘œ:"37.526729,126.860807" },

  "PP3": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP3", ์ „ํ™”๋ฒˆํ˜ธ:"02-2696-7731", ์ฃผ์†Œ:"์ค‘์•™๋กœ 326", ํŠน์ด์‚ฌํ•ญ:"์žฌ์ •๋งˆํŠธ ์•ž ๋…ธ์ƒ", ์ขŒํ‘œ:"37.52496,126.850586" },

  "PP4": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP4", ์ „ํ™”๋ฒˆํ˜ธ:"02-2694-9260", ์ฃผ์†Œ:"์‹ ์›”๋กœ 337", ํŠน์ด์‚ฌํ•ญ:"์šฐ๋ฆฌ์€ํ–‰ ์‹ ์ •๋™ ๊ธˆ์œต์„ผํ„ฐ ์•ž ๋…ธ์ƒ", ์ขŒํ‘œ:"37.521859,126.858348" },

  "PP5": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP5", ์ „ํ™”๋ฒˆํ˜ธ:"02-2654-0174", ์ฃผ์†Œ:"์‹ ๋ชฉ๋กœ 85", ํŠน์ด์‚ฌํ•ญ:"์˜ค๋ชฉ๊ต7๋ฒˆ์ถœ๊ตฌ 240๋ฏธํ„ฐ(๊ณจ๋“œ์น˜๊ณผ ์•ž)", ์ขŒํ‘œ:"37.522277,126.874334" },

  "PP6": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP6", ์ „ํ™”๋ฒˆํ˜ธ:"02-2061-4217", ์ฃผ์†Œ:"๋ชฉ๋™๋™๋กœ 152", ํŠน์ด์‚ฌํ•ญ:"์‹ ์ •2์น˜์•ˆ์„ผํ„ฐ ์•ž", ์ขŒํ‘œ:"37.519302,126.870335" },

  "PP7": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP7", ์ „ํ™”๋ฒˆํ˜ธ:"02-2654-0136/02-2644-8193", ์ฃผ์†Œ:"์‹ ์ •๋™ 129-85", ํŠน์ด์‚ฌํ•ญ:"์ฒญ๊ตฌ์•„ํŒŒํŠธ 101๋™ ์•ž", ์ขŒํ‘œ:"37.51979,126.875529" },

  "PP8": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP8", ์ „ํ™”๋ฒˆํ˜ธ:"02-2654-0262", ์ฃผ์†Œ:"๋ชฉ๋™๋™๋กœ 12๊ธธ 60", ํŠน์ด์‚ฌํ•ญ:"ํ˜„๋Œ€์•„ํŒŒํŠธ ์ž…๊ตฌ", ์ขŒํ‘œ:"37.522168,126.877685" },

  "PP9": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP9", ์ „ํ™”๋ฒˆํ˜ธ:"02-2061-6579", ์ฃผ์†Œ:"์‹ ๋ชฉ๋กœ 5", ํŠน์ด์‚ฌํ•ญ:"์‹ ์ •์‚ผ์„ฑ์ œ2๊ด€๋ฆฌ์‚ฌ๋ฌด์†Œ ์•ž", ์ขŒํ‘œ:"37.517164,126.876795" },

  "PP10": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP10", ์ „ํ™”๋ฒˆํ˜ธ:"02-2654-4323/02-2654-0138", ์ฃผ์†Œ:"์‹ ๋ชฉ๋กœ 10", ํŠน์ด์‚ฌํ•ญ:"๋ชฉ๋™ํ˜„๋Œ€์•„ํŒŒํŠธ ์ƒ๊ฐ€ ์•ž", ์ขŒํ‘œ:"37.517926,126.876297" },

  "PP11": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP11", ์ „ํ™”๋ฒˆํ˜ธ:"02-2652-9138", ์ฃผ์†Œ:"์˜ค๋ชฉ๋กœ 245", ํŠน์ด์‚ฌํ•ญ:"๋ชฉ๋™์—ญ ์ง€ํ•˜ 1์ธต(ํ™”์žฅ์‹ค ์•ž)", ์ขŒํ‘œ:"37.526109,126.8643" },

  "PP12": { ๋ถ„๋ฅ˜:"๊ณต์ค‘์ „ํ™”", ๊ณ ์œ ์ด๋ฆ„:"PP12", ์ „ํ™”๋ฒˆํ˜ธ:"02-2061-0296", ์ฃผ์†Œ:"๋ชฉ๋™๋กœ 225", ํŠน์ด์‚ฌํ•ญ:"ํ™์ต๋ณ‘์› ๋ณธ๊ด€ 2์ธต", ์ขŒํ‘œ:"37.528466,126.863688" },

};




const patrolSchedule = [


  { id:"CV1(42ํ˜ธ)", startDate:"2025-05-16", endDate:"2025-08-26", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"22:00", end:"23:00" }] },


  { id:"CV2(43ํ˜ธ)", startDate:"2025-07-05", endDate:"2025-08-04", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"22:00", end:"23:00" }] },


  { id:"CV3(43ํ˜ธ)", startDate:"2025-07-08", endDate:"2025-09-07", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"22:00", end:"23:00" }] },


  { id:"CV4(41ํ˜ธ)", startDate:"2025-05-22", endDate:"2025-08-21", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"13:00", end:"14:00" },{ start:"22:00", end:"23:00" }] },


  { id:"CV5(42ํ˜ธ)", startDate:"2025-04-15", endDate:"2025-08-14", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"12:00", end:"13:00" }] },


  { id:"CV6(43ํ˜ธ)", startDate:"2025-06-08", endDate:"2025-09-07", daysOfWeek:[1,2,3,4,5], timeRanges:[{ start:"08:00", end:"09:00" },{ start:"22:00", end:"23:00" }] },


  { id:"CV7(43ํ˜ธ)", startDate:"2025-05-23", endDate:"2025-08-22", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"23:00", end:"00:00" }] },


  { id:"CV8(42ํ˜ธ)", startDate:"2025-07-15", endDate:"2025-08-14", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"09:00", end:"10:00" },{ start:"21:00", end:"22:00" }] },


  { id:"CV10(42ํ˜ธ)", startDate:"2025-07-19", endDate:"2025-08-17", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"11:00", end:"12:00" },{ start:"21:00", end:"22:00" }] },


  { id:"CV11(41ํ˜ธ)", startDate:"2025-07-19", endDate:"2025-08-18", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"13:00", end:"14:00" }] },

  { id:"CV11-1(41ํ˜ธ)", startDate:"2025-07-19", endDate:"2025-08-18", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"17:00", end:"18:00" },{ start:"21:00", end:"22:00" }] },



  { id:"CV12(41ํ˜ธ)", startDate:"2025-07-18", endDate:"2025-08-17", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"15:00", end:"16:00" },{ start:"22:00", end:"23:00" }] },


  { id:"CV13(41ํ˜ธ)", startDate:"2025-07-31", endDate:"2025-08-30", daysOfWeek:[0,1,2,3,4,5,6], timeRanges:[{ start:"05:00", end:"06:00" },{ start:"19:00", end:"20:00" }] },


];




// ๋‚ ์งœ ๋ฒ”์œ„ ํ™•์ธ ํ•จ์ˆ˜


function isWithinDateRange(dateStr,startDateStr,endDateStr){


  const d=new Date(dateStr);


  return d>=new Date(startDateStr)&&d<=new Date(endDateStr);


}


function isDayAllowed(date,days){return days.includes(date.getDay());}


function parseCoords(s){const[a,b]=s.split(",").map(Number);return{lat:a,lng:b};}




// ๋‘ ์ž๋ฆฌ ์ˆซ์ž ํฌ๋งท


function pad(n){ return n.toString().padStart(2,"0"); }




// ์ตœ์†Œ 2๊ธ€์ž ์ด์ƒ ์—ฐ์† substring ๋ฐ˜ํ™˜


function getSubstrings(str, minLen = 2) {


  const substrings = [];


  for(let len = minLen; len <= str.length; len++) {


    for(let i = 0; i <= str.length - len; i++) {


      substrings.push(str.substring(i, i + len));


    }


  }


  return substrings;


}




// ํ…์ŠคํŠธ ์ •๊ทœํ™” (์†Œ๋ฌธ์ž, ๋„์–ด์“ฐ๊ธฐ/ํŠน์ˆ˜๋ฌธ์ž ์ œ๊ฑฐ)


function normalizeText(str){


  return (str||"").toLowerCase().replace(/[\s\-\_\.,]/g,"");


}




// ๋ฌธ์ž ์ˆœ์„œ๋Œ€๋กœ ํฌํ•จ ์—ฌ๋ถ€ ๊ฒ€์‚ฌ (๋„์–ด์“ฐ๊ธฐ ๋ฌด์‹œํ•˜๊ณ  ์ˆœ์„œ๋งŒ ๋งž์œผ๋ฉด true)


function isSequenceMatch(text, keyword){


  let idx=0;


  for(const ch of keyword){


    idx=text.indexOf(ch, idx);


    if(idx === -1) return false;


    idx++;


  }


  return true;


}




// ์ˆœ์ฐฐ ์˜ˆ์ • ํ‘œ์‹œ


function showUpcomingPatrols(){


  const now=new Date(), nowM=now.getHours()*60+now.getMinutes();


  const list=document.getElementById("upcomingList");


  list.innerHTML="";


  const upcoming=[];




  patrolSchedule.forEach(task=>{


    if(!isWithinDateRange(now.toISOString().slice(0,10),task.startDate,task.endDate)) return;


    if(!isDayAllowed(now,task.daysOfWeek)) return;




    task.timeRanges.forEach(r=>{


      const [sH,sM] = r.start.split(":").map(Number);


      const [eH,eM] = r.end.split(":").map(Number);


      const startM = sH*60 + sM - 30;


      const endM = eH*60 + eM + 30;


      const isOngoing = nowM >= sH*60 + sM && nowM <= eH*60 + eM;


      const isInWindow = nowM >= startM && nowM <= endM;




      if(isInWindow && !completedPatrols.has(task.id+"|"+sH+"|"+sM)){


        upcoming.push({task,hour:sH,minute:sM,endHour:eH,endMinute:eM,isInTime:isOngoing});


      }


    });


  });




  if(upcoming.length===0){


    list.innerHTML="<li>✅ ์˜ˆ์ • ์—†์Œ</li>";


    return;


  }




  upcoming.sort((a,b)=>(a.hour*60+a.minute)-(b.hour*60+b.minute));


  upcoming.forEach(item=>{


    const li=document.createElement("li");


    li.style.color=item.isInTime ? "blue" : "black";


    li.textContent=`${item.task.id} - ${pad(item.hour)}:${pad(item.minute)}~${pad(item.endHour)}:${pad(item.endMinute)}`;


    li.onclick=()=>{


      activePatrol = { task: item.task, hour: item.hour, minute: item.minute };


      const baseId = item.task.id.split("(")[0];


      if (!lampData[baseId]) {


        alert("ํ•ด๋‹น ID์˜ ์ขŒํ‘œ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.");


        return;


      }


      const pos = parseCoords(lampData[baseId].์ขŒํ‘œ);


      window.open(`https://www.google.com/maps/dir/?api=1&destination=${pos.lat},${pos.lng}`, "_blank");


    };


    list.appendChild(li);


  });


}




// ์ˆœ์ฐฐ ์™„๋ฃŒ ๋ฒ„ํŠผ


function completePatrol(){


  if(!activePatrol){


    alert("๐Ÿšซ ์ง„ํ–‰ ์ค‘์ธ ์ˆœ์ฐฐ์ด ์—†์Šต๋‹ˆ๋‹ค.");


    return;


  }


  completedPatrols.add(activePatrol.task.id + "|" + activePatrol.hour + "|" + activePatrol.minute);


  alert(`✅ ${activePatrol.task.id} ์ˆœ์ฐฐ ์™„๋ฃŒ!`);


  activePatrol = null;


  showUpcomingPatrols();


}




// ์ง€๋„ ๋ฐ ํด๋ฆญ ์ขŒํ‘œ ๋ณต์‚ฌ


function initMap(){


  map=new google.maps.Map(document.getElementById("map"),{center:{lat:37.3857,lng:126.6586},zoom:15});


  infoWindow=new google.maps.InfoWindow();


  map.addListener("click",e=>{


    lastClickedCoord=`${e.latLng.lat().toFixed(6)},${e.latLng.lng().toFixed(6)}`;


    document.getElementById("clickedCoords").innerText=`๐Ÿ“ ํด๋ฆญ ์ขŒํ‘œ: ${lastClickedCoord}`;


  });


}


function copyCoords(){


  if(!lastClickedCoord){


    alert("⚠️ ๋จผ์ € ์ง€๋„๋ฅผ ํด๋ฆญํ•˜์„ธ์š”.");


    return;


  }


  navigator.clipboard.writeText(lastClickedCoord).then(()=>alert("✅ ๋ณต์‚ฌ๋จ: "+lastClickedCoord));


}


function clearMarkers(){


  markers.forEach(m=>m.setMap(null));


  markers=[];


}




// ๋งˆ์ปค ์ถ”๊ฐ€: ํ•œ ๋ฒˆ ํด๋ฆญ ์‹œ ์ •๋ณด์ฐฝ, ๋‘ ๋ฒˆ ํด๋ฆญ ์‹œ ๊ธธ์ฐพ๊ธฐ ์—ด๊ธฐ


function addMarker(id, pos, color){


  const m = new google.maps.Marker({


    position: pos,


    map,


    label: id,


    icon: { url: `http://maps.google.com/mapfiles/ms/icons/${color}-dot.png` }


  });




  m.addListener("click", () => {


    const data = lampData[id];


    const content = `


      <div style="min-width:150px;">


        <strong>${data.๊ณ ์œ ์ด๋ฆ„}</strong><br/>


        ๋ถ„๋ฅ˜: ${data.๋ถ„๋ฅ˜}<br/>


        ์ „ํ™”๋ฒˆํ˜ธ: ${data.์ „ํ™”๋ฒˆํ˜ธ}<br/>


        ์ฃผ์†Œ: ${data.์ฃผ์†Œ}<br/>


        ํŠน์ด์‚ฌํ•ญ: ${data.ํŠน์ด์‚ฌํ•ญ}


      </div>`;


    infoWindow.setContent(content);


    infoWindow.open(map, m);


  });




  m.addListener("dblclick", () => {


    const coords = lampData[id].์ขŒํ‘œ.split(",");


    const lat = coords[0].trim();


    const lng = coords[1].trim();


    window.open(`https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}`, "_blank");


  });




  markers.push(m);


}




// ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ๊ฒ€์ƒ‰ ๋กœ์ง ํ†ตํ•ฉ


function searchAndShow(){


  const kw=document.getElementById("keyword").value.trim();


  if(!kw){


    alert("⚠️ ํ‚ค์›Œ๋“œ ์ž…๋ ฅํ•˜์„ธ์š”.");


    return;


  }


  clearMarkers();




  const kwNoSpace = kw.replace(/\s+/g, "").toLowerCase();


  if(kwNoSpace.length < 2){


    alert("⚠️ ๊ฒ€์ƒ‰์–ด๋Š” ์ตœ์†Œ 2๊ธ€์ž ์ด์ƒ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.");


    return;


  }




  const res = Object.keys(lampData).filter(id => {


    const data = lampData[id];




    // ๊ณ ์œ ์ด๋ฆ„(ID) ํ•„๋“œ ๊ฒ€์‚ฌ


    const name = normalizeText(data.๊ณ ์œ ์ด๋ฆ„);


    if(name === kwNoSpace) return true; // ์ •ํ™• ์ผ์น˜


    // ID ๊ฐ™์€ ๊ฒฝ์šฐ L001 ํ˜•ํƒœ์—์„œ kwNoSpace๊ฐ€ ์ ‘๋ฏธ์‚ฌ๋กœ ๋งž์œผ๋ฉด true (์˜ˆ: "001" ์ž…๋ ฅ ์‹œ L001 ๋งค์นญ)


    if(/^l\d+$/i.test(data.๊ณ ์œ ์ด๋ฆ„) && data.๊ณ ์œ ์ด๋ฆ„.toLowerCase().endsWith(kwNoSpace)) return true;


    if(isSequenceMatch(name, kwNoSpace)) return true;




    // ์ „ํ™”๋ฒˆํ˜ธ ํ•„๋“œ์—์„œ๋งŒ ๋งค์นญ


    const phone = normalizeText(data.์ „ํ™”๋ฒˆํ˜ธ);


    if(phone.includes(kwNoSpace)) return true;




    // ์ฃผ์†Œ, ๋ถ„๋ฅ˜, ํŠน์ด์‚ฌํ•ญ ๊ฐ๊ฐ์—์„œ๋งŒ ๋งค์นญ (๋ฌธ์ž ์ˆœ์„œ ํฌํ•จ)


    const fields = [normalizeText(data.์ฃผ์†Œ), normalizeText(data.๋ถ„๋ฅ˜), normalizeText(data.ํŠน์ด์‚ฌํ•ญ)];


    if(fields.some(f => isSequenceMatch(f, kwNoSpace))) return true;




    // ์ขŒํ‘œ ํ•„๋“œ์—์„œ๋งŒ ๋งค์นญ (์ˆซ์ž ์‰ผํ‘œ ํฌํ•จ ๊ทธ๋Œ€๋กœ)


    const coords = normalizeText(data.์ขŒํ‘œ);


    if(coords.includes(kwNoSpace)) return true;




    return false;


  }).map(id => ({id, pos: parseCoords(lampData[id].์ขŒํ‘œ)}));




  if(res.length === 0){


    alert("❌ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์—†์Œ");


    return;


  }


  const bounds = new google.maps.LatLngBounds();


  res.forEach(r => {


    addMarker(r.id, r.pos, "blue");


    bounds.extend(r.pos);


  });


  map.fitBounds(bounds);


}




// ์ดˆ๊ธฐํ™” ๋ฐ ์ด๋ฒคํŠธ ๋ฐ”์ธ๋”ฉ


document.addEventListener("DOMContentLoaded", () => {


  document.getElementById("upcomingPanel").addEventListener("click", e => {


    if(e.target.tagName.toLowerCase() !== "li") {


      e.currentTarget.classList.toggle("hidden");


    }


  });


});




window.onload = () => {


  initMap();


  showUpcomingPatrols();


  setInterval(showUpcomingPatrols, 60000);


};


</script>


</body>


</html>

๋Œ“๊ธ€ ์—†์Œ:

๋Œ“๊ธ€ ์“ฐ๊ธฐ