Java求geometry的面积最小外接矩形

39 篇文章 5 订阅
19 篇文章 7 订阅

Java求geometry的面积最小外接矩形

geom.getEnvelope() 得到外接矩形,不一定是面积最小;可以对多边形的每一条边求外接矩形,然后比较得到最小外接矩形

  • geom.getEnvelope()); // 外接矩形4个点
  • geom.getEnvelopeInternal()); // 外接矩形对角线俩点
  • geom.getBoundary()); // 首尾俩点
  • (new ConvexHull(geom)).getConvexHull(); // 获取凸包(凸包可以理解为线,多线闭合后的多边形)

这篇博客将分为3步进行求解;
1. 获取Envelope的外接矩形,获取面积;
2. 获取凸包,旋转获取每一条边对应的外接矩形,得到凸包面积最小外接矩形;
3. 将1获取的矩形与2获取的矩形面积进行比较,得到最终的面积最小外接矩形;

以上可得出,Envelope获取的外接矩形和凸包旋转获取的外接矩形均有可能为面积最小矩形。下边用俩个例子证明这一点;

1. 效果图

原始geom 黄色线 VS 凸包淡紫色填充:

在这里插入图片描述

原始geom VS Envelope外接矩形 VS 凸包旋转后最小面积矩形 VS EnvelopeInternal对角线 VS Boundary首尾点效果图如下:

呈现了俩组几何的结果
外接矩形面以淡紫色填充为:geom.getEnvelope()); // 外接矩形4个点以polygon面渲染;
矩形的对角线黄色线为:geom.getEnvelopeInternal()); // 外接矩形对角线俩点以线渲染;
原始geom为多点的黄色线;
geom.getBoundary()); // 首尾俩点以蓝色点渲染

在这里插入图片描述可视化可参考之前的博客: 、

Mapbox HTML可视化点,线,多线,面带底图

原始几何 VS Envelope外接矩形 VS 凸包旋转后最小面积矩形 VS EnvelopeInternal对角线 VS Boundary首尾点效果图如下:
在上图的基础上增加了凸包旋转后最小面积矩形,以淡紫色面填充;

在这里插入图片描述

1.1 凸包旋转后的面积最小外接矩形为面积最小矩形可视化如图:

如下图很明显,凸包旋转后的矩形面积比Envelope获取到的矩形面积要小。凸包旋转后的矩形是面积最小矩形。
在这里插入图片描述
代码验证下确实如此:
在这里插入图片描述

1.2 Envelope矩形为面积最小矩形

凸包旋转后的外接矩形不是面积最小矩形可视化如图:
如下图所示,直观来看,凸包旋转后的面积最小矩形 与 Envelope获取到的矩形面积看起来基本差不多,事实是Envelope获取到的矩形是面积最小矩形。
在这里插入图片描述

代码验证如下:Envelope获取到的矩形是面积最小矩形。
在这里插入图片描述

2. java源码

<dependency>
    <groupId>com.vividsolutions</groupId>
    <artifactId>jts</artifactId>
    <version>1.13</version>
</dependency>
import com.vividsolutions.jts.algorithm.ConvexHull;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

/*************************************
 *Class Name: TestGeomMinRectangle
 *Description: <测试获取Geometry面积最小外接矩形>
 *@author: Seminar
 *@create: 2022/9/25
 *@since 1.0.0
 *************************************/
@Slf4j
public class TestGeomMinRectangle {

    private static GeometryFactory gf = new GeometryFactory();

    // 多边形几何
    String polygon;
    // 线几何
    String lineString;
    // 多边几何
    String multiPoint;

    /**
     * 初始化wkt
     */
    public void initWkt() {
        this.polygon = "POLYGON ((116.42486572265626 39.99500778093748, 116.51962280273439 39.886557705928475, 116.44546508789064 39.78110197709871, 116.31637573242189 39.818029898770206, 116.27655029296876 39.93817189499188, 116.42486572265626 39.99500778093748))";

        this.lineString = "LINESTRING (117.18292236328126 40.16208338164619, 119.01489257812501 39.48284540453334)";

        this.multiPoint = "MULTIPOINT ((115.48690795898439 40.12639098502455), (115.80139160156251 40.148438503139076), (115.83847045898439 39.9665957444875), (115.90850830078126 39.854937988531276), " +
                "(115.98403930664062 39.816975090490004), (115.84808349609376 39.769491963709), (115.60638427734376 39.707186656826565), (115.44708251953126 39.82119422647455), " +
                "(115.32348632812501 39.961332959837826), (115.36605834960939 40.09593265290902), (115.59951782226564 39.940277770390324), (115.74371337890626 39.842286020743394), " +
                "(115.58715820312501 39.79271003204449), (115.76019287109376 39.7631584037253), (115.87280273437501 39.67759833072648), (115.96481323242189 40.18307014852534), " +
                "(115.69152832031251 40.2155868104582), (115.63934326171876 40.073868105094846), (115.43060302734376 39.56547053068436), (115.66955566406251 39.470125122358176), " +
                "(115.83709716796875 39.55911824217187), (115.91949462890626 39.44679856427205), (115.54046630859376 39.42346418978385), (115.07354736328125 39.63319206567459), " +
                "(115.11474609375 40.092781012494065), (115.19439697265626 40.287906612507406), (114.98291015625001 39.83385008019448), (114.97467041015626 39.47224533091451), " +
                "(115.27130126953126 39.38101803294523), (115.59265136718751 39.34067026099156))";
    }

    /**
     * wkt 转geometry
     *
     * @param wkt
     * @return
     * @throws ParseException
     */
    public static Geometry wkt2Geo(String wkt) throws ParseException {
        WKTReader reader = new WKTReader(gf);
        Geometry geom = reader.read(wkt);
        return geom;
    }

    /**
     * 旋转
     *
     * @param coord  旋转坐标
     * @param center 旋转中心
     * @param angle  角度
     * @return 旋转后结果
     */
    public static Coordinate[] get(Coordinate[] coord, Coordinate center, double angle) {
        Coordinate[] newCoord = new Coordinate[coord.length];
        double cos = Math.cos(angle), sin = Math.sin(angle);
        double xc = center.x, yc = center.y;
        Coordinate ci;
        double x, y;
        for (int i = 0; i < coord.length; i++) {
            ci = coord[i];
            x = ci.x;
            y = ci.y;
            newCoord[i] = new Coordinate(xc + cos * (x - xc) - sin * (y - yc),
                    yc + sin * (x - xc) + cos * (y - yc));
        }
        return newCoord;
    }

    /**
     * 旋转点
     *
     * @param point  被旋转的点
     * @param center 旋转中心
     * @param angle  角度
     * @return 旋转后坐标
     */
    public static Coordinate get(Coordinate point, Coordinate center, double angle) {
        double cos = Math.cos(angle);
        double sin = Math.sin(angle);
        double x = point.x;
        double y = point.y;
        double centerX = center.x;
        double centerY = center.y;
        return new Coordinate(centerX + cos * (x - centerX) - sin * (y - centerY),
                centerY + sin * (x - centerX) + cos * (y - centerY));
    }

    /**
     * 旋转
     *
     * @param geom   geometry
     * @param center 旋转中心
     * @param angle  旋转角度
     * @param gf     构造器
     * @return 旋转结果
     */
    public static Geometry get(Geometry geom, Coordinate center, double angle, GeometryFactory gf) {
        if (geom instanceof Point) {
            return get((Point) geom, center, angle, gf);
        } else if (geom instanceof Polygon) {
            return get((Polygon) geom, center, angle, gf);
        } else if (geom instanceof LineString) {
            return get((LineString) geom, center, angle, gf);
        } else if (geom instanceof LinearRing) {
            return get((LinearRing) geom, center, angle, gf);
        }
        return null;
    }

    /**
     * 旋转
     *
     * @param linearRing 旋转环
     * @param center     旋转中心
     * @param angle      旋转角度
     * @param gf         构造器
     * @return 旋转后结果
     */
    public static LinearRing get(LinearRing linearRing, Coordinate center, double angle,
                                 GeometryFactory gf) {
        return gf.createLinearRing(get(linearRing.getCoordinates(), center, angle));
    }

    /**
     * x旋转
     *
     * @param geom   旋转面
     * @param center 旋转中心
     * @param angle  旋转角度
     * @param gf     构造器
     * @return 旋转结果
     */
    public static Polygon rotationPolygon(Polygon geom, Coordinate center, double angle, GeometryFactory gf) {
        LinearRing linearRing = get((LinearRing) geom.getExteriorRing(), center, angle, gf);
        LinearRing[] linearRings = new LinearRing[geom.getNumInteriorRing()];
        for (int j = 0; j < geom.getNumInteriorRing(); j++) {
            linearRings[j] = get((LinearRing) geom.getInteriorRingN(j), center, angle, gf);
        }
        return gf.createPolygon(linearRing, linearRings);
    }

    public static Polygon getRotatedMinRectangle(Geometry geom, GeometryFactory gf) {
        // 获取凸包算法
        Geometry hull = (new ConvexHull(geom)).getConvexHull();
        if (!(hull instanceof Polygon)) {
            return null;
        }
        Polygon convexHull = (Polygon) hull;
//        System.out.println(convexHull);

        // 直接使用中心值
        Coordinate c = geom.getCentroid().getCoordinate();
//        System.out.println("==============旋转基点==============");
//        System.out.println(new GeometryFactory().createPoint(c));
//        System.out.println("==============旋转基点==============");
        Coordinate[] coords = convexHull.getExteriorRing().getCoordinates();

        double minArea = Double.MAX_VALUE;
        double minAngle = 0;
        Polygon ssr = null;
        Coordinate ci = coords[0];
        Coordinate cii;
        for (int i = 0; i < coords.length - 1; i++) {
            cii = coords[i + 1];
            double angle = Math.atan2(cii.y - ci.y, cii.x - ci.x);
            Polygon rect = (Polygon) rotationPolygon(convexHull, c, -1 * angle, gf).getEnvelope();
            double area = rect.getArea();
//            此处可以将 rotationPolygon 放到list中求最小值
//            Polygon rotationPolygon = Rotation.get(rect, c, angle, gf);
//            System.out.println(rotationPolygon);
            if (area < minArea) {
                minArea = area;
                ssr = rect;
                minAngle = angle;
            }
            ci = cii;
        }

        return rotationPolygon(ssr, c, minAngle, gf);
    }

    /**
     * 获取geom的面积最小外接矩形
     * 1. 获取Envelope的外接矩形,获取面积;
     * 2. 获取凸包,旋转获取每一条边对应的外接矩形,得到凸包面积最小外接矩形;
     * 3. 将1获取的矩形与2获取的矩形面积进行比较,得到最终的面积最小外接矩形;
     *
     * @param geom
     * @return
     */
    private Geometry getMinRectangle(Geometry geom) {
        double envelopeArea = geom.getEnvelope().getArea();
        double rotateMinArea = getRotatedMinRectangle(geom, gf).getArea();
        return envelopeArea > rotateMinArea ? getRotatedMinRectangle(geom, gf) : geom.getEnvelope();
    }

    @Test
    public void testGeom() throws ParseException {
        String lineString = "LINESTRING(120.067304 30.39167,120.067304 30.36469," +
                "120.04022 30.3512,120.013136 30.36469," +
                "120.013136 30.39167,120.04022 30.40516)";
        lineString = "LINESTRING(120.14322240845 30.236064370321," +
                "120.1233608862 30.224531990576,120.0831192649 30.238661839459," +
                "120.07632605996 30.2524671110650)";
        Geometry geom = wkt2Geo(lineString);
        log.info("origin  : {}", lineString);
        log.info("Envelope: {} {}", geom.getEnvelope().getArea(), geom.getEnvelope()); // 外接矩形4个点
        log.info("{}", geom.getEnvelopeInternal()); // 外接矩形对角线俩点
        log.info("{}", geom.getBoundary()); // 首尾俩点
        log.info("{}", (new ConvexHull(geom)).getConvexHull()); // 凸包
        log.info("Rotated : {} {}", getRotatedMinRectangle(geom, gf).getArea(), getRotatedMinRectangle(geom, gf));
        log.info("Min     : {} {}", getMinRectangle(geom).getArea(), getMinRectangle(geom));
    }
}

3. html源码

multi2.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Add multiple geometries from one GeoJSON source</title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
    <link href="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css" rel="stylesheet">
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.js"></script>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
    </style>
</head>
<body>
<div id="map"></div>
<script>

    mapboxgl.accessToken = 'xxxxx';
    var map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/streets-v11',
        center: [120.06022, 30.37818], //地图中心点
        zoom: 10
    });

    map.on('load', function () {
        map.addSource('national-park', {
            'type': 'geojson',
            'data': {
                'type': 'FeatureCollection',
                'features': [
                    //凸包
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Polygon',
                            'coordinates': [
                                [
                                    [120.04022, 30.3512], [120.013136, 30.36469],
                                    [120.013136, 30.39167], [120.04022, 30.40516],
                                    [120.067304, 30.39167],
                                    [120.067304, 30.36469], [120.04022, 30.3512]
                                ]
                            ]
                        }
                    },
                    // Envelope外接矩形
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Polygon',
                            'coordinates': [
                                [
                                    [120.013136, 30.3512], [120.013136, 30.40516],
                                    [120.067304, 30.40516], [120.067304, 30.3512]
                                ]
                            ]
                        }
                    },
                    // 旋转后最小面积外接矩形
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Polygon',
                            'coordinates': [
                                [
                                    [120.07807106579423, 30.38630713936036],
                                    [120.05653693420575, 30.34307286063966],
                                    [120.00236893420576, 30.370052860639653],
                                    [120.02390306579424, 30.413287139360353],
                                    [120.07807106579423, 30.38630713936036]
                                ]
                            ]
                        }
                    },
                    // 原始几何线
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'LineString',
                            'coordinates': [
                                [120.067304, 30.39167],
                                [120.067304, 30.36469],
                                [120.04022, 30.3512],
                                [120.013136, 30.36469],
                                [120.013136, 30.39167],
                                [120.04022, 30.40516]
                            ]
                        }
                    },
                    // getEnvelopeInternal对角线
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'LineString',
                            'coordinates': [
                                [120.013136, 30.40516],
                                [120.067304, 30.3512]
                            ]
                        }
                    },
                    // getBoundary首点
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [120.067304, 30.39167]
                        }
                    },
                    // getBoundary尾点
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [120.04022, 30.40516]
                        }
                    },


                    //凸包
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Polygon',
                            'coordinates': [
                                [
                                    [120.1233608862, 30.224531990576], [120.0831192649, 30.238661839459],
                                    [120.07632605996, 30.252467111065], [120.14322240845, 30.236064370321],
                                    [120.1233608862, 30.224531990576]
                                ]
                            ]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Polygon',
                            'coordinates': [
                                [
                                    [120.07632605996, 30.224531990576], [120.07632605996, 30.252467111065],
                                    [120.14322240845, 30.252467111065], [120.14322240845, 30.224531990576]
                                ]
                            ]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Polygon',
                            'coordinates': [
                                [
                                    [120.07253234575715, 30.236994963370464],
                                    [120.07632605996001, 30.252467111064995],
                                    [120.14322240845, 30.236064370321],
                                    [120.13942869424714, 30.22059222262647]
                                ]
                            ]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'LineString',
                            'coordinates': [
                                [
                                    120.14322240845,
                                    30.236064370321
                                ],
                                [
                                    120.1233608862,
                                    30.224531990576
                                ],
                                [
                                    120.0831192649,
                                    30.238661839459
                                ],
                                [
                                    120.07632605996,
                                    30.252467111065
                                ]
                            ]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'LineString',
                            'coordinates': [
                                [120.07632605996, 30.252467111065],
                                [120.14322240845, 30.224531990576]
                            ]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [120.07632605996, 30.252467111065]
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [120.14322240845, 30.236064370321]
                        }
                    }
                ]
            }
        });

        map.addLayer({
            'id': 'park-boundary',
            'type': 'fill',
            'source': 'national-park',
            'paint': {
                'fill-color': '#AE00F2',
                'fill-opacity': 0.4
            },
            'filter': ['==', '$type', 'Polygon']
        });

        map.addLayer({
            'id': 'route',
            'type': 'line',
            'source': 'national-park',
            'layout': {
                'line-join': 'round',
                'line-cap': 'round'
            },
            'paint': {
                'line-color': '#DEFF00',
                'line-width': 3
            },
            'filter': ['==', '$type', 'LineString']
        });

        map.addLayer({
            'id': 'park-volcanoes',
            'type': 'circle',
            'source': 'national-park',
            'paint': {
                'circle-radius': 6,
                'circle-color': '#0000FF'
            },
            'filter': ['==', '$type', 'Point']
        });
    });
</script>

</body>
</html>

参考

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python的GDAL库提供了裁剪栅格影像的功能,并且可以根据需要通过最小外接矩形来裁剪影像。下面是用Python GDAL进行最小外接矩形裁剪的步骤: 首先,导入需要的库,包括GDAL库和OGR库。GDAL库用于处理栅格数据,OGR库用于处理矢量数据。 ```python from osgeo import gdal, ogr ``` 然后,打开需要裁剪的栅格影像,并获取其投影信息和地理转换信息。 ```python # 打开栅格影像 ds = gdal.Open("input.tif") # 获取投影信息和地理转换信息 proj = ds.GetProjection() gt = ds.GetGeoTransform() ``` 接下来,定义最小外接矩形的位置和大小。可以根据需要手动指定,也可以通过矢量数据计算得到。 ```python # 手动指定最小外接矩形位置和大小 xmin, ymin, xmax, ymax = 10, 20, 30, 40 ``` 然后,创建最小外接矩形的矢量数据。 ```python # 创建最小外接矩形的矢量数据 ring = ogr.Geometry(ogr.wkbLinearRing) ring.AddPoint(xmin, ymin) ring.AddPoint(xmin, ymax) ring.AddPoint(xmax, ymax) ring.AddPoint(xmax, ymin) ring.AddPoint(xmin, ymin) poly = ogr.Geometry(ogr.wkbPolygon) poly.AddGeometry(ring) ``` 接下来,设置裁剪的参数,并执行裁剪操作。 ```python # 设置裁剪参数 options = ["ALL_TOUCHED=TRUE"] # 执行裁剪操作 gdal.Warp("output.tif", ds, format="GTiff", cutlineDSName=poly, dstSRS=proj, outputType=gdal.GDT_Float32, options=options) ``` 最后,关闭数据集并输出裁剪后的影像。 ```python # 关闭数据集 ds = None # 输出裁剪后的影像 output = gdal.Open("output.tif") ``` 以上是使用Python GDAL进行最小外接矩形裁剪的步骤。通过指定最小外接矩形的位置和大小,可以裁剪出指定区域的栅格影像。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序媛一枚~

您的鼓励是我创作的最大动力。

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

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

打赏作者

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

抵扣说明:

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

余额充值