当前位置: 首页 > 工具软件 > GeoJSON > 使用案例 >

如何简化GeoJson数据

孟宏才
2023-12-01

前言

在地理信息系统项目中我们经常会使用到GeoJson数据,不管是二维还是三维数据,如果数据量超过40M,服务器加载速度和浏览器渲染速度会变得很慢,在这里有一部分java代码来简化geoJson数据提升你的用户体验,但是牺牲了数据的精度,当然这是可以通过参数来改变你的精度大小。我们不妨来看下!

第一步

需要引入com.alibaba.fastjson.JSONArraycom.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");,各位可根据情况自行简化。

 类似资料: