001/* 002 * Copyright (c) 2015 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2015-12-23 23:08:48 +0000 (Wed, 23 Dec 2015) $' 007 * '$Revision: 34417 $' 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.bbox; 030 031import java.util.ArrayList; 032import java.util.List; 033 034import org.geotools.geometry.jts.ReferencedEnvelope; 035import org.kepler.gis.data.BoundingBoxToken; 036import org.opengis.referencing.crs.CoordinateReferenceSystem; 037 038import ptolemy.actor.TypedAtomicActor; 039import ptolemy.actor.TypedIOPort; 040import ptolemy.actor.parameters.PortParameter; 041import ptolemy.data.ArrayToken; 042import ptolemy.data.BooleanToken; 043import ptolemy.data.DoubleToken; 044import ptolemy.data.RecordToken; 045import ptolemy.data.StringToken; 046import ptolemy.data.Token; 047import ptolemy.data.expr.Parameter; 048import ptolemy.data.type.ArrayType; 049import ptolemy.data.type.BaseType; 050import ptolemy.data.type.RecordType; 051import ptolemy.data.type.Type; 052import ptolemy.kernel.CompositeEntity; 053import ptolemy.kernel.util.Attribute; 054import ptolemy.kernel.util.IllegalActionException; 055import ptolemy.kernel.util.NameDuplicationException; 056import ptolemy.kernel.util.SingletonAttribute; 057 058/** An actor that covers a bounding box with non-overlapping 059 * regions each whose side is less than a maximum length. 060 * 061 * @author Daniel Crawl 062 * @version $Id: CoverBoundingBox.java 34417 2015-12-23 23:08:48Z crawl $ 063 */ 064public class CoverBoundingBox extends TypedAtomicActor { 065 066 public CoverBoundingBox(CompositeEntity container, String name) 067 throws IllegalActionException, NameDuplicationException { 068 super(container, name); 069 070 input = new TypedIOPort(this, "input", true, false); 071 input.setTypeEquals(BoundingBoxToken.BOUNDING_BOX); 072 new SingletonAttribute(input, "_showName"); 073 074 maxLength = new PortParameter(this, "maxLength"); 075 maxLength.setTypeEquals(BaseType.DOUBLE); 076 new SingletonAttribute(maxLength.getPort(), "_showName"); 077 078 regions = new TypedIOPort(this, "regions", false, true); 079 080 labelEachRegion = new Parameter(this, "labelEachRegion"); 081 labelEachRegion.setTypeEquals(BaseType.BOOLEAN); 082 labelEachRegion.setToken(BooleanToken.TRUE); 083 } 084 085 @Override 086 public void attributeChanged(Attribute attribute) throws IllegalActionException { 087 088 if(attribute == labelEachRegion) { 089 _labelEachRegion = ((BooleanToken)labelEachRegion.getToken()).booleanValue(); 090 if(_labelEachRegion) { 091 regions.setTypeEquals(_outputTypeWithID); 092 } else { 093 regions.setTypeEquals(_outputType); 094 } 095 } else { 096 super.attributeChanged(attribute); 097 } 098 } 099 100 /* 101 @Override 102 public Object clone(Workspace workspace) throws CloneNotSupportedException { 103 CoverBoundingBox newObject = (CoverBoundingBox) super.clone(workspace); 104 newObject._regionPrefix = _regionPrefix; 105 return newObject; 106 } 107 */ 108 109 @Override 110 public void fire() throws IllegalActionException { 111 112 maxLength.update(); 113 114 Token token = input.get(0); 115 if(token == null) { 116 throw new IllegalActionException(this, "Must provide bounding box. "); 117 } 118 ReferencedEnvelope envelope = ((BoundingBoxToken)token).boundingBoxValue(); 119 120 CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem(); 121 122 final double maxY = envelope.getMaxY(); 123 final double minY = envelope.getMinY(); 124 final double minX = envelope.getMinX(); 125 final double maxX = envelope.getMaxX(); 126 127 // perform sanity checks 128 if(maxY <= minY) { 129 throw new IllegalActionException(this, 130 "Top is less than or equal to the bottom coordinates."); 131 } 132 133 if(maxX <= minX) { 134 throw new IllegalActionException(this, 135 "Right is smaller than or equal to the left coordinates."); 136 } 137 138 final double maxLengthValue = _readValue(maxLength); 139 140 if(maxLengthValue <= 0) { 141 throw new IllegalActionException(this, 142 "Maximum length must be greater than 0."); 143 } 144 145 // determine height 146 final double height = maxY - minY; 147 final double numRegionsHeight = Math.ceil(height / maxLengthValue); 148 final double regionHeightLength = height / numRegionsHeight; 149 //System.out.println("# regions h = " + regionsHeightLength + ", length = " + regionsHeightLength); 150 151 // determine width 152 final double width = maxX - minX; 153 final double numRegionsWidth = Math.ceil(width / maxLengthValue); 154 final double regionWidthLength = width / numRegionsWidth; 155 //System.out.println("# regions w = " + numRegionsWidth + ", length = " + regionsWidthLength); 156 157 List<Token> tokens = new ArrayList<Token>(); 158 int curID = 0; 159 for(int x = 1; x <= numRegionsWidth; x++) { 160 for(int y = 1; y <= numRegionsHeight; y++) { 161 double curMaxY = minY + y*regionHeightLength; 162 double curMinY = curMaxY - regionHeightLength; 163 double curMaxX = minX + x*regionWidthLength; 164 double curMinX = curMaxX - regionWidthLength; 165 BoundingBoxToken bboxToken = new BoundingBoxToken( 166 new ReferencedEnvelope(curMinX, curMaxX, curMinY, curMaxY, crs)); 167 Token curToken; 168 if(_labelEachRegion) { 169 curToken = new RecordToken(_recordNamesWithID, 170 new Token[] {bboxToken, 171 new StringToken(_regionPrefix + curID)}); 172 curID++; 173 } else { 174 curToken = bboxToken; 175 } 176 177 tokens.add(curToken); 178 179 //System.out.println(curTop + " " + curBottom + " " + curRight + " " + curLeft); 180 } 181 } 182 183 if(_debugging) { 184 _debug("Cover has " + tokens.size() + " regions."); 185 } 186 187 regions.broadcast(new ArrayToken(tokens.toArray(new Token[tokens.size()]))); 188 189 } 190 191 private double _readValue(PortParameter pp) throws IllegalActionException { 192 Token token = pp.getToken(); 193 if(token == null) { 194 throw new IllegalActionException(this, 195 "Missing value for " + pp.getName()); 196 } 197 return ((DoubleToken)token).doubleValue(); 198 } 199 200 /** The input bounding box. */ 201 public TypedIOPort input; 202 203 /** The maximum length of any side of a region within the bounding box. 204 * The units of this length are the same as the units of the 205 * coordinate system used by the bounding box. 206 */ 207 public PortParameter maxLength; 208 209 /** If true, each region is given a unique label. */ 210 public Parameter labelEachRegion; 211 212 /** The non-overlapping regions that cover the bounding box. Each region 213 * is a bounding box. 214 */ 215 public TypedIOPort regions; 216 217 private final static String[] _recordNamesWithID = 218 new String[] {"bbox", "id"}; 219 220 private final static Type _outputType = 221 new ArrayType(BoundingBoxToken.BOUNDING_BOX); 222 223 private final static Type _outputTypeWithID = 224 new ArrayType(new RecordType(_recordNamesWithID, 225 new Type[] { 226 BoundingBoxToken.BOUNDING_BOX, 227 BaseType.STRING})); 228 229 private boolean _labelEachRegion = true; 230 231 // TODO either make final static or changeable via a parameter. 232 private String _regionPrefix = "region"; 233}