I code it

Code and Life

全球地震信息的可视化(使用OpenLayers)

OpenLayers

使用OpenLayers可以很容易的搭建基于Web的GIS系统,OpenLayers支持不同的数据源(符合WMS协议的服务器,Google Maps API, Bing Maps,KML以及GeoJSON等等)。通过将不同的数据源的数据整合,我们可以开发出丰富而用户友好的GIS系统。

OpenLayers可以轻松的处理GeoJSON数据,并将其生成矢量层,我们可以将这个层叠加在其他数据源(比如OSM)提供的地图上,以得到一个完整的小应用。

最后的运行结果是这样的:

image

GeoJSON

美国地理信息调查局是一个科学组织,他公开了很多地球上的灾难信息,比如对地震的统计,并提供编程接口。它公开的地震统计信息,包含全世界各地报告过的地震,以及全美所有检测到的地震,并以多种周期(小时,天,周,月等),多种格式(GeoJSON,KML,Atom等),以便应用程序的开发者只用这些数据。

实现

设置基本环境

我们将借助bower来安装所有的代码依赖。首先,我们需要bower将所有的包都安装在components目录下,这个可以通过在当前目录的.bowerrc文件中制定directory

1
2
3
{
    "directory": "components"
}

然后运行bower安装jquery以及openlayers:

1
2
$ bower install jquery
$ bower install openlayers

通过bower安装OpenLayers之后,可以通过OpenLayers自带的build工具将所有的源码合并压缩为一个文件:

1
2
3
$ cd components/openlayers/build
$ ./build.py #将会在当前目录下生成一个OpenLayers.js的文件
$ mv OpenLayers.js ../

然后,创建一个简单的HTML文件,引用jquery.js和OpenLayers.js,以及我们的入口脚本app.js,本文所有的代码都只是修改这个文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <title>Earthquake distribution</title>
        <link rel="stylesheet" href="style.css" />
    </head>
    <body>
        <div id="container">
            <div id="map">
            </div>
        </div>
        <script src="components/jquery/jquery.js" type="text/javascript"></script>
        <script src="components/openlayers/OpenLayers.js" type="text/javascript"></script>
        <script src="app.js" type="text/javascript"></script>
    </body>
</html>

还可以运行bower init来将生成bower.json,以方便别人使用我们的应用:

1
$ bower init
基本代码

一个最简单的OpenLayers应用,只需要7行代码:

1
2
3
4
5
6
7
$(function() {
    var map = new OpenLayers.Map("map");
    var osm = new OpenLayers.Layer.OSM();

    map.addLayers([osm]);
    map.zoomToMaxExtent();
});

这段代码在id为map的HTML元素创建了一个地图,这个地图上有一个叫OSM的层(即OpenStreetMap,一个开源,开放的地图平台),并将地图缩小到边界范围(以获得最大的视野):

image

生成矢量层

通过GeoJSON生成矢量图非常容易:

1
2
3
4
5
6
7
var geo = new OpenLayers.Layer.Vector("EarthQuake", {
    strategies: [new OpenLayers.Strategy.Fixed()],
    protocol: new OpenLayers.Protocol.HTTP({
        url: '/all_day.geojson',
        format: new OpenLayers.Format.GeoJSON({ignoreExtraDims: true})
    })
});

注意此处的all_day.geojson是从USGS网站上下载的,过去一天中世界各地的所有地震统计。

上边的代码创建了一个名称为EarthQuake的矢量层,strategies中的Fixed策略表示仅请求一次资源,然后缓存在前端,不再请求。protocol表明数据来源为all_day.geojson,格式为OpenLayers.Format.GeoJSON。由于USGS返回的地理信息除了经纬度还包含深度,而OpenLayers默认只处理经纬度的,因此需要此处的ignoreExtraDims来忽略那个额外的深度信息。

image

定制样式

虽然我们已经加上了新的层,也可以看到很多表示地震的点信息,但是并不能看出哪些地震是严重的,比如里氏3级以下的地震,几乎没有危害,可以标注成一种颜色;而更高震级的可以标记成另外一种颜色。

OpenLayers可以很容易的做到这个定制化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var style = new OpenLayers.Style();

var ruleLow = new OpenLayers.Rule({
  filter: new OpenLayers.Filter.Function({
        evaluate: function(properties) {
            return properties.mag < 3.0;
        }
    }),
  symbolizer: {pointRadius: 3, fillColor: "green",
               fillOpacity: 0.5, strokeColor: "black"}
});

var ruleHigh = new OpenLayers.Rule({
  filter: new OpenLayers.Filter.Function({
        evaluate: function(properties) {
            return properties.mag >= 3.0;
        }
    }),
    symbolizer: {pointRadius: 5, fillColor: "red",
               fillOpacity: 0.7, strokeColor: "black"}
});

style.addRules([ruleLow, ruleHigh]);

geo.styleMap = new OpenLayers.StyleMap(style);

首先创建一个Style对象,为Style添加两条规则Rule,然后将Style对象包装成StyleMap并赋值给表示地震的矢量层earthquake

对于规则ruleLow,我们定义了,当一个feature的属性值mag(震级)小于三的时候后,使用绿色的,半径为3px的小圆圈来表示。而ruleHigh则定义了当震级大于等于三的时候,用红色,半径为5px的圆圈来表示。

image

加上事件处理

虽然我们已经可以直观的根据震级不同而看到不同颜色的点,但是整个应用仍然没有多少意义:它不具备于用户的交互能力。我们需要添加上事件处理,当用户点击地图上的一个圆点的时候,应该看到一个更详细的窗口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var selectControl = new OpenLayers.Control.SelectFeature(geo, {
    onSelect: onFeatureSelect,
    onUnselect: onFeatureUnselect
});

map.addControl(selectControl);
selectControl.activate();

function onFeatureSelect(feature) {
    var html = "<span>"+feature.attributes.title+"</span>";

    var popup = new OpenLayers.Popup.FramedCloud("popup",
            feature.geometry.getBounds().getCenterLonLat(),
            null,
            html,
            null,
            true
        );

    popup.panMapIfOutOfView = true;
    popup.autoSize = true;

    feature.popup = popup;

    map.addPopup(popup);
}

function onFeatureUnselect(feature) {
    map.removePopup(feature.popup);
    feature.popup.destroy();
    feature.popup = null;
}

我们在地图上添加了一个SelectFeature元素,并注册了回调函数:当矢量层中的矢量被选中之后,函数onFeatureSelect将被执行,我们可以在这个函数中添加对弹出窗口的控制。当onFeatureSelect执行时,OpenLayers会将当前的Feature传递进来,我们可以动态的取得震级,标题,链接等信息,并展现给最终用户。

[image]

如果将数据源扩大到本周的所有地震:

1
2
3
4
5
6
7
var geo = new OpenLayers.Layer.Vector("EarthQuake", {
    strategies: [new OpenLayers.Strategy.Fixed()],
    protocol: new OpenLayers.Protocol.HTTP({
        url: '/all_week.geojson',
        format: new OpenLayers.Format.GeoJSON({ignoreExtraDims: true})
    })
});

image

完整的代码示例可以看这里

Comments