edu.umn.cs.spatialHadoop.mapred.RandomShapeGenerator.java Source code

Java tutorial

Introduction

Here is the source code for edu.umn.cs.spatialHadoop.mapred.RandomShapeGenerator.java

Source

/***********************************************************************
* Copyright (c) 2015 by Regents of the University of Minnesota.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License, Version 2.0 which 
* accompanies this distribution and is available at
* http://www.opensource.org/licenses/apache2.0.php.
*
*************************************************************************/
package edu.umn.cs.spatialHadoop.mapred;

import java.io.IOException;
import java.util.Random;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.RecordReader;

import edu.umn.cs.spatialHadoop.OperationsParams;
import edu.umn.cs.spatialHadoop.core.Point;
import edu.umn.cs.spatialHadoop.core.Rectangle;
import edu.umn.cs.spatialHadoop.core.Shape;
import edu.umn.cs.spatialHadoop.core.SpatialSite;

/**
 * A record reader that generates random shapes in a specified area out of
 * the blue.
 * @author Ahmed Eldawy
 *
 * @param <S>
 */
public class RandomShapeGenerator<S extends Shape> implements RecordReader<Rectangle, S> {

    private static final byte[] NEW_LINE = System.getProperty("line.separator").getBytes();

    public enum DistributionType {
        UNIFORM, GAUSSIAN, CORRELATED, ANTI_CORRELATED, CIRCLE
    }

    /**Correlation factor for correlated, anticorrelated data*/
    private final static double rho = 0.9;

    /**The area whereshapes are generated*/
    private Rectangle mbr;

    /**Type of distribution to use for generating points*/
    private DistributionType type;

    /**Maximum edge length for generated rectangles*/
    private int rectsize;

    /**Random generator to use for generating shapes*/
    private Random random;

    /**Total size to be generated in bytes*/
    private long totalSize;

    /**The shape to use for generation*/
    private S shape;

    /**Size generated so far*/
    private long generatedSize;

    /**A temporary text used to serialize shapes to determine its size*/
    private Text text = new Text();

    /**The thickness of the circle (maxRadius - minRadius) if a circle distribution is used*/
    private double circleThickness;

    /**
     * Initialize from a FileSplit
     * @param job
     * @param split
     * @throws IOException
     */
    @SuppressWarnings("unchecked")
    public RandomShapeGenerator(Configuration job, RandomInputFormat.GeneratedSplit split) throws IOException {
        this(split.length, OperationsParams.getShape(job, "mbr").getMBR(),
                SpatialSite.getDistributionType(job, "type", DistributionType.UNIFORM), job.getInt("rectsize", 100),
                split.index + job.getLong("seed", System.currentTimeMillis()), job.getFloat("thickness", 1));
        setShape((S) SpatialSite.createStockShape(job));
    }

    public RandomShapeGenerator(long size, Rectangle mbr, DistributionType type, int rectsize, long seed,
            double circleThickness) {
        this.totalSize = size;
        this.mbr = mbr;
        this.type = type;
        this.rectsize = rectsize;
        this.random = new Random(seed);
        this.generatedSize = 0;
        this.circleThickness = circleThickness;
    }

    public void setShape(S shape) {
        this.shape = shape;
    }

    @Override
    public boolean next(Rectangle key, S value) throws IOException {
        // Generate a random shape
        generateShape(value, mbr, type, rectsize, random, circleThickness);

        // Serialize it to text first to make it easy count its size
        text.clear();
        value.toText(text);

        // Check if desired generated size has been reached
        if (text.getLength() + NEW_LINE.length + generatedSize > totalSize)
            return false;

        generatedSize += text.getLength() + NEW_LINE.length;

        return true;
    }

    @Override
    public Rectangle createKey() {
        Rectangle key = new Rectangle();
        key.invalidate();
        return key;
    }

    @Override
    public S createValue() {
        return shape;
    }

    @Override
    public long getPos() throws IOException {
        return generatedSize;
    }

    @Override
    public void close() throws IOException {
        // Nothing
    }

    @Override
    public float getProgress() throws IOException {
        if (totalSize == 0) {
            return 0.0f;
        } else {
            return Math.min(1.0f, generatedSize / (float) totalSize);
        }
    }

    private static void generateShape(Shape shape, Rectangle mbr, DistributionType type, int rectSize,
            Random random, double circleThickness) {
        if (shape instanceof Point) {
            generatePoint((Point) shape, mbr, type, random, circleThickness);
        } else if (shape instanceof Rectangle) {
            ((Rectangle) shape).x1 = random.nextDouble() * (mbr.x2 - mbr.x1) + mbr.x1;
            ((Rectangle) shape).y1 = random.nextDouble() * (mbr.y2 - mbr.y1) + mbr.y1;
            ((Rectangle) shape).x2 = Math.min(mbr.x2, ((Rectangle) shape).x1 + random.nextInt(rectSize) + 2);
            ((Rectangle) shape).y2 = Math.min(mbr.y2, ((Rectangle) shape).y1 + random.nextInt(rectSize) + 2);
        } else {
            throw new RuntimeException("Cannot generate random shapes of type: " + shape.getClass());
        }
    }

    // The standard deviation is 0.2
    public static double nextGaussian(Random rand) {
        double res;
        do {
            res = rand.nextGaussian() / 5.0;
        } while (res < -1 || res > 1);
        return res;
    }

    public static void generatePoint(Point p, Rectangle mbr, DistributionType type, Random rand,
            double circleThickness) {
        double x, y;
        switch (type) {
        case UNIFORM:
            p.x = rand.nextDouble() * (mbr.x2 - mbr.x1) + mbr.x1;
            p.y = rand.nextDouble() * (mbr.y2 - mbr.y1) + mbr.y1;
            break;
        case GAUSSIAN:
            p.x = nextGaussian(rand) * (mbr.x2 - mbr.x1) / 2.0 + (mbr.x1 + mbr.x2) / 2.0;
            p.y = nextGaussian(rand) * (mbr.y2 - mbr.y1) / 2.0 + (mbr.y1 + mbr.y2) / 2.0;
            break;
        case CORRELATED:
        case ANTI_CORRELATED:
            x = rand.nextDouble() * 2 - 1;
            do {
                y = rho * x + Math.sqrt(1 - rho * rho) * nextGaussian(rand);
            } while (y < -1 || y > 1);
            p.x = x * (mbr.x2 - mbr.x1) / 2.0 + (mbr.x1 + mbr.x2) / 2.0;
            p.y = y * (mbr.y2 - mbr.y1) / 2.0 + (mbr.y1 + mbr.y2) / 2.0;
            if (type == DistributionType.ANTI_CORRELATED)
                p.y = mbr.y2 - (p.y - mbr.y1);
            break;
        case CIRCLE:
            double degree = rand.nextDouble() * Math.PI * 2;
            double layer = rand.nextDouble() * circleThickness;
            double xradius = (mbr.x2 - mbr.x1) / 2 - layer;
            double yradius = (mbr.y2 - mbr.y1) / 2 - layer;
            double dx = Math.cos(degree) * xradius;
            double dy = Math.sin(degree) * yradius;
            p.x = (mbr.x1 + mbr.x2) / 2 + dx;
            p.y = (mbr.y1 + mbr.y2) / 2 + dy;
            break;
        default:
            throw new RuntimeException("Unrecognized distribution type: " + type);
        }
    }

}