springboot中使用geotools光滑shp矢量图形【拉普拉斯(Laplacian)光滑算法】

对shp文件中的每个要素进行矢量光滑,也就是消除锯齿,减少折线,让边线变得平滑。使用拉普拉斯(Laplacian)光滑算法。下面的示例代码是对任意 Geometry 类型进行平滑。

场景 iterations lambda 说明
普通道路线平滑 3 ~ 5 0.3 ~ 0.5 平滑明显但不过度
需要保形(不能动太多) 2 ~ 3 0.2 ~ 0.3 更保守
极度锯齿(比如 AI 输出图层) 5 ~ 8 0.4 ~ 0.6 强力平滑

效果:
在这里插入图片描述

/**
     * TODO 矢量光滑
     * 建议使用配置:
     *    场景	                  iterations	     lambda	        说明
     * 普通道路线平滑	               3 ~ 5	       0.3 ~ 0.5	  平滑明显但不过度
     * 需要保形(不能动太多)	       2 ~ 3	       0.2 ~ 0.3	  更保守
     * 极度锯齿(比如 AI 输出图层)	   5 ~ 8	       0.4 ~ 0.6	  强力平滑
     */
    private void trimZnjySmoothIterations(znjyjyParaModelJsonVo paraModelJsonVo, ZnjyInterpretationTask task) throws IOException {
        long startTime = System.currentTimeMillis();
        // 平滑的迭代次数,整数,例如 2、3、5
        Integer iterations = paraModelJsonVo.getSmoothIterations();
        // 每次迭代中顶点的移动程度,0~1 的小数,例如 0.3 表示移动到邻域平均位置的 30% 距离
        double lambda = paraModelJsonVo.getLambda();

        if (iterations == null || iterations <= 0 || lambda <= 0) {
            OpLogUtil.info("无效的矢量光滑参数,跳过处理");
            return;
        }

        FileDataStore store = null;
        FeatureIterator<SimpleFeature> features = null;
        try {
            store = getFileDataStoreStore(task);
            if (store == null) throw new IOException("无法打开SHP文件: " + task.getResultPath());

            SimpleFeatureStore featureStore = (SimpleFeatureStore) store.getFeatureSource();
            String geomName = featureStore.getSchema().getGeometryDescriptor().getLocalName();
            features = featureStore.getFeatures().features();

            List<FeatureId> fids = new ArrayList<>();
            List<Geometry> smoothGeoms = new ArrayList<>();
            int count = 0;

            while (features.hasNext()) {
                SimpleFeature f = features.next();
                Geometry g = (Geometry) f.getDefaultGeometry();
                Geometry sm = ShpFileStoreUtil.smoothGeometryLaplace(g, iterations, lambda);
                if (sm == null || sm.isEmpty()) sm = g;
                fids.add(ShpFileStoreUtil.FF.featureId(f.getID()));
                smoothGeoms.add(sm.copy());

                if (++count % 100 == 0) {
                    OpLogUtil.info("已光滑 {} 个要素", count);
                }
            }

            // 批量更新
            Map<String, Geometry> map = new HashMap<>();
            for (int i = 0; i < fids.size(); i++) {
                map.put(fids.get(i).getID(), smoothGeoms.get(i));
            }

            Transaction tx = new DefaultTransaction("laplace-smooth");
            featureStore.setTransaction(tx);
            FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
                    ((DataStore)featureStore.getDataStore())
                            .getFeatureWriter(featureStore.getSchema().getTypeName(), tx);

            int updated = 0;
            while (writer.hasNext()) {
                SimpleFeature f = writer.next();
                String id = f.getIdentifier().getID();
                if (map.containsKey(id)) {
                    f.setAttribute(geomName, map.get(id));
                    writer.write();
                    if (++updated % 100 == 0) {
                        OpLogUtil.info("已更新 {} 个要素", updated);
                    }
                }
            }
            tx.commit();
            writer.close();
            tx.close();

            OpLogUtil.info("光滑完成,共处理 {} 个要素,耗时 {} ms",
                    count, System.currentTimeMillis() - startTime);

        } catch (Exception e) {
            throw new IOException("光滑处理失败", e);
        } finally {
            if (features != null) features.close();
            if (store != null) store.dispose();
            System.gc();
        }
    }

ShpFileStoreUtil:

/**
     * 拉普拉斯(Laplacian)光滑
     * 对任意 Geometry 类型进行平滑
     */
    public static Geometry smoothGeometryLaplace(Geometry geometry, int iterations, double lambda) {
        if (geometry == null || geometry.isEmpty()) return geometry;

        GeometryFactory gf = geometry.getFactory();

        if (geometry instanceof LineString) {
            return laplaceSmoothLine((LineString) geometry, iterations, lambda);
        }

        if (geometry instanceof Polygon) {
            Polygon p = (Polygon) geometry;
            // 对外环做拉普拉斯平滑,得到 LineString
            LineString smoothedExteriorLS = laplaceSmoothLine(p.getExteriorRing(), iterations, lambda);
            // 用它的坐标序列创建 LinearRing
            LinearRing extRing = gf.createLinearRing(smoothedExteriorLS.getCoordinateSequence());

            // 如果有内环,也同样平滑并重建
            int n = p.getNumInteriorRing();
            LinearRing[] innerRings = new LinearRing[n];
            for (int i = 0; i < n; i++) {
                LineString smoothedInnerLS = laplaceSmoothLine(p.getInteriorRingN(i), iterations, lambda);
                innerRings[i] = gf.createLinearRing(smoothedInnerLS.getCoordinateSequence());
            }

            return gf.createPolygon(extRing, innerRings);
        }

        if (geometry instanceof MultiLineString) {
            MultiLineString mls = (MultiLineString) geometry;
            LineString[] arr = new LineString[mls.getNumGeometries()];
            for (int i = 0; i < arr.length; i++) {
                arr[i] = (LineString) laplaceSmoothLine((LineString) mls.getGeometryN(i), iterations, lambda);
            }
            return gf.createMultiLineString(arr);
        }

        if (geometry instanceof MultiPolygon) {
            MultiPolygon mp = (MultiPolygon) geometry;
            Polygon[] arr = new Polygon[mp.getNumGeometries()];
            for (int i = 0; i < arr.length; i++) {
                arr[i] = (Polygon) smoothGeometryLaplace(mp.getGeometryN(i), iterations, lambda);
            }
            return gf.createMultiPolygon(arr);
        }

        if (geometry instanceof GeometryCollection) {
            GeometryCollection gc = (GeometryCollection) geometry;
            Geometry[] geoms = new Geometry[gc.getNumGeometries()];
            for (int i = 0; i < geoms.length; i++) {
                geoms[i] = smoothGeometryLaplace(gc.getGeometryN(i), iterations, lambda);
            }
            return gf.createGeometryCollection(geoms);
        }

        // 其他类型不处理
        return geometry;
    }


	// 拉普拉斯光滑:只修改中间节点,端点保持不变
    public static LineString laplaceSmoothLine(LineString line, int iterations, double lambda) {
        Coordinate[] coords = line.getCoordinates();
        int n = coords.length;
        if (n < 3) return line;

        // 工作数组
        Coordinate[] curr = coords.clone();
        Coordinate[] next = new Coordinate[n];

        for (int it = 0; it < iterations; it++) {
            // 端点直接拷贝
            next[0] = new Coordinate(curr[0]);
            next[n-1] = new Coordinate(curr[n-1]);

            // 对每个中间点做 Laplacian 平滑
            for (int i = 1; i < n - 1; i++) {
                double x = curr[i].x + lambda * ((curr[i-1].x + curr[i+1].x) / 2 - curr[i].x);
                double y = curr[i].y + lambda * ((curr[i-1].y + curr[i+1].y) / 2 - curr[i].y);
                next[i] = new Coordinate(x, y);
            }

            // 交换数组
            Coordinate[] tmp = curr;
            curr = next;
            next = tmp;
        }

        return line.getFactory().createLineString(curr);
    }

	// 假设 FF 是 FilterFactory2 的实例
    public static final FilterFactory2 FF = CommonFactoryFinder.getFilterFactory2();
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐