最近项目迁入了一些原本使用ORACLE数据库的应用。然后发现,在gbase里没有那个求一点在不在不一个多边形内的空间函数。咋办呢。看来只能自定义一个了。 算法就直接百度了,使用射线法。然后在写的过程中,碰到一些性能问题。就是首先要求多边形最大最小纵横坐标。开始是在函数里计算的。后来发现每一行都要重复算一次,太浪费。就为这增加多了四个参数,提前算好,作为参数输入。不用每次计算一次。
一、先自定义一个最大最小坐标的函数,较简单。
use test;
drop FUNCTION if exists test.x_y_big_small;
DELIMITER //
CREATE FUNCTION x_y_big_small(verlst varchar(2000)) RETURNS varchar(500)
BEGIN
– 函数功能, 求给定点数据,给出最大最小纵横坐标。
– 用法 参数1 多边形顶点坐标列表,各点间用";“相隔,点的x,y之间用”,"分隔。
– test.x_y_big_small(‘91.09550580350339,29.620155895819444;91.09392478580635,29.625556121160596;91.08469020516684,29.622102522375467;91.08544478179496,29.61918256829592’)
– DECLARE resl INT;
– DECLARE ty INT;
set @verlist=verlst;
set @ver_count=LENGTH(@verlist)-LENGTH(replace(@verlist,’;’,’’))+1;
– 初始化最大最小x,y坐标,这里记第一个点为后面比较做准备。
set @max_x=to_number(SUBSTRING_INDEX(replace(SUBSTRING_INDEX(@verlist,’;’,1),SUBSTRING_INDEX(@verlist,’;’,0)||’;’,’’),’,’,1));
set @max_y=to_number(SUBSTRING_INDEX(replace(SUBSTRING_INDEX(@verlist,’;’,1),SUBSTRING_INDEX(@verlist,’;’,0)||’;’,’’),’,’,-1));
set @min_x=@max_x;
set @min_y=@max_y;
set @i=2;
– 计算最大最小x,y坐标
while @i<=@ver_count do
set @comp_x=to_number(SUBSTRING_INDEX(replace(SUBSTRING_INDEX(@verlist,’;’,@i),SUBSTRING_INDEX(@verlist,’;’,@i-1)||’;’,’’),’,’,1));
set @comp_y=to_number(SUBSTRING_INDEX(replace(SUBSTRING_INDEX(@verlist,’;’,@i),SUBSTRING_INDEX(@verlist,’;’,@i-1)||’;’,’’),’,’,-1));
if @max_x<@comp_x
then
set @max_x=@comp_x;
end if;
if @max_y<@comp_y
then
set @max_y=@comp_y;
end if;
if @min_x>@comp_x
then
set @min_x=@comp_x;
end if;
if @min_y>@comp_y
then
set @min_y=@comp_y;
end if;
set @i=@i+1;
end while;
RETURN @min_x||’,’||@max_x||’,’||@min_y||’,’||@max_y;
END //
DELIMITER ;
二、接下来,就上真正用来判断点在不在一个多边形内的函数了。
use test;
drop FUNCTION if exists test.space_inside_pnpoly;
DELIMITER //
CREATE FUNCTION space_inside_pnpoly(point_x decimal(20,14),point_y decimal(20,16),min_x decimal(20,14),max_x decimal(20,14),min_y decimal(20,14),max_y decimal(20,14),verlst varchar(2000)) RETURNS int
BEGIN
– 函数功能, 给定坐标,计算一点在不在一个多边形内。在返回1, 不在返回0。
– 此函数使用射线法来判断。
– 用法 参数1 判断点的X坐标; 参数2 判断点的y坐标;
– 参数3 min x;参数4 max x;参数5 min y; 参数6 max y;(3 -6 ,可用test.x_y_big_small()算出填 入
– 参数7 多边形顶点坐标列表,各点间用";“相隔,点的x,y之间用”,"分隔。
– 例 select test.space_inside_pnpoly(9,3,1,50,1,50,‘1,1;1,50;50,50;50,1’) from test.acid limit 1;
– DECLARE resl INT;
– DECLARE ty INT;
set @p_x=point_x;
set @p_y=point_y;
set @verlist=verlst;
set @min_x=min_x;
set @min_y=min_y;
set @max_x=max_x;
set @max_y=max_y;
set @ver_count=LENGTH(@verlist)-LENGTH(replace(@verlist,’;’,’’))+1;
– 第一种情况,判断最大最小外,返回0。 不在多边形内。
if @p_x<@min_x or @p_x>@max_x or @p_y<@min_y or @p_y>@max_y
then
RETURN 0;
end if;
– 第一线段第一点
set @first_p_x=to_number(SUBSTRING_INDEX(replace(SUBSTRING_INDEX(@verlist,’;’,1),SUBSTRING_INDEX(@verlist,’;’,0)||’;’,’’),’,’,1));
set @first_p_y=to_number(SUBSTRING_INDEX(replace(SUBSTRING_INDEX(@verlist,’;’,1),SUBSTRING_INDEX(@verlist,’;’,0)||’;’,’’),’,’,-1));
set @int_first_p_x=@first_p_x;
set @int_first_p_y=@first_p_y;
– 其它情况。
set @j=2;
set @intersect_count=0;
while @j<=@ver_count+1 do
– 取线段第二点
set @second_p_x=to_number(SUBSTRING_INDEX(replace(SUBSTRING_INDEX(@verlist,’;’,@j),SUBSTRING_INDEX(@verlist,’;’,@j-1)||’;’,’’),’,’,1));
set @second_p_y=to_number(SUBSTRING_INDEX(replace(SUBSTRING_INDEX(@verlist,’;’,@j),SUBSTRING_INDEX(@verlist,’;’,@j-1)||’;’,’’),’,’,-1));
set @cur_sec_x=@second_p_x;
set @cur_sec_y=@second_p_y;
if @j=@ver_count+1
then
set @first_p_x=@second_p_x;
set @first_p_y=@second_p_y;
set @second_p_x=@int_first_p_x;
set @second_p_y=@int_first_p_y;
end if;
– 此边的纵坐标范围
if @second_p_y>=@first_p_y
then
set @big_y=@second_p_y;
set @small_y=@first_p_y;
else
set @big_y=@first_p_y;
set @small_y=@second_p_y;
end if;
-- 此边的横坐标范围
if @second_p_x>=@first_p_x
then
set @big_x=@second_p_x;
set @small_x=@first_p_x;
else
set @big_x=@first_p_x;
set @small_x=@second_p_x;
end if;
-- 落在边上,返回1.
if @p_y>=@small_y and @p_y<=@big_y and @p_x>=@small_x and @p_x<=@big_x and ((@p_y-@second_p_y)*(@p_x-@first_p_x)) = ((@p_y-@first_p_y)*(@p_x-@second_p_x))
then
RETURN 1;
end if;
-- 其它,使用射线法判断在不在多边形内
-- 分出左右
if @second_p_x<@first_p_x
then
set @cax=@second_p_x;
set @cay=@second_p_y;
set @second_p_x=@first_p_x;
set @second_p_y=@first_p_y;
set @first_p_x=@cax;
set @first_p_y=@cay;
end if;
if @p_y>=@small_y and @p_y<@big_y
then
if @p_x>@big_x
then
set @intersect_count=@intersect_count+1;
else
-- 右斜
if @first_p_y<@second_p_y and ( (@p_y - @first_p_y)*(@second_p_x - @first_p_x ) < (@second_p_y-@first_p_y)*(@p_x - @first_p_x) )
then
set @intersect_count=@intersect_count+1;
end if;
-- 左斜
if @first_p_y>@second_p_y and ( (@p_y - @second_p_y)*(@first_p_x - @second_p_x) < (@first_p_y - @second_p_y)*(@p_x - @second_p_x))
then
set @intersect_count=@intersect_count+1;
end if;
end if;
end if;
set @first_p_x=@cur_sec_x;
set @first_p_y=@cur_sec_y;
set @j=@j+1;
end while;
set @intersect_count=@intersect_count mod 2;
RETURN @intersect_count;
END //
DELIMITER ;
二个函数定义好了,先用第一个求出最大最小纵横坐标。再填入第二个函数中,进行计算。在数据量大时,就快很多了。




