package org.geowebcache.arcgis.compact; import org.geowebcache.io.Resource; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * Abstract base class for ArcGIS compact caches. * * @author Bjoern Saxe */ public abstract class ArcGISCompactCache { protected static final String BUNDLX_EXT = ".bundlx"; protected static final String BUNDLE_EXT = ".bundle"; protected static final int BUNDLX_MAXIDX = 128; protected String pathToCacheRoot = ""; /** * Get Resource object for tile. * * @param zoom Zoom level. * @param row Row of tile. * @param col Column of tile. * @return Resource object associated with tile image data if tile exists; null otherwise. */ public abstract Resource getBundleFileResource(int zoom, int row, int col); /** * Build path to a bundle from zoom, col, and row without file extension. * * @param zoom Zoom levl * @param row Row * @param col Column * @return String containing complete path without file extension in the form * of .../Lzz/RrrrrCcccc with the number of c and r at least 4. */ protected String buildBundleFilePath(int zoom, int row, int col) { StringBuilder bundlePath = new StringBuilder(pathToCacheRoot); int baseRow = (row / BUNDLX_MAXIDX) * BUNDLX_MAXIDX; int baseCol = (col / BUNDLX_MAXIDX) * BUNDLX_MAXIDX; String zoomStr = Integer.toString(zoom); if (zoomStr.length() < 2) zoomStr = "0" + zoomStr; StringBuilder rowStr = new StringBuilder(Integer.toHexString(baseRow)); StringBuilder colStr = new StringBuilder(Integer.toHexString(baseCol)); // column and rows are at least 4 characters long final int padding = 4; while (colStr.length() < padding) colStr.insert(0, "0"); while (rowStr.length() < padding) rowStr.insert(0, "0"); bundlePath.append("L").append(zoomStr).append(File.separatorChar).append("R").append(rowStr) .append("C").append(colStr); return bundlePath.toString(); } /** * Read from a file that uses little endian byte order. * * @param filePath Path to file * @param offset Read at offset * @param length Read length bytes * @return ByteBuffer that contains read bytes and has byte order set to little endian. * The length of the byte buffer is multiple of 4, so getInt() and getLong() can be used * even when fewer bytes are read. */ protected ByteBuffer readFromLittleEndianFile(String filePath, long offset, int length) { ByteBuffer result = null; try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) { file.seek(offset); // pad to multiples of 4 so we can use getInt() and getLong() int padding = 4 - (length % 4); byte data[] = new byte[length + padding]; if (file.read(data, 0, length) != length) throw new IOException("not enough bytes read or reached end of file"); result = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); } catch (IOException e) { System.err.println(e); } return result; } }