Android通过经纬度点集合算多边形中心、重心、面积

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/shao941122/article/details/53671643

最近项目用到:在不规则任意多边形的中心点加一个图标。(e.g: xx地区发生暴雪,暴雪区域是多边形,给多边形中心加一个暴雪的图标)

之前的设计是,计算不规则多边形范围矩形bounds的中心点。这个比较简单,对于一些圆,矩形,凸多边形都比较适合。但是遇到凹多边形就会出现问题,比如一个月牙型的不规则多边形,bounds的中心点,就落到月牙外了。就有点难以接受了。

经过讨论,决定将中心改为重心。

下面上代码,

计算不规则任意多边形的中心:


 
 
  1. /**
  2. * 获取不规则多边形几何中心点
  3. *
  4. * @param mPoints
  5. * @return
  6. */
  7. public static LatLng getCenterPoint( List<LatLng> mPoints) {
  8. // 1 自己计算
  9. // 2 使用Google map API提供的方法(推荐)
  10. LatLngBounds.Builder boundsBuilder = LatLngBounds.builder();
  11. for (LatLng ll : mPoints)
  12. boundsBuilder. include(ll);
  13. return boundsBuilder.build().getCenter();
  14. }

 计算不规则任意多边形的重心:


 
 
  1. /**
  2. * 获取不规则多边形重心点
  3. *
  4. * @param mPoints
  5. * @return
  6. */
  7. public static LatLng getCenterOfGravityPoint(List<LatLng> mPoints) {
  8. double area = 0.0; //多边形面积
  9. double Gx = 0.0, Gy = 0.0; // 重心的x、y
  10. for ( int i = 1; i <= mPoints.size(); i++) {
  11. double iLat = mPoints. get(i % mPoints.size()).latitude;
  12. double iLng = mPoints. get(i % mPoints.size()).longitude;
  13. double nextLat = mPoints. get(i - 1).latitude;
  14. double nextLng = mPoints. get(i - 1).longitude;
  15. double temp = (iLat * nextLng - iLng * nextLat) / 2.0;
  16. area += temp;
  17. Gx += temp * (iLat + nextLat) / 3.0;
  18. Gy += temp * (iLng + nextLng) / 3.0;
  19. }
  20. Gx = Gx / area;
  21. Gy = Gy / area;
  22. return new LatLng(Gx, Gy);
  23. }

其中LatLng类就是一个包含经纬度点的简单类。可以自己创建一个包含 x ,y 的类代替。


 
 
  1. public class LatLng {
  2. public final double latitude;
  3. public final double longitude;
  4. }

好多人说不知道LatLngBounds类的具体实现,其实这是Google map包中的一个类,内部功能很简单,就是提供了一个构造器Builder可以不断的往里面添加经纬度点LatLng,不断计算更新Bounds范围和center中心点。

下面贴上整理后的LatLngBounds类,可以减去导入Google map包的麻烦


 
 
  1. /**
  2. * 经纬度范围类
  3. *
  4. * 复写com.google.android.gms.maps.model.LatLngBounds中核心方法
  5. *
  6. * @author maple
  7. */
  8. @Deprecated("条件允许,请使用com.google.android.gms.maps.model.LatLngBounds")
  9. class LatLngBounds constructor(
  10. private val southwest: LatLng, // 左下角 点
  11. private val northeast: LatLng // 右上角 点
  12. ) {
  13. val center: LatLng
  14. get() {
  15. // 计算中心点纬度
  16. val centerLat = ( this.southwest.latitude + this.northeast.latitude) / 2.0
  17. // 计算中心点经度
  18. val neLng = this.northeast.longitude // 右上角 经度
  19. val swLng: Double = this.southwest.longitude // 左下角 经度
  20. val centerLng: Double = if (swLng <= neLng) {
  21. (neLng + swLng) / 2.0
  22. } else {
  23. (neLng + 360.0 + swLng) / 2.0
  24. }
  25. return LatLng(centerLat, centerLng)
  26. }
  27. // 某个点是否在该范围内(包含边界)
  28. fun contains(point: LatLng): Boolean {
  29. return latContains(point.latitude) && this.lngContains(point.longitude)
  30. }
  31. // 某个纬度值是否在该范围内(包含边界)
  32. private fun latContains(lat: Double): Boolean {
  33. return this.southwest.latitude <= lat && lat <= this.northeast.latitude
  34. }
  35. // 某个经度值是否在该范围内(包含边界)
  36. private fun lngContains(lng: Double): Boolean {
  37. return if ( this.southwest.longitude <= this.northeast.longitude) {
  38. this.southwest.longitude <= lng && lng <= this.northeast.longitude
  39. } else {
  40. this.southwest.longitude <= lng || lng <= this.northeast.longitude
  41. }
  42. }
  43. // 小数据量可以使用该方法,大数据量建议使用Builder中的include()
  44. fun including(point: LatLng): LatLngBounds {
  45. val swLat = Math.min( this.southwest.latitude, point.latitude)
  46. val neLat = Math.max( this.northeast.latitude, point.latitude)
  47. var neLng = this.northeast.longitude
  48. var swLng = this.southwest.longitude
  49. val pLng = point.longitude
  50. if (! this.lngContains(pLng)) {
  51. if (zza(swLng, pLng) < zzb(neLng, pLng)) {
  52. swLng = pLng
  53. } else {
  54. neLng = pLng
  55. }
  56. }
  57. return LatLngBounds(LatLng(swLat, swLng), LatLng(neLat, neLng))
  58. }
  59. /**
  60. * LatLngBounds生成器
  61. */
  62. class Builder {
  63. private var swLat = 1.0 / 0.0 // 左下角 纬度
  64. private var swLng = 0.0 / 0.0 // 左下角 经度
  65. private var neLat = -1.0 / 0.0 // 右上角 纬度
  66. private var neLng = 0.0 / 0.0 // 右上角 经度
  67. fun include(point: LatLng): Builder {
  68. this.swLat = Math.min( this.swLat, point.latitude)
  69. this.neLat = Math.max( this.neLat, point.latitude)
  70. val pLng = point.longitude
  71. if (java.lang. Double.isNaN( this.swLng)) {
  72. this.swLng = pLng
  73. } else {
  74. if (lngContains(pLng)) {
  75. return this
  76. }
  77. if (zza( this.swLng, pLng) < zzb( this.neLng, pLng)) {
  78. this.swLng = pLng
  79. return this
  80. }
  81. }
  82. this.neLng = pLng
  83. return this
  84. }
  85. // 某个经度值是否在该范围内(包含边界)
  86. private fun lngContains(lng: Double): Boolean {
  87. return if ( this.swLng <= this.neLng) {
  88. this.swLng <= lng && lng <= this.neLng
  89. } else {
  90. this.swLng <= lng || lng <= this.neLng
  91. }
  92. }
  93. fun build(): LatLngBounds {
  94. // Preconditions.checkState(!java.lang.Double.isNaN(this.swLng), "no included points")
  95. return LatLngBounds(LatLng( this.swLat, this.swLng), LatLng( this.neLat, this.neLng))
  96. }
  97. }
  98. companion object {
  99. fun builder(): Builder {
  100. return Builder()
  101. }
  102. // 前者 - 后者
  103. private fun zza(var0: Double, var2: Double): Double {
  104. return (var0 - var2 + 360.0) % 360.0
  105. }
  106. // 后者 - 前者
  107. private fun zzb(var0: Double, var2: Double): Double {
  108. return (var2 - var0 + 360.0) % 360.0
  109. }
  110. }
  111. }

注意⚠️:LatLngBounds中including()方法  和  Builder中include()方法在具体实现上是相同,但是LatLngBounds每次都会new一个新的对象,所以不适合大批量数据时使用。

e.g:在计算一个点比较多的Polygon的范围时,建议使用Builder中的include()方法。

 

通过这张图,就可以发现中心和重心的区别

     

项目实际表现:

     

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值