├── README.md └── GeoConverter.py /README.md: -------------------------------------------------------------------------------- 1 | Python Geo Converter 2 | === 3 | 4 | 파이썬으로 개발 도중 수집한 각종 좌표계를 자유자재로 변환할 일이 있었습니다. 그러나, 다음 API의 경우 일일 3만회의 제한이 5 | 있어서 빅데이터 처리에 굉장히 제약이 많았습니다. 그러던 차에 안드로이드펍에서 좋은 소스를 찾아서 Python으로 변환하였고 6 | 다시 공유하게 되었습니다. 7 | 8 | 소스 하단에 `if __name__ == "__main__"` : 이하로 내용을 적어두었습니다. 9 | 모듈로 사용하고자 할때는 10 | 11 | ```python 12 | import GeoConverter 13 | 14 | pt = GeoConverter.GeoPoint(x, y) 15 | output = GeoConverter.convert(GeoConverter.TM, GeoConverter.GEO, pt) 16 | ``` 17 | 18 | 이런식으로 사용하시면 됩니다 :) 19 | 20 | 21 | 22 | 23 | 출처 : http://www.androidpub.com/1043970 24 | 25 | 26 | **원본소스 상단 :)** 27 | 28 | ``` 29 | /** 30 | * @author aquilegia 31 | * 32 | * The code based on hyosang(http://hyosang.kr/tc/96) and aero's blog ((http://aero.sarang.net/map/analysis.html) 33 | * License: LGPL : http://www.gnu.org/copyleft/lesser.html 34 | */ 35 | ``` 36 | -------------------------------------------------------------------------------- /GeoConverter.py: -------------------------------------------------------------------------------- 1 | """ 2 | porting from Java Source 3 | by wani (me@wani.kr) 4 | 5 | origin source : 6 | from http://www.androidpub.com/1043970 7 | """ 8 | 9 | import math 10 | 11 | class GeoPoint : 12 | def __init__(self, x=0, y=0, z = 0) : 13 | self.x = x 14 | self.y = y 15 | self.z = z 16 | 17 | def getX(self) : 18 | return self.x 19 | 20 | def getY(self) : 21 | return self.y 22 | 23 | def getZ(self) : 24 | return self.z 25 | def setX(self, x) : 26 | self.x = x 27 | def setY(self, y) : 28 | self.y = y 29 | def setZ(self, z) : 30 | self.z = z 31 | 32 | def degree2radian( degree ) : 33 | return degree * math.pi / 180.0 34 | 35 | def radian2degree( radian ) : 36 | return radian * 180.0 / math.pi 37 | 38 | def e0fn( x ) : 39 | return 1.0 - 0.25 * x * (1.0 + x / 16.0 * (3.0 + 1.25 * x)) 40 | 41 | def e1fn( x ) : 42 | return 0.375 * x * (1.0 + 0.25 * x * (1.0 + 0.46875 * x)) 43 | 44 | def e2fn( x ) : 45 | return 0.05859375 * x * x * (1.0 + 0.75 * x) 46 | 47 | def e3fn( x ) : 48 | return x * x * x * (35.0 / 3072.0) 49 | 50 | def mlfn( e0, e1, e2, e3, phi) : 51 | return e0 * phi - e1 * math.sin(2.0 * phi) + e2 * math.sin(4.0 * phi) - e3 * math.sin(6.0 * phi) 52 | 53 | def asinz( value ) : 54 | if value > 0 : 55 | value = min(1, value) 56 | else : 57 | value = max(-1, value) 58 | return math.asin(value) 59 | 60 | GEO = 0 61 | KATEC = 1 62 | TM = 2 63 | GRS80 = 3 64 | 65 | EPSLN = 0.0000000001 66 | 67 | m_arMajor = [6378137.0, 6377397.155, 6377397.155] 68 | m_arMinor = [6356752.3142, 6356078.9633422494, 6356078.9633422494] 69 | 70 | m_arScaleFactor = [1, 0.9996, 1.0, 1.0] # KATEC -> 0.9999? 71 | m_arLonCenter = [0.0, 2.22529479629277, 2.21661859489671] 72 | m_arLatCenter = [0.0, 0.663225115757845, 0.663225115757845] 73 | m_arFalseNorthing = [0.0, 600000.0, 500000.0] 74 | m_arFalseEasting = [0.0, 400000.0, 200000.0] 75 | 76 | datum_params = [-146.43, 507.89, 681.46] 77 | 78 | m_Ind = [0,0,0] 79 | m_Es = [0,0,0] 80 | m_Esp = [0,0,0] 81 | src_m = [0,0,0] 82 | dst_m = [0,0,0] 83 | 84 | tmp = m_arMinor[GEO] / m_arMajor[GEO] 85 | m_Es[GEO] = 1.0 - tmp * tmp 86 | m_Esp[GEO] = m_Es[GEO] / (1.0 - m_Es[GEO]) 87 | 88 | if (m_Es[GEO] < 0.00001) : 89 | m_Ind[GEO] = 1.0 90 | else : 91 | m_Ind[GEO] = 0.0 92 | 93 | 94 | tmp = m_arMinor[KATEC] / m_arMajor[KATEC] 95 | m_Es[KATEC] = 1.0 - tmp * tmp 96 | m_Esp[KATEC] = m_Es[KATEC] / (1.0 - m_Es[KATEC]) 97 | 98 | if (m_Es[KATEC] < 0.00001) : 99 | m_Ind[KATEC] = 1.0 100 | else : 101 | m_Ind[KATEC] = 0.0 102 | 103 | 104 | tmp = m_arMinor[TM] / m_arMajor[TM] 105 | m_Es[TM] = 1.0 - tmp * tmp 106 | m_Esp[TM] = m_Es[TM] / (1.0 - m_Es[TM]) 107 | 108 | if (m_Es[TM] < 0.00001) : 109 | m_Ind[TM] = 1.0 110 | else : 111 | m_Ind[TM] = 0.0 112 | 113 | 114 | src_m[GEO] = m_arMajor[GEO] * mlfn(e0fn(m_Es[GEO]), e1fn(m_Es[GEO]), e2fn(m_Es[GEO]), e3fn(m_Es[GEO]), m_arLatCenter[GEO]) 115 | dst_m[GEO] = m_arMajor[GEO] * mlfn(e0fn(m_Es[GEO]), e1fn(m_Es[GEO]), e2fn(m_Es[GEO]), e3fn(m_Es[GEO]), m_arLatCenter[GEO]) 116 | src_m[KATEC] = m_arMajor[KATEC] * mlfn(e0fn(m_Es[KATEC]), e1fn(m_Es[KATEC]), e2fn(m_Es[KATEC]), e3fn(m_Es[KATEC]), m_arLatCenter[KATEC]) 117 | dst_m[KATEC] = m_arMajor[KATEC] * mlfn(e0fn(m_Es[KATEC]), e1fn(m_Es[KATEC]), e2fn(m_Es[KATEC]), e3fn(m_Es[KATEC]), m_arLatCenter[KATEC]) 118 | src_m[TM] = m_arMajor[TM] * mlfn(e0fn(m_Es[TM]), e1fn(m_Es[TM]), e2fn(m_Es[TM]), e3fn(m_Es[TM]), m_arLatCenter[TM]) 119 | dst_m[TM] = m_arMajor[TM] * mlfn(e0fn(m_Es[TM]), e1fn(m_Es[TM]), e2fn(m_Es[TM]), e3fn(m_Es[TM]), m_arLatCenter[TM]) 120 | 121 | def convert( srctype, dsttype, in_pt) : 122 | tmpPt = GeoPoint() 123 | out_pt = GeoPoint() 124 | 125 | if srctype == GEO : 126 | tmpPt.setX( degree2radian(in_pt.getX()) ) 127 | tmpPt.setY( degree2radian(in_pt.getY()) ) 128 | 129 | else : 130 | tm2geo(srctype, in_pt, tmpPt) 131 | 132 | if (dsttype == GEO) : 133 | out_pt.setX( radian2degree(tmpPt.getX() ) ) 134 | out_pt.setY( radian2degree(tmpPt.getY() ) ) 135 | 136 | else : 137 | geo2tm(dsttype, tmpPt, out_pt) 138 | 139 | return out_pt 140 | 141 | def geo2tm( dsttype, in_pt, out_pt ) : 142 | 143 | transform(GEO, dsttype, in_pt) 144 | delta_lon = in_pt.getX() - m_arLonCenter[dsttype] 145 | sin_phi = math.sin(in_pt.getY()) 146 | cos_phi = math.cos(in_pt.getY()) 147 | 148 | if (m_Ind[dsttype] != 0) : 149 | b = cos_phi * math.sin(delta_lon) 150 | 151 | if ((math.abs(math.abs(b) - 1.0)) < EPSLN) : 152 | pass 153 | #Log.d("infinite error") 154 | #System.out.println("infinite error") 155 | 156 | else : 157 | b = 0 158 | x = 0.5 * m_arMajor[dsttype] * m_arScaleFactor[dsttype] * math.log((1.0 + b) / (1.0 - b)) 159 | con = math.acos(cos_phi * math.cos(delta_lon) / math.sqrt(1.0 - b * b)) 160 | 161 | if (in_pt.getY() < 0) : 162 | con = con * -1 163 | y = m_arMajor[dsttype] * m_arScaleFactor[dsttype] * (con - m_arLatCenter[dsttype]) 164 | 165 | al = cos_phi * delta_lon 166 | als = al * al 167 | c = m_Esp[dsttype] * cos_phi * cos_phi 168 | tq = math.tan(in_pt.getY()) 169 | t = tq * tq 170 | con = 1.0 - m_Es[dsttype] * sin_phi * sin_phi 171 | n = m_arMajor[dsttype] / math.sqrt(con) 172 | ml = m_arMajor[dsttype] * mlfn(e0fn(m_Es[dsttype]), e1fn(m_Es[dsttype]), e2fn(m_Es[dsttype]), e3fn(m_Es[dsttype]), in_pt.getY()) 173 | 174 | out_pt.setX( m_arScaleFactor[dsttype] * n * al * (1.0 + als / 6.0 * (1.0 - t + c + als / 20.0 * (5.0 - 18.0 * t + t * t + 72.0 * c - 58.0 * m_Esp[dsttype]))) + m_arFalseEasting[dsttype] ) 175 | out_pt.setY( m_arScaleFactor[dsttype] * (ml - dst_m[dsttype] + n * tq * (als * (0.5 + als / 24.0 * (5.0 - t + 9.0 * c + 4.0 * c * c + als / 30.0 * (61.0 - 58.0 * t + t * t + 600.0 * c - 330.0 * m_Esp[dsttype]))))) + m_arFalseNorthing[dsttype] ) 176 | 177 | 178 | def tm2geo( srctype, in_pt, out_pt) : 179 | tmpPt = GeoPoint(in_pt.getX(), in_pt.getY()) 180 | max_iter = 6 181 | if (m_Ind[srctype] != 0) : 182 | f = math.exp(in_pt.getX() / (m_arMajor[srctype] * m_arScaleFactor[srctype])) 183 | g = 0.5 * (f - 1.0 / f) 184 | temp = m_arLatCenter[srctype] + tmpPt.getY() / (m_arMajor[srctype] * m_arScaleFactor[srctype]) 185 | h = math.cos(temp) 186 | con = math.sqrt((1.0 - h * h) / (1.0 + g * g)) 187 | out_pt.setY( asinz(con) ) 188 | 189 | if (temp < 0) : 190 | out_pt.setY( out_pt.getY * -1 ) 191 | 192 | if ((g == 0) and (h == 0)) : 193 | out_pt.setX( m_arLonCenter[srctype] ) 194 | 195 | else : 196 | out_pt.setX( math.atan(g / h) + m_arLonCenter[srctype] ) 197 | 198 | tmpPt.setX( tmpPt.getX() - m_arFalseEasting[srctype] ) 199 | tmpPt.setY( tmpPt.getY() - m_arFalseNorthing[srctype] ) 200 | 201 | con = (src_m[srctype] + tmpPt.getY() / m_arScaleFactor[srctype]) / m_arMajor[srctype] 202 | phi = con 203 | 204 | i = 0 205 | 206 | while (1) : 207 | delta_Phi = ((con + e1fn(m_Es[srctype]) * math.sin(2.0 * phi) - e2fn(m_Es[srctype]) * math.sin(4.0 * phi) + e3fn(m_Es[srctype]) * math.sin(6.0 * phi)) / e0fn(m_Es[srctype])) - phi 208 | 209 | phi = phi + delta_Phi 210 | 211 | if (abs(delta_Phi) <= EPSLN) : 212 | break 213 | 214 | if (i >= max_iter) : 215 | break 216 | 217 | i += 1 218 | 219 | 220 | if (abs(phi) < (math.pi / 2)) : 221 | sin_phi = math.sin(phi) 222 | cos_phi = math.cos(phi) 223 | tan_phi = math.tan(phi) 224 | c = m_Esp[srctype] * cos_phi * cos_phi 225 | cs = c * c 226 | t = tan_phi * tan_phi 227 | ts = t * t 228 | cont = 1.0 - m_Es[srctype] * sin_phi * sin_phi 229 | n = m_arMajor[srctype] / math.sqrt(cont) 230 | r = n * (1.0 - m_Es[srctype]) / cont 231 | d = tmpPt.getX() / (n * m_arScaleFactor[srctype]) 232 | ds = d * d 233 | out_pt.setY( phi - (n * tan_phi * ds / r) * (0.5 - ds / 24.0 * (5.0 + 3.0 * t + 10.0 * c - 4.0 * cs - 9.0 * m_Esp[srctype] - ds / 30.0 * (61.0 + 90.0 * t + 298.0 * c + 45.0 * ts - 252.0 * m_Esp[srctype] - 3.0 * cs))) ) 234 | out_pt.setX( m_arLonCenter[srctype] + (d * (1.0 - ds / 6.0 * (1.0 + 2.0 * t + c - ds / 20.0 * (5.0 - 2.0 * c + 28.0 * t - 3.0 * cs + 8.0 * m_Esp[srctype] + 24.0 * ts))) / cos_phi) ) 235 | else : 236 | out_pt.setY( math.pi * 0.5 * math.sin(tmpPt.getY()) ) 237 | out_pt.setX( m_arLonCenter[srctype] ) 238 | 239 | transform(srctype, GEO, out_pt) 240 | 241 | 242 | def getDistancebyGeo( pt1, pt2) : 243 | lat1 = D2R(pt1.getY()) 244 | lon1 = D2R(pt1.getX()) 245 | lat2 = D2R(pt2.getY()) 246 | lon2 = D2R(pt2.getX()) 247 | 248 | longitude = lon2 - lon1 249 | latitude = lat2 - lat1 250 | 251 | a = math.pow(math.sin(latitude / 2.0), 2) + math.cos(lat1) * math.cos(lat2) * math.pow(math.sin(longitude / 2.0), 2) 252 | return 6376.5 * 2.0 * math.atan2(math.sqrt(a), math.sqrt(1.0 - a)) 253 | 254 | def getDistancebyKatec( pt1, pt2 ) : 255 | pt1 = convert(KATEC, GEO, pt1) 256 | pt2 = convert(KATEC, GEO, pt2) 257 | 258 | return getDistancebyGeo(pt1, pt2) 259 | 260 | 261 | def getDistancebyTm( pt1, pt2) : 262 | pt1 = convert(TM, GEO, pt1) 263 | pt2 = convert(TM, GEO, pt2) 264 | 265 | return getDistancebyGeo(pt1, pt2) 266 | 267 | 268 | def getTimebySec(distance) : 269 | return math.round(3600 * distance / 4) 270 | 271 | 272 | def getTimebyMin(distance) : 273 | return math.ceil(getTimebySec(distance) / 60) 274 | 275 | 276 | """ 277 | Author: Richard Greenwood rich@greenwoodmap.com 278 | License: LGPL as per: http://www.gnu.org/copyleft/lesser.html 279 | """ 280 | 281 | """ 282 | * convert between geodetic coordinates (longitude, latitude, height) 283 | * and gecentric coordinates (X, Y, Z) 284 | * ported from Proj 4.9.9 geocent.c 285 | """ 286 | 287 | HALF_PI = 0.5 * math.pi 288 | COS_67P5 = 0.38268343236508977 #/* cosine of 67.5 degrees */ 289 | AD_C = 1.0026000 290 | 291 | def transform( srctype, dsttype, point) : 292 | if (srctype == dsttype) : 293 | return 294 | 295 | if (srctype != 0 or dsttype != 0) : 296 | # Convert to geocentric coordinates. 297 | geodetic_to_geocentric(srctype, point) 298 | 299 | # Convert between datums 300 | if (srctype != 0) : 301 | geocentric_to_wgs84(point) 302 | 303 | 304 | if (dsttype != 0) : 305 | geocentric_from_wgs84(point) 306 | 307 | 308 | # Convert back to geodetic coordinates 309 | geocentric_to_geodetic(dsttype, point) 310 | 311 | def geodetic_to_geocentric ( type, p ) : 312 | 313 | Longitude = p.getX() 314 | Latitude = p.getY() 315 | Height = p.getZ() 316 | 317 | if (Latitude < -HALF_PI and Latitude > -1.001 * HALF_PI ) : 318 | Latitude = -HALF_PI 319 | 320 | elif (Latitude > HALF_PI and Latitude < 1.001 * HALF_PI ) : 321 | 322 | Latitude = HALF_PI 323 | elif ((Latitude < -HALF_PI) or (Latitude > HALF_PI)) : # Latitude out of range 324 | return true 325 | 326 | 327 | #/* no errors */ 328 | if (Longitude > math.pi) : 329 | Longitude -= (2*math.PI) 330 | 331 | Sin_Lat = math.sin(Latitude) 332 | Cos_Lat = math.cos(Latitude) 333 | Sin2_Lat = Sin_Lat * Sin_Lat 334 | Rn = m_arMajor[type] / (math.sqrt(1.0e0 - m_Es[type] * Sin2_Lat)) 335 | X = (Rn + Height) * Cos_Lat * math.cos(Longitude) 336 | Y = (Rn + Height) * Cos_Lat * math.sin(Longitude) 337 | Z = ((Rn * (1 - m_Es[type])) + Height) * Sin_Lat 338 | 339 | p.setX(X) 340 | p.setY(Y) 341 | p.setZ(Z) 342 | 343 | return False 344 | 345 | def geocentric_to_geodetic ( type, p ) : 346 | 347 | X = p.getX() 348 | Y = p.getY() 349 | Z = p.getZ() 350 | Latitude = 0. 351 | 352 | At_Pole = False 353 | if (X != 0.0) : 354 | Longitude = math.atan2(Y,X) 355 | 356 | else : 357 | if (Y > 0) : 358 | Longitude = HALF_PI 359 | 360 | elif (Y < 0) : 361 | Longitude = -HALF_PI 362 | 363 | else : 364 | At_Pole = true 365 | Longitude = 0.0 366 | if (Z > 0.0) : #/* north pole */ 367 | Latitude = HALF_PI 368 | 369 | elif (Z < 0.0) : #/* south pole */ 370 | Latitude = -HALF_PI 371 | 372 | else : #/* center of earth */ 373 | Latitude = HALF_PI 374 | Height = -m_arMinor[type] 375 | return 376 | 377 | 378 | W2 = X*X + Y*Y 379 | W = math.sqrt(W2) 380 | T0 = Z * AD_C 381 | S0 = math.sqrt(T0 * T0 + W2) 382 | Sin_B0 = T0 / S0 383 | Cos_B0 = W / S0 384 | Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0 385 | T1 = Z + m_arMinor[type] * m_Esp[type] * Sin3_B0 386 | Sum = W - m_arMajor[type] * m_Es[type] * Cos_B0 * Cos_B0 * Cos_B0 387 | S1 = math.sqrt(T1*T1 + Sum * Sum) 388 | Sin_p1 = T1 / S1 389 | Cos_p1 = Sum / S1 390 | Rn = m_arMajor[type] / math.sqrt(1.0 - m_Es[type] * Sin_p1 * Sin_p1) 391 | if (Cos_p1 >= COS_67P5) : 392 | Height = W / Cos_p1 - Rn 393 | 394 | elif (Cos_p1 <= -COS_67P5) : 395 | Height = W / -Cos_p1 - Rn 396 | 397 | else : 398 | Height = Z / Sin_p1 + Rn * (m_Es[type] - 1.0) 399 | 400 | if (At_Pole == False) : 401 | Latitude = math.atan(Sin_p1 / Cos_p1) 402 | 403 | p.setX(Longitude) 404 | p.setY(Latitude) 405 | p.setZ(Height) 406 | return 407 | 408 | def geocentric_to_wgs84( p ) : 409 | p.setX( p.getX() + datum_params[0] ) 410 | p.setY( p.getY() + datum_params[1] ) 411 | p.setZ( p.getZ() + datum_params[2] ) 412 | 413 | def geocentric_from_wgs84( p ) : 414 | p.setX( p.getX() - datum_params[0] ) 415 | p.setY( p.getY() - datum_params[1] ) 416 | p.setZ( p.getZ() - datum_params[2] ) 417 | 418 | 419 | #GeoPoint output 420 | if __name__ == "__main__" : 421 | print "example :D" 422 | 423 | pt = GeoPoint(205989.36192, 449778.885301) 424 | print pt.getX(), pt.getY() 425 | 426 | output = convert(TM, GEO, pt) 427 | print output.getX(), output.getY() 428 | 429 | output = convert(GEO, KATEC, output) 430 | print output.getX(), output.getY() 431 | 432 | --------------------------------------------------------------------------------