/******************************************************************************* * Copyright 2013 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.bookmark.properties.layer; import gov.nasa.worldwind.layers.Layer; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import au.gov.ga.earthsci.bookmark.AbstractBookmarkPropertyAnimator; import au.gov.ga.earthsci.bookmark.IBookmarkPropertyAnimator; import au.gov.ga.earthsci.common.math.vector.Vector2; import au.gov.ga.earthsci.layer.tree.ILayerNode; import au.gov.ga.earthsci.layer.worldwind.ITreeModel; import au.gov.ga.earthsci.worldwind.common.util.Util; /** * An {@link IBookmarkPropertyAnimator} that can animate the world layer state * between the two given property states. * <p/> * Animation semantics are as follows: * <ol> * <li>Layer matching is done solely on layer ID * <li>Layers that appear in the current world model but neither the start nor * end property are animated between their current opacity value and 0.0 (off) * <li>Layers that appear in the start and/or end properties but NOT in the * current world model are ignored * <li>Layers that appear in both start and end properties AND in the current * world model are animated between the opacities stored in the start and end * properties * <li>Layers that appear in the start property but NOT the end property are * animated between the start property opacity and 0.0 (off) * <li>Layers that appear in the end property but NOT the start property are * animated between their current opacity and the end property opacity * <li>A layer which is disabled in the current world model will be assigned an * opacity value of 0.0 where the world value is used above * </ol> * * The above rules are enumerated in the matrix below (W = world state, S = * Start property state, E = End property state, O = Off, I = Ignore). * * <pre> * W S E | Animate * -------------------------- * 1 1 1 | S -> E * 1 1 0 | S -> O * 1 0 1 | W -> E * 1 0 0 | W -> O * 0 1 1 | I * 0 1 0 | I * 0 0 1 | I * 0 0 0 | I * * </pre> * * @author James Navin (james.navin@ga.gov.au) */ public class LayersPropertyAnimator extends AbstractBookmarkPropertyAnimator { private static final double DISABLED_OPACITY_THRESHOLD = 0.001; private final ITreeModel model; private final Map<Layer, Vector2> animationVectors; private boolean initialised = false; public LayersPropertyAnimator(final ITreeModel model, final LayersProperty start, final LayersProperty end, long duration) { super(start, end, duration); this.model = model; this.animationVectors = new ConcurrentHashMap<Layer, Vector2>(); } @Override public boolean isInitialised() { return initialised; } @Override public void init() { super.init(); animationVectors.clear(); for (Layer l : model.getLayers()) { if (!(l instanceof ILayerNode)) { continue; } ILayerNode layerNode = (ILayerNode) l; animationVectors.put(layerNode, new Vector2(getStartOpacity(layerNode), getEndOpacity(layerNode))); } initialised = true; } @Override public void applyFrame() { super.applyFrame(); for (Entry<Layer, Vector2> e : animationVectors.entrySet()) { Vector2 v = e.getValue(); e.getKey().setOpacity(Util.mixDouble(getCurrentTimeAsPercent(), v.x, v.y)); e.getKey().setEnabled(e.getKey().getOpacity() > DISABLED_OPACITY_THRESHOLD); } } private double getWorldOpacity(ILayerNode l) { if (l.isEnabled()) { return l.getOpacity(); } return 0.0; } private Double getStartOpacity(ILayerNode l) { if (isInStartProperty(l)) { String opacityVal = getStartProperty().getLayerStateInfo().get(l.getId()) .get(LayersPropertyPersister.OPACITY_ATTRIBUTE_NAME); return Double.parseDouble(opacityVal); } return getWorldOpacity(l); } private Double getEndOpacity(ILayerNode l) { if (isInEndProperty(l)) { String opacityVal = getEndProperty().getLayerStateInfo().get(l.getId()) .get(LayersPropertyPersister.OPACITY_ATTRIBUTE_NAME); return Double.parseDouble(opacityVal); } return 0.0; } private boolean isInStartProperty(ILayerNode l) { return getStartProperty().getLayerStateInfo().containsKey(l.getId()); } private LayersProperty getStartProperty() { return (LayersProperty) getStart(); } private boolean isInEndProperty(ILayerNode l) { return getEndProperty().getLayerStateInfo().containsKey(l.getId()); } private LayersProperty getEndProperty() { return (LayersProperty) getEnd(); } }