Last active
March 20, 2024 06:01
-
-
Save fronteer-kr/14d7f779d52a21ac2f16 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 소스출처 : http://www.kma.go.kr/weather/forecast/digital_forecast.jsp 내부에 있음 | |
// 기상청에서 이걸 왜 공식적으로 공개하지 않을까? | |
// | |
// (사용 예) | |
// var rs = dfs_xy_conv("toLL","60","127"); | |
// console.log(rs.lat, rs.lng); | |
// | |
<script language="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; | |
} | |
//--> | |
</script> |
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))
### result :
#(60, 127)
#(97, 74)
#(53, 38)
print(gridToMap(60, 127))
print(gridToMap(97, 74))
print(gridToMap(53, 38))
### result
# 37.579871128849334, 126.98935225645432
# 35.101148844565955, 129.02478725562108
# 33.500946412305076, 126.54663058817043
Unity3D
Return은 Dictionary<string, double> LatLngToXY = new Dictionary<string, double>(); 형태로 받으시면 됩니다.
// 위도 경도 좌표 변환
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 = 0.0; // 기준점 X좌표(GRID)
double YO = 0.0; // 기1준점 Y좌표(GRID)
Dictionary<string, double> dfs_xy_conf(string code, double v1, double v2)
{
double DEGRAD = System.Math.PI / 180.0;
double RADDEG = 180.0 / System.Math.PI;
double re = RE / GRID;
double slat1 = SLAT1 * DEGRAD;
double slat2 = SLAT2 * DEGRAD;
double olon = OLON * DEGRAD;
double olat = OLAT * DEGRAD;
double sn = System.Math.Tan((System.Math.PI * 0.25f + slat2 * 0.5f)) / System.Math.Tan(System.Math.PI * 0.25f + slat1 * 0.5f);
sn = System.Math.Log(System.Math.Cos(slat1) / System.Math.Cos(slat2)) / System.Math.Log(sn);
double sf = System.Math.Tan(System.Math.PI * 0.25f + slat1 * 0.5f);
sf = System.Math.Pow(sf, sn) * System.Math.Cos(slat1) / sn;
double ro = System.Math.Tan(System.Math.PI * 0.25f + olat * 0.5f);
ro = re * sf / System.Math.Pow(ro, sn);
Dictionary<string, double> rs = new Dictionary<string, double>();
double ra, theta;
if (code == "toXY")
{
rs["lat"] = v1;
rs["lng"] = v2;
ra = System.Math.Tan(System.Math.PI * 0.25f + (v1) * DEGRAD * 0.5f);
ra = re * sf / System.Math.Pow(ra, sn);
theta = v2 * DEGRAD - olon;
if (theta > System.Math.PI) theta -= 2.0f * System.Math.PI;
if (theta < -System.Math.PI) theta += 2.0f * System.Math.PI;
theta *= sn;
rs["x"] = System.Math.Floor(ra * System.Math.Sin(theta) + XO + 0.5f);
rs["y"] = System.Math.Floor(ro - ra * System.Math.Cos(theta) + YO + 0.5f);
}
else
{
rs["x"] = v1;
rs["y"] = v2;
double xn = v1 - XO;
double yn = ro - v2 + YO;
ra = System.Math.Sqrt(xn * xn + yn * yn);
if (sn < 0.0f) ra = -ra;
double alat = System.Math.Pow((re * sf / ra), (1.0f/ sn));
alat = 2.0f * System.Math.Atan(alat) - System.Math.PI * 0.5f;
if (System.Math.Abs(xn) <= 0.0)
{
theta = 0.0f;
}
else
{
if (System.Math.Abs(yn) <= 0.0)
{
theta = System.Math.PI * 0.5f;
if (xn < 0.0f) theta = -theta;
}
else theta = System.Math.Atan2(xn, yn);
}
double alon = theta / sn + olon;
rs["lat"] = alat * RADDEG;
rs["lng"] = alon * RADDEG;
}
return rs;
}
// C#입니다.
// 없어서 추가해봅니다.
// tuple 이 Dictionary<string, double>로 가능
VilageFcstInfoSvc vfis = new VilageFcstInfoSvc();
vfis.GetData();
class WeatherApiCommon
{
/// <summary>
/// 대기오염 정보조회 서비스
/// 1 getMsrstnAcctoRltmMesureDnsty 측정소별 실시간 측정정보 조회
/// 2 getUnityAirEnvrnIdexSnstiveAboveMsrstnList 통합대기환경지수 나쁨 이상 측정소 목록조회
/// 3 getCtprvnRltmMesureDnsty 시도별 실시간 측정정보 조회
/// 4 getMinuDustFrcstDspth 대기질 예보통보 조회
/// 5 getCtprvnMesureLIst 시도별 실시간 평균정보 조회
/// 6 getCtprvnMesureSidoLIst 시군구별 실시간 평균정보 조회
/// </summary>
/// <returns></returns>
internal static string GetArpltnInforInqireSvcUrl( byte idx )
{
return @"http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/" + GetArpltnInforInqireSvcNm( idx );
}
/// <summary>
/// 오퍼레이션명(영문)
/// </summary>
/// <param name="idx">default -> 3</param>
/// <returns></returns>
private static string GetArpltnInforInqireSvcNm( byte idx )
{
switch( idx )
{
case 1:
return "getMsrstnAcctoRltmMesureDnsty";
case 2:
return "getUnityAirEnvrnIdexSnstiveAboveMsrstnList";
case 3: default:
return "getCtprvnRltmMesureDnsty";
case 4:
return "getMinuDustFrcstDspth";
case 5:
return "getCtprvnMesureLIst";
case 6:
return "getCtprvnMesureSidoLIst";
}
}
/// <summary>
/// 동네예보 조회서비스
/// 1 getUltraSrtNcst 초단가실황조회
/// 2 getUltraSrtFcst 초단가예보조회
/// 3 getVilageFcst 동네예보조회
/// 4 getFcstVersion 예보버전조회
/// </summary>
/// <returns></returns>
internal static string GetVilageFcstInfoSvcUrl( byte idx )
{
return @"http://apis.data.go.kr/1360000/VilageFcstInfoService/" + GetVilageFcstInfoSvcNm( idx );
}
/// <summary>
/// 오퍼레이션명(영문)
/// </summary>
/// <param name="idx">default -> 3</param>
/// <returns></returns>
private static string GetVilageFcstInfoSvcNm( byte idx )
{
switch( idx )
{
case 1:
return "getUltraSrtNcst";
case 2:
return "getUltraSrtFcst";
case 3: default:
return "getVilageFcst";
case 4:
return "getFcstVersion";
}
}
/// <summary>
/// 동네예보 조회서비스
/// 1 getUltraSrtNcst 초단가실황조회
/// 2 getUltraSrtFcst 초단가예보조회
/// 3 getVilageFcst 동네예보조회
/// 4 getFcstVersion 예보버전조회
/// </summary>
/// <returns></returns>
internal static string GetBaseTime( byte idx )
{
switch( idx )
{
case 1:
{
if( DateTime.Now < DateTime.Now.AddMinutes( 40 - DateTime.Now.Minute ) )
return DateTime.Now.AddHours(-1).ToString( "HH00" );
else
return DateTime.Now.ToString( "HH00" );
}
case 2:
{
if( DateTime.Now < DateTime.Now.AddMinutes( 45 - DateTime.Now.Minute ) )
return DateTime.Now.AddHours(-1).ToString( "HH00" );
else
return DateTime.Now.ToString( "HH00" );
}
case 3: case 4: default:
{
int hour = DateTime.Now.Hour;
if (DateTime.Now < DateTime.Now.AddMinutes(10 - DateTime.Now.Minute))
{
hour = DateTime.Now.AddHours(-1).Hour;
}
if (hour - 2 == 0)
{
hour = 24;
}
int select = (hour - 2) / 3;
return GetBaseTimeSelect( select );
}
}
}
private static string GetBaseTimeSelect( int idx )
{
switch( idx )
{
case 0: default:
return "0200";
case 1:
return "0500";
case 2:
return "0800";
case 3:
return "1100";
case 4:
return "1400";
case 5:
return "1700";
case 6:
return "2000";
case 7:
return "2300";
}
}
/// <summary>
/// 인증키
/// 개발인증키만 받음상태 운영인증키는 현재 발급안한상태
/// </summary>
/// <param name="bDev">개발인증키여부</param>
/// <returns></returns>
internal static string GetAuthenticationkey( bool bDev = true )
{
if( bDev )
{
return "Authenticationkey";
}
else
{
return "Authenticationkey";
}
}
}
class VilageFcstInfoSvc : WeatherApiCommon
{
private string GetUrl( bool bjson = true )
{
byte baseidx = 3;
var tuplugrid = GetWeatherDigitalForecast( "toGRID", 37.579871128849334, 126.98935225645432 );
//var tuplugrid2 = GetWeatherDigitalForecast( "toGPS", 60, 127 );
HttpClient client = new HttpClient();
string basedate = DateTime.Now.ToString( "yyyyMMdd" );
string basetime = WeatherApiCommon.GetBaseTime( baseidx );
string serviceKey = GetAuthenticationkey();
string xmljson = bjson ? "JSON" : "XML";
string url = WeatherApiCommon.GetVilageFcstInfoSvcUrl( baseidx )+ $"?ServiceKey={serviceKey}&numOfRows=900&pageNo=1";
return url + $"&dataType={xmljson}&base_date={basedate}&base_time={basetime}&nx={tuplugrid.x}&ny={tuplugrid.y}";
}
public void GetData( bool bjson = false )
{
if( bjson )
GetDataJson();
else
GetDataXml();
}
/// <summary>
/// 동네예보조회
/// </summary>
public DataTable GetDataXml()
{
string url = GetUrl(false);
var request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "GET";
using( HttpWebResponse response = request.GetResponse() as HttpWebResponse )
{
var stream = response.GetResponseStream();
DataSet dts = new DataSet();
dts.ReadXml( stream, XmlReadMode.InferSchema);
DataTable dtt = dts.Tables["item"];
return dtt;
}
}
/// <summary>
/// 동네예보조회
/// </summary>
public JToken GetDataJson()
{
string url = GetUrl();
var request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "GET";
using( HttpWebResponse response = request.GetResponse() as HttpWebResponse )
{
var reader = new StreamReader(response.GetResponseStream());
string results = reader.ReadToEnd();
JObject jObject = JObject.Parse( results );
JToken jList = jObject["response"]["body"]["items"]["item"];
return jList;
}
}
/// <summary>
/// http://www.kma.go.kr/weather/forecast/digital_forecast.jsp
/// LCC DFS 좌표변환
/// toGRID : 위경도->좌표, latX:위도, lngY:경도
/// toGPS : 좌표->위경도, latX:x, lngY:y
/// </summary>
/// <param name="code"></param>
/// <param name="latX"></param>
/// <param name="lngY"></param>
/// <returns></returns>
/// private Dictionary<string, double> GetWeatherDigitalForecast( string code, double latX, double lngY )
private ( double lat, double lng, double x, double y ) GetWeatherDigitalForecast( string code, double latX, double lngY )
{
double ra;
double theta;
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)
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.25f + slat2 * 0.5f ) / Math.Tan( Math.PI * 0.25f + slat1 * 0.5f );
sn = Math.Log( Math.Cos(slat1) / Math.Cos(slat2) ) / Math.Log(sn);
double sf = Math.Tan( Math.PI * 0.25f + slat1 * 0.5f );
sf = Math.Pow( sf, sn ) * Math.Cos(slat1) / sn;
double ro = Math.Tan( Math.PI * 0.25f + olat * 0.5f );
ro = re * sf / Math.Pow(ro, sn);
if ( "toGRID".Equals( code ) )
{
ra = Math.Tan(Math.PI * 0.25f + (latX) * DEGRAD * 0.5f);
ra = re * sf / Math.Pow(ra, sn);
theta = lngY * DEGRAD - olon;
if( theta > Math.PI )
theta -= 2.0f * Math.PI;
if( theta < -Math.PI)
theta += 2.0f * Math.PI;
theta *= sn;
return ( latX, lngY, Math.Floor(ra * Math.Sin(theta) + XO + 0.5f), Math.Floor(ro - ra * Math.Cos(theta) + YO + 0.5f) );
}
else
{
double xn = latX - XO;
double yn = ro - lngY + YO;
ra = Math.Sqrt(xn * xn + yn * yn);
if( sn < 0.0f )
ra = -ra;
double alat = Math.Pow((re * sf / ra), (1.0f/ sn));
alat = 2.0f * Math.Atan(alat) - Math.PI * 0.5f;
if( Math.Abs(xn) <= 0.0 )
theta = 0.0f;
else
{
if( Math.Abs(yn) <= 0.0 )
{
theta = Math.PI * 0.5f;
if (xn < 0.0f)
theta = -theta;
}
else
theta = Math.Atan2(xn, yn);
}
double alon = theta / sn + olon;
return ( alat * RADDEG, alon * RADDEG, latX, lngY );
}
}
}
flutter 용 공유해 볼께요. static 으로 만들었는데. 상황에 따라 변경해서 써야 할 수도 있어요.
var gridToGpsData = ConvGridGps.gridToGPS(60, 127);
var gpsToGridData = ConvGridGps.gpsToGRID(37.579871128849334, 126.98935225645432);
print(gridToGpsData);
print(gpsToGridData);
결과 :
I/flutter (27910): {x: 60, y: 127, lat: 37.61574148576467, lng: 126.98991183668376}
I/flutter (27910): {lat: 37.579871128849334, lng: 126.98935225645432, x: 60, y: 127}
import 'dart:math' as Math;
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;
}
}
// PHP 버전입니다.
// 위 [khjde1207]님의 flutter 버전을 참고했습니다.
// 결과
$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 )
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; } }
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
스위프트 4 용.
//////////////// GPS -> GRID ///////////////
func convertGRID_GPS(mode: Int, lat_X: Double, lng_Y: Double) -> LatXLngY {
let RE = 6371.00877 // 지구 반경(km)
let GRID = 5.0 // 격자 간격(km)
let SLAT1 = 30.0 // 투영 위도1(degree)
let SLAT2 = 60.0 // 투영 위도2(degree)
let OLON = 126.0 // 기준점 경도(degree)
let OLAT = 38.0 // 기준점 위도(degree)
let XO:Double = 43 // 기준점 X좌표(GRID)
let YO:Double = 136 // 기1준점 Y좌표(GRID)