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

Posted 程序媛一枚~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.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如何使用arcgis将栅格图转为矢量图,

地图瓦片整体介绍

OpenGL中着色器,渲染管线,光栅化

THREEJS案例-坐标系之点线面

SuperMap iDesktop 下载安装,生成本地瓦片,以及发布本地瓦片服务

ArcGIS生成根据点图层生成等值面并减小栅格锯齿的操作步骤