1、前言

HDF文件是遥感应用中一种常见的数据格式,因为其高度结构化的特点,笔者曾被怎样使用Hadoop处理HDF文件这个问题困扰过相当长的一段时间。于是Google各种解决方式,但都没有找到一种理想的处理办法。也曾參考过HDFGroup官方发的一篇帖子(网址在这里),里面提供了使用Hadoop针对大、中、小HDF文件的处理思路。尽管依据他提供的解决的方法,按图索骥,肯定能解决怎样使用Hadoop处理HDF文件这个问题,但个人感觉方法偏复杂且须要对HDF的数据格式有较深的理解,实现起来不太easy。于是乎,笔者又继续寻找解决方式,最终发现了一种办法,以下将对该方法进行详细说明。

2、MapReduce主程序

这里主要使用到了netcdf的库进行hdf数据流的反序列化工作(netcdf库的下载地址)。与HDF官方提供的Java库不同,netcdf仅利用Java进行HDF文件的读写操作,且这个库支持多种科学数据,包含HDF4、HDF5等多种格式。而HDF的官方Java库中,底层实际仍是用C进行HDF文件的操作。以下贴出MapReduce的Mapper函数代码:

package example;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.util.List; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper; import ucar.ma2.ArrayShort;
import ucar.nc2.Dimension;
import ucar.nc2.Group;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable; public class ReadMapper extends
Mapper<Text, BytesWritable, Text, BytesWritable> { public void map(Text key, BytesWritable value, Context context)
throws IOException, InterruptedException {
String fileName = key.toString();
NetcdfFile file = NetcdfFile.openInMemory("hdf4", value.get());
Group dataGroup = (file.findGroup("MOD_Grid_monthly_1km_VI")).findGroup("Data_Fields");
//读取到1_km_monthly_red_reflectance的变量
Variable redVar = dataGroup.findVariable("1_km_monthly_red_reflectance");
short[][] data = new short[1200][1200];
if(dataGroup != null){
ArrayShort.D2 dataArray;
//读取redVar中的影像数据
dataArray = (ArrayShort.D2) redVar.read();
List<Dimension> dimList = file.getDimensions();
//获取影像的y方向像元个数
Dimension ydim = dimList.get(0);
//获取影像的x方向像元个数
Dimension xdim = dimList.get(1);
//遍历整个影像,读取出像元的值
for(int i=0;i<xdim.getLength();i++){
for(int j=0;j<ydim.getLength();j++){
data[i][j] = dataArray.get(i, j);
}
}
}
System.out.print(file.getDetailInfo());
}
}

注意程序中的NetcdfFile.openInMemory方法,该静态方法支持从byte[]中构造HDF文件,从而实现了HDF文件的反序列化操作。以下贴出主程序的演示样例代码:

package example;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.NullOutputFormat; import example.WholeFileInputFormat; public class ReadMain {
public boolean runJob(String[] args) throws IOException,
ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
// conf.set("mapred.job.tracker", Utils.JOBTRACKER);
String rootPath= "/opt/hadoop-2.3.0/etc/hadoop";
//String rootPath="/opt/hadoop-2.3.0/etc/hadoop/";
conf.addResource(new Path(rootPath+"yarn-site.xml"));
conf.addResource(new Path(rootPath+"core-site.xml"));
conf.addResource(new Path(rootPath+"hdfs-site.xml"));
conf.addResource(new Path(rootPath+"mapred-site.xml"));
Job job = new Job(conf); job.setJobName("Job name:" + args[0]);
job.setJarByClass(ReadMain.class); job.setMapperClass(ReadMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(BytesWritable.class); job.setInputFormatClass(WholeFileInputFormat.class);
job.setOutputFormatClass(NullOutputFormat.class);
FileInputFormat.addInputPath(job, new Path(args[1]));
FileOutputFormat.setOutputPath(job, new Path(args[2]));
boolean flag = job.waitForCompletion(true);
return flag;
} public static void main(String[] args) throws ClassNotFoundException,
IOException, InterruptedException {
String[] inputPaths = new String[] { "normalizeJob",
"hdfs://192.168.168.101:9000/user/hduser/hdf/MOD13A3.A2005274.h00v10.005.2008079143041.hdf",
"hdfs://192.168.168.101:9000/user/hduser/test/" };
ReadMain test = new ReadMain();
test.runJob(inputPaths);
} }

关于MapReduce主程序有几点值得说明一下:

1、MapReduce数据的输入格式为WholeFileInputFormat.class,即不正确数据进行切分。关于该格式,能够參考另外一篇博客:怎样通过Java程序提交Yarn的计算任务,这里不再赘述。

2、本人用的是Yarn2.3.0来运行计算任务,假设用老版本号的hadoop,如1.2.0,则把以上主程序中的conf.addResource部分的代码删掉就可以。

3、以上MapReduce程序中,仅仅用到了Map函数,未设置Reduce函数。

4、以上程序用到的为HDF4格式的数据,按理说,HDF5格式的数据应该也是支持的。

3、HDF数据的格式

因为HDF数据高度结构化,因此在netcdf库的使用中,须要使用类似于"标签"的方式来訪问HDF中的详细数据。以下贴出netcdf中读出来的HDF数据的详细格式信息(即使用file.getDetailInfo()函数,打印出来的信息):

注意,ReadMapper函数中出现的类似于“MOD_Grid_monthly_1km_VI”、"Data_Fields"等信息,即依据下面HDF数据的格式信息得到的。

netcdf D:/2005-274/MOD13A3.A2005274.h00v08.005.2008079142757.hdf {
variables:
char StructMetadata.0(32000); char CoreMetadata.0(40874); char ArchiveMetadata.0(6530); group: MOD_Grid_monthly_1km_VI {
variables:
short _HDFEOS_CRS;
:Projection = "GCTP_SNSOID";
:UpperLeftPointMtrs = -2.0015109354E7, 1111950.519667; // double
:LowerRightMtrs = -1.8903158834333E7, -0.0; // double
:ProjParams = 6371007.181, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0; // double
:SphereCode = "-1"; group: Data_Fields {
dimensions:
YDim = 1200;
XDim = 1200;
variables:
short 1_km_monthly_NDVI(YDim=1200, XDim=1200);
:long_name = "1 km monthly NDVI";
:units = "NDVI";
:valid_range = -2000S, 10000S; // short
:_FillValue = -3000S; // short
:scale_factor = 10000.0; // double
:scale_factor_err = 0.0; // double
:add_offset = 0.0; // double
:add_offset_err = 0.0; // double
:calibrated_nt = 5; // int short 1_km_monthly_EVI(YDim=1200, XDim=1200);
:long_name = "1 km monthly EVI";
:units = "EVI";
:valid_range = -2000S, 10000S; // short
:_FillValue = -3000S; // short
:scale_factor = 10000.0; // double
:scale_factor_err = 0.0; // double
:add_offset = 0.0; // double
:add_offset_err = 0.0; // double
:calibrated_nt = 5; // int short 1_km_monthly_VI_Quality(YDim=1200, XDim=1200);
:_Unsigned = "true";
:long_name = "1 km monthly VI Quality";
:units = "bit field";
:valid_range = 0S, -2S; // short
:_FillValue = -1S; // short
:Legend = "\n\t Bit Fields Description (Right to Left): \n\t[0-1] : MODLAND_QA [2 bit range]\n\t\t 00: VI produced, good quality \n\t\t 01: VI produced, but check other QA \n\t\t 10: Pixel produced, but most probably cloudy \n\t\t 11: Pixel not produced due to other reasons than clouds \n\t[2-5] : VI usefulness [4 bit range] \n\t\t 0000: Highest quality \n\t\t 0001: Lower quality \n\t\t 0010..1010: Decreasing quality \n\t\t 1100: Lowest quality \n\t\t 1101: Quality so low that it is not useful \n\t\t 1110: L1B data faulty \n\t\t 1111: Not useful for any other reason/not processed \n\t[6-7] : Aerosol quantity [2 bit range] \n\t\t 00: Climatology \n\t\t 01: Low \n\t\t 10: Average \n\t\t 11: High (11) \n\t[8] : Adjacent cloud detected; [1 bit range] \n\t\t 1: Yes \n\t\t 0: No \n\t[9] : Atmosphere BRDF correction performed [1 bit range] \n\t\t 1: Yes \n\t\t 0: No \n\t[10] : Mixed clouds [1 bit range] \n\t\t 1: Yes \n\t\t 0: No \n\t[11-13] : Land/Water Flag [3 bit range] \n\t\t 000: Shallow ocean \n\t\t 001: Land (Nothing else but land) \n\t\t 010: Ocean coastlines and lake shorelines \n\t\t 011: Shallow inland water \n\t\t 100: Ephemeral water \n\t\t 101: Deep inland water \n\t\t 110: Moderate or continental ocean \n\t\t 111: Deep ocean \n\t[14] : Possible snow/ice [1 bit range] \n\t\t 1: Yes \n\t\t 0: No \n\t[15] : Possible shadow [1 bit range] \n\t\t 1: Yes \n\t\t 0: No \n"; short 1_km_monthly_red_reflectance(YDim=1200, XDim=1200);
:long_name = "1 km monthly red reflectance";
:units = "reflectance";
:valid_range = 0S, 10000S; // short
:_FillValue = -1000S; // short
:scale_factor = 10000.0; // double
:scale_factor_err = 0.0; // double
:add_offset = 0.0; // double
:add_offset_err = 0.0; // double
:calibrated_nt = 5; // int short 1_km_monthly_NIR_reflectance(YDim=1200, XDim=1200);
:long_name = "1 km monthly NIR reflectance";
:units = "reflectance";
:valid_range = 0S, 10000S; // short
:_FillValue = -1000S; // short
:scale_factor = 10000.0; // double
:scale_factor_err = 0.0; // double
:add_offset = 0.0; // double
:add_offset_err = 0.0; // double
:calibrated_nt = 5; // int short 1_km_monthly_blue_reflectance(YDim=1200, XDim=1200);
:long_name = "1 km monthly blue reflectance";
:units = "reflectance";
:valid_range = 0S, 10000S; // short
:_FillValue = -1000S; // short
:scale_factor = 10000.0; // double
:scale_factor_err = 0.0; // double
:add_offset = 0.0; // double
:add_offset_err = 0.0; // double
:calibrated_nt = 5; // int short 1_km_monthly_MIR_reflectance(YDim=1200, XDim=1200);
:long_name = "1 km monthly MIR reflectance";
:units = "reflectance";
:valid_range = 0S, 10000S; // short
:_FillValue = -1000S; // short
:Legend = "\n\t The MIR band saved in the VI product is MODIS band 7 \n\t\t Bandwidth : 2105-2155 nm \n\t\t Band center: 2130 nm \n";
:scale_factor = 10000.0; // double
:scale_factor_err = 0.0; // double
:add_offset = 0.0; // double
:add_offset_err = 0.0; // double
:calibrated_nt = 5; // int short 1_km_monthly_view_zenith_angle(YDim=1200, XDim=1200);
:long_name = "1 km monthly view zenith angle";
:units = "degrees";
:valid_range = -9000S, 9000S; // short
:_FillValue = -10000S; // short
:scale_factor = 100.0; // double
:scale_factor_err = 0.0; // double
:add_offset = 0.0; // double
:add_offset_err = 0.0; // double
:calibrated_nt = 5; // int short 1_km_monthly_sun_zenith_angle(YDim=1200, XDim=1200);
:long_name = "1 km monthly sun zenith angle";
:units = "degrees";
:valid_range = -9000S, 9000S; // short
:_FillValue = -10000S; // short
:scale_factor = 100.0; // double
:scale_factor_err = 0.0; // double
:add_offset = 0.0; // double
:add_offset_err = 0.0; // double
:calibrated_nt = 5; // int short 1_km_monthly_relative_azimuth_angle(YDim=1200, XDim=1200);
:long_name = "1 km monthly relative azimuth angle";
:units = "degrees";
:valid_range = -3600S, 3600S; // short
:_FillValue = -4000S; // short
:scale_factor = 10.0; // double
:scale_factor_err = 0.0; // double
:add_offset = 0.0; // double
:add_offset_err = 0.0; // double
:calibrated_nt = 5; // int byte 1_km_monthly_pixel_raliability(YDim=1200, XDim=1200);
:long_name = "1 km monthly pixel raliability";
:units = "rank";
:valid_range = 0B, 3B; // byte
:_FillValue = -1B; // byte
:Legend = "\n\t Rank Keys: \n\t\t[-1]: Fill/No Data-Not Processed. \n\t\t [0]: Good data - Use with confidence \n\t\t [1]: Marginal data - Useful, but look at other QA information \n\t\t [2]: Snow/Ice - Target covered with snow/ice\n\t\t [3]: Cloudy - Target not visible, covered with cloud \n"; }
}
// global attributes:
:HDFEOSVersion = "HDFEOS_V2.9";
:_History = "Direct read of HDF4 file through CDM library; HDF-EOS StructMetadata information was read";
:HDF4_Version = "4.2.1 (NCSA HDF Version 4.2 Release 1-post3, January 27, 2006)";
:featureType = "GRID";
}

Hadoop处理HDF文件的更多相关文章

  1. Hadoop之HDFS文件操作常有两种方式(转载)

    摘要:Hadoop之HDFS文件操作常有两种方式,命令行方式和JavaAPI方式.本文介绍如何利用这两种方式对HDFS文件进行操作. 关键词:HDFS文件    命令行     Java API HD ...

  2. hadoop对于压缩文件的支持及算法优缺点

    hadoop对于压缩文件的支持及算法优缺点   hadoop对于压缩格式的是透明识别,我们的MapReduce任务的执行是透明的,hadoop能够自动为我们 将压缩的文件解压,而不用我们去关心. 如果 ...

  3. Hadoop HDFS分布式文件系统设计要点与架构

      Hadoop HDFS分布式文件系统设计要点与架构     Hadoop简介:一个分布式系统基础架构,由Apache基金会开发.用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群 ...

  4. 在本机eclipse中创建maven项目,查看linux中hadoop下的文件、在本机搭建hadoop环境

    注意 第一次建立maven项目时需要在联网情况下,因为他会自动下载一些东西,不然突然终止 需要手动删除断网前建立的文件 在eclipse里新建maven项目步骤 直接新建maven项目出了错      ...

  5. Hadoop基础-镜像文件(fsimage)和编辑日志(edits)

    Hadoop基础-镜像文件(fsimage)和编辑日志(edits) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.查看日志镜像文件(如:fsimage_00000000000 ...

  6. Python解析HDF文件 分类: Python 2015-06-25 00:16 743人阅读 评论(0) 收藏

    前段时间因为一个业务的需求需要解析一个HDF格式的文件.在这之前也不知道到底什么是HDF文件.百度百科的解释如下: HDF是用于存储和分发科学数据的一种自我描述.多对象文件格式.HDF是由美国国家超级 ...

  7. 如何利用Hadoop存储小文件

    **************************************************************************************************** ...

  8. 【大数据系列】hadoop上传文件报错_COPYING_ could only be replicated to 0 nodes

    使用hadoop上传文件 hdfs dfs -put  XXX 17/12/08 17:00:39 WARN hdfs.DFSClient: DataStreamer Exception org.ap ...

  9. Hadoop Maven pom文件示例

    Hadoop Maven pom文件示例 @(Hadoop) <?xml version="1.0" encoding="UTF-8"?> < ...

随机推荐

  1. 在ASP.NET MVC 中获取当前URL、controller、action(转)

    URL的获取很简单,ASP.NET通用: [1]获取 完整url (协议名+域名+虚拟目录名+文件名+参数) string url=Request.Url.ToString(); [2]获取 虚拟目录 ...

  2. Linux进程同步之记录锁(fcntl)

    记录锁相当于线程同步中读写锁的一种扩展类型,可以用来对有亲缘或无亲缘关系的进程进行文件读与写的同步,通过fcntl函数来执行上锁操作.尽管读写锁也可以通过在共享内存区来进行进程的同步,但是fcntl记 ...

  3. 通过Java字节码发现有趣的内幕之String篇(上)(转)

    原文出处: jaffa 很多时候我们在编写Java代码时,判断和猜测代码问题时主要是通过运行结果来得到答案,本博文主要是想通过Java字节码的方式来进一步求证我们已知的东西.这里没有对Java字节码知 ...

  4. The Building Blocks-Enterprise Applications Part 2- Information Management and Business Analytics

    1. Business Analytic Applications Data Analytics Also referred to as 'Business Analytics' or 'Busine ...

  5. Bootstrap网站模板

    根据一篇文章,我再想想写下,无意义,他决定收手. 或者直接做一个简单的基本的模板它 主要知识点包含栅格系统.响应式图片.导航条(固定在顶部和底部).搜索框等等 详细每一个知识点不再赘述,參考Boots ...

  6. Android 解决Gallery下ScrollView滑动事件冲突

    在Gallery下,里面内容过长超出屏幕,这时我们可以用ScrollView来滚动,但是这样做了以后,会发现一个问题,Gallery的滑动事件和ScrollView的滑动事件起冲突,这时我们可以自定义 ...

  7. 数据验证validator 与 DWZ

    在进行系统经常使用的数据验证.数据验证可以编写自己的,它也可以用来作为现在.现在,记录这两个库的使用, validator <!DOCTYPE HTML PUBLIC "-//W3C/ ...

  8. Binder Proxy技术方案

    Binder Proxy技术方案 作者 低端码农 时间 2014.08.23 0x0 看到有多朋友尝试通过hook系统进程system_process的ioctl,以企图截获系统的IPC通讯.这个方法 ...

  9. 解决wordpress发表文章,照片不能居中的问题

    最近,随着一个年轻漂亮的女人的帮助(简直美极了.图相当火爆,性格非常好.大家闺秀型,照片给大家看看下一个突发.哈哈)获取她的个人博客,地址似乎是www.okaaok.com遇到发表文章.照片不能反正水 ...

  10. 【原创】java中的父进程子进程 —— 坑爹的java Runtime.getRuntime().exec

    最近有一个需求,需要用一个java进程启动多个子进程来完成并发任务.由于必须给用户完成任务的反馈,所以需要父进程记录子进程的生命周期. exec方法返回一个Process对象,在当前进程内调用该对象的 ...