Android 等高线绘图

本文介绍如何在Android中实现热力图的绘制,包括使用高斯核密度估计方法及绘制点阴影的方式。涵盖数据处理、数组插值、颜色计算等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 下载

import numpy as np import matplotlib.pyplot as plt from matplotlib import font_manager as fm from mpl_toolkits.mplot3d import Axes3D import matplotlib as mpl from matplotlib.colors import LinearSegmentedColormap # 定义新的颜色常量 LIGHTBLUE = [0.749, 0.862, 0.933] GOLD = [1, 0.84, 0] VIOLET = [0.93, 0.51, 0.93] DARKBLUE = [0.1, 0.1, 0.6] LIGHTGREEN = [0.564, 0.933, 0.564] # 创建自定义色图 colors = [LIGHTBLUE, LIGHTGREEN, GOLD, VIOLET] custom_cmap = LinearSegmentedColormap.from_list('custom_cmap', colors, N=256) # 设置全局绘图样式 plt.rcParams.update({ 'font.size': 12, 'axes.labelsize': 12, 'axes.titlesize': 16, 'axes.titleweight': 'bold', 'xtick.labelsize': 11, 'ytick.labelsize': 11, 'figure.figsize': (10, 8), 'figure.facecolor': '#f8f8f8', 'axes.facecolor': '#f8f8f8', 'grid.color': '#dddddd', 'grid.linestyle': '--', 'grid.alpha': 0.7, 'lines.linewidth': 1.8, 'patch.edgecolor': 'black', 'mathtext.default': 'regular' }) # 解决上标负号缺失问题的替代方案 def fix_superscript_issue(): """解决上标负号缺失问题""" plt.rcParams['mathtext.fontset'] = 'cm' # 使用Computer Modern字体 plt.rcParams['mathtext.rm'] = 'DejaVu Sans' # 常规字体 # 设置中文字体支持 def set_chinese_font(): """设置中文字体支持""" # 首选字体列表(按优先级排序) font_candidates = [ 'Microsoft YaHei', # Windows 'SimHei', # Windows 'STHeiti', # macOS 'Heiti SC', # macOS 'WenQuanYi Zen Hei', # Linux 'Noto Sans CJK SC', # Linux 'Source Han Sans SC', # Adobe开源字体 'Droid Sans Fallback' # Android字体 ] # 检查系统可用字体 available_fonts = [f.name for f in fm.fontManager.ttflist] # 查找最佳匹配字体 selected_font = None for font in font_candidates: if font in available_fonts: selected_font = font break # 设置字体 if selected_font: plt.rcParams['font.sans-serif'] = [selected_font, 'sans-serif'] plt.rcParams['axes.unicode_minus'] = False # 正确显示负号 print(f"使用字体: {selected_font}") return selected_font else: # 回退到默认字体 plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'sans-serif'] print("未找到中文字体,使用默认字体") return None # 解决上标问题 fix_superscript_issue() # 设置中文字体 chinese_font = set_chinese_font() # 数据准备 np.random.seed(42) # 最优制备条件(添加物理约束) model3_opt = np.array([38.53 + np.random.randn(), 64.52 + np.random.randn(), 58.31 + np.random.randn() * 2, 3.82 + np.random.randn() * 0.1]) model4_opt = np.array([41.26 + np.random.randn() * 0.3, 70.37 + np.random.randn() * 0.8, 47.56 + np.random.randn() * 1.5, 2.5006 + np.random.randn() * 0.05]) # 三维物理场可视化 def create_3d_visualization(): fig = plt.figure(figsize=(15, 10)) fig.suptitle('多孔膜物理场与最优条件', fontsize=16, fontweight='bold') # 创建3D曲面 ax1 = fig.add_subplot(121, projection='3d') # 生成数据 x = np.linspace(30, 50, 100) y = np.linspace(50, 90, 100) X, Y = np.meshgrid(x, y) Z = 30 + 15 * (np.sin(X / 3) * np.cos(Y / 4) + 0.3 * np.random.randn(*X.shape)) # 绘制曲面 - 使用自定义色图 surf = ax1.plot_surface(X, Y, Z, cmap=custom_cmap, edgecolor='none', alpha=0.85, antialiased=True, rstride=1, cstride=1) # 添加最优条件点 ax1.scatter(model3_opt[0], model3_opt[1], np.max(Z) + 1, s=150, color=VIOLET, edgecolor='k', linewidth=1.5) ax1.scatter(model4_opt[0], model4_opt[1], np.max(Z) + 1, s=150, color=GOLD, edgecolor='k', linewidth=1.5) # 设置标签和标题 ax1.set_xlabel('温度 (℃)', labelpad=10) ax1.set_ylabel('湿度 (%)', labelpad=10) ax1.set_zlabel('孔面积占比 (%)', labelpad=10) ax1.set_title('多孔膜物理场分布', pad=15) ax1.legend(['物理场表面', '问题3最优点', '问题4最优点'], loc='upper right') # 添加光照效果 ax1.view_init(elev=30, azim=-45) # 创建等高线图 ax2 = fig.add_subplot(122) # 绘制等高线 - 使用自定义色图 contour = ax2.contourf(X, Y, Z, 20, cmap=custom_cmap) ax2.contour(X, Y, Z, 10, colors='k', linewidths=0.5, alpha=0.5) # 添加最优条件点 ax2.scatter(model3_opt[0], model3_opt[1], s=200, color=VIOLET, edgecolor='k', linewidth=1.5, zorder=10) ax2.scatter(model4_opt[0], model4_opt[1], s=200, color=GOLD, edgecolor='k', linewidth=1.5, zorder=10) # 添加标注 - 使用mathtext渲染希腊字母 ax2.annotate(r'$\eta=%.1f\%%$' % model3_opt[2], xy=(model3_opt[0], model3_opt[1]), xytext=(model3_opt[0] + 1, model3_opt[1] + 1), arrowprops=dict(arrowstyle='->', color=VIOLET), color=VIOLET, fontsize=12, fontweight='bold') ax2.annotate(r'$\eta=%.1f\%%$' % model4_opt[2], xy=(model4_opt[0], model4_opt[1]), xytext=(model4_opt[0] + 1, model4_opt[1] - 1), arrowprops=dict(arrowstyle='->', color=GOLD), color=GOLD, fontsize=12, fontweight='bold') # 设置标签和标题 ax2.set_xlabel('温度 (℃)') ax2.set_ylabel('湿度 (%)') ax2.set_title('物理场等高线投影', pad=15) ax2.grid(True, linestyle='--', alpha=0.7) # 添加颜色条 cbar = fig.colorbar(surf, ax=ax2) cbar.set_label('孔面积占比 (%)', fontsize=12) plt.tight_layout() plt.savefig('3d_visualization.png', dpi=300, bbox_inches='tight', facecolor='#f8f8f8') plt.show() # 执行绘图函数 if __name__ == "__main__": create_3d_visualization() 这个代码生成的图中,在多孔膜物理分布场图中,最优点并不明显被覆盖了,在物理等高线投影中,两个最高点,其中一个标注出了数值,而另一个没有,还是不要标准数值了
最新发布
08-11
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值