java 使用 proj4j 实现将高斯投影坐标转换为大地经纬度坐标

maven 依赖:

<dependency>
	<groupId>org.locationtech.proj4j</groupId>
	<artifactId>proj4j</artifactId>
	<version>1.2.3</version>
</dependency>

GeoUtils.java:

import org.locationtech.proj4j.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;

public class GeoUtils {

    private static final CoordinateTransformFactory ctf = new CoordinateTransformFactory();
    private static final CRSFactory crsFactory = new CRSFactory();

    public static final String ellips_code_WGS84 = "WGS84";
    public static final String ellips_code_beijing1954 = "krass";
    public static final String ellips_code_xian1980 = "IAU76";

    private static final Map<String,String> ellips_name_code_map = new HashMap<>();

    static {
        ellips_name_code_map.put( "WGS84",ellips_code_WGS84 );
        ellips_name_code_map.put( "beijing1954",ellips_code_beijing1954 );
        ellips_name_code_map.put( "xian1980 ",ellips_code_xian1980 );
    }

    /**
     * 高斯投影坐标转经纬度坐标
     * @param gaussCoordinateVO
     */
    public static LonLatCoordinateVO  gaussProjectionCoordinate2LonLatCoordinate(GaussProjectionCoordinateVO gaussCoordinateVO){
        String ellipsCode = null;
        String ellipsName = MyStringUtils.null2EmptyWithTrim( gaussCoordinateVO.getEllipsName() );
        if( ellipsName.length() == 0 ){
            ellipsCode = ellips_code_WGS84;
            ellipsName = "WGS84";
        }else {
            ellipsCode = ellips_name_code_map.get(gaussCoordinateVO.getEllipsName());
        }
        if( ellipsCode == null ){
            throw new BusinessLogicException( "不支持的大地基准面( 椭球 )" );
        }
        // 计算带号
        Long zoneNum = (long) (gaussCoordinateVO.getX() / 1000000L);

        // 中央经度( 即中央子午线 )
        double lon_0 = 0;
        Integer zoneWidth = gaussCoordinateVO.getZoneWidth();
        if( zoneWidth == null ){
            zoneWidth = 3;
        }
        if ( zoneWidth == 3 ) {
            lon_0 = 3 * zoneNum;
        } else if ( zoneWidth == 6 ) {
            lon_0 = 6 * zoneNum - 3;
        }else {
            throw new BusinessLogicException( "目前只支持 3度带宽和6度带宽" );
        }
        // 东经偏移量
        Long x_0 = zoneNum * 1000000L + 500000L;

        String crs_params_source="+proj=tmerc +lat_0=0 +lon_0=" + lon_0 + " +k=1 +x_0=" + x_0 + " +y_0=0 +ellps=" + ellipsCode + " +units=m +no_defs";
        System.out.println( "crs_params_source = " + crs_params_source );
        CoordinateReferenceSystem crs_source = crsFactory.createFromParameters(null, crs_params_source);

        // String crs_params_target="+proj=latlong +datum=" + ellipsoidName;
        String crs_params_target="+proj=latlong";
        System.out.println( "crs_params_target = " + crs_params_target );
        CoordinateReferenceSystem  crs_target = crsFactory.createFromParameters(null, crs_params_target);

        CoordinateTransform transform = ctf.createTransform(crs_source, crs_target);
        //坐标系转换
        ProjCoordinate projCoordinate = new ProjCoordinate(gaussCoordinateVO.getX(), gaussCoordinateVO.getY());
        transform.transform(projCoordinate, projCoordinate);

        LonLatCoordinateVO lonLatCoordinateVO = new LonLatCoordinateVO();
        lonLatCoordinateVO.setLongitude(BigDecimal.valueOf( projCoordinate.x  ).setScale( 6, RoundingMode.HALF_UP ).doubleValue());
        lonLatCoordinateVO.setLatitude( BigDecimal.valueOf( projCoordinate.y  ).setScale( 6,RoundingMode.HALF_UP ).doubleValue());
        lonLatCoordinateVO.setZoneWidth( zoneWidth );
        lonLatCoordinateVO.setEllpsName( ellipsName );
        lonLatCoordinateVO.setX0( x_0 );
        lonLatCoordinateVO.setLon0( lon_0 );
        return lonLatCoordinateVO;
    }

    public static void main(String[] args) {
        GaussProjectionCoordinateVO gaussCoordinateVO = new GaussProjectionCoordinateVO();
        gaussCoordinateVO.setX( 39458073.6d );
        gaussCoordinateVO.setY( 4632485.2d );

        LonLatCoordinateVO lonLatCoordinateVO = GeoUtils.gaussProjectionCoordinate2LonLatCoordinate(gaussCoordinateVO);
        System.out.println( lonLatCoordinateVO );
    }
}

 LonLatCoordinateVO.java:

import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;

@Getter
@Setter
@ApiModel( value = "LonLatCoordinateVO",description = "LonLatCoordinateVO")
public class LonLatCoordinateVO implements Serializable {

    @ApiModelProperty( "大地经纬度坐标下的经度" )
    private Double longitude;

    @ApiModelProperty( "大地经纬度坐标下的纬度" )
    private Double latitude;

    private String ellpsName;
    private Integer zoneWidth;
    private Long x0;
    private Double lon0;

    @Override
    public String toString() {
        return "LonLatCoordinateVO{" +
                "longitude=" + longitude +
                ", latitude=" + latitude +
                '}';
    }
}

GaussProjectionCoordinateVO.java:

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;

@Getter
@Setter
@ApiModel( value = "GaussProjectionCoordinateVO",description = "GaussProjectionCoordinateVO")
public class GaussProjectionCoordinateVO implements Serializable {

    /**
     * 例如:x = 39458073.6, y = 4632485.2
     */
    @ApiModelProperty( value = "高斯投影坐标之x坐标,例如:39458073.6,必填",required = true )
    private double x;

    @ApiModelProperty( value = "高斯投影坐标之y坐标,例如:4632485.2,必填",required = true )
    private double y;

    @ApiModelProperty( value = "大地基准面( 即椭球,可选值为 'WGS84'、'beijing1954'、'xian1980' ),选填,不填默认为 WGS84",required = false )
    private String ellipsName;

    @ApiModelProperty( value = "带宽,目前值支持 3度和6度,选填,不填默认为3度",required = false )
    private Integer zoneWidth;

    @ApiModelProperty( value = "是否保存将计算出的结果保存到数据库中( true、false ),选填,默认为 false",required = false )
    private Boolean save2Db;

    @Override
    public String toString() {
        return "GaussProjectionCoordinateVO{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}

ps:

        1. 使用例如如下的工具验证此程序转换的准确性时记得填写正确的中央经度( 有时候不为117哦 )。

程序中在如下所示计算出了中央子午线( 即中央经度 ):

2. 如果你使用了 北京1954、西安1984、WGS84 之外的大地基准面( 即椭球 ),可以修改代码,自行往 ellips_name_code_map 中添加 ellips_code( 即转换参数字符串中的 ellps ),如果不知道填什么,可以先随便填一个不存在的,让代码报错,如下所示:

点击报错信息中的 "d(Proj4Parser.java:255)",调到如下代码:

进入   registry.getEllipsoid(code) 方法:

 比如你不清楚 "2000国家大地坐标系" 的 ellps 该填什么,百度 2000国家大地坐标系 的椭球信息:

在代码中搜索该长半轴和扁率:

 于是往 ellips_name_code_map 中加入下入信息:

测试转换结果:

有的网站( 例如:https://www.lddgo.net/coordinate/projection )还需要传 x_0( 好像是东经偏移值 ),也是可以在代码中找到 x_0的计算结果的:

注意输入坐标x,y不要写反了,你会发现上述网站转换时没让填写带宽(3度还是6度),其实带宽是用来计算中央子午线(中央经度)的,具体计算逻辑很简单,在代码中有体现,这里就不截图了,但是为什么我上面截图的一个转换工具需要填写中央经度呢?个人猜测是自定义坐标系使用,就是你想怎么指定中央经度都行

Java中,你可以使用`org.osgeo.proj4j`库,也称为Proj4J,这是一个轻量级的Java API,用于执行地理坐标系统的转换,包括从经纬度坐标(lon/lat)到其他投影坐标(如UTM,Mercator等)。 Proj4J支持大量的坐标系统转换规则,其中包括由PROJ.4库提供的投影模型,这是开源地理空间社区广泛使用的标准。 以下是使用Proj4J进行坐标转换的基本步骤: 1. **添加依赖**:首先,你需要将Proj4J库添加到你的项目中。如果你的项目使用Maven,可以在pom.xml文件中添加如下依赖: ```xml <dependency> <groupId>org proj4j</groupId> <artifactId>proj4j</artifactId> <version>latest version</version> </dependency> ``` 2. **创建坐标对象**:创建`org.proj4j.Coordinate`对象,存储原始的经纬度值。 ```java Coordinate latLonCoord = new Coordinate(yourLatitude, yourLongitude); ``` 3. **设置投影变换**:根据目标投影,配置`GeographicTransform`对象。 ```java String fromSRS = "+proj=longlat +ellps=WGS84"; String toSRS = "+proj=utm +zone=yourZoneNumber +datum=WGS84"; // 例如zone=35N for N America GeographicTransform transform = TransformerFactory.get().createGeographicTransformer(fromSRS, toSRS); ``` 4. **执行转换**:使用`transform`方法将经纬度转换为新的投影坐标。 ```java Coordinate projectedCoord = transform.transform(latLonCoord.getX(), latLonCoord.getY()); double x = projectedCoord.getX(); double y = projectedCoord.getY(); ``` 5. **结果检查**:`x`和`y`就是转换后的坐标值,可以根据需要进一步处理。 注意, Proj4J允许你指定任意的坐标系转换规则,所以它非常适用于各种地理空间数据分析和地图制作需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值