/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2008 - 2010, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.client; import java.awt.Color; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import javax.imageio.ImageIO; import org.opengis.geometry.Envelope; import org.opengis.metadata.Identifier; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.TemporalCRS; import org.opengis.referencing.crs.VerticalCRS; import org.opengis.util.FactoryException; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.internal.referencing.GeodeticObjectBuilder; import org.apache.sis.referencing.CRS; import org.apache.sis.referencing.crs.DefaultTemporalCRS; import org.apache.sis.referencing.CommonCRS; import org.geotoolkit.resources.Errors; import org.geotoolkit.temporal.object.TemporalUtilities; import org.apache.sis.util.Utilities; /** * Utilities methods that provide conversion functionnalities between real objects and queries * (in both ways). * * @author Cédric Briançon (Geomatys) * @author Johann Sorel (Geomatys) */ public final class RequestsUtilities { /** * List of supported mime-types. */ private static final Set<String> SUPPORTED_FORMATS = new HashSet<String>(Arrays.asList(ImageIO.getWriterMIMETypes())); static { SUPPORTED_FORMATS.addAll(Arrays.asList(ImageIO.getWriterFormatNames())); } private RequestsUtilities() {} /** * Returns a string representation of the {@code Bounding Box}. It is a comma-separated * list matching with this pattern: minx,miny,maxx,maxy. * * @param envelope The envelope to return the string representation. */ public static String toBboxValue(final Envelope envelope) { final StringBuilder builder = new StringBuilder(); final int dimEnv = envelope.getDimension(); for (int i=0; i<dimEnv; i++) { builder.append(envelope.getMinimum(i)).append(','); } for (int j=0; j<dimEnv; j++) { if (j>0) { builder.append(','); } builder.append(envelope.getMaximum(j)); } return builder.toString(); } /** * Parse a boolean from a string value. * * @param strTransparent * @return */ public static boolean toBoolean(final String strTransparent) { if (strTransparent == null) { return false; } return Boolean.parseBoolean(strTransparent.trim()); } /** * Get the {@link Color} object matching with the given string. * * @param background A string representing the color. It will be given to * {@link Color#decode(String)}. * @return The color object, or {@code null} if the given string is {@code null}. * @throws NumberFormatException if the specified string cannot be interpreted * as a decimal, octal, or hexidecimal integer. */ public static Color toColor(String background) throws NumberFormatException{ Color color = null; if (background != null) { background = background.trim(); color = Color.decode(background); } return color; } /** * Returns the CRS code for the specified envelope, or {@code null} if not found. * * @param envelope The envelope to return the CRS code. */ public static String toCrsCode(final Envelope envelope) { if (Utilities.equalsIgnoreMetadata(envelope.getCoordinateReferenceSystem(), CommonCRS.WGS84.normalizedGeographic())) { return "EPSG:4326"; } final Set<Identifier> identifiers = envelope.getCoordinateReferenceSystem().getIdentifiers(); if (identifiers != null && !identifiers.isEmpty()) { return identifiers.iterator().next().toString(); } return null; } /** * Extends the {@link Double#valueOf(String)} method, in removing the leading * and trailing whitespace. * * @param value A string representing a double value. * @return A double representation of the given string, or {@code NaN} if the * given string is null. * @throws NumberFormatException if the string can't be parsed as an double value. */ public static double toDouble(String value) throws NumberFormatException { if (value == null) { return Double.NaN; } value = value.trim(); return Double.parseDouble(value); } /** * Converts a string representing the bbox coordinates into a {@link GeneralEnvelope}. * * @param bbox Coordinates of the bounding box, seperated by comas. * @param crs The {@linkplain CoordinateReferenceSystem coordinate reference system} in * which the envelope is expressed. Must not be {@code null}. * @return The enveloppe for the bounding box specified, or an * {@linkplain GeneralEnvelope#setToInfinite infinite envelope} * if the bbox is {@code null}. * @throws IllegalArgumentException if the given CRS is {@code null}, or if the bbox string * contains too much parameters to fill the CRS ranges. */ public static Envelope toEnvelope(final String bbox, final CoordinateReferenceSystem crs) throws IllegalArgumentException { if (crs == null) { throw new IllegalArgumentException("The CRS must not be null"); } if (bbox == null) { final GeneralEnvelope infinite = new GeneralEnvelope(crs); infinite.setToInfinite(); return infinite; } final GeneralEnvelope envelope = new GeneralEnvelope(crs); final int dimension = envelope.getDimension(); final StringTokenizer tokens = new StringTokenizer(bbox, ",;"); final double[] coordinates = new double[dimension * 2]; int index = 0; while (tokens.hasMoreTokens()) { final double value = toDouble(tokens.nextToken()); if (index >= coordinates.length) { throw new IllegalArgumentException( Errors.format(Errors.Keys.IllegalCsDimension_1, coordinates.length)); } coordinates[index++] = value; } if ((index & 1) != 0) { throw new IllegalArgumentException( Errors.format(Errors.Keys.OddArrayLength_1, index)); } // Fallthrough in every cases. switch (index) { default: { while (index >= 6) { final double maximum = coordinates[--index]; final double minimum = coordinates[--index]; envelope.setRange(index >> 1, minimum, maximum); } } case 4: envelope.setRange(1, coordinates[1], coordinates[3]); case 3: case 2: envelope.setRange(0, coordinates[0], coordinates[2]); case 1: case 0: break; } /* * Checks the envelope validity. Given that the parameter order in the bounding box * is a little-bit counter-intuitive, it is worth to perform this check in order to * avoid a NonInvertibleTransformException at some later stage. */ for (index = 0; index < dimension; index++) { final double minimum = envelope.getMinimum(index); final double maximum = envelope.getMaximum(index); if (!(minimum < maximum)) { throw new IllegalArgumentException( Errors.format(Errors.Keys.IllegalRange_2, minimum, maximum)); } } return envelope; } /** * Converts a string representing the bbox coordinates into a {@link GeneralEnvelope}. * * @param bbox Coordinates of the bounding box, seperated by comas. * @param crs The {@linkplain CoordinateReferenceSystem coordinate reference system} in * which the envelope is expressed. Must not be {@code null}. * @return The enveloppe for the bounding box specified, or an * {@linkplain GeneralEnvelope#setToInfinite infinite envelope} * if the bbox is {@code null}. */ public static Envelope toEnvelope(final String bbox, final CoordinateReferenceSystem crs, final String strElevation, final String strTime) throws IllegalArgumentException, ParseException, FactoryException { final CoordinateReferenceSystem horizontalCRS = CRS.getHorizontalComponent(crs); final VerticalCRS verticalCRS; final TemporalCRS temporalCRS; final double[] dimX = new double[]{Double.NaN,Double.NaN}; final double[] dimY = new double[]{Double.NaN,Double.NaN}; final double[] dimZ = new double[]{Double.NaN,Double.NaN}; final double[] dimT = new double[]{Double.NaN,Double.NaN}; //parse bbox ----------------------------------------------------------- if (bbox == null) { //set to infinity dimX[0] = dimY[0] = Double.NEGATIVE_INFINITY; dimX[1] = dimY[1] = Double.POSITIVE_INFINITY; } else { final StringTokenizer tokens = new StringTokenizer(bbox, ",;"); final double[] values = new double[4]; int index = 0; while (tokens.hasMoreTokens()) { values[index] = toDouble(tokens.nextToken()); if (index >= 4) { throw new IllegalArgumentException(Errors.format(Errors.Keys.IndexOutOfBounds_1, index)); } index++; } if (index != 5) { throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgument_1, index)); } dimX[0] = values[0]; dimX[1] = values[2]; dimY[0] = values[1]; dimY[1] = values[3]; } //parse elevation ------------------------------------------------------ if (strElevation != null) { final double elevation = toDouble(strElevation); dimZ[0] = dimZ[1] = elevation; final VerticalCRS zCRS = CRS.getVerticalComponent(crs, true); verticalCRS = (zCRS != null) ? zCRS : CommonCRS.Vertical.MEAN_SEA_LEVEL.crs(); } else { verticalCRS = null; } //parse temporal ------------------------------------------------------- if (strTime != null) { final Date date = TemporalUtilities.parseDateSafe(strTime,true); final TemporalCRS tCRS = CRS.getTemporalComponent(crs); temporalCRS = (tCRS != null) ? tCRS : CommonCRS.Temporal.MODIFIED_JULIAN.crs(); dimT[0] = dimT[1] = ((DefaultTemporalCRS)temporalCRS).toValue(date); } else { temporalCRS = null; } //create the 2/3/4 D BBox ---------------------------------------------- if (verticalCRS != null && temporalCRS != null) { final CoordinateReferenceSystem finalCRS = new GeodeticObjectBuilder().addName("rendering bbox") .createCompoundCRS(horizontalCRS, verticalCRS, temporalCRS); final GeneralEnvelope envelope = new GeneralEnvelope(finalCRS); envelope.setRange(0, dimX[0], dimX[1]); envelope.setRange(1, dimY[0], dimY[1]); envelope.setRange(2, dimZ[0], dimZ[1]); envelope.setRange(3, dimT[0], dimT[1]); return envelope; } else if(verticalCRS != null) { final CoordinateReferenceSystem finalCRS = new GeodeticObjectBuilder().addName("rendering bbox") .createCompoundCRS(horizontalCRS, verticalCRS); final GeneralEnvelope envelope = new GeneralEnvelope(finalCRS); envelope.setRange(0, dimX[0], dimX[1]); envelope.setRange(1, dimY[0], dimY[1]); envelope.setRange(2, dimZ[0], dimZ[1]); return envelope; } else if(temporalCRS != null) { final CoordinateReferenceSystem finalCRS = new GeodeticObjectBuilder().addName("rendering bbox") .createCompoundCRS(horizontalCRS, temporalCRS); final GeneralEnvelope envelope = new GeneralEnvelope(finalCRS); envelope.setRange(0, dimX[0], dimX[1]); envelope.setRange(1, dimY[0], dimY[1]); envelope.setRange(2, dimT[0], dimT[1]); return envelope; } else { final GeneralEnvelope envelope = new GeneralEnvelope(horizontalCRS); envelope.setRange(0, dimX[0], dimX[1]); envelope.setRange(1, dimY[0], dimY[1]); return envelope; } } /** * Returns the format if it is a known format, that can be used for writting. * * @param format The format chosen, as a string. * @return The supported format, or {@code null} if the given format is {@code null}. * @throws IllegalArgumentException if the given string is not a known format. */ public static String toFormat(String format) throws IllegalArgumentException { if (format == null) { return null; } format = format.trim(); if (!SUPPORTED_FORMATS.contains(format)) { throw new IllegalArgumentException("Invalid format specified."); } return format; } /** * Extends the {@link Integer#valueOf(String)} method, in removing the leading * and trailing whitespace. * * @param value A string representing an integer value. * @return An integer representation of the given string. * @throws NumberFormatException if the given value is {@code null}, or if the * string can't be parsed as an integer value. */ public static int toInt(String value) throws NumberFormatException { if (value == null) { throw new NumberFormatException("Int value not defined."); } value = value.trim(); return Integer.parseInt(value); } /** * Takes a string with the pattern: beginVal,lastVal/otherVal. * That string will be splitted in order to return a list of ranges. * * @param ranges A string representing a list of ranges. */ public static List<Double[]> toCategoriesRange(final String ranges) { final List<Double[]> exts = new ArrayList<Double[]>(); final String[] blocks = ranges.split("/"); for(final String block : blocks){ final String[] parts = block.split(","); if(parts.length == 1){ //single value range final Double d = Double.valueOf(parts[0]); exts.add( new Double[]{d,d} ) ; }else if(parts.length == 2){ //interval range final Double d1 = Double.valueOf(parts[0]); final Double d2 = Double.valueOf(parts[1]); exts.add( new Double[]{d1,d2} ) ; }else{ //not possible, invalid string throw new IllegalArgumentException("Range definition is not valid : " + ranges); } } Collections.sort(exts, new Comparator<Double[]>(){ @Override public int compare(Double[] t, Double[] t1) { double res = t[0] - t1[0]; if (res < 0) { return -1; } if (res > 0) { return 1; } res = t[1] - t1[1]; if (res < 0) { return -1; } if (res > 0) { return 1; } return 0; } }); return exts; } }