1. 程式人生 > >android 使用proj4j庫,轉換部分解釋(Java 版糾正高程沒有計算)

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)、投影座標(平面座標)。所以投影是有很多型別的,就是不同的平面座標了。轉換出來的。