123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- package cn.com.taiji.tile.util;
- import cn.com.taiji.common.model.LayerStyleView;
- import cn.com.taiji.tile.style.ShapeStyle;
- import cn.hutool.core.util.BooleanUtil;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.lang3.StringUtils;
- import org.locationtech.jts.geom.Coordinate;
- import org.locationtech.jts.geom.Geometry;
- import org.locationtech.jts.io.ParseException;
- import org.locationtech.jts.io.WKBReader;
- import org.locationtech.jts.io.WKTReader;
- import org.locationtech.jts.io.WKTWriter;
- import org.springframework.beans.BeanUtils;
- import javax.imageio.ImageIO;
- import java.awt.*;
- import java.awt.image.BufferedImage;
- import java.io.IOException;
- import java.net.URL;
- import java.util.List;
- import java.util.Map;
- import java.util.Objects;
- import java.util.concurrent.CopyOnWriteArrayList;
- import java.util.function.Consumer;
- /**
- * @author zzyx 2024/1/5
- */
- @Slf4j
- public class ShapeUtils {
- public static final int WIDTH = 256;
- public static final int HEIGHT = 256;
- public final static WKTReader WKT_READER = new WKTReader();
- private static void setImage(String imageUrl, Consumer<BufferedImage> imageConsumer) {
- if (StringUtils.isNotBlank(imageUrl)) {
- try {
- URL url = new URL(imageUrl);
- BufferedImage image = ImageIO.read(url);
- imageConsumer.accept(image);
- } catch (IOException e) {
- log.error("Error loading image from URL: {}", imageUrl, e);
- }
- }
- }
- private static void setColorFromHex(String hexColor, Consumer<Color> colorConsumer) {
- if (StringUtils.isNotBlank(hexColor)) {
- try {
- int[] rgba = hexToRgba(hexColor, 255);
- colorConsumer.accept(new Color(rgba[0], rgba[1], rgba[2], rgba[3]));
- } catch (IllegalArgumentException e) {
- log.error("Invalid hex color format: {}", hexColor, e);
- }
- }
- }
- private static void setDashPattern(String dashPatternStr, Consumer<float[]> dashPatternConsumer) {
- if (StringUtils.isNotBlank(dashPatternStr)) {
- String[] split = dashPatternStr.split(",");
- float[] dashPatternArr = new float[split.length];
- for (int i = 0; i < split.length; i++) {
- dashPatternArr[i] = Float.parseFloat(split[i]);
- }
- dashPatternConsumer.accept(dashPatternArr);
- }
- }
- private static void drawPolygonShape(double[] bbox, Graphics2D graphics, Geometry geometry, Map<String, Object> poi, ShapeStyle shapeStyle) {
- drawPolygonToTile(shapeStyle, geometry, graphics, bbox);
- //是否需要设置字名称
- if (BooleanUtil.isTrue(shapeStyle.getIsShowName()) && StringUtils.isNotBlank(poi.get("name").toString())) {
- org.locationtech.jts.geom.Point centroid = geometry.getCentroid();
- int[] pxy = TileUtils.toPixelXY(centroid.getX(), centroid.getY(), bbox[0], bbox[1], bbox[2], bbox[3]);
- //设置字体样式
- Font font = new Font("Arial", Font.TRUETYPE_FONT, shapeStyle.getFontSize());
- String s = poi.get("name").toString();
- graphics.setColor(shapeStyle.getFontColor());
- graphics.setFont(font);
- graphics.drawString(s, pxy[0], pxy[1]);
- //设置面的图标
- BufferedImage cpi = shapeStyle.getCenterPointImage();
- if (cpi != null) {
- graphics.drawImage(TileUtils.scaleWidthHeight(cpi, 13, 13), pxy[0], pxy[1], null);
- }
- }
- }
- public static void drawLineShape(double[] bbox, Graphics2D graphics, Geometry geometry, ShapeStyle shapeStyle) {
- int[] xpoint = new int[(geometry.getCoordinates()).length];
- int[] ypoint = new int[(geometry.getCoordinates()).length];
- convertCoordinatesToPixels(geometry.getCoordinates(), xpoint, ypoint, bbox);
- graphics.setColor(shapeStyle.getColor());
- graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- if (Objects.equals(shapeStyle.getLineType(), 2)) {
- graphics.setStroke(new BasicStroke(shapeStyle.getWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, shapeStyle.getDashPattern(), 0.0f));
- } else if (Objects.equals(shapeStyle.getLineType(), 3)) {
- graphics.setStroke(new BasicStroke(shapeStyle.getWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, shapeStyle.getDashPattern(), 0.0f));
- } else {
- graphics.setStroke(new BasicStroke(shapeStyle.getWidth()));
- }
- //端点的样式可以是圆形 (CAP_ROUND)、平直 (CAP_BUTT) 或方形 (CAP_SQUARE)
- // 连接样式:线段之间的连接可以设置为斜接 (JOIN_MITER)、圆角 (JOIN_ROUND) 或平直 (JOIN_BEVEL)。
- // graphics.setStroke(new BasicStroke(10f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
- graphics.drawPolyline(xpoint, ypoint, geometry.getCoordinates().length);
- }
- public static void drawPointShape(double[] bbox, Graphics2D graphics, Geometry geometry, Map<String, Object> poi, ShapeStyle shapeStyle) {
- int[] pxy = TileUtils.toPixelXY(geometry.getCoordinate().x, geometry.getCoordinate().y, bbox[0], bbox[1], bbox[2], bbox[3]);
- // int dx = (int) (256.0D * (geometry.getCoordinate().x - bbox[0]) / (bbox[2] - bbox[0]));
- // int dy = 256 - (int) (256.0D * (geometry.getCoordinate().y - bbox[1]) / (bbox[3] - bbox[1]));
- // int[] pxy = new int[]{dx-10, dy-10};
- //设置边框 实现轮廓
- graphics.setColor(shapeStyle.getColor());
- graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- //设置字体
- //设置字体样式
- if (BooleanUtil.isTrue(shapeStyle.getIsShowName()) && StringUtils.isNotBlank(poi.get("name").toString())) {
- Font font = new Font("Arial", Font.BOLD, shapeStyle.getFontSize());
- String s = poi.get("name").toString();
- graphics.setColor(shapeStyle.getFontColor());
- graphics.setFont(font);
- graphics.drawString(s, pxy[0], pxy[1]);
- }
- BufferedImage pointMarkerImage = shapeStyle.getPointMarkerImage();
- if (pointMarkerImage != null) {
- graphics.drawImage(pointMarkerImage, pxy[0], pxy[1], null);
- } else {
- drawPointShape(shapeStyle, graphics, pxy);
- }
- }
- public static void convertCoordinatesToPixels(Coordinate[] coordinates, int[] xPoints, int[] yPoints, double[] bbox) {
- // 坐标转换逻辑
- for (int i = 0; i < coordinates.length; i++) {
- Coordinate c = coordinates[i];
- //坐标转换:对于每个坐标 c,代码调用 TileUtils.toPixelXY 方法,将地理坐标(经纬度)转换为像素坐标。这个转换考虑了一个称为 bbox(边界框)的地理区域,这个区域定义了地理坐标到像素坐标转换的上下文。bbox 通常包含边界框的最小经度、最小纬度、最大经度和最大纬度。
- int[] pxy = TileUtils.toPixelXY(c.getX(), c.getY(), bbox[0], bbox[1], bbox[2], bbox[3]);
- xPoints[i] = pxy[0];
- yPoints[i] = pxy[1];
- }
- }
- /**
- * 面填充样式
- * 在地理信息系统(GIS)和空间数据处理领域中,Geometry 类是一个常用的概念,用于表示空间对象的形状和位置。这个类通常包含各种方法来操作和查询几何形状。getCoordinates() 方法是 Geometry 类中的一个方法,它的作用是获取构成该几何形状的所有坐标点。
- * 当你调用 Geometry 对象的 getCoordinates() 方法时,它会返回一个坐标数组,这个数组包含了组成该几何形状的所有顶点坐标。这些坐标通常是 Coordinate 对象,每个 Coordinate 包含 x(经度)、y(纬度)和可选的 z(高度)值。
- * 例如,如果你的 Geometry 对象是一个多边形,getCoordinates() 方法将返回构成该多边形边界的所有顶点坐标。如果是一条线,它将返回线的所有顶点坐标。
- * 这个方法在进行空间分析和几何处理时非常有用,比如当你需要遍历几何形状的所有顶点、计算形状的边界、或者在不同几何形状之间进行坐标转换时。
- * <p>
- * Coordinate 是在地理信息系统(GIS)和计算机图形学中常用的一个基本概念,用于表示空间中的点。在多种编程环境和库中,Coordinate 类或结构体通常用于存储和操作这些点的位置信息。其主要作用和特点包括:
- * 存储位置信息:Coordinate 通常包含至少两个维度的数据,即 x(经度)和 y(纬度)。在三维空间中,它还可以包含 z(高程或深度)值。
- * 空间分析和计算:在 GIS 和计算机图形学应用中,Coordinate 用于执行各种空间分析和计算,如测量距离、定义几何形状(如线和多边形)的边界等。
- * 数据转换和处理:Coordinate 在进行坐标系转换、地图投影、空间查询等操作中也非常重要。
- * 可视化和绘图:在地图制作和空间数据可视化中,Coordinate 用于确定应在地图上绘制的点的确切位置。
- * 在不同的编程库和框架中,Coordinate 类可能还包含了其他辅助方法,如计算两个坐标之间的距离、比较坐标点是否相等等。例如,在 Java 的 JTS Topology Suite(一个常用的空间数据处理库)中,Coordinate 类就提供了这样的功能。
- */
- public static void drawPolygonToTile(ShapeStyle shapeStyle, Geometry shape, Graphics2D graphics, double[] bbox) {
- //普通面
- if (shape != null && shape.getCoordinates().length > 0) {
- int[] xpoint = new int[(shape.getCoordinates()).length];
- int[] ypoint = new int[(shape.getCoordinates()).length];
- convertCoordinatesToPixels(shape.getCoordinates(), xpoint, ypoint, bbox);
- //设置样式
- //2.设置抗锯齿
- graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- //是否填充
- if (BooleanUtil.isTrue(shapeStyle.getIsFill())) {
- //fill 是将设置的颜色全部填充
- graphics.setColor(shapeStyle.getColor());
- //设置背景面
- setBackgroundImage(graphics, shapeStyle);
- Polygon polygon = new Polygon(xpoint, ypoint, shape.getNumPoints());
- //填充
- graphics.fill(polygon);
- //设置边框颜色
- setBorderStyle(graphics, shapeStyle);
- //drawPolygon 是透明的只会出来边框
- graphics.drawPolygon(polygon);
- } else {
- setBorderStyle(graphics, shapeStyle);
- graphics.drawPolygon(xpoint, ypoint, (shape.getCoordinates()).length);
- }
- }
- }
- private static void setBackgroundImage(Graphics2D graphics, ShapeStyle shapeStyle) {
- BufferedImage backgroundImage = shapeStyle.getBgImage();
- if (backgroundImage != null) {
- TexturePaint texturePaint = new TexturePaint(backgroundImage, new Rectangle(0, 0, backgroundImage.getWidth(), backgroundImage.getHeight()));
- graphics.setPaint(texturePaint);
- }
- }
- private static void setBorderStyle(Graphics2D graphics, ShapeStyle shapeStyle) {
- //不填充只显示边框颜色
- graphics.setColor(shapeStyle.getBorderColor());
- // 设置虚线:5像素实线,5像素空白
- if (BooleanUtil.isTrue(shapeStyle.getIsDashed())) {
- Stroke dashed = new BasicStroke(shapeStyle.getBorderSize(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, shapeStyle.getDashPattern(), 0);
- graphics.setStroke(dashed);
- } else {
- graphics.setStroke(new BasicStroke(shapeStyle.getBorderSize()));
- }
- }
- public static int[] hexToRgba(String hexColor, int alpha) {
- hexColor = hexColor.replace("#", "");
- int red = Integer.valueOf(hexColor.substring(0, 2), 16);
- int green = Integer.valueOf(hexColor.substring(2, 4), 16);
- int blue = Integer.valueOf(hexColor.substring(4, 6), 16);
- return new int[]{red, green, blue, alpha};
- }
- private static void drawPointShape(ShapeStyle shapeStyle, Graphics2D graphics, int[] pxy) {
- if (BooleanUtil.isTrue(shapeStyle.getIsDashed())) {
- //如果是
- Stroke dashed = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, shapeStyle.getPointDashPattern(), 0);
- graphics.setStroke(dashed);
- }
- if (Objects.equals(shapeStyle.getPointType(), 1)) {
- //正方形
- graphics.drawRect(pxy[0], pxy[1], shapeStyle.getWidth(), shapeStyle.getHeight());
- if (BooleanUtil.isTrue(shapeStyle.getIsFill())) {
- graphics.fillRect(pxy[0], pxy[1], shapeStyle.getWidth(), shapeStyle.getHeight());
- }
- } else if (Objects.equals(shapeStyle.getPointType(), 2)) {
- //三角形
- int[] xPoints = {pxy[0], pxy[0] - shapeStyle.getHeight(), pxy[0] + shapeStyle.getHeight()};
- int[] yPoints = {pxy[1] - shapeStyle.getHeight(), pxy[1] + shapeStyle.getHeight(), pxy[1] + shapeStyle.getHeight()};
- int nPoints = 3; // 顶点数量
- graphics.drawPolygon(xPoints, yPoints, nPoints);
- if (BooleanUtil.isTrue(shapeStyle.getIsFill())) {
- graphics.fillPolygon(xPoints, yPoints, nPoints);
- }
- } else if (Objects.equals(shapeStyle.getPointType(), 3)) {
- //圆形
- graphics.drawOval(pxy[0], pxy[1], shapeStyle.getWidth(), shapeStyle.getHeight());
- if (BooleanUtil.isTrue(shapeStyle.getIsFill())) {
- graphics.fillOval(pxy[0], pxy[1], shapeStyle.getWidth(), shapeStyle.getHeight());
- }
- } else {
- //画一个简单的点
- graphics.drawArc(pxy[0], pxy[1], shapeStyle.getWidth(), shapeStyle.getHeight(), 0, 360);
- //是否填充
- if (BooleanUtil.isTrue(shapeStyle.getIsFill())) {
- graphics.fillArc(pxy[0], pxy[1], shapeStyle.getWidth(), shapeStyle.getHeight(), 0, 360);
- }
- }
- }
- public static List<Map<String, Object>> detectClick(String geoType,String bboxStr, List<Map<String, Object>> dataList, String shapeKey, List<LayerStyleView> layerStyleViews, int x, int y) {
- List<Map<String, Object>> clickSelectedDataList = new CopyOnWriteArrayList<>();
- BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
- String[] split = bboxStr.split(",");
- double[] bbox = new double[split.length];
- for (int i = 0; i < split.length; i++) {
- bbox[i] = Double.parseDouble(split[i]);
- }
- ShapeStyle shapeStyle = getShapeStyle(layerStyleViews,geoType);
- Graphics2D graphics = image.createGraphics();
- dataList.forEach(poi -> {
- Geometry geometry;
- try {
- geometry = WKT_READER.read(poi.get(shapeKey).toString());
- } catch (ParseException e) {
- throw new RuntimeException(e);
- }
- if (geometry != null) {
- drawGeometryShape(bbox, shapeStyle, graphics, poi, geometry);
- int rgb = image.getRGB(x, y);
- int alpha = (rgb >> 24) & 0xff; // 提取alpha值
- if (alpha > 0) {
- // 像素被填充
- clickSelectedDataList.add(poi);
- image.setRGB(x,y,0x00000000);
- }
- }
- });
- return clickSelectedDataList;
- }
- private static void drawGeometryShape(double[] bbox, ShapeStyle shapeStyle, Graphics2D graphics, Map<String, Object> poi, Geometry geometry) {
- if (StringUtils.equals(geometry.getGeometryType(), "Polygon")) {
- drawPolygonShape(bbox, graphics, geometry, poi, shapeStyle);
- } else if (StringUtils.equalsAny(geometry.getGeometryType(), "LinearRing", "LineString", "MultiLineString")) {
- drawLineShape(bbox, graphics, geometry, shapeStyle);
- } else if (StringUtils.equals(geometry.getGeometryType(), "Point")) {
- drawPointShape(bbox, graphics, geometry, poi, shapeStyle);
- }
- }
- public static void renderShape(BufferedImage image, String bboxStr, List<Map<String, Object>> dataList, ShapeStyle shapeStyle, String shapeKey) {
- String[] split = bboxStr.split(",");
- double[] bbox = new double[split.length];
- for (int i = 0; i < split.length; i++) {
- bbox[i] = Double.parseDouble(split[i]);
- }
- Graphics2D graphics = image.createGraphics();
- dataList.parallelStream().forEach(poi -> drawShape(shapeStyle, poi, bbox, graphics, shapeKey));
- }
- private static void drawShape(ShapeStyle shapeStyle, Map<String, Object> poi, double[] bbox, Graphics2D graphics, String shapeKey) {
- // Geometry shape = GeoUtils.getGeoUtils().createGeometry(poi.get(shapeKey));
- Geometry shape;
- try {
- shape = WKT_READER.read(poi.get(shapeKey).toString());
- } catch (ParseException e) {
- throw new RuntimeException(e);
- }
- if (shape != null) {
- drawGeometryShape(bbox, shapeStyle, graphics, poi, shape);
- }
- }
- public static Geometry convertWKBtoGeometry(byte[] wkbBytes) {
- WKBReader reader = new WKBReader();
- try {
- return reader.read(wkbBytes);
- } catch (ParseException e) {
- e.printStackTrace();
- return null;
- }
- }
- public static BufferedImage drawImage(String geoType,String bboxStr, List<Map<String, Object>> dataList, String shapeKey, List<LayerStyleView> layerStyleViews) {
- BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
- renderShape(image, bboxStr, dataList, getShapeStyle(layerStyleViews,geoType), shapeKey);
- return image;
- }
- public static ShapeStyle getShapeStyle(List<LayerStyleView> layerStyleViews,String geoType) {
- ShapeStyle shapeStyle = new ShapeStyle();
- for (LayerStyleView layerStyleView : layerStyleViews) {
- BeanUtils.copyProperties(layerStyleView, shapeStyle);
- setColorFromHex(layerStyleView.getColor(), shapeStyle::setColor);
- shapeStyle.setIsFill(Objects.equals(layerStyleView.getIsFill(), 1));
- setColorFromHex(layerStyleView.getBorderColor(), shapeStyle::setBorderColor);
- shapeStyle.setIsDashed(Objects.equals(layerStyleView.getIsDashed(), 1));
- setDashPattern(layerStyleView.getDashPattern(), shapeStyle::setDashPattern);
- setImage(layerStyleView.getBgImage(), shapeStyle::setBgImage);
- shapeStyle.setIsShowName(Objects.equals(layerStyleView.getIsShowName(), 1));
- setColorFromHex(layerStyleView.getFontColor(), shapeStyle::setFontColor);
- setImage(layerStyleView.getCenterPointImage(), shapeStyle::setCenterPointImage);
- setImage(layerStyleView.getPointMarkerImage(), shapeStyle::setPointMarkerImage);
- if (shapeStyle.getPointMarkerImage() != null) {
- if(StringUtils.equals(geoType,"1")){
- //如果类型为2图标要变大
- shapeStyle.setPointMarkerImageWidth(shapeStyle.getPointMarkerImageWidth()+10);
- shapeStyle.setPointMarkerImageHeight(shapeStyle.getPointMarkerImageHeight()+10);
- }
- shapeStyle.setPointMarkerImage(TileUtils.scaleWidthHeight(shapeStyle.getPointMarkerImage(), shapeStyle.getPointMarkerImageWidth(), shapeStyle.getPointMarkerImageHeight()));
- }
- }
- return shapeStyle;
- }
- }
|