計算任意多邊形的面積、中心、重心

最近項目用到:在不規則任意多邊形的中心點加一個圖標。(e.g: xx地區發生暴雪,暴雪區域是多邊形,給多邊形中心加一個暴雪的圖標)java

以前的設計是,計算不規則多邊形範圍矩形bounds的中心點。這個比較簡單,對於一些圓,矩形,凸多邊形都比較適合。可是遇到凹多邊形就會出現問題,好比一個月牙型的不規則多邊形,bounds的中心點,就落到月牙外了。就有點難以接受了。android

通過討論,決定將中心改成重心。git

下面上代碼,github

計算不規則任意多邊形的中心:大數據

 /**
     * 獲取不規則多邊形幾何中心點
     *
     * @param mPoints
     * @return
     */
    public static LatLng getCenterPoint(List<LatLng> mPoints) {
        // 1 本身計算
        // 2 使用Google map API提供的方法(推薦)
        LatLngBounds.Builder boundsBuilder = LatLngBounds.builder();
        for (LatLng ll : mPoints)
            boundsBuilder.include(ll);
        return boundsBuilder.build().getCenter();
    }

或者分別取經度的最大值和最小值,求和,再取平均值,取緯度的最大值和最小值,求和,再取平均值,這樣就是中心點了。ui

固然也能夠取全部經度的和的平均值和因此緯度的平均值,這樣也是中心點了。this

 

計算不規則任意多邊形的重心:google

/**
     * 獲取不規則多邊形重心點
     *
     * @param mPoints
     * @return
     */
    public static LatLng getCenterOfGravityPoint(List<LatLng> mPoints) {
        double area = 0.0;//多邊形面積
        double Gx = 0.0, Gy = 0.0;// 重心的x、y
        for (int i = 1; i <= mPoints.size(); i++) {
            double iLat = mPoints.get(i % mPoints.size()).latitude;
            double iLng = mPoints.get(i % mPoints.size()).longitude;
            double nextLat = mPoints.get(i - 1).latitude;
            double nextLng = mPoints.get(i - 1).longitude;
            double temp = (iLat * nextLng - iLng * nextLat) / 2.0;
            area += temp;
            Gx += temp * (iLat + nextLat) / 3.0;
            Gy += temp * (iLng + nextLng) / 3.0;
        }
        Gx = Gx / area;
        Gy = Gy / area;
        return new LatLng(Gx, Gy);
    }

其中LatLng類就是一個包含經緯度點的簡單類。能夠本身建立一個包含 x ,y 的類代替。spa

public class LatLng {
        public final double latitude;
        public final double longitude;
    }

好多人說不知道LatLngBounds類的具體實現,其實這是Google map包中的一個類,內部功能很簡單,就是提供了一個構造器Builder能夠不斷的往裏面添加經緯度點LatLng,不斷計算更新Bounds範圍和center中心點。.net

下面貼上整理後的LatLngBounds類,能夠減去導入Google map包的麻煩

/**
 * 經緯度範圍類
 *
 * 複寫com.google.android.gms.maps.model.LatLngBounds中核心方法
 *
 * @author maple
 */
@Deprecated("條件容許,請使用com.google.android.gms.maps.model.LatLngBounds")
class LatLngBounds constructor(
        private val southwest: LatLng,// 左下角 點
        private val northeast: LatLng // 右上角 點
) {
 
    val center: LatLng
        get() {
            // 計算中心點緯度
            val centerLat = (this.southwest.latitude + this.northeast.latitude) / 2.0
            // 計算中心點經度
            val neLng = this.northeast.longitude // 右上角 經度
            val swLng: Double = this.southwest.longitude // 左下角 經度
            val centerLng: Double = if (swLng <= neLng) {
                (neLng + swLng) / 2.0
            } else {
                (neLng + 360.0 + swLng) / 2.0
            }
            return LatLng(centerLat, centerLng)
        }
 
    // 某個點是否在該範圍內(包含邊界)
    fun contains(point: LatLng): Boolean {
        return latContains(point.latitude) && this.lngContains(point.longitude)
    }
 
    // 某個緯度值是否在該範圍內(包含邊界)
    private fun latContains(lat: Double): Boolean {
        return this.southwest.latitude <= lat && lat <= this.northeast.latitude
    }
 
    // 某個經度值是否在該範圍內(包含邊界)
    private fun lngContains(lng: Double): Boolean {
        return if (this.southwest.longitude <= this.northeast.longitude) {
            this.southwest.longitude <= lng && lng <= this.northeast.longitude
        } else {
            this.southwest.longitude <= lng || lng <= this.northeast.longitude
        }
    }
 
    // 小數據量能夠使用該方法,大數據量建議使用Builder中的include()
    fun including(point: LatLng): LatLngBounds {
        val swLat = Math.min(this.southwest.latitude, point.latitude)
        val neLat = Math.max(this.northeast.latitude, point.latitude)
        var neLng = this.northeast.longitude
        var swLng = this.southwest.longitude
        val pLng = point.longitude
        if (!this.lngContains(pLng)) {
            if (zza(swLng, pLng) < zzb(neLng, pLng)) {
                swLng = pLng
            } else {
                neLng = pLng
            }
        }
        return LatLngBounds(LatLng(swLat, swLng), LatLng(neLat, neLng))
    }
 
    /**
     * LatLngBounds生成器
     */
    class Builder {
        private var swLat = 1.0 / 0.0   // 左下角 緯度
        private var swLng = 0.0 / 0.0   // 左下角 經度
        private var neLat = -1.0 / 0.0  // 右上角 緯度
        private var neLng = 0.0 / 0.0   // 右上角 經度
 
        fun include(point: LatLng): Builder {
            this.swLat = Math.min(this.swLat, point.latitude)
            this.neLat = Math.max(this.neLat, point.latitude)
            val pLng = point.longitude
            if (java.lang.Double.isNaN(this.swLng)) {
                this.swLng = pLng
            } else {
                if (lngContains(pLng)) {
                    return this
                }
                if (zza(this.swLng, pLng) < zzb(this.neLng, pLng)) {
                    this.swLng = pLng
                    return this
                }
            }
            this.neLng = pLng
            return this
        }
 
        // 某個經度值是否在該範圍內(包含邊界)
        private fun lngContains(lng: Double): Boolean {
            return if (this.swLng <= this.neLng) {
                this.swLng <= lng && lng <= this.neLng
            } else {
                this.swLng <= lng || lng <= this.neLng
            }
        }
 
        fun build(): LatLngBounds {
            // Preconditions.checkState(!java.lang.Double.isNaN(this.swLng), "no included points")
            return LatLngBounds(LatLng(this.swLat, this.swLng), LatLng(this.neLat, this.neLng))
        }
    }
 
    companion object {
        fun builder(): Builder {
            return Builder()
        }
 
        // 前者 - 後者
        private fun zza(var0: Double, var2: Double): Double {
            return (var0 - var2 + 360.0) % 360.0
        }
 
        // 後者 - 前者
        private fun zzb(var0: Double, var2: Double): Double {
            return (var2 - var0 + 360.0) % 360.0
        }
    }
 
}

注意⚠️:LatLngBounds中including()方法  和  Builder中include()方法在具體實現上是相同,可是LatLngBounds每次都會new一個新的對象,因此不適合大批量數據時使用。

e.g:在計算一個點比較多的Polygon的範圍時,建議使用Builder中的include()方法。

Demo地址:https://github.com/shaoshuai904/GoogleMap_Demo

經過這張圖,就能夠發現中心和重心的區別

 

本文轉自:https://blog.csdn.net/shao941122/article/details/53671643

相關文章
相關標籤/搜索