在地理信息系统项目中我们经常会使用到GeoJson数据,不管是二维还是三维数据,如果数据量超过40M,服务器加载速度和浏览器渲染速度会变得很慢,在这里有一部分java代码来简化geoJson数据提升你的用户体验,但是牺牲了数据的精度,当然这是可以通过参数来改变你的精度大小。我们不妨来看下!
需要引入com.alibaba.fastjson.JSONArray
和com.alibaba.fastjson.JSONObject
这两个类,这在fastjson.jar
包里,在你的pom.xml文件里引入
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
写代码
先解析然后简化,最后下载到本地。
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.supermap.application.common.ReturnObject;
import static java.math.BigDecimal.ROUND_HALF_UP;
public class CompressGeoJsonFileUtil {
/***
* 文件转换类
* oldFilePath 需要转换的geojosn文件
* newFilePath 转换完成的geojson文件
* scale 精确到小数点后几位(这里是你的数据精度,精度越大,文件越大,反之越小)
*/
public ReturnObject compress(String oldFilePath,String newFilePath,int scale){
boolean boo = false;
//读取文件流
FileInputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
//写入文件流
FileWriter fileWriter = null;
BufferedWriter bufferedWriter = null;
//文件读取builder
StringBuilder builder = new StringBuilder();
try {
File file = new File(oldFilePath);
if (!file.exists()) {
return new ReturnObject("未找到 " + oldFilePath);
}
inputStream = new FileInputStream(file);
inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(inputStreamReader);
//读取文件字节
String str;
while ((str = bufferedReader.readLine()) != null) {
builder.append(str);
}
//先关闭读取流
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
String buffer = builder.toString();
//开始解析geojson数据
JSONObject jsonObject = resolveGeoJson(buffer,scale);
File newFile = new File(newFilePath);
if (!newFile.exists()) {
newFile.createNewFile();
}
fileWriter = new FileWriter(newFile);
bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write(jsonObject.toString());
bufferedWriter.close();
fileWriter.close();
return new ReturnObject<>("请求成功","下载到 " + newFilePath + " 成功!");
}catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
return new ReturnObject("请求失败!");
}
/**
* 解析geoJson、并压缩坐标小数位
* @param buffer json字符串
* @param scale 保留小数位后几位
* @return JSONObject
*/
JSONObject resolveGeoJson(String buffer,int scale){
//开始解析geojson数据
JSONObject jsonObject = JSONObject.parseObject(buffer);
//解析features
JSONArray array = jsonObject.getJSONArray("features");
for (int f = 0; f < array.size(); f++) {
//得到features对象
JSONObject fea = array.getJSONObject(f);
//得到geometry对象
JSONObject geometry = fea.getJSONObject("geometry");
//得到properties对象
JSONObject properties = fea.getJSONObject("properties");
//这里可以使用remove方法去掉properties里的不需要的属性值
//properties.remove("NAME");
//得到coordinates对象
JSONArray coordinates = geometry.getJSONArray("coordinates");
//得到coordinates对象中的第一个数组
for (int c = 0;c < coordinates.size();c++) {
JSONArray coo1 = coordinates.getJSONArray(c);
for (int i = 0;i < coo1.size();i++) {
//得到coordinates对象中的第二个数组
JSONArray coo2 = coo1.getJSONArray(i);
//截取坐标小数位数
//ROUND_HALF_UP 向上舍入原则
BigDecimal bigDecimal1 = coo2.getBigDecimal(0).setScale(scale,ROUND_HALF_UP);
BigDecimal bigDecimal2 = coo2.getBigDecimal(1).setScale(scale,ROUND_HALF_UP);
//移除原坐标,注意下标
coo2.remove(0);
coo2.remove(0);
//添加新坐标
coo2.add(0,bigDecimal1);
coo2.add(1,bigDecimal2);
}
}
}
return jsonObject;
}
至于ReturnObject类在我之前的博客里已经有了,这里不妨再发出来,很简单,需要引入net.sf.json.JSONObject的jar包。
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
import net.sf.json.JSONObject;
import java.io.Serializable;
/**
* 新增返回对象类
* @author sun'f
* @param <T>
*/
public class ReturnObject<T> implements Serializable {
private static final long serialVersionUID = -8617729851989592349L;
//返回状态
private boolean status;
//返回提示
private String message;
//返回对象
private T data;
/**
* 返回错误默认构造函数
*/
public ReturnObject() {
this.status = false;
this.message = "请求错误!";
this.data = null;
}
/**
* 新增双string类型构造函数
* @param message
* @param data
*/
public ReturnObject(String message,T data) {
this.status = true;
this.message = message;
this.data = data;
}
/**
* 常规构造函数
* @param status
* @param message
* @param data
*/
public ReturnObject(boolean status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
/**
* post返回成功
* 一般post请求返回值只需提供data返回数据
* @param data
*/
public ReturnObject(T data) {
this.status = true;
this.message = "请求成功!";
this.data = data;
}
/**
* post返回失败
* 一般post请求返回值只需提供data返回数据
* @param message
*/
public ReturnObject(String message) {
this.status = false;
this.message = message;
this.data = null;
}
public boolean getStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public JSONObject toJson(ReturnObject returnObject){
return JSONObject.fromObject(returnObject);
}
public com.alibaba.fastjson.JSONObject toJson(){
String json = com.alibaba.fastjson.JSONObject.toJSONString(this);
return com.alibaba.fastjson.JSONObject.parseObject(json);
}
/**
* append
* @param boo 对象append重载方法
* @return this
*/
public synchronized ReturnObject<T> append(Boolean boo){
this.setStatus(boo);
return this;
}
public synchronized ReturnObject<T> append(String str){
this.setMessage(str);
return this;
}
public synchronized ReturnObject<T> append(T t){
this.setData(t);
return this;
}
/**
* 重写hashCode方法
* @return hash值
*/
@Override
public int hashCode() {
int hash = (this.status ? 1 : 0) * 31;
hash = hash + (this.message != null ? this.message.hashCode() : 0) * 31;
hash = hash + (this.data != null ? this.data.hashCode() : 0) * 31;
return hash;
}
/**
* 重写equals方法
* @param obj 比较对象
* @return true/false
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof ReturnObject)) {
return false;
}
ReturnObject ro = (ReturnObject) obj;
return ro.getData().equals(this.data) && ro.getMessage().equals(this.message) && ro.getStatus() == this.status;
}
}
此代码精简的程度视原本数据精度而定,原精度一般是12位,这样至少可以精简2/5左右的大小。
也可以根据自己情况精简properties里的属性数据,无用的properties数据占用大量空间,只需在resolveGeoJson
方法里拿到properties
对象后删掉里面的属性即可,如properties.remove("NAME");
,各位可根据情况自行简化。