이전글 : https://small-stepping.tistory.com/603
중간에 절취선처럼 잘리는 건 캡처 문제다. 스크롤 캡처를 하니 저렇게 화면이 이상하게 나온다.
신기하게도 글을 작성하는 23.10.20 오후 9시 20분경 중기 예보 데이터가 이상하다.
앱 자체 에러가 아니라 기상청 api 들어가서 제대로 데이터 하나하나 수기로 넣어봐도 데이터가 공백으로 뜨는 상태.
내가 해결할 수 있는 문제도 아니었고 다음 주는 예비군이기에 어떻게든 이번 주 내로 작업을 끝내고 싶었다.
그래서 현 상태로 작성한다
++++ 23.10.24, 20:26: 예비군 가서도 안되길래 집와서 확인해보니 코드 넘겨줄때 살짝 꼬여있었다. 풀어주니 적상 작동
변경점 및 추가사항이다.
1. 주간 날씨 버튼 크기를 늘리고, 기상청 홈페이지로 접근하는 버튼을 상단부 날씨 정보탭(오늘 ~ 주간날씨 글씨 윗부분)으로 한정하였다.
- 주간 날씨 탭으로 넘어가려는데 자꾸 뒷화면이 눌려서 기상청 홈페이지를 너무 많이 들어가서 바꿨다.
- 참고하던 날씨 어플 또한 각 분야(날씨, 주간날씨, 레이더, 미세먼지 등..)에 따라 홈페이지 연결을 세분화 시켰고, 해당 박스 내에서만으로 한정지어서 배경을 눌렀을때 이동되게 하진 않았다.
2. 주간 날씨 activity에 사용되는 api만 따로 분리하여 작업시키게 두며 api 데이터를 연동시켰다.
- main activity에서는 6가지 api, week activity에서는 2가지 api를 연결시켰다.
- 이렇게 분할시키는게 좋은 방법인지는 모르겠다. 굳이 주간 날씨를 보지 않는 사람도 있을 것이라 생각되어 절약을 위해 분리시켰다.
- 다만 중기 예보는 3 ~ 10일자 데이터를 제공 하기 때문에 1 ~ 2일 후 데이터는 내가 다른 api에서 week activity로 끌어주어야 했다. (다시 api 호출하기엔 그 작업량이 아깝다고 생각이 되어서.)
3. 잘못 사용하고 있던 viewmodel을 뜯어 고쳤다
- 제대로 수정한건진 모르겠다. 분명 이전에 작업했던 프로젝트에선 livedata observe로 잘만 썼거늘 그 프로젝트가 올해 초였다고 다까먹고 이상한 방식으로 쓰고 있었다.
4. layout: Linear -> ConstraintLayout 변경
- 가져다 쓰고 있던 레이아웃은 Linear Layout 이었고 ConstraintLayout 사용시 성능면에서 효과적이라는 글이 자꾸 생각나서 뜯어고쳤다.
https://velog.io/@jaeyunn_15/Android-ConstraintLayout-RelativeLayout-LinearLayout
5. 거슬리던 색인 morning theme를 제거하였다.
- 따라서 남아있는 theme는 afternoon, evening, night뿐이다.
6. 1 ~ 2일 후 데이터 weekActivity에 넘겨주기.
API 통신때 어떻게 넘겨줄 수 없을까 싶었지만 lifecycle때문에 그건 불가능하다고 생각되었고, db를 쓰는게 아니라면 결국 mainActivity에서 putExtra로 넘겨주는게 제일이라 생각되었다. putExtra로 넘겨주자니 넘겨줄 데이터가 변수 한 두개가 아니라 리스트 단위였기에 문제가 발생했었다.
- Parcelable을 사용해 해결하였다.
getParcelableArrayListExtra를 사용했다.
이를 사용하기 위해선 data class에 다음과 같이 적용시켜줘야한다.
@Parcelable을 사용하기 위해선 gradle에 추가를 해줄 필요가 존재한다.
자세한 건 다음 링크를 참고하라
https://developer.android.com/kotlin/parcelize?hl=ko
7. gps 추가
- mainActivity에 gps를 추가하여 받아온 위경도를 통해 nx, ny와 추후 weekActivity에 사용되는 지역코드를 변환시켰다.
8. 기상청 위경도값 격자 nx, ny로 변경
https://gist.github.com/fronteer-kr/14d7f779d52a21ac2f16
해당 코드를 감사하게도 누가 각 언어로 올려두었다.
android 기반 java언어를 kotlin으로 바꾸어 사용했다.
internal class Coordinate {
var latitude: Double = 0.0
var longitude: Double = 0.0
var x: Double = 0.0
var y: Double = 0.0
}
class "본인의 클래스 이름" {
private val toGrid = 0
private val toGps = 1
internal fun getCoordinate(latitude: Double, longitude: Double): Coordinate {
return convertToGridGps(toGrid, latitude, longitude)
}
private fun convertToGridGps(mode: Int, latitude: Double, longitude: Double): Coordinate {
/*
re: 지구 반경(km)
grid: 격자 간격(km)
slat1: 투영 위도1(degree)
slat2: 투영 위도2(degree)
olon: 기준점 경도(degree)
olat: 기준점 위도(degree)
xo: 기준점 X 좌표(grid)
yo: 기준점 Y 좌표(grid)
*/
val RE = 6371.00877
val GRID = 5.0
val SLAT1 = 30.0
val SLAT2 = 60.0
val OLON = 126.0
val OLAT = 38.0
val xo = 43
val yo = 136
// LCC DFS 좌표변환 (code: "toGrid"(위경도 -> 좌표), "toGps"(좌표 -> 위경도))
val degrad = Math.PI / 180.0
val raddeg = 180.0 / Math.PI
val re = RE / GRID
val slat1 = SLAT1 * degrad
val slat2 = SLAT2 * degrad
val olon = OLON * degrad
val olat = OLAT * degrad
var sn = tan(Math.PI * 0.25 + slat2 * 0.5) / tan(Math.PI * 0.25 + slat1 * 0.5)
sn = ln(cos(slat1) / cos(slat2)) / ln(sn)
var sf = tan(Math.PI * 0.25 + slat1 * 0.5)
sf = sf.pow(sn) * cos(slat1) / sn
var ro = tan(Math.PI * 0.25 + olat * 0.5)
ro = re * sf / ro.pow(sn)
val rs = Coordinate()
rs.latitude = latitude
rs.longitude = longitude
when (mode) {
toGrid -> {
var ra = tan(Math.PI * 0.25 + latitude * degrad * 0.5)
ra = re * sf / ra.pow(sn)
var theta = longitude * degrad - olon
if (theta > Math.PI) theta -= 2.0 * Math.PI
if (theta < -Math.PI) theta += 2.0 * Math.PI
theta *= sn
rs.x = floor(ra * sin(theta) + xo + 0.5)
rs.y = floor(ro - ra * cos(theta) + yo + 0.5)
}
toGps -> {
val xn = latitude - xo
val yn = ro - longitude + yo
var ra = sqrt(xn * xn + yn * yn)
if (sn < 0.0) ra = -ra
var alat = (re * sf / ra).pow((1.0 / sn))
alat = 2.0 * atan(alat) - Math.PI * 0.5
var theta: Double
if (abs(xn) <= 0.0) theta = 0.0
else {
if (abs(yn) <= 0.0) {
theta = Math.PI * 0.5
if (xn < 0.0) theta = -theta
}
else theta = atan2(xn, yn)
}
val alon = theta / sn + olon
rs.latitude = alat * raddeg
rs.longitude = alon * raddeg
}
}
return rs
}
}
위 코드가 kotlin으로 변경한 코드이고, 사용할 땐 class 파일 하나를 만들어 "본인의 클래스 이름" 바꾼 뒤 저걸 다 넣는다.
이후, 다른 class에서 "본인의 클래스 이름"().getCoordinate(위도, 경도)를 넣으면 된다.
그러면 위와같이 사용할 수 있다.
9. 중기기온 지역코드 변경
문제는 이거였다. 예전에 했던 미세먼지처럼 측정소가 각지에 있는 것도 아니고 제각기에다가 중기기온과 중기육지기상은 또 지역코드가 다르고 촘촘하지도 않았다.
이 문제를 바로 검색해보니 나와 같은 고민을 한 사람이 있었다.
https://warguss.blogspot.com/2016/06/1.html
애석하게도 해결책은 없었다.
그래서 중기 기상예보는 표에 나와있는대로 하고, 중기육지기상의 경우 각 지역의 대표도시를 기준으로 중기예보를 보여주기로 했다.
그렇게 분류하니 다음과 같이 나오게 되었다.
중기기온은 주요 도시(특별시, 광역시...)와 강원 영서, 영동을 나누고
중기육지는 표에 나와있는 도시(하나하나 나눠보니 도시는 충청도, 경상도, 전라도, 강원도 등등 다 있긴했다..)와 주요도시를 추가하는데, 만약 해당하는 지역이 없다면, 대표도시의 지역코드를 따로 넣었다.
작동하는 코드는 다음과 같다.
나는 이걸 작동시키기 위해, 네이버 reversegeocoder API를 추가하였다. 네이버 API에서 골머리 썩힌 오류는 Auth 오류다.
분명 다른 곳에서 본 것처럼 header로 id와 key를 보내줬으나 auth오류가 발생했고, query로 넣어보니 잘되더라...
@GET("map-reversegeocode/v2/gc?")
suspend fun getReverseGeocode(
@Query("X-NCP-APIGW-API-KEY-ID") clientID: String,
@Query("X-NCP-APIGW-API-KEY") clientKey: String,
@Query("coords") coords: String,
@Query("orders") orders: String,
@Query("output") output: String
): ReverseGeocode.Root
하여튼, gps로 받아온 위경도를 api에 넣어 legalcode 기준으로 area1, area2를 받아왔다.
예를 들어 인천광역시(area1) 부평구,(area2) 전라북도 전주시, 강원도 양구군과 같은 방식으로 나오게 되는 것이다.
이를 위 setting 코드에 넣어주면 된다.
강원도의 경우 강원도 영서, 강원도 영동으로 나눠놨길래 영서 영동에 해당하는 지역을 넣어 두고 예외처리로 코드를 추가하였다. 강원도가 아닐경우는 그냥 리스트를 쭉 돌면서 area1이 "지역"을 포함하고 있으면 break 시켰다.
중기육지기상은 작은단위(도시)부터 돌고, 돌았는데도 midLandCode가 ""라면 큰단위(광역시, ㅇㅇ도) array를 돌게 만들어 어떻게든 code를 챙기게 만들었다.
이러면 우선 삐걱대는 부분도 많지만 얼추 그럴싸한 날씨 앱 완료다.
'개발 > AOS' 카테고리의 다른 글
Firebase - 구글 로그인 연동 / 이메일 회원가입 (0) | 2024.02.02 |
---|---|
UI 공부 (0) | 2024.01.16 |
날씨 앱 기록 02 (2) | 2023.10.13 |
날씨 앱 기록 01 (1) | 2023.10.06 |
kapt에서 KSP로 이전 (0) | 2023.10.05 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!