001/*
002 * Copyright (c) 2017 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2017-03-27 21:55:16 +0000 (Mon, 27 Mar 2017) $' 
007 * '$Revision: 34553 $'
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.pylaski;
030
031import java.text.DateFormat;
032import java.text.ParseException;
033import java.text.SimpleDateFormat;
034import java.util.ArrayList;
035import java.util.Date;
036
037import org.geotools.data.simple.SimpleFeatureCollection;
038import org.geotools.data.simple.SimpleFeatureIterator;
039import org.kepler.gis.data.VectorToken;
040import org.opengis.feature.simple.SimpleFeature;
041
042import ptolemy.actor.lib.Transformer;
043import ptolemy.actor.parameters.PortParameter;
044import ptolemy.data.ArrayToken;
045import ptolemy.data.DoubleToken;
046import ptolemy.data.IntToken;
047import ptolemy.data.StringToken;
048import ptolemy.data.Token;
049import ptolemy.data.type.ArrayType;
050import ptolemy.data.type.BaseType;
051import ptolemy.kernel.CompositeEntity;
052import ptolemy.kernel.util.IllegalActionException;
053import ptolemy.kernel.util.NameDuplicationException;
054import ptolemy.kernel.util.SingletonAttribute;
055
056/** Extract measurements in query results from GetPylaskiMeasurements.
057 *
058 * @author Ben Fleming
059 * @version $Id: PylaskiProperty.java 34553 2017-03-27 21:55:16Z crawl $
060 */
061public class PylaskiProperty extends Transformer
062{
063        //
064        public PortParameter propertyName;
065
066        // The format of the date/time values provided by the Pylaski API
067        public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ssZ";
068
069        /**
070         *
071         * @param container
072         * @param name
073         * @throws NameDuplicationException
074         * @throws IllegalActionException
075         */
076        public PylaskiProperty(CompositeEntity container, String name)
077        throws NameDuplicationException, IllegalActionException
078        {
079                super(container, name);
080
081                input.setTypeEquals(VectorToken.VECTOR);
082                output.setTypeEquals(new ArrayType(BaseType.DOUBLE));
083
084                propertyName = new PortParameter(this, "name");
085                propertyName.setStringMode(true);
086                propertyName.setTypeEquals(BaseType.STRING);
087                propertyName.getPort().setTypeEquals(BaseType.STRING);
088                new SingletonAttribute(propertyName.getPort(), "_showName");
089        }
090
091        /**
092         *
093         * @throws IllegalActionException
094         */
095        @Override
096        public void fire()
097        throws IllegalActionException
098        {
099                super.fire();
100
101                propertyName.update();
102
103                StringToken nameToken = (StringToken) propertyName.getToken();
104                String nameStr = (nameToken == null ? "" : nameToken.stringValue()).trim();
105
106                if(nameStr.isEmpty())
107                {
108                        throw new IllegalActionException(this, "Property name must not be empty.");
109                }
110
111                ArrayList<Token> propertyValues = new ArrayList<>();
112
113                VectorToken vectorToken = (VectorToken) input.get(0);
114                SimpleFeatureCollection features = vectorToken.getVectors();
115
116                SimpleFeature closestDataFeature = null;
117                SimpleFeature closestForecastFeature = null;
118
119                try(SimpleFeatureIterator iterator = features.features())
120                {
121                        while(iterator.hasNext())
122                        {
123                                SimpleFeature feature = iterator.next();
124
125                                // TODO Get closest distance
126                                // So far can't do this as required data is stripped out
127                                // Include a "must-have" property list IO port/parameter
128
129                                Object distanceMap = feature.getAttribute("distanceFromLocation");
130                                //System.out.println(distanceMap);
131                                // Object propertyValue = feature.getAttribute(nameStr);
132
133                                if(closestDataFeature == null)
134                                {
135                                        closestDataFeature = feature;
136                                }
137                        }
138                }
139
140                if(closestDataFeature != null)
141                {
142                        propertyValues.addAll(_getPropertyValueTokens(nameStr, closestDataFeature));
143                }
144
145                if(closestForecastFeature != null)
146                {
147                        propertyValues.addAll(_getPropertyValueTokens(nameStr, closestForecastFeature));
148                }
149
150                if(propertyValues.isEmpty())
151                {
152                        throw new IllegalActionException(this, "Property " + nameStr + " was not found in features.");
153                }
154
155                output.broadcast(new ArrayToken(propertyValues.toArray(new Token[propertyValues.size()])));
156        }
157
158        /**
159         *
160         * @param property
161         * @param value
162         * @return
163         */
164        private Token _getPropertyValueToken(String property, Object value)
165                throws IllegalActionException
166        {
167                switch(property)
168                {
169                        case "temperature":
170                        case "wind_speed":
171                        {
172                                return new DoubleToken(value == null ? 0 : (Double) value);
173                        }
174
175                        case "timestamp":
176                        {
177                                DateFormat df = new SimpleDateFormat(DATE_FORMAT);
178
179                                try
180                                {
181                                        Date timestamp = df.parse(value.toString());
182
183                                        return new DoubleToken((double) timestamp.getTime());
184                                }
185                                catch(ParseException e)
186                                {
187                                        //System.out.println("TIMESTAMP COULD NOT BE PARSED");
188                                        throw new IllegalActionException(this, e, "Timestamp couldn't be parsed.");
189                                }
190                        }
191                }
192
193                if(value instanceof Double)
194                {
195                        return new DoubleToken((Double) value);
196                }
197
198                if(value instanceof Integer)
199                {
200                        return new IntToken((Integer) value);
201                }
202
203                return new StringToken(value.toString());
204        }
205
206        /**
207         *
208         * @param property
209         * @param values
210         * @return
211         */
212        private ArrayList<Token> _getPropertyValueTokens(String property, Object values)
213            throws IllegalActionException
214        {
215                ArrayList<Token> tokenValues = new ArrayList<>();
216
217                if(values instanceof ArrayList)
218                {
219                        for(Object value : (ArrayList<?>) values)
220                        {
221                                tokenValues.add(_getPropertyValueToken(property, value));
222                        }
223                }
224                else
225                {
226                        tokenValues.add(_getPropertyValueToken(property, values));
227                }
228
229                return tokenValues;
230        }
231
232        /**
233         *
234         * @param property
235         * @param feature
236         * @return
237         */
238        private ArrayList<Token> _getPropertyValueTokens(String property, SimpleFeature feature)
239                throws IllegalActionException
240        {
241                return _getPropertyValueTokens(property, feature.getAttribute(property));
242        }
243}