package eu.geopaparazzi.spatialite.database.spatial.core.mbtiles; /** @author Simon Thepot aka djcoin <simon.thepot@gmail.com, simon.thepot@makina-corpus.com> */ import java.util.HashMap; import java.util.LinkedHashMap; import android.database.Cursor; public class MbTilesMetadata { // * name: The plain-english name of the tileset. // * type: overlay or baselayer // * version: The version of the tileset, as a plain number. // * description: A description of the layer as plain text. // * format: The image file format of the tile data: png or jpg public final String name, description, type, version, format; public final float[] bounds; public final int minZoom; public final int maxZoom; public final HashMap<String, String> extra; // MBTiles Specification public static final MetadataValidatorFactory metadataValidatorFactory = new MetadataValidatorFactory(); public MbTilesMetadata( String name, String description, String type, String version, String format, float[] bounds, int minZoom, int maxZoom, HashMap<String, String> extra ) { this.name = name; this.type = type; this.version = version; this.description = description; this.format = format; this.bounds = bounds; this.minZoom = minZoom; this.maxZoom = maxZoom; this.extra = extra; } @Override public String toString() { String none = " - "; String separator = " | "; StringBuilder sb = new StringBuilder("Metadata:\n"); sb.append("Name: " + (name == null ? none : name) + separator); sb.append("Type: " + (type == null ? none : type) + separator); sb.append("Version: " + (version == null ? none : version) + separator); sb.append("Description: " + (description == null ? none : description) + separator); sb.append("Format: " + (format == null ? none : format) + separator); sb.append("Bounds: " + (bounds == null ? none : bounds)); sb.append("\n\n"); sb.append("Extra: " + this.extra.toString() + ""); return sb.toString(); } public static MbTilesMetadata createFromCursor( Cursor c, int idx_col_key, int idx_col_value, MetadataValidator validator ) throws MetadataParseException { HashMap<String, String> dumped = new LinkedHashMap<String, String>(); c.moveToFirst(); do { dumped.put(c.getString(idx_col_key), c.getString(idx_col_value)); } while( c.moveToNext() ); c.close(); return validator.validate(dumped); } public static interface MetadataValidator { MbTilesMetadata validate( HashMap<String, String> hm ) throws MetadataParseException; } @SuppressWarnings("serial") public static class MetadataParseException extends Exception { public MetadataParseException( String errorMessage ) { super(errorMessage); } } public static class MetadataValidatorFactory { /* Validators for parsing Metadata */ private final static MetadataValidator VALIDATOR_1_0 = new MetadataValidator_1_0(); private final static MetadataValidator VALIDATOR_1_1 = new MetadataValidator_1_1(); public static MetadataValidator getMetadataValidatorFromVersion( String version ) { if (version.equals("1.0")) return VALIDATOR_1_0; if (version.equals("1.1")) return VALIDATOR_1_1; return null; } } // https://github.com/mapbox/mbtiles-spec/blob/master/1.1/spec.md public static class MetadataValidator_1_1 implements MetadataValidator { @Override public MbTilesMetadata validate( HashMap<String, String> hm ) throws MetadataParseException { String tmp; String name = hm.remove("name"); if (name == null) throw new MetadataParseException("No mandatory field 'name'."); String description = hm.remove("description"); if (description == null) throw new MetadataParseException("No mandatory field 'description'."); String type = hm.remove("type"); if (type == null || !(type.equals("overlay") || type.equals("baselayer"))) { // we suppose it is a baselayer by default, if not available type = "baselayer"; // throw new // MetadataParseException("No mandatory field 'type' or not in [ overlay, baselayer ]."); } String version = "1.1"; // tmp = hm.remove("version"); // if (tmp == null) // throw new MetadataParseException("No mandatory field 'version'"); // try { // Double.parseDouble(tmp); // version = tmp; // } catch (NumberFormatException e) { // throw new // MetadataParseException("Invalid syntax for mandatory field 'version'. Must be a plain number."); // } String format = hm.remove("format"); if (format == null || !(type.equals("png") || type.equals("jpg"))) throw new MetadataParseException("No mandatory field 'format' or not in [ png, jpg ]."); // optional tmp = hm.remove("bounds"); float[] bounds = null; if (tmp != null) { bounds = ValidatorHelper.parseBounds(tmp); if (bounds == null) throw new MetadataParseException("Invalid syntax for optional field 'bounds'." + "Should be latitude and longitude values in OpenLayers Bounds format - left, bottom, right, top." + "Example of the full earth: -180.0,-85,180,85"); } String minZoomStr = hm.remove("minzoom"); int minZoom = 0; if (minZoomStr != null) { minZoom = Integer.parseInt(minZoomStr); } String maxZoomStr = hm.remove("maxzoom"); int maxZoom = 22; if (maxZoomStr != null) { maxZoom = Integer.parseInt(maxZoomStr); } return new MbTilesMetadata(name, description, type, version, format, bounds, minZoom, maxZoom, hm); } } // https://github.com/mapbox/mbtiles-spec/blob/master/1.0/spec.md public static class MetadataValidator_1_0 implements MetadataValidator { @Override public MbTilesMetadata validate( HashMap<String, String> hm ) throws MetadataParseException { String name = hm.remove("name"); if (name == null) throw new MetadataParseException("No mandatory field 'name'."); String description = hm.remove("description"); if (description == null) throw new MetadataParseException("No mandatory field 'description'."); String type = hm.remove("type"); if (type == null || !(type.equals("overlay") || type.equals("baselayer"))) { // we suppose it is a baselayer by default, if not available type = "baselayer"; // throw new // MetadataParseException("No mandatory field 'type' or not in [ overlay, baselayer ]."); } String version = "1.0"; // tmp = hm.remove("version"); // if (tmp == null) // throw new MetadataParseException("No mandatory field 'version'"); // try { // Double.parseDouble(tmp); // version = tmp; // } catch (NumberFormatException e) { // throw new // MetadataParseException("Invalid syntax for mandatory field 'version'. Must be a plain number."); // } String minZoomStr = hm.remove("minzoom"); int minZoom = 0; if (minZoomStr != null) { minZoom = Integer.parseInt(minZoomStr); } String maxZoomStr = hm.remove("maxzoom"); int maxZoom = 22; if (maxZoomStr != null) { maxZoom = Integer.parseInt(maxZoomStr); } return new MbTilesMetadata(name, description, type, version, null, null, minZoom, maxZoom, hm); } } public static class ValidatorHelper { // left, bottom, right, top | Full earth: -180.0,-85,180,85 public static float[] parseBounds( String tmp ) { float[] bounds; if (tmp == null) return null; String[] splitted = tmp.split(","); if (splitted.length != 4) return null; bounds = new float[4]; try { for( int i = 0; i < splitted.length; i++ ) { bounds[i] = Float.parseFloat(splitted[i]); } } catch (NumberFormatException e) { return null; } if (bounds[0] >= -180 && bounds[0] <= 0 && bounds[2] <= 180 && bounds[2] >= 0 && bounds[1] >= -85 && bounds[1] <= 0 && bounds[3] <= 85 && bounds[3] >= 0) return bounds; else return null; } } }