Java对点、线、面生成栅格瓦片jpg,并渲染呈现

2023-11-19

Java对点、线、面生成栅格瓦片jpg,并渲染呈现

这篇博客将介绍从前端HTML页面到后端预生成栅格瓦片jpg,并提供查询接口供前端html调用呈现效果图;

1. 效果图

随机画一个多边形,以这个多边形面进行栅格瓦片生成;
在这里插入图片描述

点线面的渲染效果图如下:

多点为蓝色,线为绿色,面为红色

在这里插入图片描述
面生成为背景黑色的栅格瓦片,点线生成为背景白色的栅格瓦片效果图如下:
在这里插入图片描述

红色面的渲染效果图如下:

级别小一些只有一张图,和级别大一些多张图拼合而成,背景色可以是白色的,也可以是黑色的~;

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

蓝色点的渲染效果图如下:
在这里插入图片描述

级别大一些蓝色点的渲染效果图如下:
在这里插入图片描述

绿色线的渲染效果图如下:(1/500的分辨率认为点在线上)在这里插入图片描述
1/20分辨率距离则认为点在线上效果图,看起来线要宽一些;
在这里插入图片描述

2. 原理

2.1 面瓦片的生成

  1. 先计算多边形面的Geometry经纬度范围(外接矩形的左上角,右下角坐标);
  2. 经纬度坐标转web墨卡托坐标;
  3. 指定某一级别计算瓦片号范围;
  4. 分别遍历每一个瓦片(计算瓦片范围与多边形面是否有交集,没有交集生成一张空白图;有交集遍历每一个像素点转换为web墨卡托坐标,转换为Geometry坐标,判断点是否在多边形面上,在设置当前像素点颜色;不在给空白);
  5. 循环遍历多个级别,重复1~4步骤,即可生成多个不同级别

2.2 线瓦片的生成

  1. 先计算 多线 的Geometry经纬度范围(外接矩形的左上角,右下角坐标);
  2. 经纬度坐标转web墨卡托坐标;
  3. 指定某一级别计算瓦片号范围;
  4. 分别遍历每一个瓦片(计算瓦片范围与 线 是否有交集,没有交集生成一张空白图;有交集遍历每一个像素点转换为web墨卡托坐标,转换为Geometry 点坐标, 判断点是否在线上 ,在设置当前像素点颜色;不在给空白);
  5. 循环遍历多个级别,重复1~4步骤,即可生成多个不同级别

判断点是否在线上需要注意,可参考谷歌js提供的算法,isPointOnSegment 计算距离允许误差在分辨率范围内,则标识在线上;

2.3 多点瓦片的生成

  1. 先计算 多点 的Geometry经纬度范围(外接矩形的左上角,右下角坐标);
  2. 经纬度坐标转web墨卡托坐标;
  3. 指定某一级别计算瓦片号范围;
  4. 分别遍历每一个瓦片(计算瓦片范围与 多点 是否有交集,没有交集生成一张空白图;有交集 求瓦片范围与多点的交集点,然后遍历交集点,判断点是否在线上 ,在设置当前像素点颜色;不在给空白);
  5. 循环遍历多个级别,重复1~4步骤,即可生成多个不同级别

3. 源码

package com.demo.utils;

import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import com.vividsolutions.jts.util.GeometricShapeFactory;

import java.util.List;

/*************************************
 *Class Name: JtsUtils
 *Description: <jtsutils工具类>
 *@author: Seminar
 *@create: 2023/3/31
 *@since 1.0.0
 *************************************/
public class JtsUtils {
    static GeometryFactory factory = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 4326);

    public static Point createPoint(Coordinate pt) {

        return factory.createPoint(pt);
    }

    public static double distance(Point p1, Point p2) {
        return distance(p1.getCoordinate(), p2.getCoordinate());
    }

    public static double distance(Coordinate p1, Coordinate p2) {
        double lat1 = Math.toRadians(p1.y);
        double lon1 = Math.toRadians(p1.x);
        double lat2 = Math.toRadians(p2.y);
        double lon2 = Math.toRadians(p2.x);
        double vLon = Math.abs(lon1 - lon2);
        double vLat = Math.abs(lat1 - lat2);
        double h = haverSin(vLat) + Math.cos(lat1) * Math.cos(lat2) * haverSin(vLon);
        double distance = 1.2756274E7 * Math.asin(Math.sqrt(h));
        return distance;
    }

    private static double haverSin(double theta) {
        double v = Math.sin(theta / 2.0);
        return v * v;
    }

    public static double distance(LineString line) {
        Coordinate[] coors = line.getCoordinates();
        return distance(coors);
    }

    public static double distance(Coordinate[] coors) {
        double lineDist = 0.0;

        for(int i = 1; i < coors.length; ++i) {
            Coordinate coor1 = coors[i - 1];
            Coordinate coor2 = coors[i];
            if (coor1.x == coor2.x && coor1.y == coor2.y) {
                lineDist += 0.0;
            } else {
                lineDist += distance(coor1, coor2);
            }
        }

        return lineDist;
    }

    public static double distance(List<Coordinate> coors) {
        double lineDist = 0.0;

        for(int i = 1; i < coors.size(); ++i) {
            Coordinate coor1 = (Coordinate)coors.get(i - 1);
            Coordinate coor2 = (Coordinate)coors.get(i);
            if (coor1.x == coor2.x && coor1.y == coor2.y) {
                lineDist += 0.0;
            } else {
                lineDist += distance(coor1, coor2);
            }
        }

        return lineDist;
    }


    public static Geometry createGeometryByWKT(String wkt) throws ParseException {
        WKTReader reader = new WKTReader(factory);
        Geometry geometry = reader.read(wkt);
        return geometry;
    }

    public static LineString createLineStringByWKT(String wkt) throws ParseException {
        WKTReader reader = new WKTReader(factory);
        LineString line = (LineString)reader.read(wkt);
        return line;
    }

    public static Point createPointByWKT(String pointWkt) {
        WKTReader reader = new WKTReader(factory);
        Point point = null;

        try {
            point = (Point)reader.read(pointWkt);
        } catch (ParseException var4) {
            var4.printStackTrace();
        }

        return point;
    }

    public static Polygon createPolygonByWKT(String polygonWkt) {
        WKTReader reader = new WKTReader(factory);
        Polygon polygon = null;

        try {
            polygon = (Polygon)reader.read(polygonWkt);
        } catch (ParseException var4) {
            var4.printStackTrace();
        }

        return polygon;
    }

    public static Geometry createCircle(double x, double y, double radius) {
        GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
        shapeFactory.setNumPoints(128);
        shapeFactory.setCentre(new Coordinate(x, y));
        shapeFactory.setSize(radius * 2.0);
        return shapeFactory.createCircle();
    }
}
package com.demo.model.render;

import lombok.Data;

/**
 * 瓦片类,(单位:块)
 * 地图瓦片坐标系(Tile Coordinates)单位。
 * 瓦片坐标系以左上角为原点(0, 0),到右下角(2 ^ 图像级别 - 1, 2 ^ 图像级别 - 1)
 */
@Data
public class Tile {
    /**
     * 横向瓦片数
     */
    long x;
    /**
     * 纵向瓦片数
     */
    long y;
    /**
     * 级别
     */
    int z;

    public Tile() {
    }

    /**
     * 根据给定参数构造Tile的新实例
     *
     * @param x 横向瓦片数
     * @param y 纵向瓦片数
     */
    public Tile(long x, long y) {
        this.x = x;
        this.y = y;
    }

    /**
     * 根据给定参数构造Tile的新实例
     *
     * @param x 横向瓦片数
     * @param y 纵向瓦片数
     * @param z 级别
     */
    public Tile(long x, long y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public String toString() {
        return "Tile(" + x + "," + y + "," + z + ")";
    }
}
package com.demo.model.render;

import lombok.Data;

/**
 * 屏幕像素坐标类(单位:像素)
 * 像素坐标系(Pixel Coordinates)单位
 * 以左上角为原点(0,0),向右向下为正方向
 */
@Data
public class Pixel {
    /**
     * 横向像素
     */
    long x;
    /**
     * 纵向像素
     */
    long y;

    /**
     * 根据给定参数构造Pixel的新实例
     *
     * @param x 横向像素
     * @param y 纵向像素
     */
    public Pixel(long x, long y) {
        this.x = x;
        this.y = y;
    }
}
package com.demo.process;

import com.demo.model.render.Pixel;
import com.demo.model.render.Tile;
import com.demo.util.JtsUtils;
import com.demo.util.MercatorTransform;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import static java.lang.Math.max;
import static java.lang.Math.min;

/*************************************
 *Class Name: Geoms2JpgTile
 *Description: <多点、线、面生成瓦片>
 *@author: Seminar
 *@create: 2021/7/19
 *@since 1.0.0
 *************************************/
@Slf4j
public class Geoms2JpgTile {

    private GeometryFactory gf = new GeometryFactory();
    static MercatorTransform mercatorTransform = new MercatorTransform();
    public static int WIDTH = 256;
    public static int HEIGHT = 256;
    public static int BACKCOLOR_WHITE = ((255 << 16) | ((255 << 8) | 255));
    public static int BACKCOLOR_BLACK = ((0 << 16) | ((0 << 8) | 0));

    // 多边形几何
    String polygon;
    // 线几何
    String lineString;
    // 多边几何
    String multiPoint;

    /**
     * 初始化wkt
     */
    public void initWkt() {
        this.polygon = "POLYGON ((116.42486572265626 39.99500778093748, 116.51962280273439 39.886557705928475, 116.44546508789064 39.78110197709871, 116.31637573242189 39.818029898770206, 116.27655029296876 39.93817189499188, 116.42486572265626 39.99500778093748))";

        this.lineString = "LINESTRING (117.18292236328126 40.16208338164619, 119.01489257812501 39.48284540453334)";

        this.multiPoint = "MULTIPOINT ((115.48690795898439 40.12639098502455), (115.80139160156251 40.148438503139076), (115.83847045898439 39.9665957444875), (115.90850830078126 39.854937988531276), " +
                "(115.98403930664062 39.816975090490004), (115.84808349609376 39.769491963709), (115.60638427734376 39.707186656826565), (115.44708251953126 39.82119422647455), " +
                "(115.32348632812501 39.961332959837826), (115.36605834960939 40.09593265290902), (115.59951782226564 39.940277770390324), (115.74371337890626 39.842286020743394), " +
                "(115.58715820312501 39.79271003204449), (115.76019287109376 39.7631584037253), (115.87280273437501 39.67759833072648), (115.96481323242189 40.18307014852534), " +
                "(115.69152832031251 40.2155868104582), (115.63934326171876 40.073868105094846), (115.43060302734376 39.56547053068436), (115.66955566406251 39.470125122358176), " +
                "(115.83709716796875 39.55911824217187), (115.91949462890626 39.44679856427205), (115.54046630859376 39.42346418978385), (115.07354736328125 39.63319206567459), " +
                "(115.11474609375 40.092781012494065), (115.19439697265626 40.287906612507406), (114.98291015625001 39.83385008019448), (114.97467041015626 39.47224533091451), " +
                "(115.27130126953126 39.38101803294523), (115.59265136718751 39.34067026099156))";
    }

    /**
     * 面jpg瓦片生成
     *
     * @param zoom
     * @throws IOException
     * @throws ParseException
     */
    public void polygon2Tile(String wkt, int zoom) throws IOException, ParseException {
        Geometry geom = wkt2Geo(wkt);
        Geometry envelope = geom.getEnvelope();
        double lonMin = min(envelope.getCoordinates()[0].x, envelope.getCoordinates()[2].x);
        double lonMax = max(envelope.getCoordinates()[0].x, envelope.getCoordinates()[2].x);
        double latMin = min(envelope.getCoordinates()[0].y, envelope.getCoordinates()[2].y);
        double latMax = max(envelope.getCoordinates()[0].y, envelope.getCoordinates()[2].y);

        Tile t1 = latLng2Tile(lonMin, latMin, zoom);
        Tile t2 = latLng2Tile(lonMax, latMax, zoom);

        long tilexMin = min(t1.getX(), t2.getX());
        long tileyMin = min(t1.getY(), t2.getY());
        long tilexMax = max(t1.getX(), t2.getX());
        long tileyMax = max(t1.getY(), t2.getY());

        if (!new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom).exists()) {
            FileUtils.forceMkdir(new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom));
        }
        log.info("zoom: {}, jpgNum: {}", zoom, (tilexMax - tilexMin + 1) * (tileyMax - tileyMin + 1));
        for (long x = tilexMin; x <= tilexMax; x++) {
            for (long y = tileyMin; y <= tileyMax; y++) {
                log.info("x: {},y: {}", x, y);
                Tile tile = new Tile(x, y, zoom);
                Envelope enve = mercatorTransform.tile2Envelope(tile);
                Geometry grid = gf.toGeometry(enve);
                boolean intersects = grid.intersects(geom);
                if (intersects) {
                    // 生成栅格瓦片jpg
                    Geometry inter = grid.intersection(geom);
                    double tempLatMin = enve.getMinY();
                    double tempLatMax = enve.getMaxY(); // 纬度
                    double tempLonMin = enve.getMinX(); // 经度
                    double tempLonMax = enve.getMaxX();

                    // 经纬度转像素坐标
                    Pixel p1 = mercatorTransform.geographic2Pixel(new Coordinate(tempLonMin, tempLatMin), zoom);
                    Pixel p2 = mercatorTransform.geographic2Pixel(new Coordinate(tempLonMax, tempLatMax), zoom);
                    long minX = min(p1.getX(), p2.getX());
                    long minY = min(p1.getY(), p2.getY());
                    long maxX = max(p1.getX(), p2.getX());
                    long maxY = max(p1.getY(), p2.getY());

                    BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
                    // 生成默认图片
                    for (int h = 0; h < HEIGHT; h++) {
                        for (int w = 0; w < WIDTH; w++) {
                            // 可修改,黑色背景或者白色背景
                            image.setRGB(w, h, BACKCOLOR_BLACK);
                        }
                    }
                    //图像输出
                    // 填充面的像素
                    for (long x1 = minX; x1 <= maxX; x1++) {
                        for (long y1 = minY; y1 <= maxY; y1++) {
                            // 判断轨迹点是否位于面上
                            // 像素坐标转web墨卡托转Geom经纬度坐标
                            Coordinate coordinate = mercatorTransform.pixel2Geographic(new Pixel(x1, y1), zoom);
                            Point point = JtsUtils.createPoint(coordinate);
                            if (inter.contains(point)) {
                                int pw = (int) (x1 - 256 * x);
                                int ph = (int) (y1 - 256 * y);
                                int rgb = ((255 << 16) | ((0 << 8) | 0));
                                if (pw > 0 && pw < 256 && (ph > 0 && ph < 256)) {
                                    image.setRGB(pw, ph, rgb);
                                }
                            }
                        }
                    }
                    //图像输出
                    ImageIO.write(image, "jpg", new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom + "\\" + x + "_" + y + ".jpg"));
                } /*else {
                    BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
                    //单色通道提取
                    for (int h = 0; h < HEIGHT; h++) {
                        for (int w = 0; w < WIDTH; w++) {
                            image.setRGB(w, h, BACKCOLOR_BLACK);
                        }
                    }
                    //图像输出
                    ImageIO.write(image, "jpg", new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom + "\\" + x + "_" + y + ".jpg"));
                    log.error("{} {} no datas", x, y);
                }*/
            }
        }
    }

    /**
     * 线生成jpg瓦片
     *
     * @param wkt  wkt几何
     * @param zoom 瓦片级别
     * @throws IOException
     * @throws ParseException
     */
    public void lineString2Tile(String wkt, int zoom) throws IOException, ParseException {
        Geometry geom = wkt2Geo(wkt);
        Geometry envelope = geom.getEnvelope();

        // 计算最大最小边界框经纬度
        double lonMin = min(envelope.getCoordinates()[0].x, envelope.getCoordinates()[2].x);
        double lonMax = max(envelope.getCoordinates()[0].x, envelope.getCoordinates()[2].x);
        double latMin = min(envelope.getCoordinates()[0].y, envelope.getCoordinates()[2].y);
        double latMax = max(envelope.getCoordinates()[0].y, envelope.getCoordinates()[2].y);

        // 经纬度转web墨卡托坐标,转像素坐标,并计算瓦片号
        Tile t1 = latLng2Tile(lonMin, latMin, zoom);
        Tile t2 = latLng2Tile(lonMax, latMax, zoom);

        // 计算x,y最大最小瓦片号
        long tilexMin = min(t1.getX(), t2.getX());
        long tileyMin = min(t1.getY(), t2.getY());
        long tilexMax = max(t1.getX(), t2.getX());
        long tileyMax = max(t1.getY(), t2.getY());

        if (!new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom).exists()) {
            FileUtils.forceMkdir(new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom));
        }
        log.info("zoom: {}, jpgNum: {}", zoom, (tilexMax - tilexMin + 1) * (tileyMax - tileyMin + 1));

        for (long x = tilexMin; x <= tilexMax; x++) {
            for (long y = tileyMin; y <= tileyMax; y++) {
                Tile tile = new Tile(x, y, zoom);
                Envelope enve = mercatorTransform.tile2Envelope(tile);
                Geometry grid = gf.toGeometry(enve);
                boolean intersects = grid.intersects(geom);
                if (intersects) {
                    // 生成栅格瓦片jpg
                    double tempLatMin = enve.getMinY();
                    double tempLatMax = enve.getMaxY();
                    double tempLonMin = enve.getMinX(); // 经度
                    double tempLonMax = enve.getMaxX(); // 纬度

                    // 经纬度转像素坐标
                    Pixel p1 = mercatorTransform.geographic2Pixel(new Coordinate(tempLonMin, tempLatMin), zoom);
                    Pixel p2 = mercatorTransform.geographic2Pixel(new Coordinate(tempLonMax, tempLatMax), zoom);
                    long minX = min(p1.getX(), p2.getX());
                    long minY = min(p1.getY(), p2.getY());
                    long maxX = max(p1.getX(), p2.getX());
                    long maxY = max(p1.getY(), p2.getY());

                    BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
                    // 生成默认图片
                    for (int h = 0; h < HEIGHT; h++) {
                        for (int w = 0; w < WIDTH; w++) {
                            // 可修改,黑色背景或者白色背景
                            image.setRGB(w, h, BACKCOLOR_WHITE);
                        }
                    }
                    //图像输出
                    // 填充线的像素
                    for (long x1 = minX; x1 <= maxX; x1++) {
                        for (long y1 = minY; y1 <= maxY; y1++) {
                            // 像素坐标转web墨卡托转Geom经纬度坐标
                            Coordinate coordinate = mercatorTransform.pixel2Geographic(new Pixel(x1, y1), zoom);
                            Point point = gf.createPoint(coordinate);
                            // 判断点是否在线上  geom.intersects(point)、point.within(geom) 这俩方法都不管用,以1/500分辨率当作误差范围
                            if (isPointOnSegment(point, gf.createPoint(geom.getCoordinates()[0]),
                                    gf.createPoint(geom.getCoordinates()[1]), mercatorTransform.zoomToResolution(zoom) / 500)) {
//                                log.info("{}", JSON.toJSONString(point.getCoordinate()));
                                int pw = (int) (x1 - 256 * x);
                                int ph = (int) (y1 - 256 * y);
                                int rgb = ((0 << 16) | ((255 << 8) | 0)); // 绿色填充
                                if (pw > 0 && pw < 256 && (ph > 0 && ph < 256)) {
                                    image.setRGB(pw, ph, rgb);
                                }
                            }
                        }
                    }
                    //图像输出
                    ImageIO.write(image, "jpg", new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom + "\\" + x + "_" + y + ".jpg"));
                } /*else {
                    // 构建默认图片
                    BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
                    for (int h = 0; h < HEIGHT; h++) {
                        for (int w = 0; w < WIDTH; w++) {
                            // 可修改,黑色背景或者白色背景
                            image.setRGB(w, h, BACKCOLOR_WHITE);
                        }
                    }
                    //图像输出
                    ImageIO.write(image, "jpg", new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom + "\\" + x + "_" + y + ".jpg"));
                    log.error("{} {} no datas", x, y);
                }*/
            }
        }
    }

    /**
     * 点生成栅格jpg瓦片
     *
     * @param zoom 瓦片级别
     * @throws IOException
     * @throws ParseException
     */
    public void points2Tile(String wkt, int zoom) throws IOException, ParseException {
        Geometry geom = wkt2Geo(wkt);
        Geometry envelope = geom.getEnvelope();

        // 计算最大最小边界框经纬度
        double lonMin = min(envelope.getCoordinates()[0].x, envelope.getCoordinates()[2].x);
        double lonMax = max(envelope.getCoordinates()[0].x, envelope.getCoordinates()[2].x);
        double latMin = min(envelope.getCoordinates()[0].y, envelope.getCoordinates()[2].y);
        double latMax = max(envelope.getCoordinates()[0].y, envelope.getCoordinates()[2].y);

        // 经纬度转web墨卡托坐标,转像素坐标,并计算瓦片号
        Tile t1 = latLng2Tile(lonMin, latMin, zoom);
        Tile t2 = latLng2Tile(lonMax, latMax, zoom);

        // 计算x,y最大最小瓦片号
        long tilexMin = min(t1.getX(), t2.getX());
        long tileyMin = min(t1.getY(), t2.getY());
        long tilexMax = max(t1.getX(), t2.getX());
        long tileyMax = max(t1.getY(), t2.getY());

        if (!new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom).exists()) {
            FileUtils.forceMkdir(new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom));
        }
        log.info("zoom: {}, jpgNum: {}", zoom, (tilexMax - tilexMin + 1) * (tileyMax - tileyMin + 1));

        for (long x = tilexMin; x <= tilexMax; x++) {
            for (long y = tileyMin; y <= tileyMax; y++) {
                log.info("x: {},y: {}", x, y);
                Tile tile = new Tile(x, y, zoom);
                Envelope enve = mercatorTransform.tile2Envelope(tile);
                Geometry grid = gf.toGeometry(enve);
                boolean intersects = grid.intersects(geom);

                // 生成栅格瓦片jpg
                if (intersects) {
                    Geometry inter = grid.intersection(geom); // 点与瓦片的交集
                    // 构建默认图片
                    BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
                    for (int h = 0; h < HEIGHT; h++) {
                        for (int w = 0; w < WIDTH; w++) {
                            // 可修改,黑色背景或者白色背景
                            image.setRGB(w, h, BACKCOLOR_WHITE);
                        }
                    }
                    //图像输出
                    // 填充点的像素
                    Coordinate[] intersectionPoints = inter.getCoordinates();
                    for (Coordinate coor : intersectionPoints) {
                        // 经纬度转像素坐标
                        Pixel pixel = mercatorTransform.geographic2Pixel(coor, zoom);
                        int pw = (int) (pixel.getX() - 256 * x);
                        int ph = (int) (pixel.getY() - 256 * y);
                        int rgb = ((0 << 16) | ((0 << 8) | 255)); // 蓝色填充
                        if (pw > 0 && pw < 256 && (ph > 0 && ph < 256)) {
                            image.setRGB(pw, ph, rgb);
                        }
                    }
                    //图像输出
                    ImageIO.write(image, "jpg", new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom + "\\" + x + "_" + y + ".jpg"));
                } /*else {
                    // 构建默认图片
                    BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
                    for (int h = 0; h < HEIGHT; h++) {
                        for (int w = 0; w < WIDTH; w++) {
                            // 可修改,黑色背景或者白色背景
                            image.setRGB(w, h, BACKCOLOR_WHITE);
                        }
                    }
                    //图像输出
                    ImageIO.write(image, "jpg", new File("D:\\learn1\\geojson-demo\\jpg\\" + zoom + "\\" + x + "_" + y + ".jpg"));
                    log.error("{} {} no datas", x, y);
                }*/
            }
        }
    }

    /**
     * 判断点是否在线上
     *
     * @param a          点A
     * @param start      线起点start
     * @param end        线终点end
     * @param resolution 误差范围m
     * @return
     */
    public boolean isPointOnSegment(Point a, Point start, Point end, double resolution) {
        boolean flag = false;
        double startAdis = JtsUtils.distance(a, start);
        double endADis = JtsUtils.distance(a, end);
        double dis = JtsUtils.distance(start, end);
        if (startAdis + endADis >= dis - resolution && startAdis + endADis <= dis + resolution) {
            return true;
        }
        return flag;
    }

    /**
     * 计算经纬度所在瓦片号
     *
     * @param lon  经度
     * @param lat  纬度
     * @param zoom 瓦片级别
     * @return
     */

    public static Tile latLng2Tile(double lon, double lat, int zoom) {
        // 经纬度转墨卡托
        Coordinate mkt = mercatorTransform.geographic2Mercator(new Coordinate(lon, lat));
        // 墨卡托转像素
        Pixel pixel = mercatorTransform.mercator2Pixel(mkt, zoom);
        // 像素坐标所在瓦片
        Tile atTile = mercatorTransform.pixelAtTile(pixel);
        atTile.setZ(zoom);
        return atTile;
    }

    /**
     * wkt 转geometry
     *
     * @param wkt
     * @return
     * @throws ParseException
     */
    public Geometry wkt2Geo(String wkt) throws ParseException {
        WKTReader reader = new WKTReader(gf);
        Geometry geom = reader.read(wkt);
        return geom;
    }

    public static void main(String[] args) throws IOException, ParseException {
        Geoms2JpgTile geoms2JpgTile = new Geoms2JpgTile();
        geoms2JpgTile.initWkt();
        for (int i = 9; i <= 17; i++) {
            if (i > 14) {
                continue;
            }
            geoms2JpgTile.polygon2Tile(geoms2JpgTile.polygon, i);
            geoms2JpgTile.lineString2Tile(geoms2JpgTile.lineString, i);
            geoms2JpgTile.points2Tile(geoms2JpgTile.multiPoint, i);
        }
    }
}

参考

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java对点、线、面生成栅格瓦片jpg,并渲染呈现 的相关文章

随机推荐

  • spring id,name

    转载于 https www cnblogs com 549294286 p 9947815 html 在BeanFactory的配置中 是我们最常见的配置项 它有两个最常见的属性 即id和name 最近研究了一下 发现这两个属性还挺好玩的
  • 如何覆盖上一次commit_Git如何在已有的 commit 上再次提交?

    在一些受管控的项目上 提交代码到 git 服务器后 还需要经过审核确认才正式合入版本 一般常用 gerrit 来进行审核确认操作 如果提交的代码审核不通过 需要再次修改提交 由于是修改同一个问题 我们可能不希望生成多个 commit 信息
  • Ceph bluestore中的缓存管理

    从15年3月接触Ceph分布式存储系统 至今已经5年了 因为工作的需要 对Ceph的主要模块进行了较深入的学习 也在Ceph代码层面做了些许改进 以满足业务需要 我们主要使用M版本 最近得闲 将过往的一些学习心得 改进以及优化思路记录下了
  • CDN基本工作过程

    看了一些介绍CDN的文章 感觉这篇是讲的最清楚的 使用CDN会极大地简化网站的系统维护工作量 网站维护人员只需将网站内容注入CDN的系统 通过CDN部署在各个物理位置的服务器进行全网分发 就可以实现跨运营商 跨地域的用户覆盖 由于CDN将内
  • firewalld 放行端口

    发现 telnet报错 telnet connect to address IP No route to host 于是检查目标主机的 firewalld 发先没有放行对应端口 在 firewalld 防火墙中放行端口 可以使用 firew
  • 微信小程序报错-errCode: -1

    1 报错截图 2 报错原因 Collection remove 需要小程序端2 9 4版本或之后的版本才支持 看了眼我的调试基础库 我的版本是2 8 1 所以 3 解决方法 点击右上角的详情 本地设置 调试基础库选择2 14 0
  • js数组相加相减函数

    数组相减 reduceArray arr1 arr2 for var i arr1 length 1 i gt 0 i var a arr1 i for var j arr2 length 1 j gt 0 j var b arr2 j i
  • Qt打开包含头文件以及打开函数声明和定义的方法

    当第一次拿到被人的程序的时候不知道如何向VS或者keil这样的编译器可以直接右键打开头文件 Qt可以通过 1 鼠标放在你需要打开的头文件或者函数声明的地方 如下图我鼠标放在MainWindow上 2 Ctrl 鼠标左键单击 或者使用快捷键F
  • python-算法时间复杂度和空间复杂度

    大O表示法 O 名称 举例 1 常量时间 一次赋值 logn 对数时间 折半查找 n 线性时间 线性查找 nlogn 对数线性时间 快速排序 n 2 平方 两重循环 n 3 立方 三重循环 2 n 指数 递归求斐波那契数列 n 阶乘 旅行商
  • html文本元素

    文章目录 h p span pre code 实体字符 strong i em del s h h head 标题 一共有六级标题 hKaTeX parse error Expected got EOF at end of input 6
  • 【编译原理】 CS143 斯坦福大学公开课 第一周:简介

    youtube 1 1 Introduction to Compilers and interpreters 1 1 Introduction to Compilers and interpreters 编译器解释器介绍 两种主要的实现编程
  • three.js中聚光灯及其属性介绍

    一 聚光灯及其属性介绍 Three js中的聚光灯 SpotLight 是一种用于在场景中创建聚焦光照的光源类型 它有以下属性 color 聚光灯的颜色 intensity 聚光灯的强度 distance 聚光灯的有效距离 angle 聚光
  • [毕业设计]2023-2024年最新电子科学与技术专业毕设选题题目推荐汇总

    文章目录 1前言 2 如何选题 3 选题方向 3 1 嵌入式开发方向 3 2 物联网方向 3 3 人工智能方向 3 4 算法研究方向 3 5 学长作品展示 4 最后 1前言 近期不少学弟学妹询问学长关于电子科学与技术专业相关的毕设选题 学长
  • java如何检测连接池连接情况,如何检查是否使用了连接池

    I use HSQLDB EclipseLink Gemini on OSGI framework Felix In spite that I ve set pool in persistence xml I have serious su
  • 全网最全谷粒商城记录_01、简介-项目介绍(2022-07-06更新完成)

    声明 本教程不收取任何费用 欢迎转载 尊重作者劳动成果 不得用于商业用途 侵权必究 目录 分布式基础 全栈开发篇 分布式高级 微服务架构篇 高可用集群 架构师提升篇 希望大家 微服务架构图简单介绍 项目简介 1 项目背景 1 电商模式 1
  • JavaWeb学习笔记-part1

    互联网通信 什么是互联网通信 两台计算机通过网络实现文件共享行为 就是互联网通信 互联网通信中的角色划分 客户端 用于发送请求的计算机 服务端 用于接受请求 并满足请求的计算机 互联网通信模型 C S通信模型 client software
  • Handler机制与原理

    为什么会出现内存泄漏问题呢 分析 Handler使用是用来进行线程间通信的 所以新开启的线程是会持有Handler引用的 如果在Activity等中创建Handler 并且是非静态内部类的形式 就有可能造成内存泄漏 非静态内部类是会隐式持有
  • uniapp 开发微信小程序之新版隐私协议

    自从微信小程序官方更新隐私协议 用户必须同意之后 才能获取个人信息 这就导致在获取用户信息之前 需要有个隐私协议弹窗 大致如下图 微信小程序官方提供的API和 uniapp 开发的稍微有点区别 这里只记录 uniapp 开发的 如果需要微信
  • 高中学历的程序员,以包装的方式进入现在的公司,想跳槽咋办?

    网友自述 我在现在广州这家公司工作了两年 技术上有一定提升 但这两年我过得一直不是很快乐 因为我学历包装 所以我不敢跟同事交往太深 一直孤身一人 非常难受 可能这就是代价吧 现在我想换一个公司 我不想再用假身份了 但不知道用高中学历是否能够
  • Java对点、线、面生成栅格瓦片jpg,并渲染呈现

    Java对点 线 面生成栅格瓦片jpg 并渲染呈现 1 效果图 2 原理 2 1 面瓦片的生成 2 2 线瓦片的生成 2 3 多点瓦片的生成 3 源码 参考 这篇博客将介绍从前端HTML页面到后端预生成栅格瓦片jpg 并提供查询接口供前端h