Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,74 @@
# javascript-subway-final
# 🚇 지하철 노선도 경로 조회 미션
- 등록된 지하철 노선도에서 경로를 조회하는 기능을 구현한다.

## 🚀 기능 요구사항
> 프리코스 3주차 미션에서 사용한 코드를 참고해도 무관하다.
### UI
- [x] index.html 완성
- [x] '길찾기'버튼 눌렀을 때 결과창 visible
- [x] 최단거리, 최소시간 라디오버튼
- [x] 결과창 구성

### 기능
- [x] 최단거리 구하기
- [x] 최소시간 구하기
- [x] 데이터를 import로 불러와서 전부 edge에 추가한다.
- [x] 다익스트라 라이브러리를 이용해 **type에 따라** 최단거리를 리턴
- [x] 프로그램 시작 시 App객체 생성
- [x] 생성할 때 data.js 를 객체의 속성으로 지정
- [x] '길찾기' 버튼 누를 때마다 경로를 객체의 속성으로 지정

------------

### 경로 조회 기능
<img src="/images/path_result.jpg" width="100%">

- [x]]출발역과 도착역을 입력받아 경로를 조회한다.
- [x]경로 조회 시 총 거리, 총 소요 시간을 함께 출력한다.
- [x]경로 조회 시 `최단 거리` 또는 `최소 시간` 옵션을 선택할 수 있다.

### 예외 처리
- [x]출발역과 도착역은 2글자 이상이어야 한다. - '이름관련'
- [x]존재하지 않는 역을 출발역 또는 도착역으로 입력할 수 없다. - '이름관련'
- [x]경로 조회 시 출발역과 도착역이 같을 수 없다. - '두 역 관련'
- [x]경로 조회 시 출발역과 도착역이 연결되지 않으면 경로를 조회할 수 없다. - '두 역 관련'
- [x]그 외 정상적으로 프로그램이 수행되지 않은 경우 `alert`으로 에러를 출력한다.

### 초기 설정
- [x]프로그램 시작 시 역, 노선, 구간 데이터를 초기 설정 해야 한다.
- [x]거리와 소요 시간은 양의 정수이며 단위는 km와 분을 의미한다.
- [x]아래의 사전 등록 정보로 반드시 초기 설정을 한다.

```
1. 지하철역으로 교대, 강남, 역삼, 남부터미널, 양재, 양재시민의숲, 매봉 역 정보가 등록되어 있다.
2. 지하철 노선으로 2호선, 3호선, 신분당선이 등록되어 있다.
3. 노선에 역이 아래와 같이 등록되어 있다.(왼쪽 끝이 상행 종점)
- 2호선: 교대 - ( 2km / 3분 ) - 강남 - ( 2km / 3분 ) - 역삼
- 3호선: 교대 - ( 3km / 2분 ) - 남부터미널 - ( 6km / 5분 ) - 양재 - ( 1km / 1분 ) - 매봉
- 신분당선: 강남 - ( 2km / 8분 ) - 양재 - ( 10km / 3분 ) - 양재시민의숲
```

<br>

## ✅ 프로그래밍 요구사항
### 길찾기 관련 기능
- [x]출발역을 입력하는 input 태그는 `departure-station-name-input` id 속성값을 가진다.
- [x]도착역을 입력하는 input 태그는 `arrival-station-name-input` id 속성값을 가진다.
- [x]최단거리, 최소시간을 선택하는 radio는 `search-type` name 속성값을 가진다.
- **radio option의 default 값은 최단거리이다.**
- [x]길찾기 버튼은 `search-button` id 속성값을 가진다.
- [x]📝 결과는 `table`을 이용하여 보여준다.

## ❗️힌트
## 데이터 초기화
- [x] 초기화 데이터 DB 만들어서 연결

## 최단 경로 라이브러리
- [x] dijkstra 사용하여 구현

#### 테스트설명
<img src="/images/dijkstra_example.png" width="400">

- 역 사이의 거리를 고려하지 않는 경우 V1->V3 경로가 최단 경로
- 역 사이의 거리를 고려할 경우 V1->V3 경로의 거리는 100km, V1->V2->V3 경로의 거리는 4km이므로 최단 경로는 V1->V2->V3

Binary file added images/dijkstra_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/path_result.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/path_result.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>지하철 길찾기</title>
</head>
<body>
<div id="app">
<script type="module" src="src/index.js"></script>
<h1>🚇지하철 길찾기</h1>
출발역<input id="departure-station-name-input" /><br><br>
도착역<input id="arrival-station-name-input" /><br><br>
<input type="radio" name="search-type" value="distance" checked="checked">최단거리
<input type="radio" name="search-type" value="time">최소시간<br><br>
<button id="search-button">길 찾기</button>
<div id="result" style="display: none;">
<h3>결과</h3>
<strong id="result-type"></strong><br>
<table border="1">
<tr>
<th>총 거리</th>
<th>총 소요 시간</th>
</tr>
<tr>
<td id="total-distance"></td>
<td id="total-time"></td>
</tr>
<tr>
<td id="result-path" colspan="2"></td>
</tr>
</table>
</div>
</div>
</body>
</html>
22 changes: 22 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const SEARCH = {
BUTTON: document.getElementById('search-button'),
TYPE: document.getElementsByName('search-type'),
};

export const INPUT = {
DEPARTURE: document.getElementById('departure-station-name-input'),
ARRIVAL: document.getElementById('arrival-station-name-input'),
};

export const SEARCH_TYPE = {
PATH: '최단거리',
TIME: '최소시간',
};

export const RESULT = {
DISPLAY: document.getElementById('result'),
TYPE: document.getElementById('result-type'),
DISTANCE: document.getElementById('total-distance'),
TIME: document.getElementById('total-time'),
PATH: document.getElementById('result-path'),
};
11 changes: 11 additions & 0 deletions src/createMessages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export function createResultPathMessage(pathArray) {
return pathArray.join('->');
}

export function createDistanceMessage(distance) {
return `${distance}km`;
}

export function createTimeMessage(time) {
return `${time}분`;
}
34 changes: 34 additions & 0 deletions src/data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export const lines = [
{
name: '2호선',
stations: [
'교대',
{distance: 2, time: 3},
'강남',
{distance: 2, time: 3},
'역삼',
],
},
{
name: '3호선',
stations: [
'교대',
{distance: 3, time: 2},
'남부터미널',
{distance: 6, time: 5},
'양재',
{distance: 1, time: 1},
'매봉',
],
},
{
name: '신분당선',
stations: [
'강남',
{distance: 2, time: 8},
'양재',
{distance: 10, time: 3},
'양재시민의숲',
],
},
];
27 changes: 27 additions & 0 deletions src/display.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {RESULT} from './constants.js';

export function appendDistanceToTable(distance) {
RESULT.DISTANCE.innerText = distance;
}

export function appendTimeToTable(time) {
RESULT.TIME.innerText = time;
}

export function appendPathToTable(path) {
RESULT.PATH.innerText = path;
}

export function changeTypeTitle(type) {
if (type==='distance') {
RESULT.TYPE.innerText = '최단거리';
}
if (type==='time') {
RESULT.TYPE.innerText = '최소시간';
}
}

export function display(object) {
changeTypeTitle(object.type);
RESULT.DISPLAY.style.display = 'block';
}
17 changes: 17 additions & 0 deletions src/getMinimum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Dijkstra from './utils/Dijkstra.js';

export function minPath(data, departure, arrival, type) {
const dijkstra = new Dijkstra();
importDataToDijkstra(data, dijkstra, type);

const result = dijkstra.findShortestPath(departure, arrival);
return result;
}
function importDataToDijkstra(data, dijkstra, type) {
for (let i=0; i<data.length; i++) {
for (let j=0; j<data[i].stations.length-2; j+=2) {
dijkstra.addEdge(data[i].stations[j], data[i].stations[j+2], data[i].stations[j+1][type]);
}
}
return dijkstra;
}
25 changes: 25 additions & 0 deletions src/getTotal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
function findLineOfTwoStations(data, departure, arrival) {
let line = false;
for (let i=0; i<data.length; i++) {
if (data[i].stations.includes(departure) && data[i].stations.includes(arrival)) {
line = data[i];
}
}
return line;
}

function betweenStations(line, departure, arrival, type) {
const departure_idx = line.stations.indexOf(departure);
const arrival_idx = line.stations.indexOf(arrival);
return line.stations[Math.abs(departure_idx + arrival_idx)/2][type];
}

export function totalBetweenStations(data, path, type) {
let total=0;
for (let i=0; i<path.length-1; i++) {
const departure = path[i];
const arrival = path[i+1];
total += betweenStations(findLineOfTwoStations(data, departure, arrival), departure, arrival, type);
}
return total;
}
65 changes: 65 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable require-jsdoc */
import {INPUT, SEARCH} from './constants.js';
import {minPath} from './getMinimum.js';
import {
display,
appendDistanceToTable,
appendPathToTable,
appendTimeToTable,
} from './display.js';
import {lines} from './data.js';
import {
createDistanceMessage,
createResultPathMessage,
createTimeMessage,
} from './createMessages.js';
import {totalBetweenStations} from './getTotal.js';
import {
areStationsDifferent,
areStationsLinked,
isNameShort,
isStationAvaliable,
} from './inputValid.js';

export default function App() {
this.data = lines;
SEARCH.BUTTON.addEventListener('click', (e) => {
display(this);
this.type = changeType();
isValid(this);
this.path = minPath(this.data, INPUT.DEPARTURE.value, INPUT.ARRIVAL.value, this.type.value,);
if (!areStationsLinked(this.path)) {
window.alert('연결된 역을 입력해주세요!');
return;
}
appendDistanceToTable(createDistanceMessage(totalBetweenStations(this.data, this.path, 'distance')));
appendTimeToTable(createTimeMessage(totalBetweenStations(this.data, this.path, 'time')));
appendPathToTable(createResultPathMessage(this.path));
});
}

function changeType() {
let type;
if (SEARCH.TYPE[0].checked == true) {
type = SEARCH.TYPE[0];
}
if (SEARCH.TYPE[1].checked == true) {
type = SEARCH.TYPE[1];
}
return type;
}

function isValid(object) {
if (isNameShort(INPUT.DEPARTURE.value) || isNameShort(INPUT.ARRIVAL.value)) {
window.alert('역 이름은 2글자 이상이어야 합니다!');
return;
} else if (!isStationAvaliable(INPUT.DEPARTURE.value, object.data) || !isStationAvaliable(INPUT.DEPARTURE.value, object.data)) {
window.alert('존재하지 않는 역입니다!');
return;
} else if (!areStationsDifferent(INPUT.DEPARTURE.value, INPUT.ARRIVAL.value)) {
window.alert('서로 다른 역을 입력해주세요!');
return;
}
}

new App();
22 changes: 22 additions & 0 deletions src/inputValid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export function isNameShort(name) {
return (name.length<2);
}

export function isStationAvaliable(name, data) {
let result = false;
for (let i=0; i<data.length; i++) {
if (data[i].stations.includes(name)) {
result = true;
break;
}
}
return result;
}

export function areStationsDifferent(departure, arrival) {
return (departure!==arrival);
}

export function areStationsLinked(object) {
return (object.path!==false);
}
Loading