根据geojson数据生成热力图

该代码示例展示了一个用Java生成热力图的过程,涉及Mercator投影将经纬度转换为X/Y坐标,处理JSON数据,以及在BufferedImage上绘制图形。程序读取JSON文件中的地理边界信息,计算合适的缩放比例,并填充不同颜色来表示不同的数据等级。
摘要由CSDN通过智能技术生成

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class HeatMap {

    public static void main(String[] args) throws Exception {
        int IMAGE_WIDTH_IN_PX = 900;
        // CHANGE THIS: image height in pixel
        int IMAGE_HEIGHT_IN_PX = 500;
        double QUARTERPI = Math.PI / 4.0;
        int MINIMUM_IMAGE_PADDING_IN_PX = 50;
        String IMAGE_FILE_PATH = "heatmap.png";


        BufferedImage bufferedImage = ImageUtils.TransparencyToZero(IMAGE_WIDTH_IN_PX, IMAGE_HEIGHT_IN_PX);

        Graphics2D g = bufferedImage.createGraphics();
        Map<RenderingHints.Key, Object> map = new HashMap<RenderingHints.Key, Object>();
        map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        RenderingHints renderHints = new RenderingHints(map);
        g.setRenderingHints(renderHints);

        // min and max coordinates, used in the computation below
        Point2D.Double minXY = new Point2D.Double(-1, -1);
        Point2D.Double maxXY = new Point2D.Double(-1, -1);

        // a list of counties where each county contains a list of coordinates that form the county boundary
        Collection<FeatureExt> countyBoundaries = new ArrayList<FeatureExt>();

        JSONArray features = getFeatures("/Users/xxxxxx/Documents/WorkSpaces/src/main/resources/ym1.json");
        // for every county, convert the longitude/latitude to X/Y using Mercator projection formula
        for (int i = 0; i < features.size(); i++) {
            FeatureExt featureExt = new FeatureExt();
            JSONObject feature = features.getJSONObject(i);
            JSONArray coordinates = feature.getJSONObject("geometry").getJSONArray("coordinates");

            JSONObject properties = feature.getJSONObject("properties");
            Collection<Point2D.Double> lonLat = new ArrayList<Point2D.Double>();

            for (int j = 0; j < coordinates.getJSONArray(0).size(); j++) {
                JSONArray coordinateXY = coordinates.getJSONArray(0).getJSONArray(j);
                double longitude = coordinateXY.getDouble(0) * Math.PI / 180;
                double latitude = coordinateXY.getDouble(1) * Math.PI / 180;

                Point2D.Double xy = new Point2D.Double();
                xy.x = longitude;
                xy.y = Math.log(Math.tan(QUARTERPI + 0.5 * latitude));

                // The reason we need to determine the min X and Y values is because in order to draw the map,
                // we need to offset the position so that there will be no negative X and Y values
                minXY.x = (minXY.x == -1) ? xy.x : Math.min(minXY.x, xy.x);
                minXY.y = (minXY.y == -1) ? xy.y : Math.min(minXY.y, xy.y);

                lonLat.add(xy);
            }
            featureExt.setProperties(properties);
            featureExt.setCoordinates(lonLat);
            countyBoundaries.add(featureExt);

        }


        // readjust coordinate to ensure there are no negative values
        for (FeatureExt featureExt : countyBoundaries) {
            Collection<Point2D.Double> points = featureExt.getCoordinates();
            for (Point2D.Double point : points) {
                point.x = point.x - minXY.x;
                point.y = point.y - minXY.y;

                // now, we need to keep track the max X and Y values
                maxXY.x = (maxXY.x == -1) ? point.x : Math.max(maxXY.x, point.x);
                maxXY.y = (maxXY.y == -1) ? point.y : Math.max(maxXY.y, point.y);
            }
        }

        int paddingBothSides = MINIMUM_IMAGE_PADDING_IN_PX * 2;

        // the actual drawing space for the map on the image
        int mapWidth = IMAGE_WIDTH_IN_PX - paddingBothSides;
        int mapHeight = IMAGE_HEIGHT_IN_PX - paddingBothSides;

        // determine the width and height ratio because we need to magnify the map to fit into the given image dimension
        double mapWidthRatio = mapWidth / maxXY.x;
        double mapHeightRatio = mapHeight / maxXY.y;

        // using different ratios for width and height will cause the map to be stretched. So, we have to determine
        // the global ratio that will perfectly fit into the given image dimension
        double globalRatio = Math.min(mapWidthRatio, mapHeightRatio);

        // now we need to readjust the padding to ensure the map is always drawn on the center of the given image dimension
        double heightPadding = (IMAGE_HEIGHT_IN_PX - (globalRatio * maxXY.y)) / 2;
        double widthPadding = (IMAGE_WIDTH_IN_PX - (globalRatio * maxXY.x)) / 2;

        // for each country, draw the boundary using polygon
        for (FeatureExt featureExt : countyBoundaries) {
            Collection<Point2D.Double> points = featureExt.getCoordinates();
            Polygon polygon = new Polygon();

            for (Point2D.Double point : points) {
                int adjustedX = (int) (widthPadding + (point.getX() * globalRatio));

                // need to invert the Y since 0,0 starts at top left
                int adjustedY = (int) (IMAGE_HEIGHT_IN_PX - heightPadding - (point.getY() * globalRatio));

                polygon.addPoint(adjustedX, adjustedY);
            }
            // 定义填充颜色
            Color fillColor = getColor(featureExt.getProperties().getDouble("grid_code"));

            // 设置填充颜色
            g.setColor(fillColor);

            // 填充多边形
            g.fillPolygon(polygon);
            g.drawPolygon(polygon);
        }

        // create the image file
        ImageIO.write(bufferedImage, "PNG", new File(IMAGE_FILE_PATH));


        g.dispose();
    }

    private static JSONArray

    getFeatures(String filePath) {
        File file = new File(filePath);
        try (InputStream inputStream = new FileInputStream(file);
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }
            String json = stringBuilder.toString();
            return JSON.parseObject(json).getJSONArray("features");

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static Color

    getColor(Double gridCode) {
        if (gridCode <= 0) {
            return Color.decode("#000000");
        } else if (gridCode <= 0.5) {
            return Color.decode("#0000FF");
        } else if (gridCode <= 1) {
            return Color.decode("#00FFFF");
        } else if (gridCode <= 1.5) {
            return Color.decode("#00FF00");
        } else if (gridCode <= 2) {
            return Color.decode("#FFFF00");
        } else if (gridCode <= 3) {
            return Color.decode("#FFA500");
        } else if (gridCode > 3) {
            return Color.decode("#FF0000");
        }
        return null;
    }


}
import java.awt.*;
import java.awt.image.BufferedImage;

public class ImageUtils {
    /**
     * 顺时针旋转90度(通过交换图像的整数像素RGB 值)
     *
     * @param bi
     * @return
     */
    public static BufferedImage rotateClockwise90(BufferedImage bi) {
        int width = bi.getWidth();
        int height = bi.getHeight();
        BufferedImage bufferedImage = new BufferedImage(height, width, bi.getType());
        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++)
                bufferedImage.setRGB(height - 1 - j, width - 1 - i, bi.getRGB(i, j));
        return bufferedImage;
    }

    /**
     * 逆时针旋转90度(通过交换图像的整数像素RGB 值)
     *
     * @param bi
     * @return
     */
    public static BufferedImage rotateCounterclockwise90(BufferedImage bi) {
        int width = bi.getWidth();
        int height = bi.getHeight();
        BufferedImage bufferedImage = new BufferedImage(height, width, bi.getType());
        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++)
                bufferedImage.setRGB(j, i, bi.getRGB(i, j));
        return bufferedImage;
    }

    /**
     * 旋转180度(通过交换图像的整数像素RGB 值)
     *
     * @param bi
     * @return
     */
    public static BufferedImage rotate180(BufferedImage bi) {
        int width = bi.getWidth();
        int height = bi.getHeight();
        BufferedImage bufferedImage = new BufferedImage(width, height, bi.getType());
        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++)
                bufferedImage.setRGB(width - i - 1, height - j - 1, bi.getRGB(i, j));
        return bufferedImage;
    }

    /**
     * 水平翻转,以Y轴为中心翻转
     *
     * @param bi
     * @return
     */
    public static BufferedImage rotateHorizon(BufferedImage bi) {
        int width = bi.getWidth();
        int height = bi.getHeight();
        BufferedImage bufferedImage = new BufferedImage(width, height, bi.getType());
        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++)
                bufferedImage.setRGB(width - i - 1, j, bi.getRGB(i, j));
        return bufferedImage;
    }

    /**
     * 垂直翻转,以X轴为中心翻转
     *
     * @param bi
     * @return
     */
    public static BufferedImage rotateVertical(BufferedImage bi) {
        int width = bi.getWidth();
        int height = bi.getHeight();
        BufferedImage bufferedImage = new BufferedImage(width, height, bi.getType());
        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++)
                bufferedImage.setRGB(i, height - 1 - j, bi.getRGB(i, j));
        return bufferedImage;
    }

    /**
     * 返回指定长宽的透明背景的BufferedImage对象
     *
     * @param width
     * @param height
     * @return BufferedImage
     */
    public static BufferedImage TransparencyToZero(int width, int height) {

        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D gd = bi.createGraphics();
        bi = gd.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        gd = bi.createGraphics();

        return bi;
    }

}

参考文章:

java - Converting longitude/latitude to X/Y coordinate - Stack Overflow

其他生成方式:

cesium-heatmap

heatmap.js

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值