/******************************************************************************* * Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com) * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v3 * which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt ******************************************************************************/ package com.opendoorlogistics.core.scripts.formulae.image; import java.awt.Color; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import com.opendoorlogistics.api.tables.ODLTableReadOnly; import com.opendoorlogistics.core.cache.ApplicationCache; import com.opendoorlogistics.core.cache.RecentlyUsedCache; import com.opendoorlogistics.core.formulae.Function; import com.opendoorlogistics.core.formulae.FunctionImpl; import com.opendoorlogistics.core.formulae.FunctionParameters; import com.opendoorlogistics.core.formulae.Functions; import com.opendoorlogistics.core.gis.map.RenderProperties; import com.opendoorlogistics.core.gis.map.RenderProperties.NumericRenderProp; import com.opendoorlogistics.core.gis.map.SynchronousRenderer; import com.opendoorlogistics.core.gis.map.View; import com.opendoorlogistics.core.gis.map.data.DrawableObjectImpl; import com.opendoorlogistics.core.scripts.formulae.image.ImageFormulaUtils.FilterMode; import com.opendoorlogistics.core.tables.ColumnValueProcessor; import com.opendoorlogistics.core.tables.beans.BeanMapping.BeanDatastoreMapping; import com.opendoorlogistics.core.utils.Numbers; import com.opendoorlogistics.core.utils.SimpleSoftReferenceMap; import com.opendoorlogistics.core.utils.images.ImageUtils; /** * fmimage uses an image cache... It assumes the script execution framework takes care of refreshing etc when data changes * @author Phil * */ final public class FmImage extends FunctionImpl { final private ODLTableReadOnly table; final private int groupKeyIndx; final private RenderProperties properties; final private Mode mode; public enum Mode { SHOW_SELECTED("1", "Show only the matching objects in the table and the background map, zooming on the matching objects."), SHOW_ALL_ZOOM_SELECTED("2", "Show all objects in the table and the background map, zooming on the matching objects."), ZOOM_ONLY("3","Show background map only, zooming on the matching objects."), SHOW_SELECTED_NO_BACKGROUND("4", "Show the matching objects without the background map, zooming on the matching objects."), SHOW_INVERSE_SELECTED_NO_BACKGROUND("5", "Show the non-matching objects without the background map, zooming on the matching objects."), SHOW_INVERSE_SELECTED("6","Show the non-matching objects with the background map, zooming on the matching objects."), SHOW_MATCHING_OR_EMPTY_ZOOM_MATCHING("7","Show the matching objects or those objects with an empty or null image formula key, but zooming only on the matching objects.") ; private Mode(String keyword,String description) { this.keyword = keyword; this.description = description; } private final String keyword; private final String description; public String getDescription(){ return description; } public String getKeyword(){ return keyword; } } private FmImage(Function foreignKeyValue, ODLTableReadOnly table, int groupKeyIndx,Mode mode, Function width, Function height, Function dotsPerCM, RenderProperties flags) { super(dotsPerCM != null ? new Function[] { foreignKeyValue, width, height, dotsPerCM } : new Function[] { foreignKeyValue, width, height }); this.table = table; this.groupKeyIndx = groupKeyIndx; this.mode = mode; flags = new RenderProperties(flags); if(mode == Mode.SHOW_SELECTED_NO_BACKGROUND || mode==Mode.SHOW_INVERSE_SELECTED_NO_BACKGROUND){ ImageFormulaUtils.setNotToRenderBackgroundMap(flags); } this.properties = flags; } private RecentlyUsedCache cache(){ return ApplicationCache.singleton().get(ApplicationCache.IMAGE_FORMULAE_CACHE); } public static FmImage createFixedPixelSize(Function foreignKeyValue, ODLTableReadOnly table, int groupKeyIndx, Mode mode,Function width, Function height, RenderProperties flags) { return new FmImage(foreignKeyValue, table, groupKeyIndx,mode, width, height, null, flags); } public static FmImage createFixedPhysicalSize(Function foreignKeyValue, ODLTableReadOnly table, int groupKeyIndx,Mode mode, Function width, Function height, Function dotsPerCM, RenderProperties properties) { return new FmImage(foreignKeyValue, table, groupKeyIndx,mode, width, height, dotsPerCM, properties); } private boolean isFixedPhysicalSize() { return nbChildren() == 4; } private ImageFormulaCacheKey getCacheKey(FunctionParameters parameters) { Object[] vals = executeChildFormulae(parameters, true); if (vals == null) { return null; } // get width and height int formulaIndx = 1; Double width = Numbers.toDouble(vals[formulaIndx++]); Double height = Numbers.toDouble(vals[formulaIndx++]); if (width == null || height == null) { return null; } Double dotsPerCM = 0.0; if (isFixedPhysicalSize()) { dotsPerCM = Numbers.toDouble(vals[formulaIndx++]); } ImageFormulaCacheKey key = new ImageFormulaCacheKey(this,vals[0], width.intValue(), height.intValue(), dotsPerCM.doubleValue()); return key; } @Override public Object execute(FunctionParameters parameters) { ImageFormulaCacheKey key = getCacheKey(parameters); if (key == null) { return Functions.EXECUTION_ERROR; } // try to fetch using the cache key BufferedImage image = (BufferedImage)cache().get(key); if (image != null) { return image; } // find matching values in the other table List<DrawableObjectImpl> matchingPoints = ImageFormulaUtils.getPoints(table,groupKeyIndx, key.keyval,FilterMode.FILTER); BufferedImage ret = null; switch(mode){ case SHOW_ALL_ZOOM_SELECTED: ret = ImageFormulaUtils.createImage(key.width, key.height, key.dotsPerCM,properties,isFixedPhysicalSize(), matchingPoints,ImageFormulaUtils.getPoints(table,-1, null, FilterMode.NONE)); break; case SHOW_SELECTED: case SHOW_SELECTED_NO_BACKGROUND: ret = ImageFormulaUtils.createImage(key.width, key.height, key.dotsPerCM,properties,isFixedPhysicalSize(), matchingPoints,matchingPoints); break; case SHOW_INVERSE_SELECTED: case SHOW_INVERSE_SELECTED_NO_BACKGROUND: ret = ImageFormulaUtils.createImage(key.width, key.height, key.dotsPerCM,properties,isFixedPhysicalSize(), matchingPoints,ImageFormulaUtils.getPoints(table,groupKeyIndx, key.keyval,FilterMode.INVERSE_FILTER)); break; case ZOOM_ONLY: ret = ImageFormulaUtils.createImage(key.width, key.height, key.dotsPerCM,properties,isFixedPhysicalSize(), matchingPoints,null); break; case SHOW_MATCHING_OR_EMPTY_ZOOM_MATCHING: List<DrawableObjectImpl> matchingOrNull = ImageFormulaUtils.getPoints(table,groupKeyIndx, key.keyval,FilterMode.FILTER_MATCH_OR_NULL_VALUE); ret = ImageFormulaUtils.createImage(key.width, key.height, key.dotsPerCM,properties,isFixedPhysicalSize(), matchingPoints, matchingOrNull); break; } // add to cache cache().put(key, ret, (long)ret.getWidth() * (long)ret.getHeight() * 4); return ret; } @Override public Function deepCopy() { throw new UnsupportedOperationException(); } }