001/* 002 * Copyright (c) 2015 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2016-04-20 21:55:48 +0000 (Wed, 20 Apr 2016) $' 007 * '$Revision: 34469 $' 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.data; 030 031import java.io.Serializable; 032 033import org.geotools.geometry.GeneralEnvelope; 034import org.geotools.geometry.jts.ReferencedEnvelope; 035import org.geotools.referencing.CRS; 036import org.geotools.referencing.crs.AbstractCRS; 037import org.opengis.geometry.DirectPosition; 038import org.opengis.geometry.Envelope; 039import org.opengis.referencing.FactoryException; 040import org.opengis.referencing.crs.CoordinateReferenceSystem; 041 042import ptolemy.data.BooleanToken; 043import ptolemy.data.Token; 044import ptolemy.data.type.BaseType; 045import ptolemy.data.type.Type; 046import ptolemy.kernel.util.IllegalActionException; 047import ptolemy.util.StringUtilities; 048 049/** Token containing a GIS bounding box. 050 * 051 * @author Daniel Crawl 052 * @version $Id: BoundingBoxToken.java 34469 2016-04-20 21:55:48Z crawl $ 053 * 054 */ 055public class BoundingBoxToken extends Token { 056 057 /** Create a new BoundingBoxToken from a string value. The value is 058 * the format produced by toString(). 059 */ 060 public BoundingBoxToken(String value) throws IllegalActionException { 061 062 String parts[] = value.split(",", 2); 063 if(parts.length != 2) { 064 throw new IllegalActionException("Could not parse string " + value); 065 } 066 067 // parse the coordinates 068 String[] coords = parts[0].split(" "); 069 if(coords.length < 1 || coords.length % 2 != 0) { 070 throw new IllegalActionException("Invalid number of coordinates: " + parts[0]); 071 } 072 final int halfLength = coords.length / 2; 073 double[] min = new double[halfLength]; 074 double[] max = new double[halfLength]; 075 int i; 076 for(i = 0; i < halfLength; i++) { 077 min[i] = Double.parseDouble(coords[i]); 078 max[i] = Double.parseDouble(coords[i + halfLength]); 079 } 080 081 Envelope envelope = new GeneralEnvelope(min, max); 082 083 // parse the WKT 084 CoordinateReferenceSystem crs; 085 try { 086 crs = CRS.parseWKT(parts[1]); 087 } catch (FactoryException e) { 088 throw new IllegalActionException("Could not parse WKT: " + e.getMessage() + 089 "for: " + parts[1]); 090 } 091 092 _envelope = ReferencedEnvelope.create(envelope, crs); 093 } 094 095 /** Create a new BoundingBoxToken with an evelope. */ 096 public BoundingBoxToken(ReferencedEnvelope envelope) { 097 _envelope = envelope; 098 } 099 100 /** Create a BoundingBoxToken from a string value. */ 101 public static BoundingBoxToken bbox(String value) throws IllegalActionException { 102 return new BoundingBoxToken(value); 103 } 104 105 /** Get the bounding box. */ 106 public ReferencedEnvelope boundingBoxValue() { 107 return _envelope; 108 } 109 110 /** Get the token type. */ 111 @Override 112 public Type getType() { 113 return BOUNDING_BOX; 114 } 115 116 /** Test that the value of this Token is close to the first argument, 117 * where "close" means that the distance between them is less than 118 * or equal to the second argument. 119 * @param other The token to test closeness of this token with. 120 * @param epsilon The value that we use to determine whether two 121 * tokens are close. 122 * @return A boolean token that contains the value true if the 123 * value of this token are close to those of the 124 * argument token. 125 * If either this token or the argument token is a nil token, then 126 * a boolean token that contains the value false is returned. 127 * @exception IllegalActionException If the argument token is not 128 * of a type that can be compared with this token. 129 */ 130 @Override 131 public BooleanToken isCloseTo(Token other, double epsilon) 132 throws IllegalActionException { 133 134 if(other instanceof BoundingBoxToken) { 135 //System.out.println(_envelope.getCoordinateReferenceSystem().getName()); 136 if(((AbstractCRS)_envelope.getCoordinateReferenceSystem()).equals((AbstractCRS) 137 ((BoundingBoxToken)other)._envelope.getCoordinateReferenceSystem(), false) && 138 _envelope.boundsEquals2D(((BoundingBoxToken)other)._envelope, epsilon)) { 139 return BooleanToken.TRUE; 140 } 141 } 142 return BooleanToken.FALSE; 143 } 144 145 /** 146 * Test for equality of the values of this Token and the argument Token. If 147 * the value of this token or the value of the rightArgument token is null, 148 * then we return False. 149 * 150 * @param rightArgument 151 * The Token to test against. 152 * @exception IllegalActionException 153 * Not thrown in this base class. 154 * @return A boolean token that contains the value true if the dates are the 155 * same. 156 */ 157 @Override 158 public final BooleanToken isEqualTo(Token rightArgument) 159 throws IllegalActionException { 160 161 if(rightArgument != null && rightArgument instanceof BoundingBoxToken && 162 _envelope.equals(((BoundingBoxToken)rightArgument)._envelope)) { 163 return BooleanToken.TRUE; 164 } 165 return BooleanToken.FALSE; 166 } 167 168 /** Get the maximum x coordinate. */ 169 public double maxX() { 170 return _envelope.getMaxX(); 171 } 172 173 /** Get the maximum x coordinate. */ 174 public double maxY() { 175 return _envelope.getMaxY(); 176 } 177 178 /** Get the minimum x coordinate. */ 179 public double minX() { 180 return _envelope.getMinX(); 181 } 182 183 /** Get the minimum y coordinate. */ 184 public double minY() { 185 return _envelope.getMinY(); 186 } 187 188 /** Return a string representation of the bounding box. */ 189 @Override 190 public String toString() { 191 192 // the output format is: 193 // bbox("min1 min2 max1 max2, WKT") 194 195 DirectPosition lower = _envelope.getLowerCorner(); 196 DirectPosition upper = _envelope.getUpperCorner(); 197 198 StringBuilder buf = new StringBuilder("bbox(\""); 199 int i; 200 for(i = 0; i < lower.getDimension(); i++) { 201 buf.append(lower.getOrdinate(i)) 202 .append(" "); 203 } 204 for(i = 0; i < upper.getDimension() - 1; i++) { 205 buf.append(upper.getOrdinate(i)) 206 .append(" "); 207 } 208 209 String crsStr; 210 CoordinateReferenceSystem crs = _envelope.getCoordinateReferenceSystem(); 211 if(crs == null) { 212 // FIXME handle null crs 213 crsStr = "unknown"; 214 } else { 215 crsStr = StringUtilities.escapeString(crs.toWKT()); 216 } 217 218 buf.append(upper.getOrdinate(i)) 219 .append(", ") 220 .append(crsStr) 221 .append("\")"); 222 223 return buf.toString(); 224 } 225 226 /** The Type for the BoundingBoxToken. */ 227 public static class BoundingBoxType implements Cloneable, Type, Serializable { 228 229 private BoundingBoxType() { 230 BaseType.addType(this, "bbox", BoundingBoxToken.class); 231 } 232 233 @Override 234 public Type add(Type rightArgumentType) { 235 // TODO Auto-generated method stub 236 return this; 237 } 238 239 @Override 240 public Object clone() { 241 return this; 242 } 243 244 /** Convert the specified token into a BoundingBox token. */ 245 @Override 246 public Token convert(Token token) throws IllegalActionException { 247 if (token instanceof BoundingBoxToken) { 248 return token; 249 } else { 250 // TODO this could be done for vector and raster tokens 251 throw new IllegalActionException("Attempt to convert token " 252 + token + " into a BoundingBox token, which is not possible."); 253 } 254 } 255 256 @Override 257 public Type divide(Type rightArgumentType) { 258 // TODO Auto-generated method stub 259 return this; 260 } 261 262 @Override 263 public int getTypeHash() { 264 return hashCode(); 265 } 266 267 @Override 268 public Class<?> getTokenClass() { 269 return BoundingBoxToken.class; 270 } 271 272 @Override 273 public boolean isAbstract() { 274 return false; 275 } 276 277 @Override 278 public boolean isCompatible(Type type) { 279 return type == this; 280 } 281 282 @Override 283 public boolean isConstant() { 284 return true; 285 } 286 287 @Override 288 public boolean isInstantiable() { 289 return true; 290 } 291 292 @Override 293 public boolean isSubstitutionInstance(Type type) { 294 return type == this; 295 } 296 297 @Override 298 public Type modulo(Type rightArgumentType) { 299 // TODO Auto-generated method stub 300 return this; 301 } 302 303 @Override 304 public Type multiply(Type rightArgumentType) { 305 // TODO Auto-generated method stub 306 return this; 307 } 308 309 @Override 310 public Type one() { 311 // TODO Auto-generated method stub 312 return this; 313 } 314 315 @Override 316 public Type subtract(Type rightArgumentType) { 317 // TODO Auto-generated method stub 318 return this; 319 } 320 321 @Override 322 public String toString() { 323 return "bbox"; 324 } 325 326 @Override 327 public Type zero() { 328 // TODO Auto-generated method stub 329 return this; 330 } 331 332 } 333 334 /** The bounding box type. */ 335 public static final BoundingBoxType BOUNDING_BOX = new BoundingBoxType(); 336 337 /** The GIS envelope containing the bounding box coordinates. */ 338 private ReferencedEnvelope _envelope; 339 340}