001/*
002 * Copyright (c) 2015 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-12-16 21:30:37 +0000 (Wed, 16 Dec 2015) $' 
007 * '$Revision: 34341 $'
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.kml;
030
031import java.io.File;
032import java.io.FileNotFoundException;
033import java.io.IOException;
034import java.text.SimpleDateFormat;
035import java.util.Date;
036
037import de.micromata.opengis.kml.v_2_2_0.Kml;
038import ptolemy.actor.TypedAtomicActor;
039import ptolemy.actor.TypedIOPort;
040import ptolemy.actor.parameters.PortParameter;
041import ptolemy.data.DateToken;
042import ptolemy.data.StringToken;
043import ptolemy.data.Token;
044import ptolemy.data.type.BaseType;
045import ptolemy.kernel.CompositeEntity;
046import ptolemy.kernel.util.IllegalActionException;
047import ptolemy.kernel.util.NameDuplicationException;
048
049/** Add a timestamp to a KML file.
050 * 
051 *  @author Daniel Crawl
052 *  @version $Id: WriteTimestampToKML.java 34341 2015-12-16 21:30:37Z crawl $
053 */
054public class WriteTimestampToKML extends TypedAtomicActor {
055
056    public WriteTimestampToKML(CompositeEntity container, String name)
057            throws IllegalActionException, NameDuplicationException {
058        super(container, name);
059
060        input = new TypedIOPort(this, "input", true, false);
061        input.setTypeEquals(BaseType.STRING);
062 
063        filename = new PortParameter(this, "filename");
064        filename.setTypeEquals(BaseType.STRING);
065        filename.getPort().setTypeEquals(BaseType.STRING);
066        
067        date = new TypedIOPort(this, "date", true, false);
068        date.setTypeEquals(BaseType.DATE);
069        
070        output = new TypedIOPort(this, "output", false, true);
071        output.setTypeEquals(BaseType.STRING);
072    }
073    
074    @Override
075    public void fire() throws IllegalActionException {
076        
077        super.fire();
078        
079        String inKMLNameStr = ((StringToken)input.get(0)).stringValue();
080        if(inKMLNameStr.trim().isEmpty()) {
081            throw new IllegalActionException(this, "Input KML is empty.");
082        }
083        
084        File inKML = new File(inKMLNameStr);
085        if(!inKML.exists()) {
086            throw new IllegalActionException(this, "Input KML does not exist: " + inKML);
087        }
088        
089        filename.update();
090        File outKML = null;
091        Token token = filename.getToken();
092        if(token != null) {
093            String outKMLNameStr = ((StringToken)token).stringValue();
094            if(outKMLNameStr.trim().isEmpty()) {
095                throw new IllegalActionException(this, "Output KML name is empty.");
096            }
097            outKML = new File(outKMLNameStr);
098        } else {
099            outKML = inKML;
100        }
101        
102        Date dateVal = new Date(((DateToken)date.get(0)).getValue());
103        String formattedDateStr;
104        synchronized(KML_TIMESTAMP_FORMAT) {
105            formattedDateStr = KML_TIMESTAMP_FORMAT.format(dateVal);
106        }
107        
108        Kml[] kmls = null;
109        if(inKMLNameStr.toLowerCase().endsWith(".kmz")) {
110            try {
111                kmls = Kml.unmarshalFromKmz(inKML);
112            } catch (IOException e) {
113                throw new IllegalActionException(this, e, "Error unmarshalling KMZ.");
114            }
115            // TODO
116            throw new IllegalActionException(this, "Reading from KMZ not supported.");
117        } else {
118            kmls = new Kml[1];
119            kmls[0] = Kml.unmarshal(inKML);
120        }
121        
122        for(Kml kml: kmls) {
123            //_addTimestampToFeature(kml.getFeature(), formattedDateStr);
124            kml.getFeature().createAndSetTimeStamp().setWhen(formattedDateStr);
125        }
126        
127        if(outKML.getName().toLowerCase().endsWith("kmz") || kmls.length > 1) {
128            try {
129                Kml[] otherKMLs = new Kml[kmls.length - 1];
130                System.arraycopy(kmls, 1, otherKMLs, 0, otherKMLs.length);
131                kmls[0].marshalAsKmz(outKML.getAbsolutePath(), otherKMLs);
132            } catch (IOException e) {
133                throw new IllegalActionException(this, e, "Error writing KMZ to " + outKML);
134            }
135        } else {
136            try {
137                kmls[0].marshal(outKML);
138            } catch (FileNotFoundException e) {
139                throw new IllegalActionException(this, e, "Error writing to " + outKML);
140            }            
141        }
142        
143        output.broadcast(new StringToken(outKML.getAbsolutePath()));
144    }
145
146    /** The KML to read. */
147    public TypedIOPort input;
148    
149    /** The name of the KML file to write. If not specified, then the input KML
150     *  will be overwritten. If the input file is a KMZ containing more than
151     *  one KML, then the output is written as a KMZ. 
152     */
153    public PortParameter filename;
154    
155    /** The timestamp to set. */
156    public TypedIOPort date;
157    
158    /** The name of the updated KML file. */
159    public TypedIOPort output;
160
161    /** Format for KML timestamps. */
162    public final static SimpleDateFormat KML_TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmXXX");
163    
164    /** Set the timestamp to a Feature and all contained Features. */
165    /*
166    private void _addTimestampToFeature(Feature feature, String formattedDateStr) {
167        if(feature instanceof Placemark) {
168            ((Placemark)feature).createAndSetTimeStamp().setWhen(formattedDateStr);
169        } else {
170            List<Feature> subFeatures = null;
171            if(feature instanceof Document) {
172                subFeatures = ((Document)feature).getFeature();
173            } else if(feature instanceof Folder) {
174                subFeatures = ((Folder)feature).getFeature();
175            }
176            
177            if(subFeatures != null) {
178                for(Feature subFeature : subFeatures) {
179                    _addTimestampToFeature(subFeature, formattedDateStr);
180                }
181            }
182        }
183    }
184    */
185    
186}