空间数据库管理系统
这学期学习了空间数据库这门课程,我们选取一个城市的数据,利用Oracle数据库,结合PL/SQL编程实现了空间数据的一些查询操作。其中利用了PL/SQL语言中的存储过程、函数、游标等。
首先下载数据,并将其导入到数据库当中,一共8个表。两个点表(Points和Places)、3个线表(Roads和Railways和Waterways)、3个面表(Buildings和Landuse和Natural)。
本次管理系统共实现了两大块功能,分为PL/SQL功能和Java可视化交互功能,以下是功能列表。这篇文章先对PL/SQL功能进行实现,剩下的Java可视化界面后面再写。
下面为系统设计流程图
下面就是每个功能的设计思路以及实现代码。
目录
1、功能1(买房)
(1)功能描述:
当人们买房时,最看重的就是房子周边的环境;例如距离学校、医院和公园等场所的距离远近。
(2)主要思路:
创建一个存储过程maifang(xiaoqu_name in varchar2, school_distance in number, hospital_distance in number, park_distance in number),输入一个小区的名称,通过小区名称找到对应的信息,然后利用sdo_geom.sdo_distance函数结合输入进来的限定距离参数,计算在这个距离范围内,有哪些学校、医院、公园在之内,并且利用游标将这些点的信息存储起来,最后循环从游标中取出并输出!
(3)功能编码:
-- 定义一个存储过程,名称为:maifang
-- 其作用为:输入小区名称,以及三个距离,查找小区附近满足所输入距离范围内的学校、医院和公园信息并输出出来,帮助购房人选择
create or replace procedure maifang(xiaoqu_name in varchar2, school_distance in number, hospital_distance in number, park_distance in number)
-- 声明区
is
-- 声明变量用来存储学校、医院、公园的ID号和名称
school_ID number;
hospital_ID number;
park_ID number;
school_Name varchar2(100);
hospital_Name varchar2(100);
park_Name varchar2(100);
-- 声明游标,分别保存学校、医院、公园的查询记录
cursor c1 is
select po.osm_id, po.name
from points po, landuse la
where la.name=xiaoqu_name and sdo_geom.sdo_distance(po.geometry, la.geometry, 0.5, 'unit=mile')<school_distance and po.type='school' and po.name is not null;
cursor c2 is
select po.osm_id, po.name
from points po, landuse la
where la.name=xiaoqu_name and sdo_geom.sdo_distance(po.geometry, la.geometry, 0.5, 'unit=mile')<hospital_distance and po.type='hospital' and po.name is not null;
cursor c3 is
select na.osm_id, na.name
from natural na, landuse la
where la.name=xiaoqu_name and sdo_geom.sdo_distance(na.geometry, la.geometry, 0.5, 'unit=mile')<park_distance and na.type='park' and na.name is not null;
-- 子程序区
-- 取游标中的数据并输出查询到的学校、医院、公园的信息
begin
dbms_output.put_line('------' || xiaoqu_name || '------' ||'欢迎您!');
-- 输出学校信息
dbms_output.put_line(xiaoqu_name || ' 附近 ' || school_distance || '英里 范围内的学校有如下几所:');
open c1;
loop
fetch c1 into school_ID, school_Name;
exit when c1%notfound;
dbms_output.put_line('学校id:' || school_ID ||' ' || '学校名称:' || school_Name);
end loop;
close c1;
dbms_output.put_line(' ');
-- 输出医院信息
dbms_output.put_line(xiaoqu_name || ' 附近 ' || hospital_distance || '英里 范围内的医院有如下几所:');
open c2;
loop
fetch c2 into hospital_ID, hospital_Name;
exit when c2%notfound;
dbms_output.put_line('医院id:' || hospital_ID || ' ' || '医院名称:' || hospital_Name);
end loop;
close c2;
dbms_output.put_line(' ');
-- 输出公园信息
dbms_output.put_line(xiaoqu_name || ' 附近 ' || park_distance || '英里 范围内的公园有如下几个:');
open c3;
loop
fetch c3 into park_ID, park_Name;
exit when c3%notfound;
dbms_output.put_line('公园id:' || park_ID || ' ' || '公园名称:' || park_Name);
end loop;
close c3;
dbms_output.put_line(' ');
dbms_output.put_line('以上就是本小区附近的环境信息,请根据您的需求进行选购!');
end;
/
-- 查询宿州市内所有小区,根据查询表格来选择小区名称
select * from landuse where name like '%小区' and type='residential'
-- 调用maifang存储过程,将小区名称以及三个距离输入进去,即可得到小区附近环境信息!
-- 此处以"中通名仕家园小区"为例,并且三个距离设置为1, 4, 3
declare
begin
maifang('中通名仕家园小区', 1, 4, 3);
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:points 和 landuse
主要空间分析函数:sdo_geom.sdo_distance
测试用例:maifang(‘中通名仕家园小区’, 1, 4, 3)
2、功能2(计算自然景观面积)
(1)功能描述:
一座城市的自然景观占地面积对于一座城市的建设极为重要,现在我想要统计宿州市的各种自然景观自然景观占地面积并输出。
(2)主要思路:
经过查询,宿州市的自然景观有河堤、水域、森林和公园,创建一个计算区域面积的函数calculate_areacalculate_area(geom in sdo_geometry),并利用游标将对应自然景观循环取出,求它们的面积之和,最终输出出来!
(3)功能编码:
select distinct type from natural
select * from natural where type='water';
-- /******************************************************** 功能2 ***********************************************************/
-- 计算面积函数calculate_area:计算某个区域的面积并返回面积值
create or replace function calculate_area(geom in sdo_geometry) return number
as
begin
return mdsys.sdo_geom.sdo_area(geom, 0.5, 'unit=sq_mile');
end calculate_area;
-- 调用函数计算natural中各种自然景物的面积并根据其面积做相应的规划
declare
g sdo_geometry;
-- 定义变量来存储每种type的面积
riverbank_area number;
forest_area number;
water_area number;
park_area number;
-- 定义游标
cursor c1 is select na.geometry from natural na where type='riverbank';
cursor c2 is select na.geometry from natural na where type='forest';
cursor c3 is select na.geometry from natural na where type='water';
cursor c4 is select na.geometry from natural na where type='park';
begin
riverbank_area:=0.0;
forest_area:=0.0;
water_area:=0.0;
park_area:=0.0;
-- 计算河堤的占地面积
open c1;
loop
fetch c1 into g;
exit when c1%notfound;
riverbank_area := riverbank_area + calculate_area(g);
end loop;
close c1;
dbms_output.put_line('河堤(riverbank)的占地面积为:' || riverbank_area || '平方英里');
-- 计算森林的占地面积
open c2;
loop
fetch c2 into g;
exit when c2%notfound;
forest_area := forest_area + calculate_area(g);
end loop;
close c2;
dbms_output.put_line('森林(forest)的占地面积为:' || forest_area || '平方英里');
-- 计算水域的占地面积
open c3;
loop
fetch c3 into g;
exit when c3%notfound;
water_area := water_area + calculate_area(g);
end loop;
close c3;
dbms_output.put_line('水域(water)的占地面积为:' || water_area || '平方英里');
-- 计算公园的占地面积
open c4;
loop
fetch c4 into g;
exit when c4%notfound;
park_area := park_area + calculate_area(g);
end loop;
close c4;
dbms_output.put_line('公园(park)的占地面积为:' || park_area || '平方英里');
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:natural
主要空间分析函数:sdo_geom.sdo_area
3、功能3(拆迁)
(1)功能描述:
想要使一座城市不断的发展,就必须不断规划它的土地利用价值。当然在这个过程中,就要涉及到拆迁的问题,现在政府部门想要对一个确定的矩形区域进行拆迁,需要计算出哪些哪些建筑物受影响需要搬迁以及赔偿的金额。
(2)主要思路:
创建一个存储过程chaiqian(geom in sdo_geometry, damages in number),输入自己定义的矩形区域已经每平方英里需要赔偿的金额,在buildings表中利用sdo_geom.relate函数判断哪些建筑物在这个需要拆迁的矩形区域内部,并利用游标将其属性记录下来。后面从游标里循环取出建筑物信息并利用定义的计算面积函数calculate_area计算每栋建筑物的面积,乘以输入的赔偿金额,就可以得到总的赔偿金额,最终将结果输出出来!
(3)功能编码:
-- /******************************************************** 功能3 ***********************************************************/
-- 定义一个存储过程:chaiqian
-- 其作用为:输出指定矩形范围内的建筑物以及它的面积和赔偿金额等信息(damages为每平方英里的赔偿金)
create or replace procedure chaiqian(geom in sdo_geometry, damages in number)
is
-- 定义变量存储建筑物的ID,名称和类型和geometry
building_ID number; -- 建筑物ID
building_Name varchar2(100); -- 建筑物名称
building_Type varchar2(100); -- 建筑物类型
building_geom sdo_geometry; -- 建筑物sdo_geometry
building_area number; -- 每栋建筑物面积
building_damages number; -- 每栋建筑物的赔偿金
-- 定义游标
cursor c1 is
select bl.osm_id, bl.name, bl.type, bl.geometry
from buildings bl
where sdo_geom.relate(geom, 'CONTAINS', bl.geometry, 0.05)='CONTAINS' and bl.name is not null and bl.type is not null;
begin
dbms_output.put_line('本次拆迁可能影响的建筑物如下,请尽快做好搬迁准备!');
dbms_output.put_line(' ');
dbms_output.put_line(' ID '|| '名称 '|| '类型 ' || '面积(平方英里) ' || '赔偿金额(万元)');
open c1;
loop
fetch c1 into building_ID, building_Name, building_Type, building_geom;
exit when c1%notfound;
building_area := calculate_area(building_geom); -- 调用函数calculate_area计算面积
building_damages := building_area * damages; -- 计算每栋建筑物的赔偿金
dbms_output.put_line(building_ID || ' ' || building_Name || ' ' || building_Type ||' ' || building_area || ' ' || building_damages);
end loop;
close c1;
end;
/
begin
-- 调用存储过程chaiqian,并传入两个参数,第一个为拆迁的矩形区域(sdo_geometry类型),第二个为每平方英里的赔偿金额(number类型)
chaiqian(sdo_geometry(2003, 8307, NULL, MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,3),MDSYS.SDO_ORDINATE_ARRAY(117.1828688, 34.2010076, 117.1928688, 34.2110076)), 500000);
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:buildings
主要空间分析函数:sdo_geom.relate 和 sdo_geom.sdo_area
测试用例:chaiqian(sdo_geometry(2003, 8307, NULL, MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,3),MDSYS.SDO_ORDINATE_ARRAY(117.1828688, 34.2010076, 117.1928688, 34.2110076)), 500000);
4、功能4(修路)
(1)功能描述:
由于现在国家经济的发展,人们的生活水平和国家的综合水平都显著提高!私家车和各种卡车、货车也渐渐多了起来,这就面临一个问题:道路太窄!所以现在市政府部门想要对某条路进行增宽,想要得到路段的长度信息、拓宽的面积以及需要的原料钱。
(2)主要思路:
创建一个存储过程xiulu(xiu_name in varchar2, material_money in number),将需要拓宽的路段的名称以及修路的原料价钱输入进去,利用sdo_geom.sdo_buffer函数为路段建立缓冲并存储在游标中,循环遍历游标,利用函数sdo_geom.sdo_length计算路段长度,利用函数calculate_area计算拓宽面积,并乘以原料价钱,最终将路段信息和修路的信息输出出来!
(3)功能编码:
-- /******************************************************** 功能4 ***********************************************************/
-- 定义一个存储过程xiulu
-- 功能为:输入一条路的名称以及修路的原料价钱,将路进行拓宽,并输出
create or replace procedure xiulu(xiu_name in varchar2, material_money in number)
is
road_ID number; -- 公路编号
road_Ref varchar2(100); -- 公路编号
road_Type varchar2(100); -- 公路类型
road_geom sdo_geometry; -- 公路sdo_geometry
-- 计算得到的值存放的变量如下:
road_buffer_geom sdo_geometry; -- 该路段缓冲区sdo_geometry
road_length number; -- 该路段长度
road_buffer_area number; -- 该路段缓冲区面积
road_material_money number; -- 该路段需要原料钱
-- 将所有路段进行汇总的值存放在如下变量:
all_road_length number; -- 总的路段长度
all_buffer_area number; -- 总的缓冲区面积
all_material_money number; -- 总的原料钱
count_road number; -- 统计名为xiu_name的路一共有几段
-- 定义游标并调用sdo_buffer函数
cursor c1 is
select ro.osm_id, ro.ref, ro.type, ro.geometry, sdo_geom.sdo_buffer(ro.geometry, 0.25, 0.5, 'arc_tolerance=0.005 unit=mile') buffer_geom
from roads ro
where ro.name=xiu_name and ro.ref is not null;
begin
count_road := 0;
all_material_money := 0;
all_road_length := 0;
all_buffer_area := 0;
dbms_output.put_line('所要修的路的名称为:' || xiu_name || '! 具体路段信息以及修路信息如下所示:');
dbms_output.put_line(' ');
dbms_output.put_line('路段序号 ' || 'ID ' || '路段长度(英里) ' || '拓宽面积(平方英里) ' || '该路段原料钱(万元)');
open c1;
loop
fetch c1 into road_ID, road_Ref, road_Type, road_geom, road_buffer_geom;
exit when c1%notfound;
count_road := count_road + 1; -- 统计路段号
road_length := sdo_geom.sdo_length(road_geom, 0.05, 'unit=mile'); -- 计算路段长度函数sdo_geom.sdo_length
all_road_length := all_road_length + road_length; -- 计算总长度
road_buffer_area := calculate_area(road_buffer_geom); -- 计算需要拓宽的路段的面积
all_buffer_area := all_buffer_area + road_buffer_area; -- 计算总面积
road_material_money := road_buffer_area*material_money; -- 计算该路段修路原料钱
all_material_money := all_material_money + road_material_money; -- 计算总的原料钱
dbms_output.put_line(' ' || count_road || ' ' || road_ID || ' ' || road_length || ' ' || road_buffer_area || ' ' || road_material_money);
end loop;
close c1;
dbms_output.put_line(' ');
dbms_output.put_line(xiu_name || ' 共有 ' || count_road || ' 段');
dbms_output.put_line(xiu_name || ' 总长度为: ' || all_road_length || '英里');
dbms_output.put_line(xiu_name || ' 总拓宽面积为: ' || all_buffer_area || '平方英里');
dbms_output.put_line(xiu_name || ' 修路用的总原料钱为: ' || all_material_money || '万元');
end;
/
-- 调用存储过程,对“坝子街”进行拓宽修路,输出相应的信息
begin
xiulu('坝子街', 100);
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:roads
主要空间分析函数:sdo_geom.sdo_buffer 和 sdo_geom.sdo_length 和 sdo_geom.sdo_area
测试用例:xiulu(‘坝子街’, 100);
5、功能5(救护车)
(1)功能描述:
随着宿州市近几年的城市建设,医疗水平也大大提高!现在假设某地有一位病人突发疾病,拨打120急救电话叫救护车,医疗系统在接到急救电话之后,需要尽快根据病人所在地点派救护车,所以主要功能就是求出距离病人地点最近的几家医院。
(2)主要思路:
创建一个存储过程recent_hospital(place_name in varchar2, hospital_num in number),输入病人所在地点的名称,以及想要搜寻的医院个数,利用sdo_nn函数,根据points表中病人地址信息,结合places表中医院地点信息,找出最近的hospital_num家医院,并且利用函数sdo_geom.sdo_distance计算每家医院到病人所在地点的距离,并将结果输出!
(3)功能编码:
-- /******************************************************** 功能5 ***********************************************************/
-- 定义存储过程recent_hospital
-- 其功能为:输入你当前所在地点(places)的名称以及想要查询医院的数量,来查询医院信息并按照距离排序
create or replace procedure recent_hospital(place_name in varchar2, hospital_num in number)
is
-- 存储输入地点的行信息
row_places places %ROWTYPE;
-- 存储医院(points里的数据)的ID,名称和距离
hospital_ID number;
hospital_Name varchar2(100);
hospital_distance number;
-- 定义游标用来获取距离某地点最近的几家医院信息
cursor c1 is
select po.osm_id, po.name, sdo_geom.sdo_distance(po.geometry, pl.geometry, 0.5, 'unit=mile') distance
from points po, places pl
where pl.name=place_name and sdo_nn(po.geometry, pl.geometry)='TRUE' and po.type='hospital' and rownum <= hospital_num;
begin
-- 获取当前所在地点信息
select * into row_places from places where name=place_name;
-- 输出当前地点的信息
dbms_output.put_line('您当前所在的地点信息为:');
dbms_output.put_line('地点ID:' || row_places.osm_id || ' 地点名称:' || row_places.name || ' 地点类型:' || row_places.type);
dbms_output.put_line(' ');
dbms_output.put_line(' ');
-- 输出距离最近的几家医院的信息和距离
dbms_output.put_line('距离您最近的 ' || hospital_num || ' 家医院的信息和距离如下:');
dbms_output.put_line(' ');
dbms_output.put_line(' 医院ID ' || '医院名称 ' || '距离(英里)');
open c1;
loop
fetch c1 into hospital_ID, hospital_Name, hospital_distance;
exit when c1%notfound;
dbms_output.put_line(hospital_ID || ' ' || hospital_Name || ' ' || hospital_distance);
end loop;
close c1;
end;
/
-- 调用recent_hospital存储过程,并查询距离“赵楼”最近的5家医院信息
begin
recent_hospital('赵楼', 5);
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:points 和 places
主要空间分析函数:sdo_geom.sdo_distance 和 sdo_nn
测试用例:recent_hospital(‘赵楼’, 5);
6、功能6(出租车)
(1)功能描述:
在一座城市中叫出租车出行是非常方便的,那么面临的一个问题就是乘客在确定了自己当前所在位置和目标位置之后,如何确定最优的上车地点和下车地点,以及需要步行的距离,本功能就能解决这个问题!
(2)主要思路
创建一个存储过程taxi_pro(in_building_name in varchar2, out_building_name in varchar2),乘客输入自己当前所在建筑物名称以及想要去的建筑物名称之后,首先利用函数sdo_nn从roads表来查询距离起点和终点最近的道路,然后利用sdo_geom.sdo_closest_points函数来得到建筑物和道路最近的点,即为乘客上车和下车的最优地点,并且计算之间的距离,也就是乘客需要步行的距离!最终将这些点的坐标信息和步行距离信息输出反馈给出租车司机。
(3)功能编码:
-- /******************************************************** 功能6 ***********************************************************/
-- 定义一个存储过程taxi_pro(规划出租车最近上车地点与下车地点)
-- 功能为:输入两个建筑物的名字,为乘客规划最佳上车地点和下车地点,并输出上车点与下车点信息以及需要步行的距离信息
create or replace procedure taxi_pro(in_building_name in varchar2, out_building_name in varchar2)
is
-- 存储上车和下车地点信息的行变量
in_row_buildings buildings %ROWTYPE;
out_row_buildings buildings %ROWTYPE;
-- 上车的道路
recent_in_road_Name varchar2(100); -- 距上车建筑物最近的道路名字
recent_in_road_geom sdo_geometry; -- 距上车建筑物最近的道路sdo_geometry
-- 下车的道路
recent_out_road_Name varchar2(100); -- 距下车建筑物最近的道路名字
recent_out_road_geom sdo_geometry; -- 距下车建筑物最近的道路sdo_geometry
-- 上车信息
dist_in number; -- 上车建筑物到公路的最短距离(乘客需要步行的距离)
geom1 sdo_geometry; -- 公路最近点sdo_geometry(上车)
geom2 sdo_geometry; -- 建筑物最近点sdo_geometry(上车)
-- 下车信息
dist_out number; -- 下车建筑物到公路的最短距离(乘客需要步行的距离)
geom3 sdo_geometry; -- 公路最近点sdo_geometry(下车)
geom4 sdo_geometry; -- 建筑物最近点sdo_geometry(下车)
all_dist number; -- 总共需要步行的距离
two_point_dist number; -- 两上车点之间的直线距离
begin
-- 获取上车地点的信息
select * into in_row_buildings from buildings where name=in_building_name;
-- 获取距上车地点最近的道路名字和sdo_geometry
select ro.name, ro.geometry into recent_in_road_Name, recent_in_road_geom
from buildings bu, roads ro
where bu.name=in_building_name and ro.name is not null and sdo_nn(ro.geometry, bu.geometry)='TRUE' and rownum <= 1;
-- 获取下车地点的信息
select * into out_row_buildings from buildings where name=out_building_name;
-- 获取距下车地点最近的道路的名字和sdo_geometry
select ro.name, ro.geometry into recent_out_road_Name, recent_out_road_geom
from buildings bu, roads ro
where bu.name=out_building_name and ro.name is not null and sdo_nn(ro.geometry, bu.geometry)='TRUE' and rownum <= 1;
-- 调用sdo_geom.sdo_closest_points函数计算 上车建筑物(面) 到 上车道路(线) 最短距离并返回最近两点的信息
sdo_geom.sdo_closest_points(recent_in_road_geom, in_row_buildings.geometry, 0.05, 'unit=foot', dist_in, geom1, geom2);
-- 调用sdo_geom.sdo_closest_points函数计算 下车建筑物(面) 到 下车道路(线) 最短距离并返回最近两点的信息
sdo_geom.sdo_closest_points(recent_out_road_geom, out_row_buildings.geometry, 0.05, 'unit=foot', dist_out, geom3, geom4);
-- 计算步行总路程
all_dist := dist_in + dist_out;
-- 计算上车和下车点之间的直线距离
two_point_dist := sdo_geom.sdo_distance(geom1, geom3, 0.5, 'unit=foot');
-- 输出上车和下车的主要信息
dbms_output.put_line('*********************************************************************');
dbms_output.put_line('**********************安徽省 宿州市 出租车欢迎您!**********************');
dbms_output.put_line('*********************************************************************');
dbms_output.put_line(' ');
dbms_output.put_line('您出发建筑物名称为:' || in_building_name || '! 想要到达的目标建筑物名称为:' || out_building_name || '!');
dbms_output.put_line('上车点与下车点之间的直线距离为:' || two_point_dist || '英尺!');
dbms_output.put_line(' ');
dbms_output.put_line('以下为您的上车与下车地点信息以及步行信息:');
dbms_output.put_line('------------上车地点-------------');
dbms_output.put_line('上车建筑物最近点坐标:' || to_char(geom2.sdo_point.x) || ', ' || to_char(geom2.sdo_point.y));
dbms_output.put_line('上车道路最近点坐标:' || to_char(geom1.sdo_point.x) || ', ' || to_char(geom1.sdo_point.y));
dbms_output.put_line('从出发建筑物到上车地点需要步行的路程:' || dist_in || '英尺');
dbms_output.put_line('------------下车地点-------------');
dbms_output.put_line('下车建筑物最近点坐标:' || to_char(geom4.sdo_point.x) || ', ' || to_char(geom4.sdo_point.y));
dbms_output.put_line('下车道路最近点坐标:' || to_char(geom3.sdo_point.x) || ', ' || to_char(geom3.sdo_point.y));
dbms_output.put_line('从下车点到目标建筑物需要步行的路程:' || dist_out || '英尺');
dbms_output.put_line(' ');
dbms_output.put_line('共需要步行的路程:' || all_dist || '英尺');
end;
/
-- 假如我是一位乘客,现在想要从”广电宿舍1号楼“乘坐出租车到”淮北市体育馆“,调用taxi_pro存储过程为我规划最佳上车和下车地点
begin
taxi_pro('广电宿舍1号楼', '淮北市体育馆');
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:buildings 和 roads
主要空间分析函数:sdo_geom.sdo_distance 和 sdo_nn 和 sdo_geom.sdo_closest_points
测试用例:taxi_pro(‘广电宿舍1号楼’, ‘淮北市体育馆’);
7、功能7(捕鱼)
(1)功能描述:
宿州市内有很多条河流,近期发现河流中鱼的繁殖过多,已经影响了水体。政府部门打算利用渔船对河流里的鱼进行打捞,在保护环境的同时还能增加收益,现在需要获取宿州市内所有的河流信息、包括每条河流分为几段、每段长度、总长度,以及渔船需要花费多长时间能将其走完。
(2)主要思路
创建一个存储过程catch_fish(waterway_name in varchar2, speed in number, out_length out number, out_time out number),输入河流的名称,以及渔船的速度,利用函数sdo_geom.sdo_length来计算每段河流的长度并将其相加起来得到总的长度,用长度除以渔船速度就是需要的时间,最总将这条河流的总长度和需要的总时间通过输出参数传递出来!在程序块中,先将全市的河流信息获取到存在游标,然后循环调用这个存储过程,并将长度和时间相加,最终得到的就是全市的河流长度以及需要的时间!
(3)功能编码:
-- /******************************************************** 功能7 ***********************************************************/
-- 定义一个存储过程catch_fish(捕鱼)
-- 功能:输入河流名称,以及渔船的行驶速度,输出这条河流的长度以及行驶时间等信息;并包括两个输出参数,分别为河流总长度和总时间
create or replace procedure catch_fish(waterway_name in varchar2, speed in number, out_length out number, out_time out number)
is
-- 存储河流的信息
waterway_ID number; -- 河流的编号
waterway_Type varchar2(100); -- 河流的类型
waterway_geom sdo_geometry; -- 河流的sdo_geometry
-- 存储河流长度和行驶时间信息
waterway_length number; -- 每段河流的长度
all_length number; -- 河流总长度
waterway_time number; -- 驶过每一段需要的时间
all_time number; -- 需要的总时间
-- 定义游标获取相应名称河流的信息(由于同一名字的河流可能分为几段)
cursor c1 is
select wa.osm_id, wa.type, wa.geometry
from waterways wa
where wa.name = waterway_name;
begin
dbms_output.put_line('************************************************************************');
dbms_output.put_line('******************************### ' || waterway_name || ' ###*******************************');
dbms_output.put_line('************************************************************************');
waterway_length := 0; -- 每段河流长度初始化为0
all_length := 0; -- 总河流长度初始化为0
waterway_time := 0; -- 驶过每段时间初始化为0
all_time := 0; -- 总时间初始化为0
dbms_output.put_line('序号 ' || 'ID ' || '河流类型 ' || '河段长度(英里) ' || '时间(小时)');
open c1;
loop
fetch c1 into waterway_ID, waterway_Type, waterway_geom;
exit when c1%notfound;
-- 计算每段河流长度
waterway_length := sdo_geom.sdo_length(waterway_geom, 0.05, 'unit=mile');
-- 计算驶过每段河流的时间
waterway_time := waterway_length/speed;
-- 计算河流总长度
all_length := all_length + waterway_length;
-- 计算总时间
all_time := all_time + waterway_time;
-- 输出信息
dbms_output.put_line(' ' || c1%ROWCOUNT || ' ' || waterway_ID || ' ' || waterway_Type || ' ' || waterway_length || ' ' || waterway_time);
end loop;
dbms_output.put_line(waterway_name || ' 共有 ' || c1%ROWCOUNT || ' 段,' || '总长度为:' || all_length || ' 英里!');
close c1;
dbms_output.put_line('当驾驶船速为 ' || speed || '英里/小时 的时候,需要 ' || all_time || ' 小时能驶完整条河流!');
dbms_output.put_line(' ');
-- 给输出参数赋值
out_length := all_length;
out_time := all_time;
end;
/
-- 对waterways表中所有河流进行循环调用此存储过程
declare
water_name varchar2(100); -- 存储每条河流的名字
one_length number; -- 单条河流长度
one_time number; -- 单条河流时间
all_waterways_length number; -- 宿州市内所有河流总长度
all_waterways_time number; -- 本次捕鱼总时间
-- 定义游标获取有名称的所有河流名字
cursor c is
select distinct name
from waterways
where name is not null;
begin
all_waterways_length := 0;
all_waterways_time := 0;
open c;
loop
fetch c into water_name;
exit when c%notfound;
catch_fish(water_name, 20, one_length, one_time);
all_waterways_length := all_waterways_length + one_length;
all_waterways_time := all_waterways_time + one_time;
end loop;
dbms_output.put_line(' ');
dbms_output.put_line('宿州市共有 ' || c%ROWCOUNT || ' 条河流!' || '河流总长度:' || all_waterways_length || ' 英里!' || '本次捕鱼需要的总时间:' || all_waterways_time || ' 小时!');
close c;
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:roads
主要空间分析函数:sdo_geom.sdo_length
8、功能8(统计全市铁路长度)
(1)功能描述:
宿州市内有很多条铁路,并且分了不同的段,现在我想要统计宿州市内所有铁路的情况信息,包括计算长度,分为几段,以及总体的长度信息等,为铁路的建设提供参考!
(2)主要思路
首先创建一个计算几何体长度的函数calculate_length(geom in sdo_geometry),然后定义一个存储过程rail_length(rail_name in varchar2, out_length out number),我们需要输入铁路的名字还有一个输出参数来输出这条铁路的总长度,调用计算长度的函数,统计每段铁路的长度并相加即可得到铁路的总长度。然后用一个游标将所有铁路的名称存储进去,循环取出并调用存储过程进行计算,最终将全市的铁路信息输出出来!
(3)功能编码:
-- /******************************************************** 功能8 ***********************************************************/
-- 定义一个函数calculate_length计算长度
create or replace function calculate_length(geom in sdo_geometry) return number
as
begin
return sdo_geom.sdo_length(geom, 0.05, 'unit=mile');
end calculate_length;
-- 定义存储过程
-- 其功能为:传入一条铁路的名称,输出其各段信息,并有一个输出参数输出总长度
create or replace procedure rail_length(rail_name in varchar2, out_length out number)
is
-- 行变量存储铁路信息
row_railways railways %ROWTYPE;
-- 存储铁路长度
rail_length number; -- 每一段铁路的长度
all_length number; -- 铁路的总长度
-- 定义游标获取铁路信息
cursor c1 is
select *
from railways ra
where ra.name=rail_name;
begin
dbms_output.put_line('************************************************************************');
dbms_output.put_line('******************************### ' || rail_name || ' ###*******************************');
dbms_output.put_line('************************************************************************');
all_length := 0;
dbms_output.put_line('序号 ' || 'ID ' || '长度(英里)');
open c1;
loop
fetch c1 into row_railways;
exit when c1%notfound;
rail_length := calculate_length(row_railways.geometry); -- 调用calculate_length函数计算铁路长度
all_length := all_length + rail_length;
dbms_output.put_line(' ' || c1%ROWCOUNT || ' ' ||row_railways.osm_id || ' ' || rail_length);
end loop;
dbms_output.put_line(rail_name || ' 共有 ' || c1%ROWCOUNT || ' 段,' || '总长度为:' || all_length || ' 英里!');
close c1;
dbms_output.put_line(' ');
-- 给输出变量赋值
out_length := all_length;
end;
/
-- 循环调用存储过程计算每条铁路的长度信息并统计
declare
rail_name varchar2(100);
rail_len number; -- 每条铁路长度
all_length number; -- 所有铁路总长度
-- 获取所有铁路的名字
cursor c is
select distinct name
from railways
where name is not null and type='rail';
begin
all_length := 0; --给铁路总长度赋初值
open c;
loop
fetch c into rail_name;
exit when c%notfound;
rail_length(rail_name, rail_len);
all_length := all_length + rail_len;
end loop;
dbms_output.put_line(' ');
dbms_output.put_line('宿州市共有 ' || c%ROWCOUNT || ' 条铁路!' || '铁路总长度:' || all_length || ' 英里!');
close c;
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:railways
主要空间分析函数:sdo_geom.sdo_length
9、功能9(灌溉土地)
(1)功能描述:
由于宿州市今年出现干旱天气,需要为城镇和村庄分配河流来灌溉土地,由于每条河流的位置不同且农民的灌溉设备有限,所以每条河流仅能服务于周边一定范围内的村庄或者城镇,为了保证供水量充足,现在需要为每条河流分配!
(2)主要思路
首先创建一个存储过程river_pollution(river_name in varchar2, num in number),传入河流的名称以及能服务的距离范围,利用缓冲函数sdo_geom.sdo_buffer为每条河流建立缓冲区,然后定义一个带参数的游标,参数即为一条河流的缓冲出,利用sdo_geom.relate函数判断哪些地区和这条河流的缓冲区相交或者在这个范围之内,然后将这些信息输出出来。在主程序快中,对于每条河流循环调用这个存储过程即可为全市规划河流分配!
(3)功能编码:
-- /******************************************************** 功能9 ***********************************************************/
-- 定义一个存储过程river_pollution
-- 其功能为:输入一个河流的名称和灌溉范围,为其分配灌溉地点
create or replace procedure river_pollution(river_name in varchar2, num in number)
is
-- 存储河流信息的行变量
row_waterways waterways %ROWTYPE;
river_buffer_geom sdo_geometry; -- 存储河流缓冲区的变量
-- 存储地点信息
place_ID number;
place_Name varchar2(100);
place_Type varchar2(100);
-- 获取河流信息
cursor c1 is
select *
from waterways
where name=river_name;
-- 带参游标,参数是河流缓冲区,获取灌溉地点信息
cursor c2(geom sdo_geometry) is
select pl.osm_id, pl.name, pl.type
from places pl
where sdo_geom.relate(geom, 'ANYINTERACT', pl.geometry, 0.05)='TRUE' and pl.name is not null and pl.type is not null;
begin
dbms_output.put_line('************************************************************************');
dbms_output.put_line('******************************### ' || river_name || ' ###*******************************');
dbms_output.put_line('************************************************************************');
dbms_output.put_line(' ID ' || '地点名称 ' || '地点类型');
open c1;
loop
fetch c1 into row_waterways;
exit when c1%notfound;
river_buffer_geom := sdo_geom.sdo_buffer(row_waterways.geometry, num, 0.5, 'arc_tolerance=0.005 unit=mile');
open c2(river_buffer_geom);
loop
fetch c2 into place_ID, place_Name, place_Type;
exit when c2%notfound;
dbms_output.put_line(place_ID || ' ' || place_Name || ' ' || place_Type);
end loop;
close c2;
end loop;
close c1;
dbms_output.put_line(' ');
end;
/
declare
water_name varchar2(100); -- 存储每条河流的名字
-- 定义游标获取有名称的所有河流名字
cursor c is
select distinct name
from waterways
where name is not null;
begin
dbms_output.put_line(' 安徽省 宿州市 河流灌溉土地分配方案! ');
dbms_output.put_line('由于今年我市天气干旱,各地方土地出现严重干旱现象,为保证供水充足,现将全市河流灌溉分配方案公布如下:');
dbms_output.put_line(' ');
open c;
loop
fetch c into water_name;
exit when c%notfound;
river_pollution(water_name, 1); -- 调用存储过程
end loop;
dbms_output.put_line(' ');
dbms_output.put_line('以上为宿州市全市河流灌溉分配方案,请按照规定进行灌溉土地!');
close c;
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:waterways 和 places
主要空间分析函数:sdo_geom.sdo_buffer 和 sdo_geom.relate
10、功能10(房地产)
(1)功能描述:
现在一房地产商在建成一座小区后想要估算这座小区的大致收益,已知小区的名字,每平方米的利润以及楼层数,现在我们想要计算这座小区的面积以及大致的收益!
(2)主要思路
首先创建一个存储过程real_estate(estate_name in varchar2, price in number, storey in number),输入小区的名称,单位平方米的利润以及楼层数,首先根据小区名来找到这座小区的信息,通过函数calculate_area来计算这座小区的面积,然后乘以单价以及楼层数,即可大致计算出这个小区售完所有房子之后的总收益!
(3)功能编码:
-- /******************************************************** 功能10 ***********************************************************/
-- 定义一个存储过程real_estate(房地产)
-- 其功能为:输入小区名称、每平方米利润、楼层数,计算小区的面积和总收益信息并输出
create or replace procedure real_estate(estate_name in varchar2, price in number, storey in number)
is
square_mile constant number := 2589988.11034; -- 常量:1平方英里等于2589988.11034平方米
-- 存储小区的信息变量
estate_ID number;
estate_Type varchar2(100);
estate_geom sdo_geometry;
-- 存储小区面积和收益
estate_area number;
estate_price number;
-- 利用游标获取小区信息
cursor c1 is
select la.osm_id, la.type, la.geometry
from landuse la
where la.name=estate_name;
begin
open c1;
loop
fetch c1 into estate_ID, estate_Type, estate_geom;
exit when c1%notfound;
-- 调用函数calculate_area计算小区面积
estate_area := calculate_area(estate_geom);
-- 计算小区总共收益(万元为单位)
estate_price := estate_area * square_mile * price * storey/10000;
dbms_output.put_line('小区ID:'|| estate_ID||chr(10)||'小区名称:'||estate_name||chr(10)||'小区类型:'||estate_Type);
dbms_output.put_line('小区占地面积:'||estate_area||'平方英里'||chr(10)||'小区每平方米收益:'||price||'元'||chr(10)||'预估收益:'||estate_price || '万元');
end loop;
close c1;
end;
/
-- 以“中通名仕家园小区”为例,每平方米收益8000元,共有20层,调用real_estate存储过程
begin
real_estate('中通名仕家园小区', 8000, 20);
end;
(4)运行结果:
(5)备注说明[可选]:
涉及到的表:landuse
主要空间分析函数:sdo_geom.sdo_area
总结:
以上就是PL/SQL的实现功能,主要涉及到了空间数据库的一些操作,以及PL/SQL编程,下一篇我会利用Java连接Oracle数据库,并实现可视化操作,做一个可以和用户交互的空间数据库管理系统!