前言
本文在之前搭建的集群上,运行一个地理空间分析的示例,示例来自于《Spark高级数据分析》第八章。
Github项目地址:https://github.com/sryza/aas/tree/master/ch08-geotime ,
这个例子是通过分析纽约市2013年1月份的出租车数据,统计纽约市乘客下车点落在每个行政区的个数。
在开始正文之前,需要掌握以下基础知识:
- Scala基础语法
- Spark基础概念和原理(推荐《Spark快速大数据大分析》)
纽约出租车地理空间数据分析的主要流程:
- 数据获取
- 数据时间和和空间处理类库
- 数据预处理与地理空间分析
- 提交应用至集群,分布式计算
数据获取
本文的数据是纽约市2013年1月份乘客打车费用数据,数据大小是914.9M,解压后为2.5G。
数据下载地址
http://www.andresmh.com/nyctaxitrips/(trip_data_1.csv.zip)
数据下载方式
- 直接在window下载,上传至linux服务器,注意我的集群是docker容器,直接传到容器master节点。
- 在linux直接下载,命令如下
wget http://www.andresmh.com/nyctaxitrips/(trip_data_1.csv.zip)
数据描述
#解压数据集
unzip trip_data_1.csv.zip
# 查看前10行数据
head -n 10 trip_data_1.csv
结果如下图
数据字段描述:
vendor_id:类型 rate_code:比率 store_and_fwd_flag:是否是四驱
pickup_datatime:客人上车时间 dropoff_datatime:客人下车时间
passenger_count:载客数量 trip_time_in_secs:载客时间 trip_distance:载客距离
pickup_longitude:客人上车经度 pickup_latitude:客人上车维度
dropoff_longitude:客人下车经度 dropoff_latitude:客人下车维度
数据处理第三方类库
注意scala是可以直接调用java类库的。
时间处理类库:joda-time,nscala-time_2.11.jar(2.11对应scala版本)
本文空间关系处理库采用Esri的esri-geometry-api,当然也可以采用GeoTools等开源库。
自定义RichGeometry类封装Esri矢量空间处理接口;
package com.cloudera.datascience.geotime
import com.esri.core.geometry.{GeometryEngine, SpatialReference, Geometry}
import scala.language.implicitConversions
/**
* A wrapper that provides convenience methods for using the spatial relations in the ESRI
* GeometryEngine with a particular instance of the Geometry interface and an associated
* SpatialReference.
*
* @param geometry the geometry object
* @param spatialReference optional spatial reference; if not specified, uses WKID 4326 a.k.a.
* WGS84, the standard coordinate frame for Earth.
*/
class RichGeometry(val geometry: Geometry,
val spatialReference: SpatialReference = SpatialReference.create(4326)) extends Serializable {
def area2D(): Double = geometry.calculateArea2D()
def distance(other: Geometry): Double = {
GeometryEngine.distance(geometry, other, spatialReference)
}
def contains(other: Geometry): Boolean = {
GeometryEngine.contains(geometry, other, spatialReference)
}
def within(other: Geometry): Boolean = {
GeometryEngine.within(geometry, other, spatialReference)
}
def overlaps(other: Geometry): Boolean = {
GeometryEngine.overlaps(geometry, other, spatialReference)
}
def touches(other: Geometry): Boolean = {
GeometryEngine.touches(geometry, other, spatialReference)
}
def crosses(other: Geometry): Boolean = {
GeometryEngine.crosses(geometry, other, spatialReference)
}
def disjoint(other: Geometry): Boolean = {
GeometryEngine.disjoint(geometry, other, spatialReference)
}
}
/**
* Helper object for implicitly creating RichGeometry wrappers
* for a given Geometry instance.
*/
object RichGeometry extends Serializable {
implicit def createRichGeometry(g: Geometry): RichGeometry = new RichGeometry(g)
}
数据预处理与地理空间分析
上传原始数据到HDFS集群
#在Hdfs集群下创建taxidata目录,注意必须带/
hadoop fs -mkdir /taxidata
#上传本地物理机数据至HDFS集群
hadoop fs -put trip_data_1.csv /taxidata/trip_data_1.csv
自定义safe函数处理格式不正确的数据
详细请看代码注释第三部分
地理空间分析
获取纽约行政区划数据,利用esri gerometry类库判断各行政区下车点的记录数(详细请看代码注释第四部分)。
/**
* 打车信息类
* **/
case class Trip(
pickupTime: DateTime,
dropoffTime: DateTime,
pickupLoc: Point,
dropoffLoc: Point)
/**
* 出租车数据地理空间分析
*/
object RunGeoTime extends