kml 转geojson java依赖和setting.xml
maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qq</groupId>
<artifactId>test-neux</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test-neux</name>
<description>test-neux</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>24-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.geotools.xsd</groupId>
<artifactId>gt-xsd-kml</artifactId>
<version>24-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.7</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>osgeo</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
</repository>
<repository>
<id>osgeo-snapshot</id>
<name>OSGeo Snapshot Repository</name>
<url>https://repo.osgeo.org/repository/snapshot/</url>
</repository>
</repositories>
</project>
settings.xml
可以先把阿里镜像注解掉,如果mirrorOf为*的话 上面的repositories 将失效导致拉取失败(以下镜像mirroOf为central,也可以直接注释)
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>
http://maven.aliyun.com/nexus/content/groups/public/
</url>
<mirrorOf>central</mirrorOf>
</mirror>
java 代码
package com.fc.v2.controller.admin.goview;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fc.v2.common.base.BaseController;
import com.fc.v2.common.domain.AjaxResult;
import com.fc.v2.common.enums.TransTypeEnum;
import com.fc.v2.model.kml.CoordinateTransformUtil;
import com.fc.v2.model.kml.GPS;
import io.swagger.annotations.Api;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.kml.KMLConfiguration;
import org.geotools.xsd.PullParser;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
@Api(value = "前端模板类别表")
@Controller
@RequestMapping("/api/bimview/kml")
@CrossOrigin
public class KmlController extends BaseController {
private static Logger logger = LoggerFactory.getLogger(KmlController.class);
@GetMapping("getTransTypeList")
@ResponseBody
public AjaxResult getTransTypeList(){
return AjaxResult.successData(200,TransTypeEnum.getData());
}
@PostMapping("kml2json")
@ResponseBody
public AjaxResult kml2Geojson(MultipartFile file,String transType) throws Exception {
if (file==null){
throw new RuntimeException("空文件,请上传kml文件");
}
checkFileName(file.getOriginalFilename());
String str = kmlToGeojson(file.getInputStream()).toString();
JSONObject jsonObj = JSON.parseObject(str);
transzb(jsonObj,transType);
checkGeometer(jsonObj);
return AjaxResult.successData(200,JSON.toJSON(jsonObj));
}
//过滤一些无geometry的jsonObject
private void checkGeometer(JSONObject jsonObj) {
Object features = jsonObj.get("features");
if (features instanceof JSONArray){
JSONArray jsonArray = (JSONArray) features;
Iterator<Object> iterator = jsonArray.stream().iterator();
jsonArray=jsonArray.stream().filter(iter -> ((JSONObject)iter).containsKey("geometry")).collect(Collectors.toCollection(JSONArray::new));
jsonObj.put("features",jsonArray);
}
}
public void transzb(JSONObject jsonObj, String transType) {
Iterator<String> keys = jsonObj.keySet().iterator();// jsonObject.keys();
while (keys.hasNext()) {
String key = keys.next();
if (key.equals("coordinates")){
StringBuffer sb=new StringBuffer();
JSONArray jsonArray = (JSONArray) jsonObj.get(key);
for (Object o : jsonArray) {
String zbString=(String) o;
String[] split = zbString.split(",");
double[] transform = CoordinateTransformUtil.transform(new GPS(Double.parseDouble(split[0]), Double.parseDouble(split[1])), transType);
sb.append(Arrays.toString(transform)+" ");
}
sb.deleteCharAt(sb.length()-1);
jsonObj.put("coordinates",sb.toString());
continue;
}
Object o = jsonObj.get(key);
if (o instanceof JSONObject){
JSONObject object= (JSONObject) o;
transzb(object, transType);
} else if(o instanceof JSONArray) {
JSONArray jsonArray= (JSONArray) o;
transJsonArray( jsonArray,transType);
}
}
}
public void transJsonArray(JSONArray jsonArray, String transType){
for (Object o : jsonArray) {
if (o instanceof JSONArray){
transJsonArray((JSONArray) o, transType);
}else{
if (o instanceof JSONObject){
JSONObject jsonObject= (JSONObject) o;
transJsonObject(jsonObject,transType);
}
}
}
}
public void transJsonObject(JSONObject jsonObject, String transType){
Iterator<String> keys = jsonObject.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
if (key.equals("coordinates")){
StringBuffer sb=new StringBuffer();
JSONArray jsonArray = (JSONArray) jsonObject.get(key);
JSONArray allArray = new JSONArray();
JSONArray bigArray;
boolean singeZBFlag=false;
for (int i = 0; i < jsonArray.size(); i++) {
Object o = jsonArray.get(i);
if(o instanceof BigDecimal){
bigArray=new JSONArray();
double one = ((BigDecimal)jsonArray.get(0)).doubleValue();
double two = ((BigDecimal)jsonArray.get(1)).doubleValue();
double[] transform = CoordinateTransformUtil.transform(new GPS(one, two), transType);
bigArray.add(0,transform[0]);
bigArray.add(1,transform[1]);
bigArray.add(2,jsonArray.get(2));
singeZBFlag=true;
//单个坐标 不需要循环
jsonObject.put(key,bigArray);
break;
}
//多个坐标数组
JSONArray zbArray=jsonArray.getJSONArray(i);
JSONArray newzbArray=new JSONArray();
double one = ((BigDecimal)zbArray.get(0)).doubleValue();
double two = ((BigDecimal)zbArray.get(1)).doubleValue();
double[] transform = CoordinateTransformUtil.transform(new GPS(one, two), transType);
newzbArray.add(0,transform[0]);
newzbArray.add(1,transform[1]);
newzbArray.add(2,zbArray.get(2));
allArray.add(i, newzbArray);
}
if (!singeZBFlag){
jsonObject.put(key,allArray);
}
continue;
}
Object o = jsonObject.get(key);
if (o instanceof JSONArray){
JSONArray jsonArray= (JSONArray) o;
transJsonArray(jsonArray, transType);
}else if(o instanceof JSONObject){
JSONObject jsonObject1= (JSONObject) o;
transJsonObject(jsonObject1, transType);
}
}
}
private void checkFileName(String originalFilename) {
String substring = originalFilename.substring(originalFilename.lastIndexOf("."));
if (!substring.equals(".kml")) {
throw new RuntimeException("文件格式不是kml,转换失败");
}
}
public StringWriter kmlToGeojson(InputStream input) throws IOException {
try {
PullParser parser = new PullParser(new KMLConfiguration(), input, SimpleFeature.class);
FeatureJSON fjson = new FeatureJSON(new GeometryJSON(7));
StringWriter sw = new StringWriter();
// ArrayList<SimpleFeature> features = new ArrayList<>();
SimpleFeature simpleFeature = (SimpleFeature) parser.parse();
sw.append("{ \"type\": \"FeatureCollection\", \"features\": [");
while (simpleFeature != null) {
SimpleFeatureType featureType = simpleFeature.getFeatureType();
fjson.setFeatureType(featureType);
fjson.writeFeature(simpleFeature, sw);
simpleFeature = (SimpleFeature) parser.parse();
if (simpleFeature != null) {
sw.append(",");
}
}
sw.append("]}");
sw.close();
return sw;
} catch (Exception e) {
logger.error("KML 文件解析报错:{}" + e.getMessage());
return null;
} finally {
Objects.requireNonNull(input, "KML 文件输入流为空").close();
}
}
// For input string: "0.2lf"
}
类型转换
package com.fc.v2.common.enums;
import java.util.ArrayList;
import java.util.List;
public enum TransTypeEnum {
bd09togcj02("百度转火星", 1),
bd09towgs84("百度转84", 2),
gcj02towgs84("火星转84", 3),
wgs84togcj02("84转火星", 4),
gcj02tobd09("火星转百度", 5),
wgs84tobd09("84转百度", 6)
;
private String name ;
private int index ;
private TransTypeEnum(String name , int index ){
this.name = name ;
this.index = index ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public static List<String> getData(){
List<String> list=new ArrayList<>();
for (TransTypeEnum stateEnum :
TransTypeEnum.values()) {
list.add(stateEnum.getName());
}
return list;
}
public static int getIndexByName(String name){
int i=0;
for (TransTypeEnum transTypeEnum :
TransTypeEnum.values()) {
if (transTypeEnum.getName().equals(name)){
i= transTypeEnum.getIndex();
}
}
return i;
}
public static TransTypeEnum getEnum(String name){
TransTypeEnum transTypeEnum = null;
for (TransTypeEnum transTypeEnum1 :
TransTypeEnum.values()) {
if (transTypeEnum1.getName().equals(name)){
transTypeEnum = transTypeEnum1;
break;
}
}
return transTypeEnum;
}
}
结果集`
package com.fc.v2.common.domain;
import java.util.HashMap;
/**
* @ClassName: AjaxResult
* @Description: ajax操作消息提醒
* @author fuce
* @date 2018年8月18日
*
*/
public class AjaxResult extends HashMap<String, Object>
{
private static final long serialVersionUID = 1L;
/**
* 初始化一个新创建的 Message 对象
*/
public AjaxResult()
{
}
/**
* 返回错误消息
*
* @return 错误消息
*/
public static AjaxResult error()
{
return error(500, "操作失败");
}
/**
* 返回错误消息
*
* @param msg 内容
* @return 错误消息
*/
public static AjaxResult error(String msg)
{
return error(500, msg);
}
/**
* 返回错误消息
*
* @param code 错误码
* @param msg 内容
* @return 错误消息
*/
public static AjaxResult error(int code, String msg)
{
AjaxResult json = new AjaxResult();
json.put("code", code);
json.put("msg", msg);
return json;
}
/**
* 返回成功消息
*
* @param msg 内容
* @return 成功消息
*/
public static AjaxResult success(String msg)
{
AjaxResult json = new AjaxResult();
json.put("msg", msg);
json.put("code", 200);
return json;
}
/**
* 返回成功消息
*
* @param msg 内容
* @return 成功消息
*/
public static AjaxResult successNullData(String msg)
{
AjaxResult json = new AjaxResult();
json.put("msg", msg);
json.put("data", null);
json.put("code", 200);
return json;
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success()
{
return AjaxResult.success("操作成功");
}
public static AjaxResult successData(int code, Object value){
AjaxResult json = new AjaxResult();
json.put("code", code);
json.put("data", value);
return json;
}
/**
* 返回成功消息
*
* @param key 键值
* @param value 内容
* @return 成功消息
*/
@Override
public AjaxResult put(String key, Object value)
{
super.put(key, value);
return this;
}
}
转换工具
package com.fc.v2.model.kml;
import java.util.Arrays;
/**
* 百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的工具
* <p>
* 参考 https://github.com/wandergis/coordtransform 实现的Java版本
*
* @author geosmart
*/
public class CoordinateTransformUtil {
static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
// π
static double pi = 3.1415926535897932384626;
// 长半轴
static double a = 6378245.0;
// 扁率
static double ee = 0.00669342162296594323;
/**
* 百度坐标系(BD-09)转WGS坐标
*
* @param lng 百度坐标纬度
* @param lat 百度坐标经度
* @return WGS84坐标数组
*/
public static double[] bd09towgs84(double lng, double lat) {
double[] gcj = bd09togcj02(lng, lat);
double[] wgs84 = gcj02towgs84(gcj[0], gcj[1]);
return wgs84;
}
/**
* WGS坐标转百度坐标系(BD-09)
*
* @param lng WGS84坐标系的经度
* @param lat WGS84坐标系的纬度
* @return 百度坐标数组
*/
public static double[] wgs84tobd09(double lng, double lat) {
double[] gcj = wgs84togcj02(lng, lat);
double[] bd09 = gcj02tobd09(gcj[0], gcj[1]);
return bd09;
}
/**
* 火星坐标系(GCJ-02)转百度坐标系(BD-09)
* <p>
* 谷歌、高德——>百度
*
* @param lng 火星坐标经度
* @param lat 火星坐标纬度
* @return 百度坐标数组
*/
public static double[] gcj02tobd09(double lng, double lat) {
double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_pi);
double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_pi);
double bd_lng = z * Math.cos(theta) + 0.0065;
double bd_lat = z * Math.sin(theta) + 0.006;
return new double[]{bd_lng, bd_lat};
}
/**
* 百度坐标系(BD-09)转火星坐标系(GCJ-02)
* <p>
* 百度——>谷歌、高德
*
* @param bd_lon 百度坐标纬度
* @param bd_lat 百度坐标经度
* @return 火星坐标数组
*/
public static double[] bd09togcj02(double bd_lon, double bd_lat) {
double x = bd_lon - 0.0065;
double y = bd_lat - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
double gg_lng = z * Math.cos(theta);
double gg_lat = z * Math.sin(theta);
return new double[]{gg_lng, gg_lat};
}
/**
* WGS84转GCJ02(火星坐标系)
*
* @param lng WGS84坐标系的经度
* @param lat WGS84坐标系的纬度
* @return 火星坐标数组
*/
public static double[] wgs84togcj02(double lng, double lat) {
if (out_of_china(lng, lat)) {
return new double[]{lng, lat};
}
double dlat = transformlat(lng - 105.0, lat - 35.0);
double dlng = transformlng(lng - 105.0, lat - 35.0);
double radlat = lat / 180.0 * pi;
double magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[]{mglng, mglat};
}
/**
* GCJ02(火星坐标系)转GPS84
*
* @param lng 火星坐标系的经度
* @param lat 火星坐标系纬度
* @return WGS84坐标数组
*/
public static double[] gcj02towgs84(double lng, double lat) {
if (out_of_china(lng, lat)) {
return new double[]{lng, lat};
}
double dlat = transformlat(lng - 105.0, lat - 35.0);
double dlng = transformlng(lng - 105.0, lat - 35.0);
double radlat = lat / 180.0 * pi;
double magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
double mglat = lat + dlat;
double mglng = lng + dlng;
return new double[]{lng * 2 - mglng, lat * 2 - mglat};
}
/**
* 纬度转换
*
* @param lng
* @param lat
* @return
*/
public static double transformlat(double lng, double lat) {
double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0;
return ret;
}
/**
* 经度转换
*
* @param lng
* @param lat
* @return
*/
public static double transformlng(double lng, double lat) {
double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lng * pi) + 40.0 * Math.sin(lng / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(lng / 12.0 * pi) + 300.0 * Math.sin(lng / 30.0 * pi)) * 2.0 / 3.0;
return ret;
}
/**
* 判断是否在国内,不在国内不做偏移
*
* @param lng
* @param lat
* @return
*/
public static boolean out_of_china(double lng, double lat) {
if (lng < 72.004 || lng > 137.8347) {
return true;
} else if (lat < 0.8293 || lat > 55.8271) {
return true;
}
return false;
}
public static void main(String[] args) {
// System.out.println(Arrays.toString(gcj02tobd09(120.236701, 29.726141)));
System.out.println(Arrays.toString(transform(new GPS(114.62252360,22.84313590),"百度转84")));
System.out.println(Arrays.toString(bd09towgs84(120.24794,29.72979)));
// System.out.println(Arrays.toString(wgs84tobd09(120.23710857657026, 29.72591672366825)));
//
//
// System.out.println(Arrays.toString(gcj02tobd09(120.236701, 29.726141)));
}
public static double[] transform(GPS corOld, String transType)
{
switch (transType)
{
case "百度转火星":
return bd09togcj02(corOld.getLon(), corOld.getLat());
case "百度转84":
return bd09towgs84(corOld.getLon(), corOld.getLat());
case "火星转84":
return gcj02towgs84(corOld.getLon(), corOld.getLat());
case "84转火星":
return wgs84togcj02(corOld.getLon(), corOld.getLat());
case "火星转百度":
return gcj02tobd09(corOld.getLon(), corOld.getLat());
case "84转百度":
return wgs84tobd09(corOld.getLon(), corOld.getLat());
default:
return null;
}
}
}