기상청 날씨정보 API 격자/위경도 변환
2022.05.24
https://gist.github.com/fronteer-kr/14d7f779d52a21ac2f16 를 참고하여 php버전을 만듬.
백업용으로 이곳에 기록해둠.
PHP
소스코드
class ConvGridGps {
const RE = 6371.00877; // 지구 반경(km)
const GRID = 5.0; // 격자 간격(km)
const SLAT1 = 30.0; // 투영 위도1(degree)
const SLAT2 = 60.0; // 투영 위도2(degree)
const OLON = 126.0; // 기준점 경도(degree)
const OLAT = 38.0; // 기준점 위도(degree)
const XO = 43; // 기준점 X좌표(GRID)
const YO = 136; // 기1준점 Y좌표(GRID)
const DEGRAD = M_PI / 180.0;
const RADDEG = 180.0 / M_PI;
const re = self::RE / self::GRID;
const slat1 = self::SLAT1 * self::DEGRAD;
const slat2 = self::SLAT2 * self::DEGRAD;
const olon = self::OLON * self::DEGRAD;
const olat = self::OLAT * self::DEGRAD;
function sn(){
$snTmp = tan(M_PI * 0.25 + self::slat2 * 0.5) / tan(M_PI * 0.25 + self::slat1 * 0.5);
return log(cos(self::slat1) / cos(self::slat2)) / log($snTmp);
}
function sf(){
$sfTmp = tan(M_PI * 0.25 + self::slat1 * 0.5);
return pow($sfTmp, $this->sn()) * cos(self::slat1) / $this->sn();
}
function ro(){
$roTmp = tan(M_PI * 0.25 + self::olat * 0.5);
return self::re * $this->sf() / pow($roTmp, $this->sn());
}
function gridToGPS($v1, $v2) {
$rs['x'] = $v1;
$rs['y'] = $v2;
$xn = (int)($v1 - self::XO);
$yn = (int)($this->ro() - $v2 + self::YO);
$ra = sqrt($xn * $xn + $yn * $yn);
if ($this->sn() < 0.0) $ra = -$ra;
$alat = pow((self::re * $this->sf() / $ra), (1.0 / $this->sn()));
$alat = 2.0 * atan($alat) - M_PI * 0.5;
if (abs($xn) <= 0.0) {
$theta = 0.0;
} else {
if (abs($yn) <= 0.0) {
$theta = M_PI * 0.5;
if ($xn < 0.0) $theta = -$theta;
} else
$theta = atan2($xn, $yn);
}
$alon = $theta / $this->sn() + self::olon;
$rs['lat'] = $alat * self::RADDEG;
$rs['lng'] = $alon * self::RADDEG;
return $rs;
}
function gpsToGRID($v1, $v2) {
$rs['lat'] = $v1;
$rs['lng'] = $v2;
$ra = tan(M_PI * 0.25 + ($v1) * self::DEGRAD * 0.5);
$ra = self::re * $this->sf() / pow($ra, $this->sn());
$theta = $v2 * self::DEGRAD - self::olon;
if ($theta > M_PI) $theta -= 2.0 * M_PI;
if ($theta < -M_PI) $theta += 2.0 * M_PI;
$theta *= $this->sn();
$rs['x'] = floor(($ra * sin($theta) + self::XO + 0.5));
$rs['y'] = floor(($this->ro() - $ra * cos($theta) + self::YO + 0.5));
return $rs;
}
}
$ConvGridGps = new ConvGridGps();
$gridToGpsData = $ConvGridGps->gridToGPS(60,127);
$gpsToGridData = $ConvGridGps->gpsToGRID(37.579871128849334, 126.98935225645432);
print_r($gridToGpsData);
print_r($gpsToGridData);
실행결과
Array ( [x] => 60 [y] => 127 [lat] => 37.615741485765 [lng] => 126.98991183668 )
Array ( [lat] => 37.579871128849 [lng] => 126.98935225645 [x] => 60 [y] => 127 )
Flutter
import 'dart:math' as Math;
void main() {
var gridToGpsData = ConvGridGps.gridToGPS(60, 127);
var gpsToGridData = ConvGridGps.gpsToGRID(37.579871128849334, 126.98935225645432);
print(gridToGpsData);
print(gpsToGridData);
}
class ConvGridGps {
static const double RE = 6371.00877; // 지구 반경(km)
static const double GRID = 5.0; // 격자 간격(km)
static const double SLAT1 = 30.0; // 투영 위도1(degree)
static const double SLAT2 = 60.0; // 투영 위도2(degree)
static const double OLON = 126.0; // 기준점 경도(degree)
static const double OLAT = 38.0; // 기준점 위도(degree)
static const double XO = 43; // 기준점 X좌표(GRID)
static const double YO = 136; // 기1준점 Y좌표(GRID)
static const double DEGRAD = Math.pi / 180.0;
static const double RADDEG = 180.0 / Math.pi;
static double get re => RE / GRID;
static double get slat1 => SLAT1 * DEGRAD;
static double get slat2 => SLAT2 * DEGRAD;
static double get olon => OLON * DEGRAD;
static double get olat => OLAT * DEGRAD;
static double get snTmp =>
Math.tan(Math.pi * 0.25 + slat2 * 0.5) /
Math.tan(Math.pi * 0.25 + slat1 * 0.5);
static double get sn =>
Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(snTmp);
static double get sfTmp => Math.tan(Math.pi * 0.25 + slat1 * 0.5);
static double get sf => Math.pow(sfTmp, sn) * Math.cos(slat1) / sn;
static double get roTmp => Math.tan(Math.pi * 0.25 + olat * 0.5);
static double get ro => re * sf / Math.pow(roTmp, sn);
static gridToGPS(int v1, int v2) {
var rs = {};
double theta;
rs['x'] = v1;
rs['y'] = v2;
int xn = (v1 - XO).toInt();
int yn = (ro - v2 + YO).toInt();
var ra = Math.sqrt(xn * xn + yn * yn);
if (sn < 0.0) ra = -ra;
var alat = Math.pow((re * sf / ra), (1.0 / sn));
alat = 2.0 * Math.atan(alat) - Math.pi * 0.5;
if (xn.abs() <= 0.0) {
theta = 0.0;
} else {
if (yn.abs() <= 0.0) {
theta = Math.pi * 0.5;
if (xn < 0.0) theta = -theta;
} else
theta = Math.atan2(xn, yn);
}
var alon = theta / sn + olon;
rs['lat'] = alat * RADDEG;
rs['lng'] = alon * RADDEG;
return rs;
}
static gpsToGRID(double v1, double v2) {
var rs = {};
double theta;
rs['lat'] = v1;
rs['lng'] = v2;
var ra = Math.tan(Math.pi * 0.25 + (v1) * DEGRAD * 0.5);
ra = re * sf / Math.pow(ra, sn);
theta = v2 * DEGRAD - olon;
if (theta > Math.pi) theta -= 2.0 * Math.pi;
if (theta < -Math.pi) theta += 2.0 * Math.pi;
theta *= sn;
rs['x'] = (ra * Math.sin(theta) + XO + 0.5).floor();
rs['y'] = (ro - ra * Math.cos(theta) + YO + 0.5).floor();
return rs;
}
}
실행결과
Flutter Web Bootstrap: Programmatic
{x: 60, y: 127, lat: 37.61574148576467, lng: 126.98991183668376}
{lat: 37.579871128849334, lng: 126.98935225645432, x: 60, y: 127}
Python
소스코드
import math
NX = 149 ## X축 격자점 수
NY = 253 ## Y축 격자점 수
Re = 6371.00877 ## 지도반경
grid = 5.0 ## 격자간격 (km)
slat1 = 30.0 ## 표준위도 1
slat2 = 60.0 ## 표준위도 2
olon = 126.0 ## 기준점 경도
olat = 38.0 ## 기준점 위도
xo = 210 / grid ## 기준점 X좌표
yo = 675 / grid ## 기준점 Y좌표
first = 0
if first == 0 :
PI = math.asin(1.0) * 2.0
DEGRAD = PI/ 180.0
RADDEG = 180.0 / PI
re = Re / grid
slat1 = slat1 * DEGRAD
slat2 = slat2 * DEGRAD
olon = olon * DEGRAD
olat = olat * DEGRAD
sn = math.tan(PI * 0.25 + slat2 * 0.5) / math.tan(PI * 0.25 + slat1 * 0.5)
sn = math.log(math.cos(slat1) / math.cos(slat2)) / math.log(sn)
sf = math.tan(PI * 0.25 + slat1 * 0.5)
sf = math.pow(sf, sn) * math.cos(slat1) / sn
ro = math.tan(PI * 0.25 + olat * 0.5)
ro = re * sf / math.pow(ro, sn)
first = 1
def mapToGrid(lat, lon, code = 0 ):
ra = math.tan(PI * 0.25 + lat * DEGRAD * 0.5)
ra = re * sf / pow(ra, sn)
theta = lon * DEGRAD - olon
if theta > PI :
theta -= 2.0 * PI
if theta < -PI :
theta += 2.0 * PI
theta *= sn
x = (ra * math.sin(theta)) + xo
y = (ro - ra * math.cos(theta)) + yo
x = int(x + 1.5)
y = int(y + 1.5)
return x, y
def gridToMap(x, y, code = 1):
x = x - 1
y = y - 1
xn = x - xo
yn = ro - y + yo
ra = math.sqrt(xn * xn + yn * yn)
if sn < 0.0 :
ra = -ra
alat = math.pow((re * sf / ra), (1.0 / sn))
alat = 2.0 * math.atan(alat) - PI * 0.5
if math.fabs(xn) <= 0.0 :
theta = 0.0
else :
if math.fabs(yn) <= 0.0 :
theta = PI * 0.5
if xn < 0.0 :
theta = -theta
else :
theta = math.atan2(xn, yn)
alon = theta / sn + olon
lat = alat * RADDEG
lon = alon * RADDEG
return lat, lon
print(mapToGrid(37.579871128849334, 126.98935225645432))
print(mapToGrid(35.101148844565955, 129.02478725562108))
print(mapToGrid(33.500946412305076, 126.54663058817043))
print(gridToMap(60, 127))
print(gridToMap(97, 74))
print(gridToMap(53, 38))
실행결과
### result :
#(60, 127)
#(97, 74)
#(53, 38)
### result
# 37.579871128849334, 126.98935225645432
# 35.101148844565955, 129.02478725562108
# 33.500946412305076, 126.54663058817043
Javascript
소스코드
// LCC DFS 좌표변환을 위한 기초 자료
//
var RE = 6371.00877; // 지구 반경(km)
var GRID = 5.0; // 격자 간격(km)
var SLAT1 = 30.0; // 투영 위도1(degree)
var SLAT2 = 60.0; // 투영 위도2(degree)
var OLON = 126.0; // 기준점 경도(degree)
var OLAT = 38.0; // 기준점 위도(degree)
var XO = 43; // 기준점 X좌표(GRID)
var YO = 136; // 기1준점 Y좌표(GRID)
//
// LCC DFS 좌표변환 ( code : "toXY"(위경도->좌표, v1:위도, v2:경도), "toLL"(좌표->위경도,v1:x, v2:y) )
//
function dfs_xy_conv(code, v1, v2) {
var DEGRAD = Math.PI / 180.0;
var RADDEG = 180.0 / Math.PI;
var re = RE / GRID;
var slat1 = SLAT1 * DEGRAD;
var slat2 = SLAT2 * DEGRAD;
var olon = OLON * DEGRAD;
var olat = OLAT * DEGRAD;
var sn = Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5);
sn = Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(sn);
var sf = Math.tan(Math.PI * 0.25 + slat1 * 0.5);
sf = Math.pow(sf, sn) * Math.cos(slat1) / sn;
var ro = Math.tan(Math.PI * 0.25 + olat * 0.5);
ro = re * sf / Math.pow(ro, sn);
var rs = {};
if (code == "toXY") {
rs['lat'] = v1;
rs['lng'] = v2;
var ra = Math.tan(Math.PI * 0.25 + (v1) * DEGRAD * 0.5);
ra = re * sf / Math.pow(ra, sn);
var theta = v2 * DEGRAD - olon;
if (theta > Math.PI) theta -= 2.0 * Math.PI;
if (theta < -Math.PI) theta += 2.0 * Math.PI;
theta *= sn;
rs['x'] = Math.floor(ra * Math.sin(theta) + XO + 0.5);
rs['y'] = Math.floor(ro - ra * Math.cos(theta) + YO + 0.5);
}
else {
rs['x'] = v1;
rs['y'] = v2;
var xn = v1 - XO;
var yn = ro - v2 + YO;
ra = Math.sqrt(xn * xn + yn * yn);
if (sn < 0.0) - ra;
var alat = Math.pow((re * sf / ra), (1.0 / sn));
alat = 2.0 * Math.atan(alat) - Math.PI * 0.5;
if (Math.abs(xn) <= 0.0) {
theta = 0.0;
}
else {
if (Math.abs(yn) <= 0.0) {
theta = Math.PI * 0.5;
if (xn < 0.0) - theta;
}
else theta = Math.atan2(xn, yn);
}
var alon = theta / sn + olon;
rs['lat'] = alat * RADDEG;
rs['lng'] = alon * RADDEG;
}
return rs;
}
// 실행
var rs = dfs_xy_conv("toLL","60","127");
console.log(rs.lat, rs.lng);
Android
소스코드
public class MainActivity extends AppCompatActivity {
public static int TO_GRID = 0;
public static int TO_GPS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LatXLngY tmp = convertGRID_GPS(TO_GRID, 37.579871128849334, 126.98935225645432);
LatXLngY tmp2 = convertGRID_GPS(TO_GRID, 35.101148844565955, 129.02478725562108);
LatXLngY tmp3 = convertGRID_GPS(TO_GRID, 33.500946412305076, 126.54663058817043);
Log.e(">>", "x = " + tmp.x + ", y = " + tmp.y);
Log.e(">>", "x = " + tmp2.x + ", y = " + tmp2.y);
Log.e(">>", "x = " + tmp3.x + ", y = " + tmp3.y);
}
private LatXLngY convertGRID_GPS(int mode, double lat_X, double lng_Y) {
double RE = 6371.00877; // 지구 반경(km)
double GRID = 5.0; // 격자 간격(km)
double SLAT1 = 30.0; // 투영 위도1(degree)
double SLAT2 = 60.0; // 투영 위도2(degree)
double OLON = 126.0; // 기준점 경도(degree)
double OLAT = 38.0; // 기준점 위도(degree)
double XO = 43; // 기준점 X좌표(GRID)
double YO = 136; // 기1준점 Y좌표(GRID)
//
// LCC DFS 좌표변환 ( code : "TO_GRID"(위경도->좌표, lat_X:위도, lng_Y:경도), "TO_GPS"(좌표->위경도, lat_X:x, lng_Y:y) )
//
double DEGRAD = Math.PI / 180.0;
double RADDEG = 180.0 / Math.PI;
double re = RE / GRID;
double slat1 = SLAT1 * DEGRAD;
double slat2 = SLAT2 * DEGRAD;
double olon = OLON * DEGRAD;
double olat = OLAT * DEGRAD;
double sn = Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5);
sn = Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(sn);
double sf = Math.tan(Math.PI * 0.25 + slat1 * 0.5);
sf = Math.pow(sf, sn) * Math.cos(slat1) / sn;
double ro = Math.tan(Math.PI * 0.25 + olat * 0.5);
ro = re * sf / Math.pow(ro, sn);
LatXLngY rs = new LatXLngY();
if (mode == TO_GRID) {
rs.lat = lat_X;
rs.lng = lng_Y;
double ra = Math.tan(Math.PI * 0.25 + (lat_X) * DEGRAD * 0.5);
ra = re * sf / Math.pow(ra, sn);
double theta = lng_Y * DEGRAD - olon;
if (theta > Math.PI) theta -= 2.0 * Math.PI;
if (theta < -Math.PI) theta += 2.0 * Math.PI;
theta *= sn;
rs.x = Math.floor(ra * Math.sin(theta) + XO + 0.5);
rs.y = Math.floor(ro - ra * Math.cos(theta) + YO + 0.5);
} else {
rs.x = lat_X;
rs.y = lng_Y;
double xn = lat_X - XO;
double yn = ro - lng_Y + YO;
double ra = Math.sqrt(xn * xn + yn * yn);
if (sn < 0.0) {
ra = -ra;
}
double alat = Math.pow((re * sf / ra), (1.0 / sn));
alat = 2.0 * Math.atan(alat) - Math.PI * 0.5;
double theta = 0.0;
if (Math.abs(xn) <= 0.0) {
theta = 0.0;
} else {
if (Math.abs(yn) <= 0.0) {
theta = Math.PI * 0.5;
if (xn < 0.0) {
theta = -theta;
}
} else theta = Math.atan2(xn, yn);
}
double alon = theta / sn + olon;
rs.lat = alat * RADDEG;
rs.lng = alon * RADDEG;
}
return rs;
}
class LatXLngY {
public double lat;
public double lng;
public double x;
public double y;
}
}
나머지 언어는 https://gist.github.com/fronteer-kr/14d7f779d52a21ac2f16 를 참고