edu.umn.cs.spatialHadoop.visualization.FrequencyMap.java Source code

Java tutorial

Introduction

Here is the source code for edu.umn.cs.spatialHadoop.visualization.FrequencyMap.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.visualization;

import java.awt.Color;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.umn.cs.spatialHadoop.core.Rectangle;

/**
 * A frequency map that can be used to visualize data as heat maps
 * @author Ahmed Eldawy
 *
 */
public class FrequencyMap extends Canvas {
    @SuppressWarnings("unused")
    private static final Log LOG = LogFactory.getLog(FrequencyMap.class);

    public static enum SmoothType {
        Flat, Gaussian
    };

    /**The kernel to use for stamping points*/
    protected float[][] kernel;

    /**Frequencies*/
    protected float[][] frequencies;

    /**Radius to smooth nearboy points*/
    private int radius;

    /**The minimum value to be used while drawing the heat map*/
    private float min;

    /**The maximum value to be used while drawing the heat map*/
    private float max;

    /**
     * Initialize an empty frequency map to be used to deserialize 
     */
    public FrequencyMap() {
        System.setProperty("java.awt.headless", "true");
    }

    /**
     * Initializes a frequency map with the given dimensions
     * @param width
     * @param height
     */
    public FrequencyMap(Rectangle inputMBR, int width, int height, int radius, SmoothType smoothType) {
        System.setProperty("java.awt.headless", "true");
        this.inputMBR = inputMBR;
        this.width = width;
        this.height = height;
        this.frequencies = new float[width][height];
        this.min = -1;
        this.max = -2;
        initKernel(radius, smoothType);
    }

    /**
     * Initialize a frequency map with the given radius and kernel type
     * @param radius
     * @param smoothType
     */
    protected void initKernel(int radius, SmoothType smoothType) {
        this.radius = radius;
        // initialize the kernel according to the radius and kernel type
        kernel = new float[radius * 2][radius * 2];
        switch (smoothType) {
        case Flat:
            for (int dx = -radius; dx < radius; dx++) {
                for (int dy = -radius; dy < radius; dy++) {
                    if (dx * dx + dy * dy < radius * radius) {
                        kernel[dx + radius][dy + radius] = 1.0f;
                    }
                }
            }
            break;
        case Gaussian:
            int stdev = 8;
            // Apply two-dimensional Gaussian function
            // http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function
            for (int dx = -radius; dx < radius; dx++) {
                for (int dy = -radius; dy < radius; dy++) {
                    kernel[dx + radius][dy + radius] = (float) Math
                            .exp(-(dx * dx + dy * dy) / (2.0 * stdev * stdev));
                }
            }
        }
    }

    /**
     * Sets the range of value to be used while drawing the heat map
     * @param min
     * @param max
     */
    public void setValueRange(float min, float max) {
        this.min = min;
        this.max = max;
    }

    @Override
    public void write(DataOutput out) throws IOException {
        super.write(out);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPOutputStream gzos = new GZIPOutputStream(baos);
        ByteBuffer bbuffer = ByteBuffer.allocate(getHeight() * 4 + 8);
        bbuffer.putInt(getWidth());
        bbuffer.putInt(getHeight());
        gzos.write(bbuffer.array(), 0, bbuffer.position());
        for (int x = 0; x < getWidth(); x++) {
            bbuffer.clear();
            for (int y = 0; y < getHeight(); y++) {
                bbuffer.putFloat(frequencies[x][y]);
            }
            gzos.write(bbuffer.array(), 0, bbuffer.position());
        }
        gzos.close();

        byte[] serializedData = baos.toByteArray();
        out.writeInt(serializedData.length);
        out.write(serializedData);
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        super.readFields(in);
        int length = in.readInt();
        byte[] serializedData = new byte[length];
        in.readFully(serializedData);
        ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
        GZIPInputStream gzis = new GZIPInputStream(bais);

        byte[] buffer = new byte[8];
        gzis.read(buffer);
        ByteBuffer bbuffer = ByteBuffer.wrap(buffer);
        int width = bbuffer.getInt();
        int height = bbuffer.getInt();
        // Reallocate memory only if needed
        if (width != this.getWidth() || height != this.getHeight())
            frequencies = new float[width][height];
        buffer = new byte[getHeight() * 4];
        for (int x = 0; x < getWidth(); x++) {
            int size = 0;
            while (size < buffer.length) {
                size += gzis.read(buffer, size, buffer.length - size);
            }
            bbuffer = ByteBuffer.wrap(buffer);
            for (int y = 0; y < getHeight(); y++) {
                frequencies[x][y] = bbuffer.getFloat();
            }
        }
    }

    public void mergeWith(FrequencyMap another) {
        Point offset = projectToImageSpace(another.getInputMBR().x1, another.getInputMBR().y1);
        int xmin = Math.max(0, offset.x);
        int ymin = Math.max(0, offset.y);
        int xmax = Math.min(this.getWidth(), another.getWidth() + offset.x);
        int ymax = Math.min(this.getHeight(), another.getHeight() + offset.y);
        for (int x = xmin; x < xmax; x++) {
            for (int y = ymin; y < ymax; y++) {
                this.frequencies[x][y] += another.frequencies[x - offset.x][y - offset.y];
            }
        }
    }

    public BufferedImage asImage() {
        if (min >= max) {
            // Values not set. Autodetect
            min = Float.MAX_VALUE;
            max = -Float.MAX_VALUE;
            for (int x = 0; x < this.getWidth(); x++) {
                for (int y = 0; y < this.getHeight(); y++) {
                    if (frequencies[x][y] < min)
                        min = frequencies[x][y];
                    if (frequencies[x][y] > max)
                        max = frequencies[x][y];
                }
            }
        }
        BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
        for (int x = 0; x < this.getWidth(); x++) {
            for (int y = 0; y < this.getHeight(); y++) {
                Color color = calculateColor(frequencies[x][y], min, max);
                image.setRGB(x, y, color.getRGB());
            }
        }
        return image;
    }

    /**
     * Adds a point to the frequency map
     * @param cx
     * @param cy
     */
    public void addPoint(int cx, int cy) {
        for (int dx = -radius; dx < radius; dx++) {
            for (int dy = -radius; dy < radius; dy++) {
                int imgx = cx + dx;
                int imgy = cy + dy;
                if (imgx >= 0 && imgx < getWidth() && imgy >= 0 && imgy < getHeight())
                    frequencies[imgx][imgy] += kernel[dx + radius][dy + radius];
            }
        }
    }

    public int getWidth() {
        return frequencies == null ? 0 : frequencies.length;
    }

    public int getHeight() {
        return frequencies == null ? 0 : frequencies[0].length;
    }

    /* The following methods are used to compute the gradient */

    protected Color[] colors;
    protected float[] hues;
    protected float[] saturations;
    protected float[] brightnesses;

    public enum GradientType {
        GT_HSB, GT_RGB
    };

    protected GradientType gradientType;

    public void setGradientInfor(Color color1, Color color2, GradientType gradientType) {
        this.colors = new Color[] { color1, color2 };
        this.hues = new float[colors.length];
        this.saturations = new float[colors.length];
        this.brightnesses = new float[colors.length];

        for (int i = 0; i < colors.length; i++) {
            float[] hsbvals = new float[3];
            Color.RGBtoHSB(colors[i].getRed(), colors[i].getGreen(), colors[i].getBlue(), hsbvals);
            hues[i] = hsbvals[0];
            saturations[i] = hsbvals[1];
            brightnesses[i] = hsbvals[2];
        }
        this.gradientType = gradientType;
    }

    protected Color calculateColor(float value, float minValue, float maxValue) {
        Color color;
        if (value < minValue) {
            color = colors[0];
        } else if (value > maxValue) {
            color = colors[1];
        } else {
            // Interpolate between two colors according to gradient type
            float ratio = (value - minValue) / (maxValue - minValue);
            if (gradientType == GradientType.GT_HSB) {
                // Interpolate between two hues
                float hue = hues[0] * (1.0f - ratio) + hues[1] * ratio;
                float saturation = saturations[0] * (1.0f - ratio) + saturations[1] * ratio;
                float brightness = brightnesses[0] * (1.0f - ratio) + brightnesses[1] * ratio;
                color = Color.getHSBColor(hue, saturation, brightness);
                int alpha = (int) (colors[0].getAlpha() * (1.0f - ratio) + colors[1].getAlpha() * ratio);
                color = new Color(color.getRGB() & 0xffffff | (alpha << 24), true);
            } else if (gradientType == GradientType.GT_RGB) {
                // Interpolate between colors
                int red = (int) (colors[0].getRed() * (1.0f - ratio) + colors[1].getRed() * ratio);
                int green = (int) (colors[0].getGreen() * (1.0f - ratio) + colors[1].getGreen() * ratio);
                int blue = (int) (colors[0].getBlue() * (1.0f - ratio) + colors[1].getBlue() * ratio);
                int alpha = (int) (colors[0].getAlpha() * (1.0f - ratio) + colors[1].getAlpha() * ratio);
                color = new Color(red, green, blue, alpha);
            } else {
                throw new RuntimeException("Unsupported gradient type: " + gradientType);
            }
        }
        return color;
    }

}