/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2017, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.mbstyle.sprite; import com.google.common.collect.ImmutableMap; import org.geotools.util.logging.Logging; import org.json.simple.JSONObject; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; /** * Wrapper that takes the sprite index file (as a JSONObject) for a Mapbox Sprite Sheet and parses the all the individual icons as * {@link IconInfo} objects. For example: * * <pre> * <code> * { * "goldfish": { * "height": 32, * "pixelRatio": 1, * "width": 32, * "x": 64, * "y": 64 * }, * "owl": { * "height": 64, * "pixelRatio": 1, * "width": 64, * "x": 0, * "y": 0 * } * } * </code> * </pre> * * * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#sprite">https://www.mapbox.com/mapbox-gl-js/style-spec/#sprite</a> * */ public class SpriteIndex { private String spriteIndexUrl; private JSONObject json; private Map<String, IconInfo> icons; private static final Logger LOGGER = Logging.getLogger(SpriteIndex.class); /** * * @param spriteIndexUrl The URL of the sprite index file (used for error messages). * @param json The sprite index file as a {@link JSONObject}. */ public SpriteIndex(String spriteIndexUrl, JSONObject json) { this.spriteIndexUrl = spriteIndexUrl; this.json = json; this.icons = new HashMap<>(); for (Object key : this.json.keySet()) { if (key instanceof String) { String iconName = (String) key; try { IconInfo iconInfo = parseIconInfoFromIndex(this.json, iconName); icons.put(iconName, iconInfo); } catch (Exception e) { LOGGER.warning("Mapbox sprite icon index file " + this.spriteIndexUrl + " contained invalid value for key \"" + iconName + "\". Exception was: " + e.getMessage()); } } } } /** * Parse the {@link IconInfo} for the provided iconName in the provided icon index. * * @param iconIndex The icon index file. * @param iconName The name of the icon in the index file. * @return An {@link IconInfo} for the icon. */ protected static IconInfo parseIconInfoFromIndex(JSONObject iconIndex, String iconName) { if (!iconIndex.containsKey(iconName)) { throw new MBSpriteException( "Sprite index file does not contain entry for icon with name: " + iconName); } Object o = iconIndex.get(iconName); if (!(o instanceof JSONObject)) { throw new MBSpriteException("Error parsing sprite index for \"" + iconName + "\": Expected JSONObject, but is " + o.getClass().getSimpleName()); } return new IconInfo(iconName, (JSONObject) o); } /** * Get the names and data of all icons in the index * * @return An immutable map of the icon name to the corresponding {@link IconInfo} */ public ImmutableMap<String, IconInfo> getIcons() { return ImmutableMap.copyOf(icons); } /** * Get information about a single icon from the index * @param iconName Name of the icon * @return Info object describing the icons */ public IconInfo getIcon(String iconName) { if (!icons.containsKey(iconName)) { throw new MBSpriteException("Mapbox sprite icon index file " + this.spriteIndexUrl + " does not contain icon with name: " + iconName); } else { return icons.get(iconName); } } /** * Wrapper for parsing the properties of an individual sprite index entry (JSONObject) for a single icon. For example: * * <pre> * <code> * { * "width": 32, * "height": 32, * "x": 0, * "y": 0, * "pixelRatio": 1 * } * </code> * </pre> * * * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#sprite">https://www.mapbox.com/mapbox-gl-js/style-spec/#sprite</a> * */ public static class IconInfo { private String iconName; private JSONObject json; /** * * @param iconName The name of this sprite icon (used for error messages) * @param json The sprite index entry for this icon, as a {@link JSONObject} */ public IconInfo(String iconName, JSONObject json) { this.iconName = iconName; this.json = json; } public int getWidth() { return intOrException("width"); } public int getHeight() { return intOrException("height"); } public int getX() { return intOrException("x"); } public int getY() { return intOrException("y"); } public int getPixelRatio() { return intOrException("pixelRatio"); } private int intOrException(String k) { if (!json.containsKey(k)) { throw new MBSpriteException("Mapbox sprite icon with name \"" + iconName + "\" is missing required property: " + k); } Object o = json.get(k); try { if (o instanceof Number) { return ((Number) o).intValue(); } else if (o instanceof String) { return Integer.valueOf((String) o); } else { throw new IllegalArgumentException(); } } catch (Exception e) { throw new MBSpriteException( "Mapbox sprite icon with name \"" + iconName + "\" contains invalid value for property \"" + k + "\". Expected integer, but was: " + o.getClass().getSimpleName(), e); } } } }