这个项目已经完成好几个月了,一直没有写总结。现在把这篇总结补上。
POSTGRES数据库。我们软件的任务是,将数据库中的数据以表格、图表、地图等形式展示出来。其中,我负责GIS地图(地理信息系统)的开发,包括地图的绘制(ArcGis, UDig)、地图发布(GeoServer)、地图展示(OpenLayers, JavaScript)和部分数据库查询操作(JPA)。该项目使用 Seam-2.3框架,以JBoss-7.1为web应用服务器。
系统登陆界面如下:
主界面如下:
GIS界面如下:
虽然之前有Win32桌面应用开发基础和Java语言基础,但这次的GIS系统跟它们完全不搭边。这让我在老师分配下来任务时一度认为几乎不可能完成。其实老师的意思也是让我边学边做。。好吧,开干!
ArcGis是一套专业的,用于处理地理数据的软件。编辑地图用ArcMap就如同处理图片要使用PhotoShop一样自然。因此我的首要任务就是学习如何使用ArcMap。由于这套软件只能在Windows上运行,于是我不得不先在Windows下开发地图,然后再切换回Ubuntu编写Java代码。
ArcGis破解方法见: http://jingyan.baidu.com/article/1612d5001bc1c3e20f1eee7f.html
首先我从网上下载到了全国铁路规划图、全国省区和地级市规划图。地图矢量图文件以.shp结尾,而地图中的元素信息(如点元素的label)则保存在*.dbf文件中,用excel就能打开查看。 打开ArcMap,将所有shp文件导入,便得到全国省区,地级市和铁路规划图。这里我们只需要山东地图,所以要将无用的部分cut掉。在地图上右键选择“开始编辑”,用选择工具选中除山东省以外的部分,再按键盘上的Del键删除。这是一种很笨的方法,不过很有效。这时候老师发现,地图上少了一条铁路,或许是因为这条线路不太起眼所以没画上吧。于是我干脆自己给添上了。在编辑模式下,单击浮动工具栏上的线型元素,在模板中选中铁路样式,最后再在起点和终点拖出一条直线即可。完成编辑后要点击 "保存并完成编辑",否则修改不会生效。
这部分没有任何技术含量,完全是在拖鼠标,比较无聊。
GeoServer是一个免费、开源的地图服务器,采用Java编写而成。其实我们平时所看到的百度地图,Google Map都是类似于GeoServer这样的地图服务器软件所返回的图片数据。我们可以将shp文件直接发布到GeoServer中,也可以让GeoServer从数据库中获取地图数据。当用户请求到来时,GeoServer(大多数情况下)会以图片的形式返回地图数据。
GeoServer的使用方法见我另一篇文章: 使用GeoServer-2.3发布地图
在本项目中,我们需要让GeoServer从Postgre数据库中读取地图数据而不是直接发布shp文件。因此还需要安装PostGis-2.0,来为Postgre添加保存地理数据的功能,并将shp文件导入到数据表中。过程大致如下:
进入Postgre根目录的bin目录下,执行以下命令将shp文件转换为sql语句:
shp2pgsql -s "4326" -W "GBK" D:\demo\map\sd_city.shp sd_city > D:\sd_city.sql
执行以下命令执行包含地理数据的sql语句:
psql -d shandong -f D:\sd_city.sql -U postgres -W
注意,位于bin目录下的shp2pgsql.exe是PostGIS提供的工具,如果没有安装PostGis,shp2pgsql.exe就不存在。
SLD是一种类似于xml的标记语言,用于规定GeoServer如何渲染几何元素,在功能上类似于CSS。例如 shp中包含一些点元素,则SLD可以用来指定这些点的背景色,边框,大小,label的显示位置等。示例如下:
<FeatureTypeStyle>
<Rule>
<PointSymbolizer>
<Graphic>
<Mark>
<WellKnownName>circle</WellKnownName>
<Fill>
<CssParameter name="fill">#FF0000</CssParameter>
</Fill>
</Mark>
<Size>6</Size>
</Graphic>
</PointSymbolizer>
</Rule>
</FeatureTypeStyle>
这会让点显示为红色:
而
<FeatureTypeStyle>
<Rule>
<PointSymbolizer>
<Graphic>
<Mark>
<WellKnownName>triangle</WellKnownName>
<Fill>
<CssParameter name="fill">#009900</CssParameter>
<CssParameter name="fill-opacity">0.2</CssParameter>
</Fill>
<Stroke>
<CssParameter name="stroke">#000000</CssParameter>
<CssParameter name="stroke-width">2</CssParameter>
</Stroke>
</Mark>
<Size>12</Size>
</Graphic>
</PointSymbolizer>
</Rule>
</FeatureTypeStyle>
能让点显示为带边框的绿色三角形:
这就是SLD的作用。在不同的缩放级别下显示不同的元素,也可以通过SLD来完成。最终,这个项目所用的地图使用了较为复杂的SLD,比如把线(Line)元素渲染成
就需要100多行SLD。
有了地图服务器,也有了地图,接下来如何才能让用户在浏览器中看到地图呢?这就是OpenLayers的功能了。OpenLayers是一个用于开发WebGIS客户端的JavaScript库。我们可以使用OpenLayers向提供WMS(Web Map Service)服务的GeoServer发送请求,将返回的地图图片显示在浏览器中,并添加导航,经纬度显示,缩放,popup等功能。
发送请求的代码示例如下:
// 向GeoServer请求地图
function initMap() {
map = new OpenLayers.Map(
"main_map",
{ // 设置允许的缩放范围
'minScale': '2300000',
'maxScale': '80000' // 1km
}
);
wmsLayer = new OpenLayers.Layer.WMS(
"JiNan Railroad",
"http://" + window.location.hostname + ":" + window.location.port + "/geoserver/Demo/wms",
{
'service': 'WMS',
'version': '1.1.1',
'request': 'GetMap',
'layers': 'jinan',
'styles': '',
'format': 'image/png',
'TILED': true // 告诉geoserver启用缓存
}
);
}
其中OpenLayers.Map的第一个参数"main_map"是一个div的id,OpenLayers会将地图显示到这个div中。
添加图层,添加控件,设置缩放中心的代码示例如下:
// 添加control
map.addControl(new OpenLayers.Control.MousePosition()); // 鼠标位置
map.addControl(new OpenLayers.Control.ScaleLine()); // 比例尺
map.addControl(new OpenLayers.Control.PanZoomBar()); // 缩放条
map.removeControl(map.getControlsByClass('OpenLayers.Control.Zoom')[0]); //删除默认控件
// 添加layers
map.addLayer(vector_layer);
map.addLayer(markersLayer);
map.addLayer(wmsLayer);
// 设置默认缩放级别和缩放中心
if(!map.getCenter()) {
map.setCenter(new OpenLayers.LonLat(118.5, 36.5),0);
}
地图页面的JS代码共有600多行,实现的功能如下:
1. 向地图中添加mark点,分三类,分别是受电弓、B值、温度。不同的点用不同的图片表示。
2. 每隔10分钟向服务器发送一个ajax请求,以更新mark点数据。
3. 鼠标点击mark点会显示popup框,可以查看该监测点的各项数据,和摄像头照片。
4. 地图右下角滚动显示mark点的更新信息,并提供 '在地图中标出' 功能。
5. 读取后台生成的JSON数据。
其中第2个功能的实现思路为,在地图页面中添加一个隐藏的iframe,在iframe所指向的子页面中通过RichFaces的a4j标签发送ajax请求,最后在子页面中调用父页面的JS函数将得到的数据传到父页面(地图页面)中。
iframe:
<iframe src="JsonUpdateData.seam" width="0" height="0"></iframe>
JsonUpdateData.xhtml:
<h:form>
<a:poll id="poll" interval="600000" enabled="true" render="update-frame" />
</h:form>
<a:outputPanel id="update-frame">
<h:outputText id="jsonDataUpdate" value='#{alarmAction.getRecentEquipData("empty") }' />
<script type="text/javascript">
console.log('iframe: update data');
var jsonStr = document.getElementById("jsonDataUpdate").innerHTML;
var jsonObj = eval('(' + jsonStr + ')');
function reset() {
// 1秒后再更新测量数据
if(parent.onUpdateData(jsonObj) == false) {
console.log(new Date().toLocaleTimeString() + ':更新测量数据失败,1秒后重试');
setTimeout(reset, 1000);
}
}
reset();
</script>
</a:outputPanel>
子页面调用父页面的方法,只需用parent.fun()即可。
后台通过JPA查询数据库,将设备数据封装成AlarmInfo对象,然后在页面中通过EL表达式调用AlarmAction的getEquipJson()方法来生成JSON数据。示例如下:
JavaScript代码:
var equipments = #{alarmAction.getEquipJson()};
Java代码:
public String getEquipJson() {
List<Equipment> list = entityManager.createQuery("select e from Equipment e order by e.code")
.getResultList();
StringBuilder str = new StringBuilder("{");
for(Equipment e : list) {
str.append("'" + e.getCode() + "':" +
"{ 'id':'" + e.getId().toString() + "'," +
"'name':'" + e.getName() + "'," +
"'low':'" + e.getAlarmLow() + "'," +
"'high':'" + e.getAlarmHigh() + "'," +
"'code':'" + e.getCode() + "'," +
"'type':'" + e.getType() + "'," +
"'yPos':'" + e.getGisX().toString() + "'," +
"'xPos':'" + e.getGisY().toString() + "'," +
"'status':'" + e.getStatus() + "'},"
);
}
int lastIndex = str.lastIndexOf(",");
str.replace(lastIndex, lastIndex + 1, "}");
return str.toString();
}
1. 要随时重构你的代码,想方设法提高程序可读性。否则,你可能改Bug的时候连自己的代码是啥意思都记不着了。(非常非常重要
)
2. 尽量多写注释。
3. 在localhost上运行正常,永远不能确定在服务器上也能正常运行。
4. 遇到问题时用Google而不是Baidu。
5. 提高JavaScript水平。
6. 一定要把需求搞明白再开始工作。
7. 有人催你的时候一定不要急,否则你更搞不定。
8. 有问题尽量自己解决,不到万不得以不要打扰别人。(别人或许比你更忙)
9.熬夜是得不偿失的。
10. 只阅读英文资料。
有一次,铁路局的领导要来视察,这个项目是重点视察内容。这时程序已经部署到服务器上了,而我的地图偏偏出了些问题。于是我开始debug。如果单单如此倒也没什么,要命的是,客户那边还一遍遍给我们打电话催,问bug解决没。这时老师就帮我挡着,说快了,一会就好了。而我呢,满头大汗在找错误,心想,完了,我死定了。。然后我悲剧地发现这个错误不是改一行两行就能解决的,而是一个逻辑错误。。在催促的压力下,30分钟后,bug解决了。。。好险!如果是在公司的话,估计第二天我就不用来上班了。。
这是自己参与的第一个项目,回想起来,感觉又可笑又感慨。可笑的是自己当时经验不足,犯了不少低级错误,现在想想真是非常丢人;感慨的是,如果没有第一次的历练,自己永远也具备不了独立做项目的能力。当时我才大一,还有的是时间。如果是在公司上班犯这些错误,那。。后果不堪设想。。。。。