|
@@ -0,0 +1,255 @@
|
|
|
+package cn.com.taiji.tile.util;
|
|
|
+
|
|
|
+import cn.com.taiji.tile.model.POI;
|
|
|
+import cn.com.taiji.tile.style.ShapeStyle;
|
|
|
+import cn.hutool.core.util.BooleanUtil;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.locationtech.jts.geom.Coordinate;
|
|
|
+import org.locationtech.jts.geom.Geometry;
|
|
|
+
|
|
|
+import javax.imageio.ImageIO;
|
|
|
+import java.awt.*;
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Objects;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author zzyx 2024/1/5
|
|
|
+ */
|
|
|
+public class ShapeUtils {
|
|
|
+
|
|
|
+
|
|
|
+ public static final int WIDTH = 256;
|
|
|
+ public static final int HEIGHT = 256;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取瓦片字节数组
|
|
|
+ *
|
|
|
+ * @param bboxStr 逗号拼接的bbox字符串
|
|
|
+ * @param dataList 数据集合
|
|
|
+ * @param shapeStyle 样式
|
|
|
+ * @return 瓦片字节数组
|
|
|
+ */
|
|
|
+ public static byte[] getTileByte(String bboxStr, List<POI> dataList, ShapeStyle shapeStyle) {
|
|
|
+ BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
|
|
|
+ String[] split = bboxStr.split(",");
|
|
|
+ double[] bbox = new double[split.length];
|
|
|
+ Graphics2D graphics = image.createGraphics();
|
|
|
+ for (POI poi : dataList) {
|
|
|
+ Geometry shape = GeoUtils.getGeoUtils().createGeometry(poi.getShape());
|
|
|
+ //判断数据点线面
|
|
|
+ if (StringUtils.equals(shape.getGeometryType(), "Polygon")) {
|
|
|
+ drawPolygonShape(bbox, graphics, shape, poi, shapeStyle);
|
|
|
+ } else if (StringUtils.equalsAny(shape.getGeometryType(), "LinearRing", "LineString", "MultiLineString")) {
|
|
|
+ drawLineShape(bbox, graphics, shape, shapeStyle);
|
|
|
+ } else if (StringUtils.equals(shape.getGeometryType(), "Point")) {
|
|
|
+ drawPointShape(bbox, graphics, shape, poi, shapeStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
|
|
+ try {
|
|
|
+ ImageIO.write(image, "png", buffer);
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return buffer.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static void drawPolygonShape(double[] bbox, Graphics2D graphics, Geometry geometry, POI poi, ShapeStyle shapeStyle) {
|
|
|
+ drawPolygonToTile(shapeStyle, geometry, graphics, bbox);
|
|
|
+ //是否需要设置字名称
|
|
|
+ if (BooleanUtil.isTrue(shapeStyle.getIsShowName()) && StringUtils.isNotBlank(poi.getName())) {
|
|
|
+ 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.getName();
|
|
|
+ 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.getLineColor());
|
|
|
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
+ if (Objects.equals(shapeStyle.getLineType(), 2)) {
|
|
|
+ graphics.setStroke(new BasicStroke(shapeStyle.getLineWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, shapeStyle.getLineDashPattern(), 0.0f));
|
|
|
+ } else if (Objects.equals(shapeStyle.getLineType(), 3)) {
|
|
|
+ graphics.setStroke(new BasicStroke(shapeStyle.getLineWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, shapeStyle.getLineDashPattern(), 0.0f));
|
|
|
+ } else {
|
|
|
+ graphics.setStroke(new BasicStroke(shapeStyle.getLineWidth()));
|
|
|
+ }
|
|
|
+ //端点的样式可以是圆形 (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, POI poi, ShapeStyle shapeStyle) {
|
|
|
+ int[] pxy = TileUtils.toPixelXY(geometry.getCoordinate().x, geometry.getCoordinate().y, bbox[0], bbox[1], bbox[2], bbox[3]);
|
|
|
+ //设置边框 实现轮廓
|
|
|
+ graphics.setColor(shapeStyle.getPointColor());
|
|
|
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
+ //设置字体
|
|
|
+ //设置字体样式
|
|
|
+ if (BooleanUtil.isTrue(shapeStyle.getPointFontShow())) {
|
|
|
+ Font font = new Font("Arial", Font.BOLD, shapeStyle.getPointFontSize());
|
|
|
+ String s = poi.getName();
|
|
|
+ graphics.setColor(shapeStyle.getPointFontColor());
|
|
|
+ graphics.setFont(font);
|
|
|
+ graphics.drawString(s, pxy[0], pxy[1]);
|
|
|
+ }
|
|
|
+ BufferedImage pointMarkerImage = shapeStyle.getPointMarkerImage();
|
|
|
+ if (pointMarkerImage != null) {
|
|
|
+ graphics.drawImage(TileUtils.scaleWidthHeight(pointMarkerImage, shapeStyle.getPointWidth(), shapeStyle.getPointHeight()), 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.getBackgroundImage();
|
|
|
+ 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()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Converts a hexadecimal color string to RGBA format.
|
|
|
+ *
|
|
|
+ * @param hexColor The hexadecimal color string (e.g., "#2860F1").
|
|
|
+ * @param alpha The alpha value (0-255) for opacity.
|
|
|
+ * @return An array containing the RGBA values.
|
|
|
+ */
|
|
|
+ public static int[] hexToRgba(String hexColor, int alpha) {
|
|
|
+ // Remove the hash (#) sign if it exists
|
|
|
+ hexColor = hexColor.replace("#", "");
|
|
|
+
|
|
|
+ // Parse the string
|
|
|
+ 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 the RGBA values
|
|
|
+ return new int[]{red, green, blue, alpha};
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void drawPointShape(ShapeStyle shapeStyle, Graphics2D graphics, int[] pxy) {
|
|
|
+ if (BooleanUtil.isTrue(shapeStyle.getPointDashed())) {
|
|
|
+ //如果是
|
|
|
+ Stroke dashed = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, shapeStyle.getPointDashPattern(), 0);
|
|
|
+ graphics.setStroke(dashed);
|
|
|
+ }
|
|
|
+ if (Objects.equals(shapeStyle.getShapeType(), 1)) {
|
|
|
+ //正方形
|
|
|
+ graphics.drawRect(pxy[0], pxy[1], shapeStyle.getPointWidth(), shapeStyle.getPointHeight());
|
|
|
+ if (BooleanUtil.isTrue(shapeStyle.getPointFill())) {
|
|
|
+ graphics.fillRect(pxy[0], pxy[1], shapeStyle.getPointWidth(), shapeStyle.getPointHeight());
|
|
|
+ }
|
|
|
+ } else if (Objects.equals(shapeStyle.getShapeType(), 2)) {
|
|
|
+ //三角形
|
|
|
+ int[] xPoints = {pxy[0], pxy[0] - shapeStyle.getPointHeight(), pxy[0] + shapeStyle.getPointHeight()};
|
|
|
+ int[] yPoints = {pxy[1] - shapeStyle.getPointHeight(), pxy[1] + shapeStyle.getPointHeight(), pxy[1] + shapeStyle.getPointHeight()};
|
|
|
+ int nPoints = 3; // 顶点数量
|
|
|
+ graphics.drawPolygon(xPoints, yPoints, nPoints);
|
|
|
+ if (BooleanUtil.isTrue(shapeStyle.getPointFill())) {
|
|
|
+ graphics.fillPolygon(xPoints, yPoints, nPoints);
|
|
|
+ }
|
|
|
+ } else if (Objects.equals(shapeStyle.getShapeType(), 3)) {
|
|
|
+ //圆形
|
|
|
+ graphics.drawOval(pxy[0], pxy[1], shapeStyle.getPointWidth(), shapeStyle.getPointHeight());
|
|
|
+ if (BooleanUtil.isTrue(shapeStyle.getPointFill())) {
|
|
|
+ graphics.fillOval(pxy[0], pxy[1], shapeStyle.getPointWidth(), shapeStyle.getPointHeight());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ //画一个简单的点
|
|
|
+ graphics.drawArc(pxy[0], pxy[1], shapeStyle.getPointWidth(), shapeStyle.getPointHeight(), 0, 360);
|
|
|
+ //是否填充
|
|
|
+ if (BooleanUtil.isTrue(shapeStyle.getPointFill())) {
|
|
|
+ graphics.fillArc(pxy[0], pxy[1], shapeStyle.getPointWidth(), shapeStyle.getPointHeight(), 0, 360);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|