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

news/2024/9/20 21:43:59

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

    • 1. 效果图
    • 2. 原理
      • 2.1 面瓦片的生成
      • 2.2 线瓦片的生成
      • 2.3 多点瓦片的生成
    • 3. 源码
    • 参考

这篇博客将介绍从前端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) {// 生成栅格瓦片jpgGeometry 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) {// 生成栅格瓦片jpgdouble 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);// 生成栅格瓦片jpgif (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);}}
}

参考

  • 高德地图数学计算库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pgtn.cn/news/17718.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Jupyter的使用方法

一、Jupyter新建python3文件的方法 1、打开桌面快捷方式&#xff0c;或者开始所有程序&#xff0c;找到anaconda文件夹&#xff0c;点击jupyter 2、点击new&#xff0c;选择python3 3、在1处输入代码&#xff0c;在2处点击运行 4、点击1处&#xff0c;在2处输入文件名字&#…

[vb+mo] visual baisc 6.0 基于mapobjects 2.4 开发的数字化校园电子地图

程序的源代码下载地址: https://docs.google.com/ 请安装VB6.0企业版(不是企业版运行会报错,因为缺少相应的控件)和ESRI MO2.4 程序的质量一般,因为时间仓促,主要是毕业设计时间仓促.希望大家多多改进.有什么问题可以发邮件欢迎交流. 程序的主窗口代码: 通用变量定义Private l…

Windows版本Anaconda安装教程

1、点击安装包&#xff0c;右键-以管理员身份运行安装文件&#xff0c;如图所示&#xff1a; 2、直接点击next即可 3、接着进入到许可协议的界面&#xff0c;这里点击I agree选项&#xff0c;也就是我同意的意思。 4、接着进入到用户选择的界面&#xff0c;选择all users选项&a…

Python 对图像进行base64编码及解码读取为numpy、opencv、matplot需要的格式

Python 对图像进行base64编码及解码读取为numpy、opencv、matplot需要的格式1. 效果图2. 源码参考这篇博客将介绍Python如何对图像进行base64编解码及读取为numpy、opencv、matplot需要的格式。 1. 效果图 原始图如下&#xff1a; base64转换为numpy数组效果图如下&#xff…

OpenCV使用 GrabCut 算法进行交互式前景提取

OpenCV使用 GrabCut 算法进行交互式前景提取 1. 效果图2. 源码参考这篇博客将介绍如何使用Python,OpenCV中的GrabCut 算法来提取图像中的前景,并为此创建一个交互式应用程序。 1. 效果图 官方示例——lena 原始图 VS grabcut前景设置 VS 前景抠图效果如下: 鼠标右键绘制矩…

制作图片热点

制作图片热点以前经常用图片做一些链接&#xff0c;而那是用dw做的很简单&#xff0c;今天看到一本书&#xff0c;也学着做了一个。 书上那个绝对定位到图片上的区域&#xff0c;我不知道是怎么实现:hover显示边框的&#xff0c;按它的弄&#xff0c;ff正常&#xff0c;在ie下根…

一步步学会使用ASP.NET 4 WEB应用程序中使用URL Routing(翻译)

创建路由 路由就是将URL路径映射到具体的物理文件。若要将路由添加到网站中&#xff0c;请使用 RouteCollection.MapPageRoute 方法将它们添加到RouteTable类的静态Routes属性。 将用于添加路由的方法添加到 Global.asax 文件中 如果网站还没有 Global.asax 文件&#xff0c;…

Java判断Geometry点是否在线Geometry LineString上

Java判断Geometry点是否在线上1. 效果图2. 源码参考这篇博客将实现一个简单的算法&#xff0c;判断地图上的一个点是否在已知的一条线上&#xff1b;首先地球为球面&#xff0c;经纬度为double类型保留有6~14位小数&#xff0c;直接的求距离不太合适。 Geometry point Geometry…