如何简化GeoJson数据

如何解析并简化GeoJson数据

前言

在地理信息系统项目中我们经常会使用到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");,各位可根据情况自行简化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
当然可以!我们可以使用GeoJSON数据进行线段聚类的示例。假设我们有一个包含多个线段的GeoJSON文件,每个线段都具有方向、起点和终点信息。以下是一个简化的示例: ```json { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [0, 0], [1, 1] ] }, "properties": { "direction": "north", "start_point": [0, 0], "end_point": [1, 1] } }, { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [2, 2], [3, 3] ] }, "properties": { "direction": "east", "start_point": [2, 2], "end_point": [3, 3] } }, { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [4, 4], [5, 5] ] }, "properties": { "direction": "north", "start_point": [4, 4], "end_point": [5, 5] } } ] } ``` 这个示例包含了三个线段,每个线段都有方向、起点和终点的信息。现在我们可以使用这些属性进行线段聚类。 你可以选择合适的聚类算法,比如K-means算法。为了演示目的,我们假设要将线段聚类为两个簇,即根据方向和起点、终点的相似性将线段分组。 以下是使用Python和scikit-learn库进行线段聚类的示例代码: ```python import json from sklearn.cluster import KMeans # 读取GeoJSON文件 with open('lines.geojson') as file: data = json.load(file) # 提取特征 features = [] for feature in data['features']: direction = feature['properties']['direction'] start_point = feature['properties']['start_point'] end_point = feature['properties']['end_point'] features.append([start_point[0], start_point[1], end_point[0], end_point[1], direction]) # 执行K-means聚类 kmeans = KMeans(n_clusters=2) kmeans.fit(features) # 获取聚类结果 labels = kmeans.labels_ # 输出每个线段的聚类结果 for i, feature in enumerate(data['features']): print(f"Line {i+1}: Cluster {labels[i]}") ``` 这个示例将线段聚类为两个簇,并输出每个线段所属的聚类结果。 请注意,这只是一个简单的示例,实际应用中可能需要进行更多的数据预处理和特征工程,并根据具体需求选择合适的聚类算法和参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sun_falls

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值