001/*
002 * Copyright (c) 2014-2015 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-12-17 19:34:32 +0000 (Thu, 17 Dec 2015) $' 
007 * '$Revision: 34347 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029package org.kepler.gis.actor;
030
031import java.util.ArrayList;
032import java.util.List;
033
034import ptolemy.actor.TypedAtomicActor;
035import ptolemy.actor.TypedIOPort;
036import ptolemy.actor.parameters.PortParameter;
037import ptolemy.data.ArrayToken;
038import ptolemy.data.BooleanToken;
039import ptolemy.data.DoubleToken;
040import ptolemy.data.RecordToken;
041import ptolemy.data.StringToken;
042import ptolemy.data.Token;
043import ptolemy.data.expr.Parameter;
044import ptolemy.data.type.ArrayType;
045import ptolemy.data.type.BaseType;
046import ptolemy.data.type.RecordType;
047import ptolemy.data.type.Type;
048import ptolemy.kernel.CompositeEntity;
049import ptolemy.kernel.util.Attribute;
050import ptolemy.kernel.util.IllegalActionException;
051import ptolemy.kernel.util.NameDuplicationException;
052import ptolemy.kernel.util.SingletonAttribute;
053
054/** An actor that covers a bounding box with non-overlapping
055 *  regions each whose side is less than a maximum length.
056 *  
057 *  @deprecated Use CoverBoundingBox instead.
058 *  @see org.kepler.gis.actor.bbox.CoverBoundingBox
059 *  
060 *  @author Daniel Crawl
061 *  @version $Id: SplitBoundingBox.java 34347 2015-12-17 19:34:32Z crawl $
062 */
063public class SplitBoundingBox extends TypedAtomicActor {
064
065        public SplitBoundingBox(CompositeEntity container, String name)
066                        throws IllegalActionException, NameDuplicationException {
067                super(container, name);
068                
069                top = new PortParameter(this, "top");
070                top.setTypeEquals(BaseType.DOUBLE);
071                new SingletonAttribute(top.getPort(), "_showName");
072
073                bottom = new PortParameter(this, "bottom");
074                bottom.setTypeEquals(BaseType.DOUBLE);
075                new SingletonAttribute(bottom.getPort(), "_showName");
076
077                left = new PortParameter(this, "left");
078                left.setTypeEquals(BaseType.DOUBLE);
079                new SingletonAttribute(left.getPort(), "_showName");
080
081                right = new PortParameter(this, "right");
082                right.setTypeEquals(BaseType.DOUBLE);
083                new SingletonAttribute(right.getPort(), "_showName");
084
085                maxLength = new PortParameter(this, "maxLength");
086                maxLength.setTypeEquals(BaseType.DOUBLE);
087                new SingletonAttribute(maxLength.getPort(), "_showName");
088
089                regions = new TypedIOPort(this, "regions", false, true);
090
091                labelEachRegion = new Parameter(this, "labelEachRegion");
092                labelEachRegion.setTypeEquals(BaseType.BOOLEAN);
093                labelEachRegion.setToken(BooleanToken.TRUE);
094        }
095        
096        @Override
097        public void attributeChanged(Attribute attribute) throws IllegalActionException {
098                
099                if(attribute == labelEachRegion) {
100                        _labelEachRegion = ((BooleanToken)labelEachRegion.getToken()).booleanValue();
101                        if(_labelEachRegion) {
102                                regions.setTypeEquals(_outputTypeWithID);
103                        } else {
104                                regions.setTypeEquals(_outputType);
105                        }
106                } else {
107                        super.attributeChanged(attribute);
108                }
109        }
110        
111        /*
112    @Override
113    public Object clone(Workspace workspace) throws CloneNotSupportedException {
114        SplitBoundingBox newObject = (SplitBoundingBox) super.clone(workspace);
115        newObject._regionPrefix = _regionPrefix;
116        return newObject;
117    }
118    */
119
120        @Override
121        public void fire() throws IllegalActionException {
122                
123                top.update();
124                bottom.update();
125                left.update();
126                right.update();
127                maxLength.update();
128
129                final double topValue = _readValue(top);
130                final double bottomValue = _readValue(bottom);
131                final double leftValue = _readValue(left);
132                final double rightValue = _readValue(right);
133                
134                // perform sanity checks
135                if(topValue <= bottomValue) {
136                        throw new IllegalActionException(this,
137                                "Top is less than or equal to the bottom coordinates.");
138                }
139                
140                if(rightValue <= leftValue) {
141                        throw new IllegalActionException(this,
142                                "Right is smaller than or equal to the left coordinates.");
143                }
144
145                final double maxLengthValue = _readValue(maxLength);
146                
147                if(maxLengthValue <= 0) {
148                        throw new IllegalActionException(this,
149                                "Maximum length must be greater than 0.");
150                }
151                        
152                // determine height
153                final double height = topValue - bottomValue;
154                final double numRegionsHeight = Math.ceil(height / maxLengthValue);
155                final double regionHeightLength = height / numRegionsHeight;
156                //System.out.println("# regions h = " + regionsHeightLength + ", length = " + regionsHeightLength);
157                                
158                // determine width
159                final double width = rightValue - leftValue;
160                final double numRegionsWidth = Math.ceil(width / maxLengthValue);
161                final double regionWidthLength = width / numRegionsWidth;
162                //System.out.println("# regions w = " + numRegionsWidth + ", length = " + regionsWidthLength);
163                
164                List<RecordToken> tokens = new ArrayList<RecordToken>();
165                int curID = 0;
166                for(int x = 1; x <= numRegionsWidth; x++) {
167                        for(int y = 1; y <= numRegionsHeight; y++) {
168                                DoubleToken curTop =
169                                        new DoubleToken(bottomValue + y*regionHeightLength);
170                                DoubleToken curBottom =
171                                        new DoubleToken(curTop.doubleValue() - regionHeightLength);
172                                DoubleToken curRight =
173                                        new DoubleToken(leftValue + x*regionWidthLength);
174                                DoubleToken curLeft =
175                                        new DoubleToken(curRight.doubleValue() - regionWidthLength);
176                                RecordToken record = null;
177                                if(_labelEachRegion) {
178                                        record = new RecordToken(_recordNamesWithID,
179                                                        new Token[] {curTop, curBottom, curRight, curLeft,
180                                                                new StringToken(_regionPrefix + curID)});
181                                        curID++;
182                                } else {
183                                        record = new RecordToken(_recordNames,
184                                                        new Token[] {curTop, curBottom, curRight, curLeft});
185                                }
186                                
187                                tokens.add(record);
188                                
189                                //System.out.println(curTop + " " + curBottom + " " + curRight + " " + curLeft);
190                        }                       
191                }
192
193                regions.broadcast(new ArrayToken(tokens.toArray(new Token[tokens.size()])));
194
195        }
196        
197        private double _readValue(PortParameter pp) throws IllegalActionException {
198                Token token = pp.getToken();
199                if(token == null) {
200                        throw new IllegalActionException(this,
201                                "Missing value for " + pp.getName());
202                }
203                return ((DoubleToken)token).doubleValue();
204        }
205        
206        /** The latitude of the top of the bounding box. */
207        public PortParameter top;
208   
209        /** The latitude of the bottom of the bounding box. */
210        public PortParameter bottom;
211        
212        /** The longitude of the left of the bounding box. */
213        public PortParameter left;
214
215        /** The longitude of the right of the bounding box. */
216        public PortParameter right;
217        
218        /** The maximum length of any side of a region within the bounding box. */
219        public PortParameter maxLength;
220
221        /** If true, each region is given a unique label. */
222        public Parameter labelEachRegion;
223        
224        /** The non-overlapping regions that cover the bounding box. */
225        public TypedIOPort regions;
226        
227        private final static String[] _recordNames =
228                new String[] {"top", "bottom", "right", "left"};
229        private final static String[] _recordNamesWithID =
230                new String[] {"top", "bottom", "right", "left", "id"};
231        
232        private final static Type _outputType =
233                new ArrayType(new RecordType(_recordNames,
234                new Type[] {
235                    BaseType.DOUBLE,
236                    BaseType.DOUBLE,
237                    BaseType.DOUBLE,
238                    BaseType.DOUBLE}));
239
240        private final static Type _outputTypeWithID =
241                new ArrayType(new RecordType(_recordNamesWithID,
242                new Type[] {
243                        BaseType.DOUBLE,
244                        BaseType.DOUBLE,
245                        BaseType.DOUBLE,
246                        BaseType.DOUBLE,
247                        BaseType.STRING}));
248
249        private boolean _labelEachRegion = true;
250        
251        // TODO either make final static or changeable via a parameter.
252        private String _regionPrefix = "region";
253}