前面章节已经大概描述了在SpatialHadoop中R-Tree空间索引实现的步骤,本章节就从源码角度来看下该算法是怎么实现的。
================================================================
在这个类中主要实现了对输入数据集的一个随机抽样,控制抽样的有两个参数,一个抽样的大小,第二个是抽样的个数。大小由内存计算决定,小于100MB,个数由输入要素集的总个数决定,为0.1%的比率。
public void map(Rectangle cell, Text line, OutputCollector<IntWritable, Text> output, Reporter reporter) throws IOException { if (random.nextFloat() < sampleRatio) { switch (conversion) { case None: output.collect(key, line); break; case ShapeToPoint: inShape.fromText(line); Rectangle mbr = inShape.getMBR(); if (mbr != null) { Point center = mbr.getCenterPoint(); line.clear(); center.toText(line); output.collect(key, line); } break; case ShapeToRect: inShape.fromText(line); mbr = inShape.getMBR(); if (mbr != null) { line.clear(); mbr.toText(line); output.collect(key, line); } break; } }以上代码是抽样过程中的Map函数。其中random是随机生成的一个介于0.0-1之间的float,与sampleratio进行对比,这里默认是0.0099******,这样每一个map到的要素被执行的概率约为0.1%。由于是面要素,所以这里取其中心点。
在这个类中开始计算分割的方法。这里用到了STR算法。这里用STR算法用了两次,第一次是对采样点X进行排序。排完之后计算分割的行和列以及分割线。如何计算呢?上代码。
partitioner.createFromPoints(inMBR, sample.toArray(new Point[sample.size()]), partitionCapacity);------------------------------------------------------------------
这里我们知道了整个数据集的面积,知道了要分多少行,要分多少列,还有一堆抽样点。那么如何将所有抽样点进行划分呢?
我们先看X方向(经度),这里X代表着列。
第一列,当column=0的时候,我们只用计算第一列的右边的X分隔线,因为左边就是输入要素集的边界啦。
int col_quantile = (column + 1) * points.length / columns; this.xSplits[column] = col_quantile == points.length ? mbr.x2 : points[col_quantile-1].x;第一行是计算第一列的点编号,这里就是平均,比如我有100个点,分了10列,那么第一个点就取X排序的第十个点作为分隔线就可以了;第二行开始计算第一列的右分隔线。这里只是做了一个判断,判断你要计算的点是否在范围内,如果在,那就取当前的点,如果不在就取要素集的最右边界线就可以了。
这样就取完了第一列。下面开始取这一列的所有行。同上开始循环啦。
也要对Y进行一次排序。排完之后,开始取Y的分隔线。
// Compute y-splits for this column for (int row = 0; row < rows; row++) { int row_quantile = (prev_quantile * (rows - (row+1)) + col_quantile * (row+1)) / rows; // Determine y split for this row. Last row has a special handling this.ySplits[column * rows + row] = row_quantile == col_quantile ? mbr.y2 : points[row_quantile].y; }最终我们得到了如何划分整个区域的规则,即要划分多少行,多少列,以及从哪里开始划分。
取到当前要素之后,判断它与块边界的关系,来划分所有的要素。
while (reader.nextKeyValue()) { Iterable<Shape> shapes = reader.getCurrentValue(); if (replicate) { for (final Shape s : shapes) { partitioner.overlapPartitions(s, new ResultCollector<Integer>() { @Override public void collect(Integer id) { partitionID.set(id); try { recordWriter.write(partitionID, s); } catch (IOException e) { throw new RuntimeException(e); } } }); } } else { for (final Shape s : shapes) { int pid = partitioner.overlapPartition(s); if (pid != -1) { partitionID.set(pid); recordWriter.write(partitionID, s); } } }
GeoHadoop