android 使用proj4j庫,轉換部分解釋(Java 版糾正高程沒有計算)
之前寫過一篇文章介紹proj4,不過後面測試發現那個庫高程沒有參與計算,所以自己除錯原始碼,封裝了一個高程參與計算的。搞著也是不容易啊。下面介紹庫還是proj4j-0.1.1.jar
1、封裝座標轉換的程式碼
package com.mapzoom.demo.until; import org.osgeo.proj4j.CoordinateReferenceSystem; import org.osgeo.proj4j.CoordinateTransform; import org.osgeo.proj4j.Proj4jException; import org.osgeo.proj4j.ProjCoordinate; import org.osgeo.proj4j.datum.GeocentricConverter; /** * 修復proj4 高程沒有轉換的bug */ public class BLHCoordinateTransform implements CoordinateTransform { private CoordinateReferenceSystem srcCRS; private CoordinateReferenceSystem tgtCRS; private ProjCoordinate geoCoord = new ProjCoordinate(0.0D, 0.0D, 0.0D); private boolean doInverseProjection = true; private boolean doForwardProjection = true; private boolean doDatumTransform = false; private boolean transformViaGeocentric = false; private GeocentricConverter srcGeoConv; private GeocentricConverter tgtGeoConv; public BLHCoordinateTransform(CoordinateReferenceSystem srcCRS, CoordinateReferenceSystem tgtCRS) { this.srcCRS = srcCRS; this.tgtCRS = tgtCRS; this.doInverseProjection = srcCRS != null && srcCRS != CoordinateReferenceSystem.CS_GEO; this.doForwardProjection = tgtCRS != null && tgtCRS != CoordinateReferenceSystem.CS_GEO; this.doDatumTransform = this.doInverseProjection && this.doForwardProjection && srcCRS.getDatum() != tgtCRS.getDatum(); if (this.doDatumTransform) { boolean isEllipsoidEqual = srcCRS.getDatum().getEllipsoid().isEqual(tgtCRS.getDatum().getEllipsoid()); if (!isEllipsoidEqual) { this.transformViaGeocentric = true; } if (srcCRS.getDatum().hasTransformToWGS84() || tgtCRS.getDatum().hasTransformToWGS84()) { this.transformViaGeocentric = true; } if (this.transformViaGeocentric) { this.srcGeoConv = new GeocentricConverter(srcCRS.getDatum().getEllipsoid()); this.tgtGeoConv = new GeocentricConverter(tgtCRS.getDatum().getEllipsoid()); } } } public CoordinateReferenceSystem getSourceCRS() { return this.srcCRS; } public CoordinateReferenceSystem getTargetCRS() { return this.tgtCRS; } public ProjCoordinate transform(ProjCoordinate src, ProjCoordinate tgt) throws Proj4jException { if (this.doInverseProjection) { this.srcCRS.getProjection().inverseProjectRadians(src, this.geoCoord); //高程賦值 this.geoCoord.z = src.z; } else { this.geoCoord.setValue(src); } if (this.doDatumTransform) { this.datumTransform(this.geoCoord); } if (this.doForwardProjection) { this.tgtCRS.getProjection().projectRadians(this.geoCoord, tgt); //高程賦值 tgt.z = this.geoCoord.z; } else { tgt.setValue(this.geoCoord); } return tgt; } //涉及到七引數 或者 三引數轉換 private void datumTransform(ProjCoordinate pt) { // if (this.srcCRS.getDatum().isEqual(this.tgtCRS.getDatum())) { if (this.transformViaGeocentric) { this.srcGeoConv.convertGeodeticToGeocentric(pt); if (this.srcCRS.getDatum().hasTransformToWGS84()) { this.srcCRS.getDatum().transformFromGeocentricToWgs84(pt); } if (this.tgtCRS.getDatum().hasTransformToWGS84()) { this.tgtCRS.getDatum().transformToGeocentricFromWgs84(pt); } this.tgtGeoConv.convertGeocentricToGeodetic(pt); } // } } }
2、測試程式碼
package com.mapzoom.demo.activity; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; import com.mapzoom.demo.R; import com.mapzoom.demo.until.BLHCoordinateTransform; import org.osgeo.proj4j.CRSFactory; import org.osgeo.proj4j.CoordinateReferenceSystem; import org.osgeo.proj4j.CoordinateTransform; import org.osgeo.proj4j.CoordinateTransformFactory; import org.osgeo.proj4j.ProjCoordinate; import org.osgeo.proj4j.io.Proj4FileReader; import java.io.IOException; import java.util.Locale; public class ProjTestActivity extends AppCompatActivity implements View.OnClickListener { TextView showTextView, showTextView1, showTextView2, showTextView3,showTextView4,showTextView5; CRSFactory crsFactory = new CRSFactory(); CoordinateTransformFactory ctf = new CoordinateTransformFactory(); ProjCoordinate projCoordinate = new ProjCoordinate(); BLHCoordinateTransform blhCoordinateTransform = null; CoordinateReferenceSystem wgs84; CoordinateReferenceSystem targetSystem; private String showResult = ""; private CoordinateTransform transform; private double dNorth = 0; private double dEast = 0; private double dHigh = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_proj_test); initWGS84(); initUI(); } private void initUI() { showTextView = (TextView) findViewById(R.id.textView); showTextView1 = (TextView) findViewById(R.id.textView1); showTextView2 = (TextView) findViewById(R.id.textView3); showTextView3 = (TextView) findViewById(R.id.textView4); showTextView4 = (TextView) findViewById(R.id.textView5); showTextView5 = (TextView) findViewById(R.id.textView6); findViewById(R.id.button).setOnClickListener(this); findViewById(R.id.button1).setOnClickListener(this); findViewById(R.id.button2).setOnClickListener(this); findViewById(R.id.button3).setOnClickListener(this); findViewById(R.id.button4).setOnClickListener(this); String result = String.format(Locale.ENGLISH, "%.3f", 113.0) + "\n" + String.format(Locale.ENGLISH, "%.3f", 23.0) + "\n" + String.format(Locale.ENGLISH, "%.3f", 10.0) + "\n"; showTextView5.setText(result); } private void initWGS84() { //wgs84 ����4326 Proj4FileReader proj4FileReader = new Proj4FileReader(); String[] paramStr = new String[0]; //Դ����ϵͳ try { paramStr = proj4FileReader.readParametersFromFile("epsg", "4326"); } catch (IOException e) { e.printStackTrace(); } // String wgs84_param = "+title=long/lat:WGS84 +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degress"; wgs84 = crsFactory.createFromParameters("WGS84", paramStr); } private void initMathTransform() { //beijng54 ����2435 Proj4FileReader proj4FileReader = new Proj4FileReader(); String[] paramStr = new String[0]; try { paramStr = proj4FileReader.readParametersFromFile("epsg", "2435"); } catch (IOException e) { e.printStackTrace(); } targetSystem = crsFactory.createFromParameters("2435", paramStr); transform = ctf.createTransform(wgs84, targetSystem); blhCoordinateTransform = new BLHCoordinateTransform(wgs84, targetSystem); } public void transform(double x, double y, double z) { if (transform != null) { projCoordinate.setValue(x, y, z); transform.transform(projCoordinate, projCoordinate); dNorth = projCoordinate.y; dEast = projCoordinate.x; dHigh = projCoordinate.z; } } public void blhTransform(double x, double y, double z) { if (blhCoordinateTransform != null) { projCoordinate.setValue(x, y, z); blhCoordinateTransform.transform(projCoordinate, projCoordinate); dNorth = projCoordinate.y; dEast = projCoordinate.x; dHigh = projCoordinate.z; } } @Override public void onClick(View v) { CRSFactory crsFactory = new CRSFactory(); String result = ""; String proj4 = ""; switch (v.getId()) { case R.id.button: initWGS84(); initMathTransform(); transform(113, 23, 10); result = String.format(Locale.ENGLISH, "%.3f", dNorth) + "\n" + String.format(Locale.ENGLISH, "%.3f", dEast) + "\n" + String.format(Locale.ENGLISH, "%.3f", dHigh) + "\n"; showTextView.setText(result); break; case R.id.button1: initWGS84(); //tmerc 指的是高斯投影 proj4 = "+proj=tmerc +lat_0=0 +lon_0=114 +k=1 +x_0=500000 +y_0=0 +ellps=krass +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"; targetSystem = crsFactory.createFromParameters("2435", proj4); blhCoordinateTransform = new BLHCoordinateTransform(wgs84, targetSystem); transform(113, 23, 10); result = String.format(Locale.ENGLISH, "%.3f", dNorth) + "\n" + String.format(Locale.ENGLISH, "%.3f", dEast) + "\n" + String.format(Locale.ENGLISH, "%.3f", dHigh) + "\n"; showTextView1.setText(result); break; case R.id.button2: initWGS84(); initMathTransform(); blhTransform(113, 23, 10); result = String.format(Locale.ENGLISH, "%.3f", dNorth) + "\n" + String.format(Locale.ENGLISH, "%.3f", dEast) + "\n" + String.format(Locale.ENGLISH, "%.3f", dHigh) + "\n"; showTextView2.setText(result); break; case R.id.button3: initWGS84(); proj4 = "+proj=tmerc +lat_0=0 +lon_0=114 +k=1 +x_0=500000 +y_0=0 +ellps=krass +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"; targetSystem = crsFactory.createFromParameters("2435", proj4); blhCoordinateTransform = new BLHCoordinateTransform(wgs84, targetSystem); blhTransform(113, 23, 10); result = String.format(Locale.ENGLISH, "%.3f", dNorth) + "\n" + String.format(Locale.ENGLISH, "%.3f", dEast) + "\n" + String.format(Locale.ENGLISH, "%.3f", dHigh) + "\n"; showTextView3.setText(result); break; case R.id.button4: //+proj=longlat 指的是經緯度座標 proj4 = "+proj=longlat +ellps=krass +no_defs"; wgs84 = crsFactory.createFromParameters("wgs84", proj4); proj4 = "+proj=tmerc +lat_0=0 +lon_0=114 +k=1 +x_0=500000 +y_0=0 +ellps=krass +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"; targetSystem = crsFactory.createFromParameters("2435", proj4); blhCoordinateTransform = new BLHCoordinateTransform(wgs84, targetSystem); blhTransform(113, 23, 10); result = String.format(Locale.ENGLISH, "%.3f", dNorth) + "\n" + String.format(Locale.ENGLISH, "%.3f", dEast) + "\n" + String.format(Locale.ENGLISH, "%.3f", dHigh) + "\n"; showTextView4.setText(result); break; default: break; } } }
3、測試效果
最後面的是原始經緯度,分別轉換了。用的是高斯投影。
4、部分prj語句解釋
蘭勃脫投影
"+proj=lcc +a=6378137 +b=6356752.3142 +x_0=500000 +y_0=0 +lon_0=114 +lat_1=35 +lat_2=35 +k=1 +lat_0=0"
該字串定義的就是蘭勃脫投影(+proj=lcc),橢球長軸為6378137m(+a=6378137),短軸為6356752.3142m(+b=6356752.3142),向東偏移500000m(+x_0=500000),向北偏移0m(+y_0=0),中央子午線是東經114°(+lon_0=114),第一標準緯線是北緯35°(+lat_1=35),第二標準緯線是北緯35°(+lat_2=35),比例為1(+k=1),起始緯度為0°(+lat_0=0,赤道)。
需要說明的是,第一標準緯線和第二標準緯線一致是為切投影,或者不輸入第二標準緯線,兩者不一樣時,為割投影。
墨卡託投影
"+proj=merc +a=6378137 +b=6356752.3142 +x_0=500000 +y_0=0 +lon_0=114 +k=1 +lat_ts=60"
該字串定義的是墨卡託投影(+proj=merc),橢球長軸為6378137m(+a=6378137),短軸為6356752.3142m(+b=6356752.3142),向東偏移500000m(+x_0=500000),向北偏移0m(+y_0=0),中央子午線是東經114°(+lon_0=114),比例為1(+k=1),真緯度為北緯60°(+lat_ts=60,也就是舊庫裡說的割緯線半徑)。
高斯—克呂格投影
"+proj=tmerc +a=6378137 +b=6356752.3142 +x_0=500000 +y_0=0 +lon_0=114 +k=1 +lat_0=0"
該字串定義的高斯投影(+proj=tmerc),橢球長軸為6378137m(+a=6378137),短軸為6356752.3142m(+b=6356752.3142),向東偏移500000m(+x_0=500000),向北偏移0m(+y_0=0),中央子午線是東經114°(+lon_0=114),比例為1(+k=1,當+k=0.996時可變為通用橫軸墨卡託投影),起始緯度為0°(+lat_0=0,赤道)。
其中(+proj=longlat)代表是經緯度座標
其中(+proj=geocent)代表是空間座標座標
一條語句代表一個橢球下面的座標。有什麼什麼橢球下的經緯度座標(longlat)、空間座標|(geocent)、投影座標(平面座標)。所以投影是有很多型別的,就是不同的平面座標了。轉換出來的。