├── .gitignore ├── core ├── lib │ ├── gdal-centos7x64.jar │ ├── gdal-win64.jar │ └── gdal.jar ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── wowtools │ └── fasttiff │ └── core │ ├── Lrc2Bbox.java │ └── TileableTiff.java ├── pom.xml └── web ├── pom.xml └── src └── main ├── java └── org │ └── wowtools │ └── fasttiff │ └── web │ ├── StartUpFastTiff.java │ ├── controller │ ├── TileService.java │ └── TileServiceController.java │ ├── service │ ├── MapServerMeta.java │ └── TileConfig.java │ └── util │ └── PngEncoder.java └── resources ├── application.yml ├── libgdalconstjni.so ├── libgdaljni.so ├── libgnmjni.so ├── libogrjni.so ├── libosrjni.so └── tile.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .settings/ 3 | .project 4 | .classpath 5 | *.iml 6 | *.class 7 | target/ 8 | nb-configuration.xml 9 | nb_jr_remoting.cfg 10 | rebel.xml -------------------------------------------------------------------------------- /core/lib/gdal-centos7x64.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmiao/fasttiff/d6f9ce8f6d8ad2be8c12a59a9c99456096f2e02d/core/lib/gdal-centos7x64.jar -------------------------------------------------------------------------------- /core/lib/gdal-win64.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmiao/fasttiff/d6f9ce8f6d8ad2be8c12a59a9c99456096f2e02d/core/lib/gdal-win64.jar -------------------------------------------------------------------------------- /core/lib/gdal.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmiao/fasttiff/d6f9ce8f6d8ad2be8c12a59a9c99456096f2e02d/core/lib/gdal.jar -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | fasttiff 7 | org.wowtools.fasttiff 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | org.wowtools.fasttiff 13 | core 14 | 15 | 16 | org.gdal 17 | gdal 18 | 2.2.3 19 | system 20 | 21 | ${project.basedir}/lib/gdal-win64.jar 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 3.8.1 31 | 32 | 11 33 | 11 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /core/src/main/java/org/wowtools/fasttiff/core/Lrc2Bbox.java: -------------------------------------------------------------------------------- 1 | package org.wowtools.fasttiff.core; 2 | 3 | /** 4 | * 将层级行列转换为3857坐标系下的bbox 5 | * 6 | * @author liuyu 7 | * @date 2018/2/23 8 | */ 9 | public class Lrc2Bbox { 10 | 11 | private static final int[] pow = new int[30]; 12 | 13 | static { 14 | int n = 1; 15 | for (int i = 0; i < pow.length; i++) { 16 | pow[i] = n; 17 | n = n * 2; 18 | } 19 | } 20 | 21 | private static final double E_r = 20037508.342787001D; 22 | 23 | /** 24 | * 将层级行列转换为3857坐标系下的bbox 25 | * 26 | * @param level 27 | * @param row 28 | * @param column 29 | * @return 30 | */ 31 | public static double[] toBbox3857(int level, int row, int column) { 32 | int m = pow[level]; 33 | return new double[]{-E_r + E_r * 2.0D / m * column, E_r - E_r * 2.0D / m * (row + 1), 34 | -E_r + E_r * 2.0D / m * (column + 1), E_r - E_r * 2.0D / m * row}; 35 | } 36 | 37 | /** 38 | * 将3857坐标系下的x、y转为行列号 39 | * 40 | * @param level 41 | * @param x 42 | * @param y 43 | * @return 44 | */ 45 | public static int[] xy2Lrc(int level, double x, double y) { 46 | int m = pow[level]; 47 | int column1 = (int) ((E_r + x) / (E_r * 2.0D / m)); 48 | int row1 = (int) ((E_r - y) / (E_r * 2.0D / m)); 49 | return new int[]{row1, column1}; 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /core/src/main/java/org/wowtools/fasttiff/core/TileableTiff.java: -------------------------------------------------------------------------------- 1 | package org.wowtools.fasttiff.core; 2 | 3 | import org.gdal.gdal.Band; 4 | import org.gdal.gdal.Dataset; 5 | import org.gdal.gdal.gdal; 6 | import org.gdal.gdalconst.gdalconstConstants; 7 | 8 | import java.awt.*; 9 | import java.awt.image.BufferedImage; 10 | 11 | /** 12 | * 可切片的tiff 13 | * 14 | * @author liuyu 15 | * @date 2018/2/23 16 | */ 17 | public class TileableTiff { 18 | static { 19 | gdal.AllRegister(); 20 | } 21 | 22 | private final String tiffPath; 23 | private final int hashCode; 24 | 25 | private final Dataset dataset; 26 | 27 | private final Band bandA; 28 | private final Band bandR; 29 | private final Band bandG; 30 | private final Band bandB; 31 | 32 | private final double[] geoTransform;//获取tiff左上角坐标、每个像素宽度等 33 | 34 | private final int iXSize;//tiff宽(像素) 35 | private final int iYSize;//tiff高(像素) 36 | 37 | /** 38 | * @param tiffPath tiff文件绝对路径,如 e:/test.t.ff 39 | */ 40 | public TileableTiff(String tiffPath) { 41 | dataset = gdal.Open(tiffPath, gdalconstConstants.GA_ReadOnly); 42 | if (dataset == null) { 43 | throw new RuntimeException("读取tiff文件异常:\n" + tiffPath + "\n" + gdal.GetLastErrorMsg()); 44 | } 45 | this.tiffPath = tiffPath; 46 | hashCode = tiffPath.hashCode(); 47 | Band[] argb = getArgbBands(dataset); 48 | bandA = argb[0]; 49 | bandR = argb[1]; 50 | bandG = argb[2]; 51 | bandB = argb[3]; 52 | 53 | geoTransform = dataset.GetGeoTransform(); 54 | 55 | iXSize = dataset.getRasterXSize(); 56 | iYSize = dataset.getRasterYSize(); 57 | } 58 | 59 | /** 60 | * 按照google标准的层级按规范,从tiff文件上复制出一块切片,并生成一个BufferedImage对象 61 | * 62 | * @param level 层级 63 | * @param row 行号 64 | * @param column 列号 65 | * @param tileWidth 生成的BufferedImage的宽度 66 | * @param tileHeight 生成的BufferedImage的高度 67 | * @return 68 | */ 69 | public BufferedImage getTile(int level, int row, int column, int tileWidth, int tileHeight) { 70 | /** 1、将层级行列号转为与tiff相同坐标系的bbox **/ 71 | double[] bbox3857 = Lrc2Bbox.toBbox3857(level, row, column); 72 | //TODO dataset.GetProjection()获取tiff坐标系,并转为3857,这里直接默认了tiff坐标系为4326 73 | double[] low = mercator2lonLat(bbox3857[0], bbox3857[1]);//左下角 74 | double[] up = mercator2lonLat(bbox3857[2], bbox3857[3]);//右上角 75 | double xmin = low[0], ymin = low[1], xmax = up[0], ymax = up[1]; 76 | 77 | /** 2、计算getTileRgb方法所需参数 **/ 78 | //TODO 没有考虑geoTransform的旋转参数 79 | double x0 = geoTransform[0], dx = geoTransform[1], y0 = geoTransform[3], dy = geoTransform[5]; 80 | int startX = (int) ((xmin - x0) / dx); 81 | int startY = (int) ((ymax - y0) / dy); 82 | int endX = (int) ((xmax - x0) / dx + 0.5); 83 | int endY = (int) ((ymin - y0) / dy + 0.5); 84 | if (startX > iXSize || startY > iYSize || endX < 0 || endY < 0) { 85 | return null;//不在范围内,直接返回null 86 | } 87 | int tiffWidth = endX - startX; 88 | int tiffHeight = endY - startY; 89 | if (tiffWidth > iXSize || tiffHeight > iYSize) { 90 | return null;//切片比tiff还大,出于效率考虑就不处理了 91 | } 92 | /** 3、得到rgbArr并转换为img**/ 93 | BufferedImage img = new BufferedImage(tileWidth, tileHeight, Transparency.TRANSLUCENT); 94 | int drawStartX = 0, drawStartY = 0; 95 | if (startX < 0) { 96 | startX = 0; 97 | double w0 = tiffWidth; 98 | tiffWidth = endX; 99 | int tileWidth0 = tileWidth; 100 | tileWidth = (int) (tiffWidth / w0 * tileWidth); 101 | drawStartX = tileWidth0 - tileWidth; 102 | } else if (endX > iXSize) { 103 | endX = iXSize - 1; 104 | double w0 = tiffWidth; 105 | tiffWidth = endX - startX; 106 | tileWidth = (int) (tiffWidth / w0 * tileWidth); 107 | } 108 | if (startY < 0) { 109 | startY = 0; 110 | double h0 = tiffHeight; 111 | tiffHeight = endY; 112 | int tileHeight0 = tileHeight; 113 | tileHeight = (int) (tiffHeight / h0 * tileHeight); 114 | drawStartY = tileHeight0 - tileHeight; 115 | } else if (endY > iYSize) { 116 | endY = iYSize - 1; 117 | double h0 = tiffHeight; 118 | tiffHeight = endY - startY; 119 | tileHeight = (int) (tiffHeight / h0 * tileHeight); 120 | } 121 | if (tiffWidth > iYSize) { 122 | System.out.println(111); 123 | } 124 | int[] rgbArr = getTileRgb(startX, startY, tiffWidth, tiffHeight, tileWidth, tileHeight); 125 | img.setRGB(drawStartX, drawStartY, tileWidth, tileHeight, rgbArr, 0, tileWidth); 126 | return img; 127 | } 128 | 129 | private double[] mercator2lonLat(double mercatorX, double mercatorY) { 130 | double[] xy = new double[2]; 131 | double x = mercatorX / 20037508.34 * 180; 132 | double y = mercatorY / 20037508.34 * 180; 133 | y = 180 / Math.PI * (2 * Math.atan(Math.exp(y * Math.PI / 180)) - Math.PI / 2); 134 | xy[0] = x; 135 | xy[1] = y; 136 | return xy; 137 | } 138 | 139 | /** 140 | * 从tiff文件上复制出一块切片,并生成一个BufferedImage对象的rgb数组 141 | * 142 | * @param startX 在tiff上取切片的左上角x坐标 143 | * @param startY 在tiff上取切片的左上角y坐标 144 | * @param tiffWidth 在tiff上取切片的宽度(像素) 145 | * @param tiffHeight 在tiff上取切片的高度(像素) 146 | * @param tileWidth 生成的BufferedImage的宽度 147 | * @param tileHeight 生成的BufferedImage的高度 148 | * @return 149 | */ 150 | private int[] getTileRgb(int startX, int startY, int tiffWidth, int tiffHeight, int tileWidth, int tileHeight) { 151 | int size = tileWidth * tileHeight; 152 | int[] rgbArr = new int[size]; 153 | //取出rgba分量,再合并成rgb int放入数组 154 | int[] rasterA = new int[size]; 155 | int[] rasterR = new int[size]; 156 | int[] rasterG = new int[size]; 157 | int[] rasterB = new int[size]; 158 | synchronized (this) { 159 | //异步操作会引起jni error 160 | bandA.ReadRaster(startX, startY, tiffWidth, tiffHeight, tileWidth, tileHeight, gdalconstConstants.GDT_Int32, rasterA); 161 | bandR.ReadRaster(startX, startY, tiffWidth, tiffHeight, tileWidth, tileHeight, gdalconstConstants.GDT_Int32, rasterR); 162 | bandG.ReadRaster(startX, startY, tiffWidth, tiffHeight, tileWidth, tileHeight, gdalconstConstants.GDT_Int32, rasterG); 163 | bandB.ReadRaster(startX, startY, tiffWidth, tiffHeight, tileWidth, tileHeight, gdalconstConstants.GDT_Int32, rasterB); 164 | } 165 | 166 | for (int i = 0; i < size; i++) { 167 | int v = (rasterR[i]/4 << 16) + (rasterG[i]/4 << 8) + rasterB[i]/4; 168 | //去除黑边 169 | if (0 == v) { 170 | rgbArr[i] = 0xffffff; 171 | } else { 172 | rgbArr[i] = (255 << 24) + v; 173 | } 174 | } 175 | 176 | return rgbArr; 177 | } 178 | 179 | /** 180 | * 获取tiff的rgba band,默认认为tiff的四个band依次为r,g,b,a,若tiff格式不同,请覆写此方法 181 | * 182 | * @param hDataset 183 | * @return 184 | */ 185 | protected Band[] getArgbBands(Dataset hDataset) { 186 | return new Band[]{ 187 | hDataset.GetRasterBand(4), 188 | hDataset.GetRasterBand(1), 189 | hDataset.GetRasterBand(2), 190 | hDataset.GetRasterBand(3) 191 | }; 192 | } 193 | 194 | public double[] getGeoTransform() { 195 | return geoTransform; 196 | } 197 | 198 | @Override 199 | public boolean equals(Object obj) { 200 | if (obj instanceof TileableTiff) { 201 | return tiffPath.equals(((TileableTiff) obj).tiffPath); 202 | } 203 | return false; 204 | } 205 | 206 | @Override 207 | public int hashCode() { 208 | return hashCode; 209 | } 210 | 211 | @Override 212 | protected void finalize() throws Throwable { 213 | bandA.delete(); 214 | bandR.delete(); 215 | bandG.delete(); 216 | bandB.delete(); 217 | dataset.delete(); 218 | super.finalize(); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.wowtools.fasttiff 8 | fasttiff 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | core 13 | web 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-parent 18 | 1.5.2.RELEASE 19 | 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-compiler-plugin 26 | 3.8.1 27 | 28 | 11 29 | 11 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | fasttiff 7 | org.wowtools.fasttiff 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | UTF-8 13 | 4.12 14 | 15 | org.wowtools.fasttiff 16 | web 17 | 18 | 19 | org.wowtools.fasttiff 20 | core 21 | 1.0-SNAPSHOT 22 | 23 | 24 | org.wowtools 25 | catframe-common 26 | 1.4STABLE 27 | 28 | 29 | org.json 30 | json 31 | 20160212 32 | 33 | 34 | junit 35 | junit 36 | ${junit.version} 37 | test 38 | 39 | 40 | 41 | org.springframework.cloud 42 | spring-cloud-starter-eureka 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-web 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-test 52 | test 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.springframework.cloud 60 | spring-cloud-dependencies 61 | Dalston.RC1 62 | pom 63 | import 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-maven-plugin 73 | 74 | true 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-compiler-plugin 80 | 3.8.1 81 | 82 | 11 83 | 11 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | spring-milestones 92 | Spring Milestones 93 | https://repo.spring.io/milestone 94 | 95 | false 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /web/src/main/java/org/wowtools/fasttiff/web/StartUpFastTiff.java: -------------------------------------------------------------------------------- 1 | package org.wowtools.fasttiff.web; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | 7 | /** 8 | * @author liuyu 9 | * @date 2018/2/24 10 | */ 11 | @SpringBootApplication 12 | @EnableEurekaClient 13 | public class StartUpFastTiff { 14 | public static void main(String[] args) { 15 | SpringApplication.run(StartUpFastTiff.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /web/src/main/java/org/wowtools/fasttiff/web/controller/TileService.java: -------------------------------------------------------------------------------- 1 | package org.wowtools.fasttiff.web.controller; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.core.annotation.Order; 5 | import org.springframework.stereotype.Service; 6 | import org.wowtools.common.utils.AsyncTaskUtil; 7 | import org.wowtools.fasttiff.core.TileableTiff; 8 | import org.wowtools.fasttiff.web.service.TileConfig; 9 | 10 | import java.awt.*; 11 | import java.awt.image.BufferedImage; 12 | import java.io.File; 13 | import java.util.ArrayList; 14 | import java.util.LinkedList; 15 | import java.util.List; 16 | import java.util.Stack; 17 | import java.util.concurrent.Callable; 18 | import java.util.concurrent.atomic.AtomicInteger; 19 | 20 | /** 21 | * 切片服务,负责管理TileableTiff,并提供对应切片 22 | * 23 | * @author liuyu 24 | * @date 2018/2/26 25 | */ 26 | @Service 27 | @Order(value = 2) 28 | public class TileService implements CommandLineRunner { 29 | private final AtomicInteger _idx = new AtomicInteger(0);//用于均衡地选取TileableTiff 30 | private final int tileWidth = 256; 31 | 32 | private TileableTiff[][] allTiff; 33 | 34 | /** 35 | * 街区所有tiff,合并为一个切片图片 36 | * 37 | * @param level 38 | * @param row 39 | * @param col 40 | * @return 41 | */ 42 | public BufferedImage getTile(int level, int row, int col) { 43 | int idx = Math.abs(_idx.addAndGet(1)); 44 | 45 | List> tasks = new ArrayList<>(allTiff.length); 46 | for (TileableTiff[] tileableTiffs : allTiff) { 47 | TileableTiff tileableTiff = tileableTiffs[idx % tileableTiffs.length]; 48 | tasks.add(() -> { 49 | try { 50 | return tileableTiff.getTile(level, row, col, tileWidth, tileWidth); 51 | } catch (Exception e) { 52 | return null; 53 | } 54 | }); 55 | } 56 | ArrayList subTiles = AsyncTaskUtil.executeAsyncTasksAndReturn(tasks); 57 | BufferedImage res = new BufferedImage(tileWidth, tileWidth, Transparency.TRANSLUCENT); 58 | Graphics g = res.getGraphics(); 59 | for (BufferedImage subTile : subTiles) { 60 | if (null != subTile) { 61 | g.drawImage(subTile, 0, 0, null); 62 | } 63 | } 64 | g.dispose(); 65 | return res; 66 | } 67 | 68 | @Override 69 | public void run(String... args) throws Exception { 70 | String tiffRoot = TileConfig.install.getTiffRoot(); 71 | int coreSize = TileConfig.install.getCoreSize(); 72 | File root = new File(tiffRoot); 73 | Stack stack = new Stack<>(); 74 | stack.push(root); 75 | LinkedList list = new LinkedList<>(); 76 | while (!stack.empty()) { 77 | File f = stack.pop(); 78 | if (f.isDirectory()) { 79 | for (File sub : f.listFiles()) { 80 | stack.push(sub); 81 | } 82 | } else { 83 | String fileName = f.getName(); 84 | fileName = fileName.substring(fileName.lastIndexOf(".") + 1); 85 | if ("tif".equals(fileName) || "tiff".equals(fileName)) { 86 | String path = f.getPath(); 87 | try { 88 | TileableTiff[] tileableTiffs = new TileableTiff[coreSize]; 89 | for (int i = 0; i < coreSize; i++) { 90 | tileableTiffs[i] = new TileableTiff(path); 91 | } 92 | list.add(tileableTiffs); 93 | System.out.println("加载tiff文件完成:" + path); 94 | } catch (Exception e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | } 99 | } 100 | allTiff = new TileableTiff[list.size()][]; 101 | list.toArray(allTiff); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /web/src/main/java/org/wowtools/fasttiff/web/controller/TileServiceController.java: -------------------------------------------------------------------------------- 1 | package org.wowtools.fasttiff.web.controller; 2 | 3 | import org.json.JSONObject; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import org.wowtools.fasttiff.core.TileableTiff; 11 | import org.wowtools.fasttiff.web.service.MapServerMeta; 12 | import org.wowtools.fasttiff.web.util.PngEncoder; 13 | 14 | import javax.imageio.ImageIO; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.awt.image.BufferedImage; 17 | import java.io.IOException; 18 | import java.io.OutputStream; 19 | import java.util.Random; 20 | 21 | /** 22 | * 切片服务Controller 23 | * 24 | * @author liuyu 25 | * @date 2018/2/24 26 | */ 27 | @RestController() 28 | @RequestMapping("/tiled") 29 | public class TileServiceController { 30 | 31 | @Autowired 32 | private TileService tileService; 33 | 34 | @RequestMapping({"/{layer}"}) 35 | public String getilemetainfo(@PathVariable("layer") String layer, @RequestParam("f") String form, 36 | HttpServletResponse response) { 37 | MapServerMeta meta = new MapServerMeta(layer); 38 | return meta.getMapServerMetainfo().toString(); 39 | } 40 | 41 | 42 | @RequestMapping({"/{layer}/tile/{level}/{row}/{col}"}) 43 | public void getile(@PathVariable("layer") String layer, @PathVariable("level") int level, @PathVariable("row") int row, @PathVariable("col") int col, HttpServletResponse response) { 44 | /* 允许跨域的主机地址 */ 45 | response.setHeader("Access-Control-Allow-Origin", "*"); 46 | /* 允许跨域的请求方法GET, POST, HEAD 等 */ 47 | response.setHeader("Access-Control-Allow-Methods", "*"); 48 | /* 重新预检验跨域的缓存时间 (s) */ 49 | response.setHeader("Access-Control-Max-Age", "3600"); 50 | /* 允许跨域的请求头 */ 51 | response.setHeader("Access-Control-Allow-Headers", "*"); 52 | /* 是否携带cookie */ 53 | response.setHeader("Access-Control-Allow-Credentials", "true"); 54 | 55 | BufferedImage img = tileService.getTile(level,row,col); 56 | response.setContentType("image/png"); 57 | OutputStream os = null; 58 | try { 59 | os = response.getOutputStream(); 60 | ImageIO.write(img,"png",os); 61 | //这种写法性能较高,但图片压缩率低,给客户端压力打 62 | // byte[] bt = new PngEncoder(img,true).pngEncode(); 63 | // os.write(bt); 64 | os.flush(); 65 | } catch (Exception e) { 66 | throw new RuntimeException(e); 67 | }finally{ 68 | if(null!=os){ 69 | try { 70 | os.close(); 71 | } catch (IOException e) { 72 | } 73 | } 74 | 75 | } 76 | } 77 | 78 | @RequestMapping({"/{layer}/tilemap/{level}/{row}/{col}/{width}/{height}"}) 79 | public String getileN(@PathVariable("layer") String layer, @PathVariable("level") int level, @PathVariable("row") int row, 80 | @PathVariable("col") int col, @PathVariable("width") int w, @PathVariable("height") int h, 81 | HttpServletResponse response) { 82 | JSONObject jo = new JSONObject(); 83 | jo.put("valid", true); 84 | JSONObject joLocation = new JSONObject(); 85 | joLocation.put("left", col); 86 | joLocation.put("top", row); 87 | joLocation.put("width", w); 88 | joLocation.put("height", h); 89 | int n = w * h; 90 | int[] arr = new int[n]; 91 | for (int i = 0; i < n; i++) { 92 | arr[i] = 1; 93 | } 94 | jo.put("location", joLocation); 95 | jo.put("data", arr); 96 | return jo.toString(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /web/src/main/java/org/wowtools/fasttiff/web/service/MapServerMeta.java: -------------------------------------------------------------------------------- 1 | package org.wowtools.fasttiff.web.service; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONObject; 5 | 6 | public class MapServerMeta { 7 | private static class LayerExtent { 8 | int minLevel; 9 | int maxLevel; 10 | double[] minxy; 11 | double[] maxxy; 12 | } 13 | 14 | 15 | private String layername; 16 | 17 | private int dpi = 96; 18 | 19 | private double[] originlonlat; 20 | private JSONArray lyrs = new JSONArray(); 21 | 22 | private double minScale = 0; 23 | private double maxScale = 0; 24 | private int minLevel; 25 | private int maxLevel; 26 | 27 | double[] minxy; 28 | double[] maxxy; 29 | 30 | private String layerName; 31 | 32 | public MapServerMeta(String layerName) { 33 | this.originlonlat = new double[]{-2.0037508342787E7, 2.0037508342787E7}; 34 | this.layerName = layerName; 35 | minxy = new double[]{10848780.278959094, 2364183.440747929}; 36 | maxxy = new double[]{11841311.259604478, 3462945.6138322023}; 37 | minLevel = 0; 38 | maxLevel = 29; 39 | } 40 | 41 | public JSONObject getMapServerMetainfo() { 42 | 43 | JSONObject data = new JSONObject(); 44 | 45 | data.put("currentVersion", "10.3"); 46 | data.put("serviceDescription", layerName); 47 | data.put("mapName", layerName); 48 | data.put("description", "xxxxxxxxx"); 49 | data.put("copyrightText", "xxxxxxxx"); 50 | data.put("supportsDynamicLayers", false); 51 | 52 | JSONObject spatialReference = new JSONObject(); 53 | spatialReference.put("wkid", 102100); 54 | spatialReference.put("latestWkid", 3857); 55 | data.put("spatialReference", spatialReference); 56 | 57 | data.put("singleFusedMapCache", true); 58 | 59 | data.put("layers", getlayers()); 60 | 61 | data.put("tileInfo", gettileinfo()); 62 | 63 | data.put("initialExtent", getinitialExtent()); 64 | 65 | data.put("fullExtent", getfullExtent()); 66 | 67 | data.put("minScale", getminScale()); 68 | 69 | data.put("maxScale", getmaxScale()); 70 | 71 | data.put("units", getunits()); 72 | 73 | data.put("supportedImageFormatTypes", supportedImageFormatTypes()); 74 | 75 | data.put("documentInfo", getdocumentInfo()); 76 | 77 | data.put("capabilities", "Map,Tilemap"); 78 | 79 | data.put("supportedQueryFormats", "JSON"); 80 | 81 | data.put("exportTilesAllowed", false); 82 | 83 | data.put("maxRecordCount", 1000); 84 | data.put("maxImageHeight", 4096); 85 | data.put("maxImageWidth", 4096); 86 | 87 | return data; 88 | 89 | } 90 | 91 | private JSONObject gettileinfo() { 92 | JSONObject tileInfo = new JSONObject(); 93 | 94 | tileInfo.put("rows", 256); 95 | tileInfo.put("cols", 256); 96 | tileInfo.put("dpi", this.dpi); 97 | tileInfo.put("format", "JPEG"); 98 | tileInfo.put("compressionQuality", 90); 99 | 100 | JSONObject origin = new JSONObject(); 101 | origin.put("x", this.originlonlat[0]); 102 | origin.put("y", this.originlonlat[1]); 103 | tileInfo.put("origin", origin); 104 | 105 | JSONObject spatialReference2 = new JSONObject(); 106 | spatialReference2.put("wkid", 102100); 107 | spatialReference2.put("latestWkid", 3857); 108 | tileInfo.put("spatialReference", spatialReference2); 109 | 110 | tileInfo.put("lods", getlods()); 111 | 112 | return tileInfo; 113 | } 114 | 115 | private JSONArray getlods() { 116 | double resolution = 156543.03392800014; 117 | double scale = 5.91657527591555E8; 118 | 119 | for (int i1 = minLevel; i1 <= maxLevel; i1++) { 120 | JSONObject lod = new JSONObject(); 121 | lod.put("level", i1); 122 | lod.put("resolution", resolution / (Math.pow(2, i1))); 123 | lod.put("scale", scale / (Math.pow(2, i1))); 124 | lyrs.put(lod); 125 | double Scale = scale / (Math.pow(2, i1)); 126 | minScale = minScale > 0 ? Math.max(minScale, Scale) : Scale; 127 | maxScale = maxScale > 0 ? Math.min(maxScale, Scale) : Scale; 128 | } 129 | return lyrs; 130 | } 131 | 132 | private JSONArray getlayers() { 133 | JSONArray lyrs = new JSONArray(); 134 | 135 | JSONObject layerinfo = new JSONObject(); 136 | layerinfo.put("id", 0); 137 | layerinfo.put("name", this.layername); 138 | layerinfo.put("parentLayerId", -1); 139 | layerinfo.put("defaultVisibility", true); 140 | layerinfo.put("subLayerIds", JSONObject.NULL); 141 | layerinfo.put("minScale", 0); 142 | layerinfo.put("maxScale", 0); 143 | lyrs.put(0, layerinfo); 144 | return lyrs; 145 | 146 | } 147 | 148 | private JSONObject getinitialExtent() { 149 | JSONObject data = new JSONObject(); 150 | data.put("xmin", this.minxy[0]); 151 | data.put("ymin", this.minxy[1]); 152 | data.put("xmax", this.maxxy[0]); 153 | data.put("ymax", this.maxxy[1]); 154 | JSONObject spatialReference = new JSONObject(); 155 | spatialReference.put("wkid", 102100); 156 | spatialReference.put("latestWkid", 3857); 157 | data.put("spatialReference", spatialReference); 158 | 159 | return data; 160 | 161 | } 162 | 163 | private JSONObject getfullExtent() { 164 | 165 | JSONObject data = new JSONObject(); 166 | data.put("xmin", this.minxy[0]); 167 | data.put("ymin", this.minxy[1]); 168 | data.put("xmax", this.maxxy[0]); 169 | data.put("ymax", this.maxxy[1]); 170 | 171 | JSONObject spatialReference = new JSONObject(); 172 | spatialReference.put("wkid", 102100); 173 | spatialReference.put("latestWkid", 3857); 174 | data.put("spatialReference", spatialReference); 175 | return data; 176 | 177 | } 178 | 179 | private double getminScale() { 180 | return minScale; 181 | } 182 | 183 | private double getmaxScale() { 184 | return maxScale; 185 | } 186 | 187 | private String getunits() { 188 | return "esriMeters"; 189 | } 190 | 191 | private String supportedImageFormatTypes() { 192 | return "PNG"; 193 | } 194 | 195 | private JSONObject getdocumentInfo() { 196 | JSONObject data = new JSONObject(); 197 | data.put("Author", "xxxxxx"); 198 | data.put("Comments", ""); 199 | data.put("Subject", ""); 200 | data.put("Category", ""); 201 | data.put("AntialiasingMode", "None"); 202 | data.put("TextAntialiasingMode", "Force"); 203 | 204 | return data; 205 | } 206 | 207 | } 208 | -------------------------------------------------------------------------------- /web/src/main/java/org/wowtools/fasttiff/web/service/TileConfig.java: -------------------------------------------------------------------------------- 1 | package org.wowtools.fasttiff.web.service; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.context.annotation.PropertySource; 6 | import org.springframework.core.annotation.Order; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author liuyu 11 | * @date 2018/2/26 12 | */ 13 | @Component 14 | @PropertySource(value = "classpath:/tile.properties",encoding="utf-8") 15 | @Order(value=1) 16 | public class TileConfig implements CommandLineRunner { 17 | public static TileConfig install; 18 | @Value("${tiffRoot}") 19 | private String tiffRoot; 20 | @Value("${coreSize}") 21 | private int coreSize; 22 | 23 | public String getTiffRoot() { 24 | return tiffRoot; 25 | } 26 | 27 | public int getCoreSize() { 28 | return coreSize; 29 | } 30 | 31 | @Override 32 | public void run(String... args) throws Exception { 33 | install = this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /web/src/main/java/org/wowtools/fasttiff/web/util/PngEncoder.java: -------------------------------------------------------------------------------- 1 | package org.wowtools.fasttiff.web.util; 2 | 3 | 4 | import java.awt.Image; 5 | import java.awt.image.ImageObserver; 6 | import java.awt.image.PixelGrabber; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.IOException; 9 | import java.util.zip.CRC32; 10 | import java.util.zip.Deflater; 11 | import java.util.zip.DeflaterOutputStream; 12 | 13 | /** 14 | * PngEncoder takes a Java Image object and creates a byte string which can be saved as a PNG file. 15 | * The Image is presumed to use the DirectColorModel. 16 | * 17 | *

Thanks to Jay Denny at KeyPoint Software 18 | * http://www.keypoint.com/ 19 | * who let me develop this code on company time.

20 | * 21 | *

You may contact me with (probably very-much-needed) improvements, 22 | * comments, and bug fixes at:

23 | * 24 | *

david@catcode.com

25 | * 26 | *

This library is free software; you can redistribute it and/or 27 | * modify it under the terms of the GNU Lesser General Public 28 | * License as published by the Free Software Foundation; either 29 | * version 2.1 of the License, or (at your option) any later version.

30 | * 31 | *

This library is distributed in the hope that it will be useful, 32 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 33 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 34 | * Lesser General Public License for more details.

35 | * 36 | *

You should have received a copy of the GNU Lesser General Public 37 | * License along with this library; if not, write to the Free Software 38 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 39 | * A copy of the GNU LGPL may be found at 40 | * http://www.gnu.org/copyleft/lesser.html

41 | * 42 | * @author J. David Eisenberg 43 | * @version 1.5, 19 Oct 2003 44 | * 45 | * CHANGES: 46 | * -------- 47 | * 19-Nov-2002 : CODING STYLE CHANGES ONLY (by David Gilbert for Object Refinery Limited); 48 | * 19-Sep-2003 : Fix for platforms using EBCDIC (contributed by Paulo Soares); 49 | * 19-Oct-2003 : Change private fields to protected fields so that 50 | * PngEncoderB can inherit them (JDE) 51 | * Fixed bug with calculation of nRows 52 | */ 53 | 54 | public class PngEncoder { 55 | 56 | /** Constant specifying that alpha channel should be encoded. */ 57 | public static final boolean ENCODE_ALPHA = true; 58 | 59 | /** Constant specifying that alpha channel should not be encoded. */ 60 | public static final boolean NO_ALPHA = false; 61 | 62 | /** Constants for filter (NONE) */ 63 | public static final int FILTER_NONE = 0; 64 | 65 | /** Constants for filter (SUB) */ 66 | public static final int FILTER_SUB = 1; 67 | 68 | /** Constants for filter (UP) */ 69 | public static final int FILTER_UP = 2; 70 | 71 | /** Constants for filter (LAST) */ 72 | public static final int FILTER_LAST = 2; 73 | 74 | /** IHDR tag. */ 75 | protected static final byte IHDR[] = {73, 72, 68, 82}; 76 | 77 | /** IDAT tag. */ 78 | protected static final byte IDAT[] = {73, 68, 65, 84}; 79 | 80 | /** IEND tag. */ 81 | protected static final byte IEND[] = {73, 69, 78, 68}; 82 | 83 | /** The png bytes. */ 84 | protected byte[] pngBytes; 85 | 86 | /** The prior row. */ 87 | protected byte[] priorRow; 88 | 89 | /** The left bytes. */ 90 | protected byte[] leftBytes; 91 | 92 | /** The image. */ 93 | protected Image image; 94 | 95 | /** The width. */ 96 | protected int width, height; 97 | 98 | /** The byte position. */ 99 | protected int bytePos, maxPos; 100 | 101 | /** CRC. */ 102 | protected CRC32 crc = new CRC32(); 103 | 104 | /** The CRC value. */ 105 | protected long crcValue; 106 | 107 | /** Encode alpha? */ 108 | protected boolean encodeAlpha; 109 | 110 | /** The filter type. */ 111 | protected int filter; 112 | 113 | /** The bytes-per-pixel. */ 114 | protected int bytesPerPixel; 115 | 116 | /** The compression level. */ 117 | protected int compressionLevel; 118 | 119 | /** 120 | * Class constructor 121 | */ 122 | public PngEncoder() { 123 | this(null, false, FILTER_NONE, 0); 124 | } 125 | 126 | /** 127 | * Class constructor specifying Image to encode, with no alpha channel encoding. 128 | * 129 | * @param image A Java Image object which uses the DirectColorModel 130 | * @see java.awt.Image 131 | */ 132 | public PngEncoder(Image image) { 133 | this(image, false, FILTER_NONE, 0); 134 | } 135 | 136 | /** 137 | * Class constructor specifying Image to encode, and whether to encode alpha. 138 | * 139 | * @param image A Java Image object which uses the DirectColorModel 140 | * @param encodeAlpha Encode the alpha channel? false=no; true=yes 141 | * @see java.awt.Image 142 | */ 143 | public PngEncoder(Image image, boolean encodeAlpha) { 144 | this(image, encodeAlpha, FILTER_NONE, 0); 145 | } 146 | 147 | /** 148 | * Class constructor specifying Image to encode, whether to encode alpha, and filter to use. 149 | * 150 | * @param image A Java Image object which uses the DirectColorModel 151 | * @param encodeAlpha Encode the alpha channel? false=no; true=yes 152 | * @param whichFilter 0=none, 1=sub, 2=up 153 | * @see java.awt.Image 154 | */ 155 | public PngEncoder(Image image, boolean encodeAlpha, int whichFilter) { 156 | this(image, encodeAlpha, whichFilter, 0); 157 | } 158 | 159 | 160 | /** 161 | * Class constructor specifying Image source to encode, whether to encode alpha, filter to use, 162 | * and compression level. 163 | * 164 | * @param image A Java Image object 165 | * @param encodeAlpha Encode the alpha channel? false=no; true=yes 166 | * @param whichFilter 0=none, 1=sub, 2=up 167 | * @param compLevel 0..9 168 | * @see java.awt.Image 169 | */ 170 | public PngEncoder(Image image, boolean encodeAlpha, int whichFilter, int compLevel) { 171 | this.image = image; 172 | this.encodeAlpha = encodeAlpha; 173 | setFilter(whichFilter); 174 | if (compLevel >= 0 && compLevel <= 9) { 175 | this.compressionLevel = compLevel; 176 | } 177 | } 178 | 179 | /** 180 | * Set the image to be encoded 181 | * 182 | * @param image A Java Image object which uses the DirectColorModel 183 | * @see java.awt.Image 184 | * @see java.awt.image.DirectColorModel 185 | */ 186 | public void setImage(Image image) { 187 | this.image = image; 188 | pngBytes = null; 189 | } 190 | 191 | /** 192 | * Creates an array of bytes that is the PNG equivalent of the current image, specifying 193 | * whether to encode alpha or not. 194 | * 195 | * @param encodeAlpha boolean false=no alpha, true=encode alpha 196 | * @return an array of bytes, or null if there was a problem 197 | */ 198 | public byte[] pngEncode(boolean encodeAlpha) { 199 | byte[] pngIdBytes = {-119, 80, 78, 71, 13, 10, 26, 10}; 200 | 201 | if (image == null) { 202 | return null; 203 | } 204 | width = image.getWidth(null); 205 | height = image.getHeight(null); 206 | 207 | /* 208 | * start with an array that is big enough to hold all the pixels 209 | * (plus filter bytes), and an extra 200 bytes for header info 210 | */ 211 | pngBytes = new byte[((width + 1) * height * 3) + 200]; 212 | 213 | /* 214 | * keep track of largest byte written to the array 215 | */ 216 | maxPos = 0; 217 | 218 | bytePos = writeBytes(pngIdBytes, 0); 219 | //hdrPos = bytePos; 220 | writeHeader(); 221 | //dataPos = bytePos; 222 | if (writeImageData()) { 223 | writeEnd(); 224 | pngBytes = resizeByteArray(pngBytes, maxPos); 225 | } 226 | else { 227 | pngBytes = null; 228 | } 229 | return pngBytes; 230 | } 231 | 232 | /** 233 | * Creates an array of bytes that is the PNG equivalent of the current image. 234 | * Alpha encoding is determined by its setting in the constructor. 235 | * 236 | * @return an array of bytes, or null if there was a problem 237 | */ 238 | public byte[] pngEncode() { 239 | return pngEncode(encodeAlpha); 240 | } 241 | 242 | /** 243 | * Set the alpha encoding on or off. 244 | * 245 | * @param encodeAlpha false=no, true=yes 246 | */ 247 | public void setEncodeAlpha(boolean encodeAlpha) { 248 | this.encodeAlpha = encodeAlpha; 249 | } 250 | 251 | /** 252 | * Retrieve alpha encoding status. 253 | * 254 | * @return boolean false=no, true=yes 255 | */ 256 | public boolean getEncodeAlpha() { 257 | return encodeAlpha; 258 | } 259 | 260 | /** 261 | * Set the filter to use 262 | * 263 | * @param whichFilter from constant list 264 | */ 265 | public void setFilter(int whichFilter) { 266 | this.filter = FILTER_NONE; 267 | if (whichFilter <= FILTER_LAST) { 268 | this.filter = whichFilter; 269 | } 270 | } 271 | 272 | /** 273 | * Retrieve filtering scheme 274 | * 275 | * @return int (see constant list) 276 | */ 277 | public int getFilter() { 278 | return filter; 279 | } 280 | 281 | /** 282 | * Set the compression level to use 283 | * 284 | * @param level 0 through 9 285 | */ 286 | public void setCompressionLevel(int level) { 287 | if (level >= 0 && level <= 9) { 288 | this.compressionLevel = level; 289 | } 290 | } 291 | 292 | /** 293 | * Retrieve compression level 294 | * 295 | * @return int in range 0-9 296 | */ 297 | public int getCompressionLevel() { 298 | return compressionLevel; 299 | } 300 | 301 | /** 302 | * Increase or decrease the length of a byte array. 303 | * 304 | * @param array The original array. 305 | * @param newLength The length you wish the new array to have. 306 | * @return Array of newly desired length. If shorter than the 307 | * original, the trailing elements are truncated. 308 | */ 309 | protected byte[] resizeByteArray(byte[] array, int newLength) { 310 | byte[] newArray = new byte[newLength]; 311 | int oldLength = array.length; 312 | 313 | System.arraycopy(array, 0, newArray, 0, Math.min(oldLength, newLength)); 314 | return newArray; 315 | } 316 | 317 | /** 318 | * Write an array of bytes into the pngBytes array. 319 | * Note: This routine has the side effect of updating 320 | * maxPos, the largest element written in the array. 321 | * The array is resized by 1000 bytes or the length 322 | * of the data to be written, whichever is larger. 323 | * 324 | * @param data The data to be written into pngBytes. 325 | * @param offset The starting point to write to. 326 | * @return The next place to be written to in the pngBytes array. 327 | */ 328 | protected int writeBytes(byte[] data, int offset) { 329 | maxPos = Math.max(maxPos, offset + data.length); 330 | if (data.length + offset > pngBytes.length) { 331 | pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, data.length)); 332 | } 333 | System.arraycopy(data, 0, pngBytes, offset, data.length); 334 | return offset + data.length; 335 | } 336 | 337 | /** 338 | * Write an array of bytes into the pngBytes array, specifying number of bytes to write. 339 | * Note: This routine has the side effect of updating 340 | * maxPos, the largest element written in the array. 341 | * The array is resized by 1000 bytes or the length 342 | * of the data to be written, whichever is larger. 343 | * 344 | * @param data The data to be written into pngBytes. 345 | * @param nBytes The number of bytes to be written. 346 | * @param offset The starting point to write to. 347 | * @return The next place to be written to in the pngBytes array. 348 | */ 349 | protected int writeBytes(byte[] data, int nBytes, int offset) { 350 | maxPos = Math.max(maxPos, offset + nBytes); 351 | if (nBytes + offset > pngBytes.length) { 352 | pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, nBytes)); 353 | } 354 | System.arraycopy(data, 0, pngBytes, offset, nBytes); 355 | return offset + nBytes; 356 | } 357 | 358 | /** 359 | * Write a two-byte integer into the pngBytes array at a given position. 360 | * 361 | * @param n The integer to be written into pngBytes. 362 | * @param offset The starting point to write to. 363 | * @return The next place to be written to in the pngBytes array. 364 | */ 365 | protected int writeInt2(int n, int offset) { 366 | byte[] temp = {(byte) ((n >> 8) & 0xff), (byte) (n & 0xff)}; 367 | return writeBytes(temp, offset); 368 | } 369 | 370 | /** 371 | * Write a four-byte integer into the pngBytes array at a given position. 372 | * 373 | * @param n The integer to be written into pngBytes. 374 | * @param offset The starting point to write to. 375 | * @return The next place to be written to in the pngBytes array. 376 | */ 377 | protected int writeInt4(int n, int offset) { 378 | byte[] temp = {(byte) ((n >> 24) & 0xff), 379 | (byte) ((n >> 16) & 0xff), 380 | (byte) ((n >> 8) & 0xff), 381 | (byte) (n & 0xff)}; 382 | return writeBytes(temp, offset); 383 | } 384 | 385 | /** 386 | * Write a single byte into the pngBytes array at a given position. 387 | * 388 | * @param b The integer to be written into pngBytes. 389 | * @param offset The starting point to write to. 390 | * @return The next place to be written to in the pngBytes array. 391 | */ 392 | protected int writeByte(int b, int offset) { 393 | byte[] temp = {(byte) b}; 394 | return writeBytes(temp, offset); 395 | } 396 | 397 | /** 398 | * Write a PNG "IHDR" chunk into the pngBytes array. 399 | */ 400 | protected void writeHeader() { 401 | int startPos; 402 | 403 | startPos = bytePos = writeInt4(13, bytePos); 404 | bytePos = writeBytes(IHDR, bytePos); 405 | width = image.getWidth(null); 406 | height = image.getHeight(null); 407 | bytePos = writeInt4(width, bytePos); 408 | bytePos = writeInt4(height, bytePos); 409 | bytePos = writeByte(8, bytePos); // bit depth 410 | bytePos = writeByte((encodeAlpha) ? 6 : 2, bytePos); // direct model 411 | bytePos = writeByte(0, bytePos); // compression method 412 | bytePos = writeByte(0, bytePos); // filter method 413 | bytePos = writeByte(0, bytePos); // no interlace 414 | crc.reset(); 415 | crc.update(pngBytes, startPos, bytePos - startPos); 416 | crcValue = crc.getValue(); 417 | bytePos = writeInt4((int) crcValue, bytePos); 418 | } 419 | 420 | /** 421 | * Perform "sub" filtering on the given row. 422 | * Uses temporary array leftBytes to store the original values 423 | * of the previous pixels. The array is 16 bytes long, which 424 | * will easily hold two-byte samples plus two-byte alpha. 425 | * 426 | * @param pixels The array holding the scan lines being built 427 | * @param startPos Starting position within pixels of bytes to be filtered. 428 | * @param width Width of a scanline in pixels. 429 | */ 430 | protected void filterSub(byte[] pixels, int startPos, int width) { 431 | int i; 432 | int offset = bytesPerPixel; 433 | int actualStart = startPos + offset; 434 | int nBytes = width * bytesPerPixel; 435 | int leftInsert = offset; 436 | int leftExtract = 0; 437 | 438 | for (i = actualStart; i < startPos + nBytes; i++) { 439 | leftBytes[leftInsert] = pixels[i]; 440 | pixels[i] = (byte) ((pixels[i] - leftBytes[leftExtract]) % 256); 441 | leftInsert = (leftInsert + 1) % 0x0f; 442 | leftExtract = (leftExtract + 1) % 0x0f; 443 | } 444 | } 445 | 446 | /** 447 | * Perform "up" filtering on the given row. 448 | * Side effect: refills the prior row with current row 449 | * 450 | * @param pixels The array holding the scan lines being built 451 | * @param startPos Starting position within pixels of bytes to be filtered. 452 | * @param width Width of a scanline in pixels. 453 | */ 454 | protected void filterUp(byte[] pixels, int startPos, int width) { 455 | int i, nBytes; 456 | byte currentByte; 457 | 458 | nBytes = width * bytesPerPixel; 459 | 460 | for (i = 0; i < nBytes; i++) { 461 | currentByte = pixels[startPos + i]; 462 | pixels[startPos + i] = (byte) ((pixels[startPos + i] - priorRow[i]) % 256); 463 | priorRow[i] = currentByte; 464 | } 465 | } 466 | 467 | /** 468 | * Write the image data into the pngBytes array. 469 | * This will write one or more PNG "IDAT" chunks. In order 470 | * to conserve memory, this method grabs as many rows as will 471 | * fit into 32K bytes, or the whole image; whichever is less. 472 | * 473 | * 474 | * @return true if no errors; false if error grabbing pixels 475 | */ 476 | protected boolean writeImageData() { 477 | int rowsLeft = height; // number of rows remaining to write 478 | int startRow = 0; // starting row to process this time through 479 | int nRows; // how many rows to grab at a time 480 | 481 | byte[] scanLines; // the scan lines to be compressed 482 | int scanPos; // where we are in the scan lines 483 | int startPos; // where this line's actual pixels start (used for filtering) 484 | 485 | byte[] compressedLines; // the resultant compressed lines 486 | int nCompressed; // how big is the compressed area? 487 | 488 | //int depth; // color depth ( handle only 8 or 32 ) 489 | 490 | PixelGrabber pg; 491 | 492 | bytesPerPixel = (encodeAlpha) ? 4 : 3; 493 | 494 | Deflater scrunch = new Deflater(compressionLevel); 495 | ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024); 496 | 497 | DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes, scrunch); 498 | try { 499 | while (rowsLeft > 0) { 500 | nRows = Math.min(32767 / (width * (bytesPerPixel + 1)), rowsLeft); 501 | nRows = Math.max( nRows, 1 ); 502 | 503 | int[] pixels = new int[width * nRows]; 504 | 505 | pg = new PixelGrabber(image, 0, startRow, 506 | width, nRows, pixels, 0, width); 507 | try { 508 | pg.grabPixels(); 509 | } 510 | catch (Exception e) { 511 | System.err.println("interrupted waiting for pixels!"); 512 | return false; 513 | } 514 | if ((pg.getStatus() & ImageObserver.ABORT) != 0) { 515 | System.err.println("image fetch aborted or errored"); 516 | return false; 517 | } 518 | 519 | /* 520 | * Create a data chunk. scanLines adds "nRows" for 521 | * the filter bytes. 522 | */ 523 | scanLines = new byte[width * nRows * bytesPerPixel + nRows]; 524 | 525 | if (filter == FILTER_SUB) { 526 | leftBytes = new byte[16]; 527 | } 528 | if (filter == FILTER_UP) { 529 | priorRow = new byte[width * bytesPerPixel]; 530 | } 531 | 532 | scanPos = 0; 533 | startPos = 1; 534 | for (int i = 0; i < width * nRows; i++) { 535 | if (i % width == 0) { 536 | scanLines[scanPos++] = (byte) filter; 537 | startPos = scanPos; 538 | } 539 | scanLines[scanPos++] = (byte) ((pixels[i] >> 16) & 0xff); 540 | scanLines[scanPos++] = (byte) ((pixels[i] >> 8) & 0xff); 541 | scanLines[scanPos++] = (byte) ((pixels[i]) & 0xff); 542 | if (encodeAlpha) { 543 | scanLines[scanPos++] = (byte) ((pixels[i] >> 24) & 0xff); 544 | } 545 | if ((i % width == width - 1) && (filter != FILTER_NONE)) { 546 | if (filter == FILTER_SUB) { 547 | filterSub(scanLines, startPos, width); 548 | } 549 | if (filter == FILTER_UP) { 550 | filterUp(scanLines, startPos, width); 551 | } 552 | } 553 | } 554 | 555 | /* 556 | * Write these lines to the output area 557 | */ 558 | compBytes.write(scanLines, 0, scanPos); 559 | 560 | startRow += nRows; 561 | rowsLeft -= nRows; 562 | } 563 | compBytes.close(); 564 | 565 | /* 566 | * Write the compressed bytes 567 | */ 568 | compressedLines = outBytes.toByteArray(); 569 | nCompressed = compressedLines.length; 570 | 571 | crc.reset(); 572 | bytePos = writeInt4(nCompressed, bytePos); 573 | bytePos = writeBytes(IDAT, bytePos); 574 | crc.update(IDAT); 575 | bytePos = writeBytes(compressedLines, nCompressed, bytePos); 576 | crc.update(compressedLines, 0, nCompressed); 577 | 578 | crcValue = crc.getValue(); 579 | bytePos = writeInt4((int) crcValue, bytePos); 580 | scrunch.finish(); 581 | return true; 582 | } 583 | catch (IOException e) { 584 | System.err.println(e.toString()); 585 | return false; 586 | } 587 | } 588 | 589 | /** 590 | * Write a PNG "IEND" chunk into the pngBytes array. 591 | */ 592 | protected void writeEnd() { 593 | bytePos = writeInt4(0, bytePos); 594 | bytePos = writeBytes(IEND, bytePos); 595 | crc.reset(); 596 | crc.update(IEND); 597 | crcValue = crc.getValue(); 598 | bytePos = writeInt4((int) crcValue, bytePos); 599 | } 600 | 601 | } 602 | -------------------------------------------------------------------------------- /web/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | client: 3 | serviceUrl: 4 | defaultZone: http://10.111.58.121:10000/eureka/ 5 | enabled: false 6 | server: 7 | tomcat: 8 | uri-encoding: UTF-8 9 | port: 11001 10 | context-path: /fasttiff 11 | spring: 12 | application: 13 | name: fasttiff 14 | -------------------------------------------------------------------------------- /web/src/main/resources/libgdalconstjni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmiao/fasttiff/d6f9ce8f6d8ad2be8c12a59a9c99456096f2e02d/web/src/main/resources/libgdalconstjni.so -------------------------------------------------------------------------------- /web/src/main/resources/libgdaljni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmiao/fasttiff/d6f9ce8f6d8ad2be8c12a59a9c99456096f2e02d/web/src/main/resources/libgdaljni.so -------------------------------------------------------------------------------- /web/src/main/resources/libgnmjni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmiao/fasttiff/d6f9ce8f6d8ad2be8c12a59a9c99456096f2e02d/web/src/main/resources/libgnmjni.so -------------------------------------------------------------------------------- /web/src/main/resources/libogrjni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmiao/fasttiff/d6f9ce8f6d8ad2be8c12a59a9c99456096f2e02d/web/src/main/resources/libogrjni.so -------------------------------------------------------------------------------- /web/src/main/resources/libosrjni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmiao/fasttiff/d6f9ce8f6d8ad2be8c12a59a9c99456096f2e02d/web/src/main/resources/libosrjni.so -------------------------------------------------------------------------------- /web/src/main/resources/tile.properties: -------------------------------------------------------------------------------- 1 | tiffRoot=D:/BaiduNetdiskDownload/切片Demo/ 2 | coreSize=2 3 | --------------------------------------------------------------------------------