气象灾害风险普查中雷灾资料的数据清洗与百度地图正/逆地理编码使用
前言
在自然灾害风险普查工作当中,有一项是对雷灾的统计。它的具体操作就是,根据原始资料(.txt)
,然后进行相关数据筛选。最终的统计结果如下图所示。即 区划代码
,区域名称
,雷灾起数
,人员伤亡个数
,经济损失
。
上表中,区划代码是最容易获得的,它有一个专一的映射表格。
-
区域名称
是精确到乡镇
一级 -
起数
则是该乡镇发生雷灾的次数 -
人员
伤亡则是死亡的人数 -
经济损失
是泪在所造成的损失。
提供的原始数据是
整整20本书
。。。。
经过不懈的努力,终于是把其数字化成了下图所示格式。
但是新的问题接踵而至,如何对这一条条数据进行数据清洗
,筛选
,并获得我们想要的结果呢?对于一个只给出学校名,工厂名的地点,我们如何获得他所在的乡镇呢?我们如何统计死亡人数?如何界定损失?总不能,一条一条的读下去吧。
思路
其实我们在日常生活中,如果想去一个地点,我们通常会使用百度地图
或者高德地图
,输入目的地,就会为我们规划相应的路线,并给出目的地的经纬度,有了经纬度
,我们通过逆向地理编码
就可以轻松获得目的地所在的乡镇。
介于此,我们思考使用百度地图来获得每一个雷灾所发生的乡镇。
百度地图开放平台为我们提供了很多好用的地图服务接口。
我们选择地理编码,在这里我们可以阅读相关接口文档。
在对地理编码有了一定得了解之后,我们知道,要获得百度提供的地图服务,那必须获得一个它提供的AK码,才能调用接口。
那么我们进入控制台,创建应用
跟着页面提示,一步一步操作。整个流程下来,不超过2分钟。
完成之后,我们就获得了AK码
。
接下来就是写代码,来实现我们上面的思路了。
代码实现
coding!!!
1.获取模糊地名getFuzzyPlaceName
根据原始数据的格式以及相应规律,我们可以用下面的代码对每一条数据进行拆分,把它炸开(致敬老罗)。然后返回一个模糊地名
/**
* 获取模糊地名
* @param txt
* @return
*/
public static String getFuzzyPlaceName(String txt) {
if (txt.contains("击")) {
String[] split = txt.split("击");
if (split[0].contains(",")) {
return split[0].substring(split[0].indexOf(",") + 1);
}
if (split[0].contains(",")) {
return split[0].substring(split[0].indexOf(",") + 1);
}
}
return null;
}
2.目的地经纬度获取getTownLatLon
有了模糊地名,我们将模糊地名放入到相应的接口地址中,服务器会给我们返回一个字符串,如下所示:
{
"result": {
"level": "政府机构",
"confidence": 70,
"location": {
"lng": 115.25352396163791,
"lat": 36.06484688624098
},
"precise": 1,
"comprehension": 57
},
"status": 0
}
具体的代码实现如下图所示
public static JSONObject getTownLatLon(String name) {
try {
//ak=um3EyGR9TQYrlCEhacbygTrvBSHQzBf8
String urlAddress = "https://api.map.baidu.com/geocoding/v3/?address=" + name + "&output=json&ak=um3EyGR9TQYrlCEhacbygTrvBSHQzBf8&callback=showLocation";
StringBuilder sb = new StringBuilder();
URL url = new URL(urlAddress);
URLConnection connection = url.openConnection();
if (connection != null) {
InputStreamReader kk = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(kk);
String data;
while ((data = br.readLine()) != null) {
sb.append(data);
}
kk.close();
}
String str = sb.toString();
String[] split = str.split("\\(");
String[] split1 = split[1].split("\\)");
String s = split1[0];
JSONObject json = JSON.parseObject(s);
if (json.getInteger("status") == 0) {
return json.getJSONObject("result").getJSONObject("location");
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
通过上面的方法,我们返回了一个Json字符串,这个字符串里面包含了目的地的经度和纬度。
有了经度和纬度,我们通过逆地理编码
,就可以获得目的地所在的乡镇。
3.目的地所在乡镇的获取getTownByLatAndLng
根据获得的经纬度
,我们通过访问相应的接口,服务器会返回给我们一个json字符串
,如下所示:
{
"status": 0,
"result": {
"location": {
"lng": 115.26599353332347,
"lat": 36.070504134133987
},
"formatted_address": "河南省濮阳市南乐县",
"business": "",
"addressComponent": {
"country": "中国",
"country_code": 0,
"country_code_iso": "CHN",
"country_code_iso2": "CN",
"province": "河南省",
"city": "濮阳市",
"city_level": 2,
"district": "南乐县",
"town": "张果屯镇",
"town_code": "410923104",
"distance": "",
"direction": "",
"adcode": "410923",
"street": "",
"street_number": ""
},
"pois": [],
"roads": [],
"poiRegions": [],
"sematic_description": "",
"cityCode": 209
}
}
可以获得目的地所处的乡镇
,以及乡镇所对应的行政区划代码
。
具体代码实现如下所示。
public static String getTownByLatAndLng(String lat, String lng) {
try {
String urlAddress = "http://api.map.baidu.com/reverse_geocoding/v3/?ak=um3EyGR9TQYrlCEhacbygTrvBSHQzBf8&output=json&extensions_town=true&coordtype=wgs84ll&location=" + lat + "," + lng + "";
StringBuilder sb = new StringBuilder();
URL url = new URL(urlAddress);
URLConnection connection = url.openConnection();
if (connection != null) {
InputStreamReader kk = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(kk);
String data;
while ((data = br.readLine()) != null) {
sb.append(data);
}
kk.close();
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
通过该方法我们就可以获得目的地所在的乡镇
以及乡镇区划代码
。哈哈哈,大功告成。这两个要素我们就搞定了。
4.死亡人数获取getDiePeopleNumber
死亡人数的获取,也是根据自然语言的规律,进行相应的数据清洗。结果可能不是很准确,还要根据后续的实践队代码进行调整。
/**
* 因为死亡人数比较敏感,所以对于得到的结果,要进行二次检查
* @param txt
* @return
*/
public static int getDiePeopleNumber(String txt) {
//会出现 "身亡" 字眼
//当出现"某某"或者"某","1人","1名",一般是一个人。"2人","1对"一般是两个人
if (txt.contains("身亡")) {
if (txt.contains("某某") || txt.contains("某") || txt.contains("1人") || txt.contains("1名")) {
return 1;
}
//这里判断比较复杂,1对往往存在不是人的情况,这里汇集一下"1对农民夫妇"
if (txt.contains("2人") || txt.contains("1对农民夫妇")) {
return 2;
}
}
return 0;
}
5.经济损失获取getLoss
损失是一个很难界定的东西,其中该方法返回的0--》是因为无法判断损失给的值。其他数字则是根据原文给的数字写的。
/**
* 损失是一个很难界定的东西,其中该方法返回的0--》是因为无法判断损失给的值。其他数字则是根据原文给的数字写的。
*
* @param txt
* @return
*/
public static String getLoss(String txt) {
String[] split = txt.split(",|。|,|、");
for (String s : split) {
//这里存在一个问题,可能字符中包含多个万元关键字,那么我们就取第一个
if (s.contains("万元")) {
String chinese = "[\u4e00-\u9fa5]";
//汉字字符集
Pattern p = Pattern.compile(chinese);
Matcher m = p.matcher(s);
return m.replaceAll("");
}
}
return "0";
}
总结
至此,风险普查中雷灾统计最复杂的步骤我们已经搞定了。剩下的就是体力活了,即将数据整理到数据库,或者熟悉的工具里面。这里就不再赘述了。
版权属于: 依依东望