扩展切片格式 |
切片格式类定义了具体的瓦片存储与组织方式,包括比例尺、瓦片拆分与目录组织、命名等。根据扩展需要,对这些内容一一定义,就完成了对整个瓦片格式的定义。
扩展形式如下:
public class NationalCacheStandardTileset extends AbstractImageTileset { ... }
需要实现 AbstractImageTileset 类中未实现的方法,具体包括:
字段/方法 | 描述 |
protected boolean doUpdateMetaData(ImageMetaData metaData, TileVersionList tileVersions) | 更新瓦片的元信息 |
public void put(ImageTileInfo tileInfo) | 创建或更新一个瓦片 |
public ImageTileInfo get(Tile tile) | 获取一个瓦片 |
public String getName() | 获取存储的片集的名称 |
切片格式类主要定义了具体的瓦片存储与组织方式,包括比例尺、瓦片拆分与目录组织、命名等,对应《地理信息公共服务平台——电子地图数据规范》中的瓦片结构定义如下:
在扩展类中,按照上述结构,定义瓦片的命名和存储规则并创建存储路径,部分代码如下:
//根据规范,设置支持的比例尺。 private static final double nationalCacheStandardScale[] = new double[] { 295829355.45, 147914677.73, 73957338.86, 36978669.43, 18489334.72, 9244667.36, 4622333.68, 2311166.84, 1155583.42, 577791.71, 288895.85, 144447.93, 72223.96, 36111.98, 18055.99, 9028.00, 4514.00, 2257.00, 1128.50, 564.25 }; //规范中定义的目录结构。 private static final String levelStr = "L"; private static final String rowStr = "R"; private static final String colStr = "C"; //规范中支持的瓦片后缀。 private static final String pngFormat = "png"; private static final String jpgFormat = "jpg"; private static final String dotStr = "."; //创建存储瓦片的目录。 public NationalCacheStandardTileset(File cacheFile) { if (!cacheFile.exists()) { cacheFile.mkdirs(); } if (!cacheFile.isDirectory()) { throw new IllegalArgumentException("cacheFile is not a directory!"); } this.cacheFile = cacheFile; this.cacheName = cacheFile.getName(); }
doUpdateMetaData 方法用于更新 Tileset 的元信息,由于该规范中并没有这个概念,所以此方法不用实现。
@Override protected boolean doUpdateMetaData(ImageMetaData metaData, TileVersionList tileVersions) { return false; }
因为分布式切图服务采取了纯色瓦片优化策略来提升切图和存储的效率,所以在存储一张瓦片的时候,需要判断获取的瓦片是否为纯色瓦片,如果是纯色的瓦片需要转化并存储为瓦片文件(即一个 image)。
@Override public void put(ImageTileInfo tileInfo) throws PutTileFailedException { checkTile(tileInfo); byte[] data = tileInfo.tileData; if (data == null) { return; } //分布式切图中生成的纯色图片都是用一段信息来表示的,这里先判断是否是纯色图片,如果是纯色就使用工具方法获取这段信息代表的图片。 if (MBTilesUtil.isDistributedPureImage(data)) { data = MBTilesUtil.transformPureImageToCommonImageData(data); } storeTileData(tileInfo, data); }
通过 get(Tile tile)方法可以获取一张生成的瓦片,并按照指定的规范命名。
@Override public ImageTileInfo get(Tile tile) { checkTile(tile); ImageTileInfo tileInfo = new ImageTileInfo(); tileInfo.resolution = tile.resolution; tileInfo.scaleDenominator = tile.scaleDenominator; tileInfo.x = tile.x; tileInfo.y = tile.y; //获取生成的瓦片。 File imgFile = getPNGImageFile(tile); String format = null; if (!imgFile.exists()) { // png 后缀的文件找不到则尝试去找 jpg 后缀的图片 String fileName = imgFile.getName().split("\\.")[0] + dotStr + jpgFormat; imgFile = new File(imgFile.getParentFile(), fileName); format = new String(jpgFormat); } else { format = new String(pngFormat); } //找到瓦片后,根据规范命名。 if (imgFile.exists()) { tileInfo.tileData = getImageData(imgFile, format); } return tileInfo; }
通过 getName()方法可以获取存储的瓦片集的名称,由前缀和瓦片路径创建时的名称缓存文件名组成。
@Override public String getName() { if (this.cacheFile != null && this.cacheFile.exists()) { return namePrefix + cacheFile.getName(); } return null; }
完整的扩展示例类如下:
NationalCacheStandardTileset.java
package com.supermap.services.tilesource.impl; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.Locale; import javax.imageio.ImageIO; import org.apache.commons.io.FileUtils; import com.supermap.services.tilesource.ImageMetaData; import com.supermap.services.tilesource.ImageTileInfo; import com.supermap.services.tilesource.MBTilesUtil; import com.supermap.services.tilesource.PutTileFailedException; import com.supermap.services.tilesource.Tile; import com.supermap.services.tilesource.TileVersionList; //定义了该规范对应的切片集。该类根据标准中定义的格式,实现了瓦片的存储与管理。 public class NationalCacheStandardTileset extends AbstractImageTileset { private static final String namePrefix = "NationalCacheStandard_tileset_"; //根据规范,设置支持的比例尺。 private static final double nationalCacheStandardScale[] = new double[] { 295829355.45, 147914677.73, 73957338.86, 36978669.43, 18489334.72, 9244667.36, 4622333.68, 2311166.84, 1155583.42, 577791.71, 288895.85, 144447.93, 72223.96, 36111.98, 18055.99, 9028.00, 4514.00, 2257.00, 1128.50, 564.25 }; //规范中定义的目录结构。 private static final String levelStr = "L"; private static final String rowStr = "R"; private static final String colStr = "C"; //规范中支持的瓦片后缀。 private static final String pngFormat = "png"; private static final String jpgFormat = "jpg"; private static final String dotStr = "."; private File cacheFile; private String cacheName; public NationalCacheStandardTileset() { } //创建存储瓦片的目录。 public NationalCacheStandardTileset(File cacheFile) { if (!cacheFile.exists()) { cacheFile.mkdirs(); } if (!cacheFile.isDirectory()) { throw new IllegalArgumentException("cacheFile is not a directory!"); } this.cacheFile = cacheFile; this.cacheName = cacheFile.getName(); } //此方法用于更新 Tileset 的元信息,由于该规范中并没有这个概念,所以此方法不用实现。 @Override protected boolean doUpdateMetaData(ImageMetaData metaData, TileVersionList tileVersions) { return false; } //获取瓦片,根据规范命名。 @Override public ImageTileInfo get(Tile tile) { checkTile(tile); ImageTileInfo tileInfo = new ImageTileInfo(); tileInfo.resolution = tile.resolution; tileInfo.scaleDenominator = tile.scaleDenominator; tileInfo.x = tile.x; tileInfo.y = tile.y; //获取生成的瓦片。 File imgFile = getPNGImageFile(tile); String format = null; if (!imgFile.exists()) { // png 后缀的文件找不到则尝试去找 jpg 后缀的图片 String fileName = imgFile.getName().split("\\.")[0] + dotStr + jpgFormat; imgFile = new File(imgFile.getParentFile(), fileName); format = new String(jpgFormat); } else { format = new String(pngFormat); } //找到瓦片后,根据规范命。 if (imgFile.exists()) { tileInfo.tileData = getImageData(imgFile, format); } return tileInfo; } //获取 image 文件。 private byte[] getImageData(File picFile, String format) { if (picFile == null || !picFile.exists()) { return null; } ByteArrayOutputStream bos = null; try { BufferedImage pic = ImageIO.read(picFile); bos = new ByteArrayOutputStream(); ImageIO.write(pic, format.toUpperCase(Locale.ENGLISH), bos); return bos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } finally { if (bos != null) { try { bos.close(); } catch (Exception ex) { ex.printStackTrace(); } } } return null; } //获取存储的瓦片集的名称。 @Override public String getName() { if (this.cacheFile != null && this.cacheFile.exists()) { return namePrefix + cacheFile.getName(); } return null; } //向生成的瓦片集中增加瓦片,即把瓦片存储到指定的位置。 @Override public void put(ImageTileInfo tileInfo) throws PutTileFailedException { checkTile(tileInfo); byte[] data = tileInfo.tileData; if (data == null) { return; } //分布式切图中生成的纯色图片都是用一段信息来表示的,这里先判断是否是纯色图片,如果是的话就使用工具方法获取这段信息代表的图片。 if (MBTilesUtil.isDistributedPureImage(data)) { data = MBTilesUtil.transformPureImageToCommonImageData(data); } storeTileData(tileInfo, data); } //存储瓦片。 private void storeTileData(ImageTileInfo tileInfo, byte[] data) throws PutTileFailedException { try { File imgFile = getPNGImageFile(tileInfo); if (!imgFile.getParentFile().exists()) { imgFile.getParentFile().mkdirs(); } FileUtils.writeByteArrayToFile(imgFile, data); } catch (Exception e) { throw new PutTileFailedException(); } } //根据规范定义的瓦片存储格式,获取生成的瓦片并给瓦片命名,包括根据比例尺和行列号命名的目录结构、瓦片名称构成,形式如:\L1\R0\C0.png。 private File getPNGImageFile(Tile tileInfo) { int level = getLevel(tileInfo.scaleDenominator); File levelFile = new File(cacheFile, levelStr + level); File rowFile = new File(levelFile, rowStr + tileInfo.y); File colFile = new File(rowFile, colStr + tileInfo.x + dotStr + pngFormat); return colFile; } private void checkTile(Tile tile) { if (tile == null) { throw new IllegalArgumentException("tile cannot be null!"); } } public String getCacheName() { return cacheName; } //获取切图的比例尺。 public static int getLevel(double scaleDenominator) { // 个位相等都认为是包含在规范内的比例尺 for (int i = 0; i < nationalCacheStandardScale.length; i++) { if (Math.abs(scaleDenominator - nationalCacheStandardScale[i]) <= 1.0) { return i + 1; } } throw new IllegalArgumentException("scaleDenominator input is out of standard!"); } }