在腾讯地图上发一张自定义的手绘地图,由于手绘地图像素都比较高,加载一整张图速度极慢。将手绘地图按照地图的瓦片规则切片分开加载。
https://baike.baidu.com/item/%E7%93%A6%E7%89%87%E5%9C%B0%E5%9B%BE/8006049?fr=aladdin
https://lbs.qq.com/webDemoCenter/javascriptV2/mapType/mapOverlayImage
解:根据源图的首尾坐标反向计算拉升后图片的宽高比例,通过比例将源图的尺寸进行拉升(只增大尺寸,不压缩尺寸)。
解:合成的切割源图的尺寸(长、宽)必须是瓦片切图个数(横向、纵向)的倍数,因为在切割瓦片时的尺寸都是整数。所以在合成切割源图时,如果尺寸不是瓦片切图个数的倍数,需要将图片尺寸放大为最临近的一个倍数值。
解:切图过程中存在很多空白透明图,无需切割。在切割前先判断对应瓦片尺寸中的图是否为全空白图,如果是全空白图则不需要切图处理。
解:适当降低源图尺寸,提高服务器的内存配置。
解:这儿主要是生成的切割源图尺寸较大,只需要记录切割源图的参数信息,不真正生成切割源图。然后根据切割源图的参数 信息,对拉升源图进行切割填充,生成指定层级的瓦片源图。然后循环对瓦片源图进行切割得到瓦片图。
解:
第一种:提高源图的像素质量。
第二种:Graphics2D
在画图前进行缩放设置
/*
* java提供了4个缩放的微调选项
* image.SCALE_SMOOTH //平滑优先(图片质量好、切割速度慢)
* image.SCALE_FAST //速度优先(图片质量差、切割速度快)
* image.SCALE_AREA_AVERAGING //区域均值(图片质量好、切割速度慢)
* image.SCALE_REPLICATE //像素复制型缩放(图片质量差、切割速度快)
* image.SCALE_DEFAULT //默认缩放模式(图片质量差、切割速度快)
*/
Image image = bi.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
解:当切图层级小于图片所在的理论层级时,以理论层级为准。
http://www.what21.com/sys/view/java_java-gui_1456896004842.html
https://blog.csdn.net/love_QJP/article/details/81475055
https://blog.csdn.net/mygisforum/article/details/22997879
https://blog.csdn.net/wanm9/article/details/52319061
https://blog.csdn.net/iteye_18766/article/details/82642863
https://blog.csdn.net/iteye_12605/article/details/82240200
PictureCutUtil
工具封装package com.example.picturecut.util;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import static com.sun.org.apache.xalan.internal.lib.ExsltMath.power;
/**
* 腾讯地图叠加自定义瓦片tile map手绘图处理
* @author jdp
*/
public class PictureCutUtil {
/**
* 腾讯地图-自定义瓦片图切图
* @param fromFile 源文件
* @param tempFilePath 临时文件路径
* @param group 切图分组(唯一)
* @param lonStart 开始经度
* @param latStart 开始纬度
* @param lonEnd 结束经度
* @param latEnd 结束纬度
* @param cutStartLevel 切片层级开始
* @param cutStartLevel 切片层级结束
*/
public static void tencentPictureCut(File fromFile, String tempFilePath, String group, double lonStart, double latStart, double lonEnd, double latEnd, Integer cutStartLevel, Integer cutEndLevel){
String toFilePath = tempFilePath + "/" + group;
String cutSrcBasePath = tempFilePath + "/" + group +"/cutsrc";
File toFile = new File(toFilePath + "/cutsrc.png");
// 瓦片单元格大小px(默认256)
int cellPixel = 256;
// 切图层级范围(默认14~18级),切图的层级必须<=切图所在的理论层级
if(cutStartLevel==null || cutStartLevel <= 0){
cutStartLevel = 14;
}
if(cutEndLevel==null || cutEndLevel <= 0){
cutEndLevel = 18;
}
// 通过首尾坐标合成拉伸后的图片
Map<String, Object> offsetMap = getOffset(lonStart, latStart, lonEnd, latEnd,cellPixel);
// 计算实际偏移尺寸
// 源图参数(切图理论层级)
double offsetTop = Double.valueOf(offsetMap.get("top").toString());
double offsetRight = Double.valueOf(offsetMap.get("right").toString());
double offsetBottom = Double.valueOf(offsetMap.get("bottom").toString());
double offsetLeft = Double.valueOf(offsetMap.get("left").toString());
int level = Integer.valueOf(offsetMap.get("level").toString());
// 如果理论层级大于切图层级,则从理论层级开始切图
if(level > cutStartLevel){
cutStartLevel = level;
}
if(cutStartLevel > cutEndLevel){
cutEndLevel = cutStartLevel;
}
// 合成拉伸后的源图
Map<String, Object> cutSrcMap = composeCutSrcPng(fromFile,toFile,offsetTop,offsetRight,offsetBottom,offsetLeft,cellPixel,level);
// 拉伸后源图参数(切图理论层级)
int cellSize = Integer.valueOf(cutSrcMap.get("standardSize").toString())/(1<<(cutStartLevel-level));
int realTop = Integer.valueOf(cutSrcMap.get("realTop").toString()) % cellSize;
int realRight = Integer.valueOf(cutSrcMap.get("realRight").toString()) % cellSize;
int realBottom = Integer.valueOf(cutSrcMap.get("realBottom").toString()) % cellSize;
int realLeft = Integer.valueOf(cutSrcMap.get("realLeft").toString()) % cellSize;
// 源图瓦片参数(切图开始层级)
int[] start = LonLatToXYZ(lonStart, latStart, cutStartLevel);
int[] end = LonLatToXYZ(lonEnd, latEnd, cutStartLevel);
// 源图切图高度范围
int heightStart = 0;
int heightEnd = 0;
for (int y = start[1]; y <= end[1]; y++) {
// 源图切图宽度范围
int widthStart = 0;
int widthEnd = 0;
// 计算每个单元偏移量
int cellTop = 0;
int cellBottom = 0;
// 上边
if(y == start[1]){
heightEnd = cellSize - realTop;
cellTop = realTop;
}
// 下右边
else if(y == end[1]){
heightEnd = heightStart + (cellSize - realBottom);
cellBottom = realBottom;
}
// 中间部分
else{
heightEnd = heightStart + cellSize;
}
for (int x = start[0]; x <= end[0]; x++) {
int cellRight = 0;
int cellLeft = 0;
// 左边
if(x == start[0]){
widthEnd = cellSize - realLeft;
cellLeft = realLeft;
}
// 右边
else if(x == end[0]){
widthEnd = widthStart + (cellSize - realRight);
cellRight = realRight;
}
// 中间部分
else{
widthEnd = widthStart + cellSize;
}
// 源图切割
String cutSrcFilePath = cutSrcBasePath + "/" + cutStartLevel + "-" + x + "-" + y + ".png";
File cutSrcFile = new File(cutSrcFilePath);
srcPictureCut(toFile,cutSrcFilePath,widthStart,heightStart,widthEnd,heightEnd);
// 源图像素填充
resizePng(cutSrcFile,cutSrcFile,0,0,cellTop,cellRight,cellBottom,cellLeft,false);
// 根据源图切割瓦片图
pictureCut(cutSrcFile,toFilePath,256,256,cutStartLevel,cutStartLevel,cutEndLevel,x,y);
widthStart += widthEnd - widthStart;
}
heightStart += heightEnd - heightStart;
}
// 删除切图源文件
deleteAll(new File(cutSrcBasePath));
}
public static void srcPictureCut(File srcImageFile, String toFilePath, int widthStart, int heightStart, int widthEnd, int heightEnd) {
try {
// 读取源图像
ImageIO.setUseCache(false);
BufferedImage bi = ImageIO.read(srcImageFile);
// 源图宽度
int srcWidth = bi.getWidth();
// 源图高度
int srcHeight = bi.getHeight();
// 切图宽度
int cutWidth = widthEnd - widthStart;
// 切图高度
int cutHeight = heightEnd - heightStart;
// 源文件
/*
* java提供了4个缩放的微调选项
* image.SCALE_SMOOTH //平滑优先(图片质量好、切割速度慢)
* image.SCALE_FAST //速度优先(图片质量差、切割速度快)
* image.SCALE_AREA_AVERAGING //区域均值(图片质量好、切割速度慢)
* image.SCALE_REPLICATE //像素复制型缩放(图片质量差、切割速度快)
* image.SCALE_DEFAULT //默认缩放模式(图片质量差、切割速度快)
*/
Image sourceImage = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
if(!isTransparent(bi,widthStart,heightStart,widthEnd,heightEnd)){
// 绘制切图
// 四个参数分别为图像起点坐标和宽高
ImageFilter cropFilter = new CropImageFilter(widthStart, heightStart, widthEnd, heightEnd);
Image cutImage = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(sourceImage.getSource(), cropFilter));
BufferedImage cutBufferedImage = new BufferedImage(cutWidth, cutHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D cutG2d = cutBufferedImage.createGraphics();
// 设置背景全透明
// 返回 BufferedImage支持指定透明度,与此相适应的数据布局和颜色模型 GraphicsConfiguration。
cutBufferedImage = cutG2d.getDeviceConfiguration().createCompatibleImage(cutWidth, cutHeight, Transparency.TRANSLUCENT);
cutG2d = cutBufferedImage.createGraphics();
// 消除锯齿
cutG2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
cutG2d.drawImage(cutImage, 0, 0, null);
cutG2d.dispose();
// 输出为文件
File file = new File(toFilePath);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
ImageIO.write(cutBufferedImage, "PNG", file);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 适配地图的瓦片切图工具
* @param srcImageFile 源图片
* @param toFilePath 切片目标文件分组
* @param destWidth 目标切片宽度px
* @param destHeight 目标切片高度px
* @param level 图片所在层级
* @param cutStartLevel 切图开始层级
* @param cutEndLevel 切图结束层级
* @param tileNumberX x轴初始瓦片参数值
* @param tileNumberY y轴初始瓦片参数值
*/
public static void pictureCut(File srcImageFile, String toFilePath, int destWidth, int destHeight, int level,
int cutStartLevel, int cutEndLevel, int tileNumberX, int tileNumberY) {
try {
// 读取源图像
ImageIO.setUseCache(false);
BufferedImage bi = ImageIO.read(srcImageFile);
// 源图宽度
int srcWidth = bi.getWidth();
// 源图高度
int srcHeight = bi.getHeight();
// 切图宽度
int cutWidth = bi.getWidth();
// 切图高度
int cutHeight = bi.getHeight();
// 控制切图宽高系数,减小切图误差
int zz = 1;
// z坐标控制
for (int z = level; z <= cutEndLevel; z++) {
// 从【切图开始层级】开始切图
if (z >= cutStartLevel && srcWidth >= cutWidth && srcHeight >= cutHeight) {
// 源文件
/*
* java提供了4个缩放的微调选项
* image.SCALE_SMOOTH //平滑优先(图片质量好、切割速度慢)
* image.SCALE_FAST //速度优先(图片质量差、切割速度快)
* image.SCALE_AREA_AVERAGING //区域均值(图片质量好、切割速度慢)
* image.SCALE_REPLICATE //像素复制型缩放(图片质量差、切割速度快)
* image.SCALE_DEFAULT //默认缩放模式(图片质量差、切割速度快)
*/
Image sourceImage = bi.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
// 切片横向数量
int cols = 0;
// 切片纵向数量
int rows = 0;
// 计算切片的横向和纵向数量
if (srcWidth % cutWidth == 0) {
cols = srcWidth / cutWidth;
} else {
cols = (int) Math.floor(srcWidth / cutWidth) + 1;
}
if (srcHeight % cutHeight == 0) {
rows = srcHeight / cutHeight;
} else {
rows = (int) Math.floor(srcHeight / cutHeight) + 1;
}
// 循环建立切片(横向切片)
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
int widthStart = j * cutWidth;
int heightStart = i * cutHeight;
int widthEnd = (j + 1) * cutWidth;
if(widthEnd > srcWidth){
widthEnd = srcWidth;
}
int heightEnd = (i + 1) * cutHeight;
if(heightEnd > srcHeight){
heightEnd = srcHeight;
}
if(!isTransparent(bi,widthStart,heightStart,widthEnd,heightEnd)){
// 绘制切图
// 四个参数分别为图像起点坐标和宽高
ImageFilter cropFilter = new CropImageFilter(widthStart, heightStart, widthEnd, heightEnd);
Image cutImage = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(sourceImage.getSource(), cropFilter));
BufferedImage bufferedImage = new BufferedImage(cutWidth, cutHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D cutG2d = bufferedImage.createGraphics();
// 设置背景全透明
// 返回 BufferedImage支持指定透明度,与此相适应的数据布局和颜色模型 GraphicsConfiguration。
bufferedImage = cutG2d.getDeviceConfiguration().createCompatibleImage(cutWidth, cutHeight, Transparency.TRANSLUCENT);
cutG2d = bufferedImage.createGraphics();
// 消除锯齿
cutG2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
cutG2d.drawImage(cutImage, 0, 0, null);
cutG2d.dispose();
// 通过切图绘制缩小后的瓦片图
if(destWidth != 0 && destHeight != 0){
/*
* java提供了4个缩放的微调选项
* image.SCALE_SMOOTH //平滑优先(图片质量好、切割速度慢)
* image.SCALE_FAST //速度优先(图片质量差、切割速度快)
* image.SCALE_AREA_AVERAGING //区域均值(图片质量好、切割速度慢)
* image.SCALE_REPLICATE //像素复制型缩放(图片质量差、切割速度快)
* image.SCALE_DEFAULT //默认缩放模式(图片质量差、切割速度快)
*/
Image destImage = bufferedImage.getScaledInstance(destWidth, destHeight, Image.SCALE_SMOOTH);
bufferedImage = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D destG2d = bufferedImage.createGraphics();
// 设置背景全透明
// 返回 BufferedImage支持指定透明度,与此相适应的数据布局和颜色模型 GraphicsConfiguration。
bufferedImage = destG2d.getDeviceConfiguration().createCompatibleImage(destWidth, destHeight, Transparency.TRANSLUCENT);
destG2d = bufferedImage.createGraphics();
// 消除锯齿
destG2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
destG2d.drawImage(destImage, 0, 0, null);
destG2d.dispose();
}
// 输出为文件
File file = new File(toFilePath + "/" + z + "/" + z + "-" + (j + tileNumberX) + "-" + (i + tileNumberY) + ".png");
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
ImageIO.write(bufferedImage, "PNG", file);
}
}
}
}
// 瓦片参数值重置
tileNumberY *= 2;
tileNumberX *= 2;
// 切片参宽高度重置
cutWidth = (int) Math.ceil(1.0*srcWidth / (1 << zz));
cutHeight = (int) Math.ceil(1.0*srcHeight / (1 << zz));
zz++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 裁剪、拉伸、压缩PNG图片工具类
* @param fromFile 源文件
* @param toFile 裁剪后的文件
* @param outputWidth 裁剪宽度px(小于等于0时使用原图尺寸)
* @param outputHeight 裁剪高度px(小于等于0时使用原图尺寸)
* @param offsetTop 上轴偏移量(左上角为基准)
* @param offsetRight 右轴偏移量(左上角为基准)
* @param offsetBottom 下轴偏移量(左上角为基准)
* @param offsetLeft 左轴偏移量(左上角为基准)
* @param proportion 是否是等比缩放
*/
public static void resizePng(File fromFile, File toFile, int outputWidth, int outputHeight, int offsetTop, int offsetRight, int offsetBottom, int offsetLeft, boolean proportion) {
try {
ImageIO.setUseCache(false);
BufferedImage bi = ImageIO.read(fromFile);
if (outputWidth <= 0) {
outputWidth = bi.getWidth();
}
if (outputHeight <= 0) {
outputHeight = bi.getHeight();
}
int newWidth;
int newHeight;
// 判断是否是等比缩放
if (proportion) {
// 为等比缩放计算输出的图片宽度及高度
double rate1 = ((double) bi.getWidth(null)) / (double) outputWidth + 0.1;
double rate2 = ((double) bi.getHeight(null)) / (double) outputHeight + 0.1;
// 根据缩放比率大的进行缩放控制
double rate = rate1 < rate2 ? rate1 : rate2;
newWidth = (int) (((double) bi.getWidth(null)) / rate);
newHeight = (int) (((double) bi.getHeight(null)) / rate);
} else {
// 输出的图片宽度
newWidth = outputWidth;
// 输出的图片高度
newHeight = outputHeight;
}
/*
* java提供了4个缩放的微调选项
* image.SCALE_SMOOTH //平滑优先(图片质量好、切割速度慢)
* image.SCALE_FAST //速度优先(图片质量差、切割速度快)
* image.SCALE_AREA_AVERAGING //区域均值(图片质量好、切割速度慢)
* image.SCALE_REPLICATE //像素复制型缩放(图片质量差、切割速度快)
* image.SCALE_DEFAULT //默认缩放模式(图片质量差、切割速度快)
*/
Image image = bi.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
BufferedImage bufferedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedImage.createGraphics();
// 设置背景全透明
// 返回 BufferedImage支持指定透明度,与此相适应的数据布局和颜色模型 GraphicsConfiguration。
System.err.println("【切图】新合成图大小:"+(newWidth + offsetLeft + offsetRight)+"x"+(newHeight + offsetTop + offsetBottom));
bufferedImage = g2d.getDeviceConfiguration().createCompatibleImage(newWidth + offsetLeft + offsetRight, newHeight + offsetTop + offsetBottom, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = bufferedImage.createGraphics();
// 消除锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawImage(image, offsetLeft, offsetTop, null);
g2d.dispose();
if(!toFile.getParentFile().exists()){
toFile.getParentFile().mkdirs();
}
ImageIO.write(bufferedImage, "PNG", toFile);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据瓦片参数和偏移量合成切割源图
* @param fromFile 源文件
* @param toFile 目标文件
* @param offsetTop 上偏移量
* @param offsetRight 右偏移量
* @param offsetBottom 下偏移量
* @param offsetLeft 左偏移量
* @param cellPixel 瓦片图像素
* @param tileLevel 源图理论层级
*/
public static Map<String,Object> composeCutSrcPng(File fromFile, File toFile, double offsetTop, double offsetRight, double offsetBottom, double offsetLeft, int cellPixel, int tileLevel) {
Map<String, Object> map = new HashMap<String, Object>();
// 图片占有像素
double posseWidth = cellPixel-offsetLeft-offsetRight;
double posseHeight = cellPixel-offsetTop-offsetBottom;
try {
// 计算原图实际偏移尺寸
ImageIO.setUseCache(false);
BufferedImage bufferedImage = ImageIO.read(fromFile);
// 源图尺寸
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
// 拉伸后图片的尺寸
int tensileWidth = width;
int tensileHeight = height;
if(width > height){
tensileHeight = (int) Math.round(width*posseHeight/posseWidth);
// 缩放
if(tensileHeight < height){
tensileHeight = height;
tensileWidth = (int) Math.round(height*(posseWidth)/(posseHeight));
}
}else{
tensileWidth = (int) Math.round(height*(posseWidth)/(posseHeight));
// 缩放
if(tensileWidth < width){
tensileWidth = width;
tensileHeight = (int) Math.round(width*posseHeight/posseWidth);
}
}
// 计算拉伸后图片的实际偏移量
double realTop = offsetTop*tensileHeight/posseHeight;
double realRight = offsetRight*tensileWidth/posseWidth;
double realBottom = offsetBottom*tensileHeight/posseHeight;
double realLeft = offsetLeft*tensileWidth/posseWidth;
// 自动生成合理尺寸的原图
int widthTotal = (int) Math.round(tensileWidth + realRight + realLeft);
int heightTotal = (int) Math.round(tensileHeight + realTop + realBottom);
// 自动优化图片为正方形,且生成对应tileLevel层级的标准尺寸图片,减小切图误差
// 切图的标准尺寸
int standardSize = 0;
// 对应层级横向(或纵向)切割图片数
int zoom = 1 << (18-tileLevel);
// 计算标准尺寸切割图实际偏移尺寸
if(widthTotal >= heightTotal){
standardSize = (int) (Math.ceil(1.0*widthTotal/zoom)*zoom);
}else if(widthTotal < heightTotal){
standardSize = (int) (Math.ceil(1.0*heightTotal/zoom)*zoom);
}
int widthPadding = (standardSize - widthTotal);
int heightPadding = (standardSize - heightTotal);
if(widthPadding != 0){
int rightNum = (int) (widthPadding*realRight/widthTotal);
int leftNum = (int) (widthPadding*realLeft/widthTotal);
realRight += rightNum;
realLeft += leftNum;
tensileWidth += widthPadding - rightNum - leftNum;
}
if(heightPadding != 0){
int topNum = (int) (heightPadding*realTop/heightTotal);
int bottomNum = (int) (heightPadding*realBottom/heightTotal);
realTop += topNum;
realBottom += bottomNum;
tensileHeight += heightPadding - topNum - bottomNum;
}
// 生成拉伸后的图片
resizePng(fromFile,toFile,tensileWidth,tensileHeight,0,0,0,0,false);
// 返回偏移参数信息
map.put("standardSize",standardSize);
map.put("tensileWidth",tensileWidth);
map.put("tensileHeight",tensileHeight);
map.put("realTop",(int)Math.round(realTop));
map.put("realRight",(int)Math.round(realRight));
map.put("realBottom",(int)Math.round(realBottom));
map.put("realLeft",(int)Math.round(realLeft));
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
/**
* 谷歌下转换经纬度对应的层行列
*
* @param lon 经度
* @param lat 维度
* @param zoom 在第zoom层进行转换
* @return
*/
public static int[] LonLatToXYZ(double lon, double lat, int zoom) {
double n = Math.pow(2, zoom);
double tileX = ((lon + 180) / 360) * n;
double tileY = (1 - (Math.log(Math.tan(Math.toRadians(lat)) + (1 / Math.cos(Math.toRadians(lat)))) / Math.PI)) / 2 * n;
int[] xy = new int[2];
xy[0] = (int) Math.floor(tileX);
xy[1] = (int) Math.floor(tileY);
return xy;
}
/**
* 瓦片参数转经纬度
*
* @param x 经度瓦片参数
* @param y 纬度瓦片参数
* @param z z轴层级
* @return
*/
public static double[] XYZtoLonlat(int x, int y, int z) {
double n = Math.pow(2, z);
double lon = x / n * 360.0 - 180.0;
double lat = Math.atan(Math.sinh(Math.PI * (1 - 2 * y / n)));
lat = lat * 180.0 / Math.PI;
double[] lonlat = new double[2];
lonlat[0] = lon;
lonlat[1] = lat;
return lonlat;
}
/**
* 经度到像素X值
* @param lon 经度
* @param level z轴层级
* @return
*/
static double lonToPixel(double lon, int level) {
return (lon + 180) * (256 << level) / 360;
}
/**
* 像素X到经度
* @param pixelX 像素X
* @param level z轴层级
* @return
*/
static double pixelToLon(double pixelX, int level) {
return pixelX * 360 / (256 << level) - 180;
}
/**
* 纬度到像素Y
* @param lat 纬度
* @param level z轴层级
* @return
*/
static double latToPixel(double lat, int level) {
double siny = Math.sin(lat * Math.PI / 180);
double y = Math.log((1 + siny) / (1 - siny));
return (128 << level) * (1 - y / (2 * Math.PI));
}
/**
* 像素Y到纬度
* @param pixelY 像素Y
* @param level z轴层级
* @return
*/
static double pixelToLat(double pixelY, int level) {
// 当然还可以更精确
final double E = 2.7128;
double y = 2 * Math.PI * (1 - pixelY / (128 << level));
double z = power(E, y);
double siny = (z - 1) / (z + 1);
return Math.asin(siny) * 180 / Math.PI;
}
/**
* 根据层级确定图片上右下左的偏移量
* @param lonStar 开始经度
* @param latStar 开始纬度
* @param lonEnd 结束经度
* @param latEnd 结束纬度
* @param cellPixel 瓦片单元格大小px(默认256)
* @return
*/
public static Map<String, Object> getOffset(double lonStar, double latStar, double lonEnd, double latEnd, int cellPixel) {
int[] star = {};
int[] end = {};
// 切图理论层级
int level = 18;
do {
star = LonLatToXYZ(lonStar, latStar, level);
end = LonLatToXYZ(lonEnd, latEnd, level);
level--;
} while (star[0] != end[0] || star[1] != end[1]);
Map<String, Object> map = new HashMap<String, Object>();
level += 1;
int[] tileNumber = LonLatToXYZ(lonStar, latStar, level);
int tileNumberX = tileNumber[0];
int tileNumberY = tileNumber[1];
map.put("level", level);
map.put("top", latToPixel(latStar, level) - (cellPixel * tileNumberY));
map.put("right", cellPixel * (tileNumberX + 1) - lonToPixel(lonEnd, level));
map.put("bottom", cellPixel * (tileNumberY + 1) - latToPixel(latEnd, level));
map.put("left", lonToPixel(lonStar, level) - (cellPixel * tileNumberX));
map.put("tileNumberX", tileNumber[0]);
map.put("tileNumberY", tileNumber[1]);
return map;
}
/**
* 判断图片是否为纯透明背景图
* @param bufImg 图片流
* @param widthStart 判断宽的起点
* @param heightStart 判断高的起点
* @param widthEnd 判断宽的终点
* @param heightEnd 判断高的终点
* @return
*/
public static boolean isTransparent(BufferedImage bufImg, int widthStart, int heightStart, int widthEnd, int heightEnd){
widthStart = widthStart<=0?0:widthStart;
heightStart = heightStart<=0?0:heightStart;
widthEnd = widthEnd<=0?bufImg.getWidth():widthEnd;
heightEnd = heightEnd<=0?bufImg.getHeight():heightEnd;
for (int i = widthStart; i < widthEnd; i++) {
for (int j = heightStart; j < heightEnd; j++) {
int pixel = bufImg.getRGB(i, j);
if (pixel >> 24 != 0) {
return false;
}
}
}
return true;
}
/**
* 获取当前文件夹下的所有文件
* @param path
* @param allfilelist
* @return
*/
public static ArrayList<File> getallfile(File path, ArrayList<File> allfilelist) {
// 路径存在
if (path.exists()) {
// 判断文件是否是文件夹,如果是,开始递归
if (path.isDirectory()) {
File f[] = path.listFiles();
for (File file2 : f) {
getallfile(file2, allfilelist);
}
} else {
allfilelist.add(path);
}
}
return allfilelist;
}
/**
* 删除所有文件
* @param path
*/
public static void deleteAll(File path) {
// 路径存在
if (!path.exists()){
return;
}
// 是文件
if (path.isFile()){
path.delete();
return;
}
File[] files = path.listFiles();
for (int i = 0; i < files.length; i++) {
deleteAll(files[i]);
}
path.delete();
}
public static void main(String[] args) throws IOException {
String group = "djy-test";
String tempFilePath = "C:/myFile/pictureCut";
// String toFilePath = tempFilePath + "/" + group+ "/djy-big.png";
// String drawingMapImg = "https://htwz-wzy.obs.cn-north-4.myhuaweicloud.com:443/htwz-wzy/4199caf6-7d76-442f-b89d-60fc38d36fb3%E9%83%BD%E6%B1%9F%E5%A0%B0-huqingqing.png";
// URL url = new URL(drawingMapImg);
// FileUtils.copyURLToFile(url, new File(toFilePath));
// File fromFile = new File(toFilePath);
File fromFile = new File(tempFilePath + "/djy-src.png");
// 切图开始层级
int cutStartLevel = 11;
int cutEndLevel = 18;
// 首坐标
double lon1 = 103.604822;
double lat1 = 31.00991;
// 尾坐标
double lon2 = 103.618107;
double lat2 = 30.993703;
// // 拉伸坐标
// // 首坐标
// double lon1 = 103.60408;
// double lat1 = 31.012203;
// // 尾坐标
// double lon2 = 103.62408;
// double lat2 = 30.992203;
Date currDate = new Date();
tencentPictureCut(fromFile,tempFilePath, group, lon1, lat1, lon2, lat2, cutStartLevel, cutEndLevel);
System.out.println("总时间:" + ((new Date()).getTime() - currDate.getTime()) / 1000);
// 源文件删除
// fromFile.delete();
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。