您的位置:首页 > 其它

判断一个点是否在多边形内部 - 射线法Scala实现

2018-03-29 16:34 489 查看

原文链接

判断一个点是否在多边形内部 [1] 射线法思路
判断一个点是否在多边形内部 [2] 射线法实现最近有个需求,就是判断一个经纬度落在哪个城市上.用scala实现的
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable.ArrayBuffer

object Day29 {
val sc: SparkContext = scInitialization //初始化sc
def main(args: Array[String]): Unit = {
getPoint
}

//判断某个点是否在一个多边形中
/**
*
* @param p    待测的某个点 类型:元组(Double,Double)
* @param poly 存储多个城市边界的多边形顶点 类型:数组 存储的是元组(String,Array[(Double,Double)])
* @return
*/
def pointInPolygon(p: (Double, Double), poly:  Array[(String,Array[(Double,Double)])]): String = {
//遍历所有城市
for (i <- poly.indices) {
val arrs = poly(i)._2 //取出第i个城市的边界顶点的集合
val f = rayCasting(p, arrs)
if (f.equals("in")) {  //in表示在第i个城市里面
return poly(i)._1
}
}
"外星人"
}

/**
* 射线法的实现
* @param p  待测点 类型:元组(Double,Double)
* @param poly  城市边界顶点集合 类型:存储元组(Double,Double)的数组
* @return  "in"表示待测点在城市里面  "on"表示不在
*/
def rayCasting(p: (Double, Double), poly: Array[(Double, Double)]): String ={
val px = p._1 //待测点的经度 相当于x
val py = p._2 //带测点的纬度 相当于y
var flag = false
val l = poly.length
var j = l - 1
for(i <-0 until poly.length ) {  //取出边界的相邻两个点
val sx = poly(i)._1
val sy = poly(i)._2
val tx = poly(j)._1
val ty = poly(j)._2
// 点与多边形顶点重合
4000
if ((sx == px && sy == py) || (tx == px && ty == py)) {
return "in"
}

// 判断线段两端点是否在射线两侧
//思路:作p点平行于y轴的射线 作s,t的平行线直线  如果射线穿过线段,则py的值在sy和ty之间
if ((sy < py && ty >= py) || (sy >= py && ty < py)) {
// 线段上与射线 Y 坐标相同的点的 X 坐标 求射线与线段的交点
val x = sx + (py - sy) * (tx - sx) / (ty - sy)

// 点在多边形的边上
if (x == px) {
return "on"
}

// 射线穿过多边形的边界
if (x > px) {
flag = !flag
}
}
j = i
}
// 射线穿过多边形边界的次数为奇数时点在多边形内
if(flag)
return "in"
else
return  "on"
}

//获取多边形数据
/*
*用一个集合来存取多边形的顶点,形成一个多边形范围  返回的是存储有n个城市 每个城市有n个边界点
*/
def getPolygon: Array[(String,Array[(Double,Double)])] = {
val input = sc.textFile("E:\\city.txt")
val out = input.map(x => { //清洗数据
val s = x.split("\001")(4)
val arr = new ArrayBuffer[(Double, Double)]()
val p = s.split("[@;\\|]")
for (i <- 0 until p.length if p(i).contains(",")) {
val ar = p(i).split(",")
arr.append((ar(0).toDouble, ar(1).toDouble))
}
(x.split("\001")(1),arr.toArray)
})//最后数据格式:(城市名,数组)
//将rdd取出到内存中来
val s = out.collect()
s
}

//获取待测点数据
def getPoint():Unit ={
val lines =sc.textFile("E:\\agps.txt")
val out = lines.filter(f = x => { //清洗数据
val s = x.split("\\|")
s.length > 13 && !s(12).isEmpty && !s(13).isEmpty
}).map(f = x => {
val s = x.split("\\|")
val longitude = s(12).toDouble
val latitude = s(13).toDouble
((longitude, latitude),1)
}).reduceByKey(_+_).map(x=>
x._1
)//最后数据格式 (经度,纬度)
val rules = getPolygon   //拿到城市边界集合数组
val file = out.map(x =>{
val city = pointInPolygon(x,rules) //并行处理,返回所在的城市名
s"${x._1}|${x._2}|$city"
})
file.saveAsTextFile("E:\\output")
}
//sc初始化
private def scInitialization = {
val conf = new SparkConf().setMaster("local").setAppName("Day27")
val sc = new SparkContext(conf)
sc
}
}
本人只是一个scala初学者,写个播客做个记录备忘。有错误或者更好的见解欢迎留言指导
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: