Extending tile format |
The tile format class defines specific tile storage and organization method, including scale, tile split and catalog organization, naming, etc. According to the the demand of extension, the definition of the entire tile format is complete if all these contents have been defined.
The extension is as follows:
public class NationalCacheStandardTileset extends AbstractImageTileset { ... }
Unimplemented methods in the AbstractImageTileset class needs to be implemented, including:
Field/Method | Description |
protected boolean doUpdateMetaData(ImageMetaData metaData, TileVersionList tileVersions) | Update tile meta-information |
public void put(ImageTileInfo tileInfo) | Create or update a tile |
public ImageTileInfo get(Tile tile) | Get a tile |
public String getName() | Get stored tile set name |
The tile format class primarily defines specific tile storage and organization method, including scale, tile split and catalog organization, naming, etc. In the Geographic Information Public Service Platform -- Electronic Map Data Specification, tile structure is defined as follows:
In the extended class, according to the above structure, name and storage structure are defined and storage path is created. Part of the code segment is as follows:
//Set supported scales according to the specification 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 }; //Catalog structure defined in the specification private static final String levelStr = "L"; private static final String rowStr = "R"; private static final String colStr = "C"; //Tile extension supported in the specification private static final String pngFormat = "png"; private static final String jpgFormat = "jpg"; private static final String dotStr = "."; //Create catalog to store tiles 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(); }
The doUpdateMetaData method is used to update the meta-information of Tileset. Since there is no such concept in the specification, the implementation of this method is not necessary.
@Override protected boolean doUpdateMetaData(ImageMetaData metaData, TileVersionList tileVersions) { return false; }
Since Distributed Map Tiling service employs pure color tile optimization policy to enhance tiling and storage efficiency, a tile needs to be judged whether it is in pure color while storing a tile. If the tile is in pure color, it needs to be transformed and stored as tile file, that is, an image.
@Override public void put(ImageTileInfo tileInfo) throws PutTileFailedException { checkTile(tileInfo); byte[] data = tileInfo.tileData; if (data == null) { return; } //Pure color pictures generated in distributed tiling are represented in a piece of information. Here the picture will be determined whether it is in pure color. If it is in pure color, the picture represented by this piece of information will be acquired through tool method. if (MBTilesUtil.isDistributedPureImage(data)) { data = MBTilesUtil.transformPureImageToCommonImageData(data); } storeTileData(tileInfo, data); }
A generated tile can be acquired through the get(Tile tile) method and will be named after specified specification.
@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; //Get generated tile File imgFile = getPNGImageFile(tile); String format = null; if (!imgFile.exists()) { // The picture with the extension of jpg will be searched is file with extension of png cannot be found String fileName = imgFile.getName().split("\\.")[0] + dotStr + jpgFormat; imgFile = new File(imgFile.getParentFile(), fileName); format = new String(jpgFormat); } else { format = new String(pngFormat); } //Name the tile according to the specification after finding the tile if (imgFile.exists()) { tileInfo.tileData = getImageData(imgFile, format); } return tileInfo; }
The name of the stored tile set can be acquired through the getName() method, which is composed of prefix and the name cache file name while creating tile path.
@Override public String getName() { if (this.cacheFile != null && this.cacheFile.exists()) { return namePrefix + cacheFile.getName(); } return null; }
The complete extension example class is as follows:
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; //Define the tile set corresponding tot he specification. This class implements the storage and management of the tiles according to the format defined in the specification public class NationalCacheStandardTileset extends AbstractImageTileset { private static final String namePrefix = "NationalCacheStandard_tileset_"; //Set scale according to the specification 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 }; //Catalog structure defined in the specification private static final String levelStr = "L"; private static final String rowStr = "R"; private static final String colStr = "C"; //Tile extension supported in the specification 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() { } //Create catalog to store tiles 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(); } //This method is used to update the meta-information of Tileset. Since there is no such concept, the implementation of this method is not necessary @Override protected boolean doUpdateMetaData(ImageMetaData metaData, TileVersionList tileVersions) { return false; } //Get tiles and name the tiles according to the specification @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; //Get generated tiles File imgFile = getPNGImageFile(tile); String format = null; if (!imgFile.exists()) { // Try to search pictures with the extension of jpg if files with the extension of png cannot be found String fileName = imgFile.getName().split("\\.")[0] + dotStr + jpgFormat; imgFile = new File(imgFile.getParentFile(), fileName); format = new String(jpgFormat); } else { format = new String(pngFormat); } //Name the tiles according to specification after finding them if (imgFile.exists()) { tileInfo.tileData = getImageData(imgFile, format); } return tileInfo; } //Get image file 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; } //Acquire stored tile set name @Override public String getName() { if (this.cacheFile != null && this.cacheFile.exists()) { return namePrefix + cacheFile.getName(); } return null; } //Add tiles to generated tile set, that is, store tiles to specified position @Override public void put(ImageTileInfo tileInfo) throws PutTileFailedException { checkTile(tileInfo); byte[] data = tileInfo.tileData; if (data == null) { return; } //Pure color pictures generated in distributed tiling are represented in a piece of information. Here the picture will be determined whether it is in pure color. If it is in pure color, the picture represented by this piece of information will be acquired through tool method. if (MBTilesUtil.isDistributedPureImage(data)) { data = MBTilesUtil.transformPureImageToCommonImageData(data); } storeTileData(tileInfo, data); } //Store tiles 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(); } } //Get generated tiles and name the tiles according to tile storage format defined by the specification. Name of the tile can be: \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; } //Get scale of the tiles 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!"); } }