Android 等高线绘图(又称热力图)
注:以下数据为一个一维的图片数组
注: 绘制方法来自https://github.com/ChristianFF/HeatMapForAndroid
https://github.com/HeartlandSoftware/AndroidHeatMap注:可使用两种方法绘制
使用高斯核密度估计方法绘制
使用绘制点阴影的方式绘制
使用高斯核密度估计方法绘制
- 主要流程:
- 处理数据
- 使用高斯核密度估计算计进行数组插值
- 根据所得数组绘制Bitmap
一、处理数据
根据一维的图片数组,转换成点坐标和值,封装到点对象里
//循环遍历数组
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
n = i * HEIGHT + j;
if (data[n] > startValue) {
tmp_sum += data[n];
}
tmp = (data[n] - startValue) / (endValue - 0);
if (tmp > filter) {
//根据数组值所在位置和值转换为点坐标和压力值
//clamp为一个工具方法,可根据(百分比,最小值,最大值),得出具体数值
addData(new DataPoint((int) (clamp((float) j / 60, 0.0f, mWidth)),
(int) (clamp((float) i / 50, 0.0f, mHeight)),
clamp(tmp, 0.0, 100.0)));
}
}
}
二、使用高斯核密度估计算计进行数组插值
1.创建一个二维数组,大小依据要生成的bitmap大小相等,如
double[][] intensity = new double[mWidth + mRadius * 2][mHeight + mRadius * 2];
2.将上面处理好的点数据填充到该数组里面
for (DataPint w : mData) {
int bucketX = w.x;
int bucketY = w.y;
if (bucketX < mWidth && bucketX >= 0
&& bucketY < mHeight && bucketY >= 0)
intensity[bucketX][bucketY] += w.intensity;
}
3.使用高斯核密度估计算法处理该数组
double[][] convolved = convolve(intensity, mKernel);
//高斯核密度估计算法
static double[][] convolve(double[][] grid, double[] kernel) {
int radius = (int) Math.floor((double) kernel.length / 2.0);
int dimOldW = grid.length;
int dimOldH = grid[0].length;
int dimW = dimOldW - 2 * radius;
int dimH = dimOldH - 2 * radius;
int lowerLimit = radius;
int upperLimitW = radius + dimW - 1;
int upperLimitH = radius + dimH - 1;
double[][] intermediate = new double[dimOldW][dimOldH];
int x, y, x2, xUpperLimit, initial;
double val;
for (x = 0; x < dimOldW; x++) {
for (y = 0; y < dimOldH; y++) {
val = grid[x][y];
if (val != 0) {
xUpperLimit = ((upperLimitW < x + radius) ? upperLimitW : x + radius) + 1;
initial = (lowerLimit > x - radius) ? lowerLimit : x - radius;
for (x2 = initial; x2 < xUpperLimit; x2++) {
intermediate[x2][y] += val * kernel[x2 - (x - radius)];
}
}
}
}
double[][] outputGrid = new double[dimW][dimH];
int y2, yUpperLimit;
for (x = lowerLimit; x < upperLimitW + 1; x++) {
for (y = 0; y < dimOldH; y++) {
val = intermediate[x][y];
if (val != 0) {
yUpperLimit = ((upperLimitH < y + radius) ? upperLimitH : y + radius) + 1;
initial = (lowerLimit > y - radius) ? lowerLimit : y - radius;
for (y2 = initial; y2 < yUpperLimit; y2++) {
outputGrid[x - radius][y2 - radius] += val * kernel[y2 - (y - radius)];
}
}
}
}
return outputGrid;
}
其中mKernel为核数组,由以下方法所得
mKernel = generateKernel(mRadius, mRadius / 3.0);
static double[] generateKernel(int radius, double sd) {
double[] kernel = new double[radius * 2 + 1];
for (int i = -radius; i <= radius; i++) {
kernel[i + radius] = (Math.exp(-i * i / (2 * sd * sd)));
}
return kernel;
}
最终返回处理好的二维数组
三、根据所得数组绘制Bitmap
根据所得数组值计算图片每个像素的颜色并保存为数组
int i, j, index, col;
double val;
int colors[] = new int[dimW * dimH];
for (i = 0; i < dimH; i++) {
for (j = 0; j < dimW; j++) {
val = grid[j][i];
index = i * dimW + j;
//根据值计算颜色,并插入对应的颜色数组中的位置
col = (int) (val * colorMapScaling);
if (val != 0) {
if (col < colorMap.length) colors[index] = colorMap[col];
else colors[index] = maxColor;
} else {
colors[index] = Color.TRANSPARENT;
}
}
}
根据颜色数组,绘制Bitmap
Bitmap tile = Bitmap.createBitmap(dimW, dimH, Bitmap.Config.ARGB_8888);
tile.setPixels(colors, 0, dimW, 0, 0, dimW, dimH);
使用绘制点阴影的方式绘制
- 主要流程
- 处理数据
- 绘制阴影
- 描绘颜色
- 生成Bitmap
一、处理数据处理数据
与上一种绘制方法相同
二、绘制阴影
创建一张bitmap,大小与最后所得bitmap相同
backBuffer = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888);
//使图片变透明
backBuffer.eraseColor(Color.TRANSPARENT);
创建Canvas,并封装上得bitmap,并根据点数据进行画圆,并使之渐变透明,形成阴影
for (WeightedLatLng p : data) {
drawAlphaCircle(p.x, p.y, p.intensity);
}
private void drawAlphaCircle(float x, float y, double intensity) {
RadialGradient g = new RadialGradient(x, y, radius, Color.argb(
(int) (intensity / maxIntensity * 255), 0, 0, 0), Color.TRANSPARENT,
Shader.TileMode.CLAMP);
Paint gp = new Paint();
gp.setShader(g);
myCanvas.drawCircle(x, y, radius, gp);
}
三、绘制颜色
创建渐变颜色条数组
private int colors[] = new int[]{0xffff0000, 0xff00ff00};
private float positions[] = new float[]{0.0f, 1.0f};
Bitmap bit = Bitmap.createBitmap(256, 1, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(bit);
LinearGradient grad;
grad = new LinearGradient(0, 0, 256, 1, colors, positions, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setShader(grad);
canvas.drawLine(0, 0, 256, 1, paint);
palette = new int[256];
bit.getPixels(palette, 0, 256, 0, 0, 256, 1);
根据阴影透明度,绘制颜色
backBuffer.getPixels(pixels, 0, width, x, y, width, height);
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
int pixel = pixels[i + (j * width)];
//the pixels alpha value (0-255)
int alpha = 0xff & (pixel >> 24);
//clamp the alpha value to user specified bounds
int clampAlpha;
if (alpha < maxOpacity) {
if (alpha < minOpacity) {
clampAlpha = minOpacity;
} else {
clampAlpha = alpha;
}
} else {
clampAlpha = maxOpacity;
}
//set the pixels colour to its corresponding colour in the palette
pixels[i + (j * width)] = ((0xff & clampAlpha) << 24) | (0xffffff & palette[alpha]);
}
}
四、生成Bitmap
backBuffer.setPixels(pixels, 0, width, x, y, width, height);
backBUffer为最终所得图片
* 源码可在上面GitHub上获取 , 或在 http://download.youkuaiyun.com/download/z896435317/9955057 下载