권주희

Merge branch 'feature/getCurAirInfo' into 'develop'

Feature/get cur air info

- implement route air condition api
- connect the api with client
- display the route air condition result

See merge request !5
...@@ -58,6 +58,30 @@ const getPosition = (lat, lon) => { ...@@ -58,6 +58,30 @@ const getPosition = (lat, lon) => {
58 console.log(error.response); 58 console.log(error.response);
59 }); 59 });
60 }; 60 };
61 +/* GET route airCondition listing. */
62 +router.get("/route", async function (req, res, next) {
63 + console.log("출발지:", req.query.departure);
64 + console.log("도착지:", req.query.arrival);
65 +
66 + let dep = JSON.parse(req.query.departure);
67 + let depLat = dep["Ha"];
68 + let depLon = dep["Ga"];
69 +
70 + let arr = JSON.parse(req.query.arrival);
71 + let arrLat = arr["Ha"];
72 + let arrLon = arr["Ga"];
73 + let airCondition = "";
74 +
75 + let response = await getRoute(depLat, depLon, arrLat, arrLon)
76 + .then((routeInformation) =>
77 + routeAirCondition(depLat, depLon, routeInformation)
78 + )
79 + .then((routeInformation) => {
80 + airCondition = routeInformation;
81 + });
82 +
83 + res.send(airCondition);
84 +});
61 85
62 const getCondition = (encodedStation) => { 86 const getCondition = (encodedStation) => {
63 return axios 87 return axios
...@@ -78,4 +102,70 @@ const getCondition = (encodedStation) => { ...@@ -78,4 +102,70 @@ const getCondition = (encodedStation) => {
78 }); 102 });
79 }; 103 };
80 104
105 +const getRoute = (depLat, depLon, arrLat, arrLon) => {
106 + return axios
107 + .get(
108 + "https://maps.googleapis.com/maps/api/directions/json?origin=" +
109 + depLat +
110 + "," +
111 + depLon +
112 + "&destination=" +
113 + arrLat +
114 + "," +
115 + arrLon +
116 + "&mode=transit&departure_time=now&key=" +
117 + googleMapKey +
118 + "&language=ko"
119 + )
120 + .then(function (response) {
121 + console.log(response["data"]);
122 + let routeInformation = [];
123 + for (
124 + let i = 0;
125 + i < response["data"].routes[0]["legs"][0]["steps"].length;
126 + i++
127 + ) {
128 + let info = {};
129 + info["instruction"] =
130 + response["data"].routes[0]["legs"][0]["steps"][i][
131 + "html_instructions"
132 + ];
133 + info["location"] =
134 + response["data"].routes[0]["legs"][0]["steps"][i]["end_location"];
135 + info["duration"] =
136 + response["data"].routes[0]["legs"][0]["steps"][i]["duration"]["text"];
137 + info["travel_mode"] =
138 + response["data"].routes[0]["legs"][0]["steps"][i]["travel_mode"];
139 + routeInformation.push(info);
140 + }
141 + // console.log(routeInformation);
142 + return routeInformation;
143 + })
144 + .catch(function (error) {
145 + console.log(error.response);
146 + });
147 +};
148 +
149 +const routeAirCondition = async (depLat, depLon, routeInformation) => {
150 + await getPosition(depLat, depLon)
151 + .then((encodedStation) => getCondition(encodedStation))
152 + .then((result) => {
153 + let info = {};
154 + info["airCondition"] = result;
155 + routeInformation.push(info);
156 + });
157 + for (let i = 0; i < routeInformation.length - 1; i++) {
158 + await getPosition(
159 + routeInformation[i]["location"]["lat"],
160 + routeInformation[i]["location"]["lng"]
161 + )
162 + .then((encodedStation) => getCondition(encodedStation))
163 + .then((result) => {
164 + routeInformation[i]["airCondition"] = result;
165 + });
166 + }
167 + console.log(routeInformation);
168 + return routeInformation;
169 +};
170 +
81 module.exports = router; 171 module.exports = router;
......
...@@ -11,7 +11,9 @@ import good from "./gradeIcon/smile.png"; ...@@ -11,7 +11,9 @@ import good from "./gradeIcon/smile.png";
11 import bad from "./gradeIcon/angry.png"; 11 import bad from "./gradeIcon/angry.png";
12 import veryBad from "./gradeIcon/devil.png"; 12 import veryBad from "./gradeIcon/devil.png";
13 import next from "./gradeIcon/next.png"; 13 import next from "./gradeIcon/next.png";
14 - 14 +import start from "./gradeIcon/start.png";
15 +import finish from "./gradeIcon/finish.png";
16 +import loading from "./gradeIcon/loading.gif";
15 /* global kakao */ 17 /* global kakao */
16 18
17 export default class Home extends Component { 19 export default class Home extends Component {
...@@ -28,30 +30,27 @@ export default class Home extends Component { ...@@ -28,30 +30,27 @@ export default class Home extends Component {
28 infoWindow: new kakao.maps.CustomOverlay({}), 30 infoWindow: new kakao.maps.CustomOverlay({}),
29 region: "은평구청", 31 region: "은평구청",
30 curAirCondition: null, 32 curAirCondition: null,
31 - 33 + routeInformation: null,
32 - // curAirCondition: null,
33 - // routeInformation: null,
34 }; 34 };
35 - this.removeAllChildNods = this.removeAllChildNods.bind(this);
36 - this.displayInfowindow = this.displayInfowindow.bind(this);
37 } 35 }
38 36
39 - searchPlaces() { 37 + searchPlaces = () => {
40 var keyword = document.getElementById("keyword").value; 38 var keyword = document.getElementById("keyword").value;
41 39
42 if (!keyword.replace(/^\s+|\s+$/g, "")) { 40 if (!keyword.replace(/^\s+|\s+$/g, "")) {
43 alert("키워드를 입력해주세요!"); 41 alert("키워드를 입력해주세요!");
44 return false; 42 return false;
45 } 43 }
44 +
46 // 장소검색 객체를 통해 키워드로 장소검색을 요청합니다 45 // 장소검색 객체를 통해 키워드로 장소검색을 요청합니다
47 this.state.placeSearch.keywordSearch( 46 this.state.placeSearch.keywordSearch(
48 keyword, 47 keyword,
49 this.placesSearchCB.bind(this) 48 this.placesSearchCB.bind(this)
50 ); 49 );
51 - } 50 + };
52 51
53 // 장소검색이 완료됐을 때 호출되는 콜백함수 입니다 52 // 장소검색이 완료됐을 때 호출되는 콜백함수 입니다
54 - placesSearchCB(data, status, pagination) { 53 + placesSearchCB = (data, status, pagination) => {
55 if (status === kakao.maps.services.Status.OK) { 54 if (status === kakao.maps.services.Status.OK) {
56 // 정상적으로 검색이 완료됐으면 55 // 정상적으로 검색이 완료됐으면
57 // 검색 목록과 마커를 표출합니다 56 // 검색 목록과 마커를 표출합니다
...@@ -66,9 +65,9 @@ export default class Home extends Component { ...@@ -66,9 +65,9 @@ export default class Home extends Component {
66 alert("검색 결과 중 오류가 발생했습니다."); 65 alert("검색 결과 중 오류가 발생했습니다.");
67 return; 66 return;
68 } 67 }
69 - } 68 + };
70 69
71 - displayPlaces(places) { 70 + displayPlaces = (places) => {
72 var listEl = document.getElementById("placesList"), 71 var listEl = document.getElementById("placesList"),
73 menuEl = document.getElementById("menu_wrap"), 72 menuEl = document.getElementById("menu_wrap"),
74 fragment = document.createDocumentFragment(), 73 fragment = document.createDocumentFragment(),
...@@ -118,13 +117,13 @@ export default class Home extends Component { ...@@ -118,13 +117,13 @@ export default class Home extends Component {
118 117
119 // 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다 118 // 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
120 this.state.map.setBounds(bounds); 119 this.state.map.setBounds(bounds);
121 - } 120 + };
122 // 커스텀 오버레이를 닫기 위해 호출되는 함수입니다 121 // 커스텀 오버레이를 닫기 위해 호출되는 함수입니다
123 - closeOverlay() { 122 + closeOverlay = () => {
124 this.state.infoWindow.setMap(null); 123 this.state.infoWindow.setMap(null);
125 - } 124 + };
126 125
127 - getListItem(index, places) { 126 + getListItem = (index, places) => {
128 var el = document.createElement("li"), 127 var el = document.createElement("li"),
129 itemStr = 128 itemStr =
130 '<span class="markerbg marker_' + 129 '<span class="markerbg marker_' +
...@@ -153,9 +152,33 @@ export default class Home extends Component { ...@@ -153,9 +152,33 @@ export default class Home extends Component {
153 el.className = "item"; 152 el.className = "item";
154 153
155 return el; 154 return el;
156 - } 155 + };
157 156
158 - addMarker(position, idx, title) { 157 + getRouteAirCondition = () => {
158 + this.setState({
159 + buttonClicked: true,
160 + });
161 + API.get("/airCondition/route", {
162 + params: {
163 + departure: this.state.departure,
164 + arrival: this.state.arrival,
165 + },
166 + })
167 + .then((response) => {
168 + this.setState({
169 + routeInformation: response.data,
170 + });
171 + console.log(this.state.routeInformation);
172 + })
173 + .catch(function (error) {
174 + console.log(error);
175 + })
176 + .finally(function () {
177 + // always executed
178 + });
179 + };
180 +
181 + addMarker = (position, idx, title) => {
159 var imageSrc = 182 var imageSrc =
160 "http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png", // 마커 이미지 url, 스프라이트 이미지를 씁니다 183 "http://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png", // 마커 이미지 url, 스프라이트 이미지를 씁니다
161 imageSize = new kakao.maps.Size(36, 37), // 마커 이미지의 크기 184 imageSize = new kakao.maps.Size(36, 37), // 마커 이미지의 크기
...@@ -175,20 +198,20 @@ export default class Home extends Component { ...@@ -175,20 +198,20 @@ export default class Home extends Component {
175 this.state.markers.push(marker); // 배열에 생성된 마커를 추가합니다 198 this.state.markers.push(marker); // 배열에 생성된 마커를 추가합니다
176 199
177 return marker; 200 return marker;
178 - } 201 + };
179 202
180 // 지도 위에 표시되고 있는 마커를 모두 제거합니다 203 // 지도 위에 표시되고 있는 마커를 모두 제거합니다
181 - removeMarker() { 204 + removeMarker = () => {
182 for (var i = 0; i < this.state.markers.length; i++) { 205 for (var i = 0; i < this.state.markers.length; i++) {
183 this.state.markers[i].setMap(null); 206 this.state.markers[i].setMap(null);
184 } 207 }
185 this.setState({ 208 this.setState({
186 markers: [], 209 markers: [],
187 }); 210 });
188 - } 211 + };
189 212
190 // 검색결과 목록 하단에 페이지번호를 표시는 함수입니다 213 // 검색결과 목록 하단에 페이지번호를 표시는 함수입니다
191 - displayPagination(pagination) { 214 + displayPagination = (pagination) => {
192 var paginationEl = document.getElementById("pagination"), 215 var paginationEl = document.getElementById("pagination"),
193 fragment = document.createDocumentFragment(), 216 fragment = document.createDocumentFragment(),
194 i; 217 i;
...@@ -216,7 +239,7 @@ export default class Home extends Component { ...@@ -216,7 +239,7 @@ export default class Home extends Component {
216 fragment.appendChild(el); 239 fragment.appendChild(el);
217 } 240 }
218 paginationEl.appendChild(fragment); 241 paginationEl.appendChild(fragment);
219 - } 242 + };
220 243
221 removeAllChildNods(el) { 244 removeAllChildNods(el) {
222 while (el.hasChildNodes()) { 245 while (el.hasChildNodes()) {
...@@ -226,7 +249,7 @@ export default class Home extends Component { ...@@ -226,7 +249,7 @@ export default class Home extends Component {
226 249
227 // 검색결과 목록 또는 마커를 클릭했을 때 호출되는 함수입니다 250 // 검색결과 목록 또는 마커를 클릭했을 때 호출되는 함수입니다
228 // 인포윈도우에 장소명을 표시합니다 251 // 인포윈도우에 장소명을 표시합니다
229 - displayInfowindow(marker, title) { 252 + displayInfowindow = (marker, title) => {
230 console.log(marker); 253 console.log(marker);
231 254
232 let content = document.createElement("div"); 255 let content = document.createElement("div");
...@@ -316,7 +339,7 @@ export default class Home extends Component { ...@@ -316,7 +339,7 @@ export default class Home extends Component {
316 this.state.infoWindow.setContent(content); 339 this.state.infoWindow.setContent(content);
317 this.state.infoWindow.setPosition(marker.getPosition()); 340 this.state.infoWindow.setPosition(marker.getPosition());
318 this.state.infoWindow.setMap(this.state.map); 341 this.state.infoWindow.setMap(this.state.map);
319 - } 342 + };
320 343
321 componentDidMount() { 344 componentDidMount() {
322 // 컴포넌트가 만들어지고, render 함수가 호출된 이후에 호출되는 메소 345 // 컴포넌트가 만들어지고, render 함수가 호출된 이후에 호출되는 메소
...@@ -382,6 +405,128 @@ export default class Home extends Component { ...@@ -382,6 +405,128 @@ export default class Home extends Component {
382 ); 405 );
383 } 406 }
384 let routeAirCondition = null; 407 let routeAirCondition = null;
408 + let tempRouteAirCondition = [];
409 + if (this.state.buttonClicked != null) {
410 + routeAirCondition = <img src={loading} width="300px" heigth="300px" />;
411 + }
412 + if (this.state.routeInformation != null) {
413 + for (let i = 0; i < this.state.routeInformation.length - 1; i++) {
414 + tempRouteAirCondition.push(this.state.routeInformation[i]);
415 + }
416 + let pm10ImageArray = new Array();
417 + let pm25ImageArray = new Array();
418 + for (let i = 0; i < this.state.routeInformation.length; i++) {
419 + let pm10Image = null;
420 + let pm25Image = null;
421 + switch (this.state.routeInformation[i]["airCondition"].pm10Grade) {
422 + case "1":
423 + pm10Image = <img src={veryGood} width="50px" heigth="100px" />;
424 + break;
425 + case "2":
426 + pm10Image = <img src={good} width="50px" heigth="100px" />;
427 + break;
428 + case "3":
429 + pm10Image = <img src={bad} width="50px" heigth="100px" />;
430 + break;
431 + case "4":
432 + pm10Image = <img src={veryBad} width="50px" heigth="100px" />;
433 + break;
434 + default:
435 + pm10Image = null;
436 + }
437 + switch (this.state.routeInformation[i]["airCondition"].pm25Grade) {
438 + case "1":
439 + pm25Image = <img src={veryGood} width="50px" heigth="100px" />;
440 + break;
441 + case "2":
442 + pm25Image = <img src={good} width="50px" heigth="100px" />;
443 + break;
444 + case "3":
445 + pm25Image = <img src={bad} width="50px" heigth="100px" />;
446 + break;
447 + case "4":
448 + pm25Image = <img src={veryBad} width="50px" heigth="100px" />;
449 + break;
450 + default:
451 + pm25Image = null;
452 + }
453 + pm10ImageArray[i] = pm10Image;
454 + pm25ImageArray[i] = pm25Image;
455 + }
456 + routeAirCondition = (
457 + <table>
458 + <thead>
459 + <tr>
460 + <th>
461 + <br />
462 + 경로별 미세먼지 정보
463 + </th>
464 + </tr>
465 + </thead>
466 + <tbody>
467 + <tr>
468 + <td>
469 + <img src={start} width="50px" heigth="30px" /> <br />
470 + <h6>
471 + {this.state.departureTitle} <br /> 미세먼지 등급
472 + <br />
473 + {pm10ImageArray[this.state.routeInformation.length - 1]}
474 + <br />
475 + 미세먼지 지수 :{" "}
476 + {
477 + this.state.routeInformation[
478 + this.state.routeInformation.length - 1
479 + ]["airCondition"].pm10Value
480 + }
481 + <br />
482 + </h6>
483 + </td>
484 + {tempRouteAirCondition.map((listValue, index) => {
485 + if (index != tempRouteAirCondition.length - 1) {
486 + return (
487 + <td key={index}>
488 + <img src={next} width="40px" heigth="25px" />
489 + <h6>
490 + {listValue.instruction}
491 + <br />
492 + 소요시간 : {listValue.duration}
493 + <br />
494 + 미세먼지 등급
495 + <br />
496 + {pm10ImageArray[index]}
497 + <br />
498 + 미세먼지 지수 : <br />
499 + {listValue.airCondition.pm10Value}
500 + <br />
501 + </h6>
502 + </td>
503 + );
504 + } else {
505 + return (
506 + <td key={index}>
507 + <img src={finish} width="50px" heigth="30px" />
508 + <br />
509 + <h6>
510 + {listValue.instruction}
511 + <br />
512 + 소요시간 : {listValue.duration}
513 + <br />
514 + 미세먼지 등급
515 + <br />
516 + {pm10ImageArray[index]}
517 + <br />
518 + 미세먼지 지수 : {listValue.airCondition.pm10Value}
519 + <br />
520 + </h6>
521 + </td>
522 + );
523 + }
524 + })}
525 + </tr>
526 + </tbody>
527 + </table>
528 + );
529 + }
385 530
386 return ( 531 return (
387 <section id="home"> 532 <section id="home">
......