/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008, Open Source Geospatial Foundation (OSGeo) * * 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; * version 2.1 of the License. * * 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.geotools.xacml.geoxacml.cond; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.geotools.geometry.jts.JTS; import org.geotools.referencing.CRS; import org.geotools.xacml.geoxacml.attr.GeometryAttribute; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import com.sun.xacml.cond.EvaluationResult; import com.sun.xacml.cond.FunctionBase; import com.sun.xacml.ctx.Status; import com.vividsolutions.jts.geom.Geometry; /** * @author Christian Mueller * * Abstract base class for GeoXACML functions. * * Also responsible for coordinate transformations. WGS84 is the common CRS, if * transformation has to be done, the target CRS is always WGS84 * */ public abstract class GeoXACMLFunctionBase extends FunctionBase { protected static final String NAME_PREFIX = "urn:ogc:def:function:geoxacml:1.0:"; protected static final String COMMON_CRS_NAME = "EPSG:4326"; protected static CoordinateReferenceSystem COMMON_CRS = null; public GeoXACMLFunctionBase(String functionName, int functionId, String paramType, boolean paramIsBag, int numParams, int minParams, String returnType, boolean returnsBag) { super(functionName, functionId, paramType, paramIsBag, numParams, minParams, returnType, returnsBag); } public GeoXACMLFunctionBase(String functionName, int functionId, String paramType, boolean paramIsBag, int numParams, String returnType, boolean returnsBag) { super(functionName, functionId, paramType, paramIsBag, numParams, returnType, returnsBag); } public GeoXACMLFunctionBase(String functionName, int functionId, String returnType, boolean returnsBag) { super(functionName, functionId, returnType, returnsBag); } public GeoXACMLFunctionBase(String functionName, int functionId, String[] paramTypes, boolean[] paramIsBag, String returnType, boolean returnsBag) { super(functionName, functionId, paramTypes, paramIsBag, returnType, returnsBag); } /** * @param array * array of GeometryAttributes, length == 2 * @return array with possible replaced GeometryAttributes (transformed to WGS84) * @throws GeoXACMLException * * This method tries to avoid transformations. * * No transformation in the following situations: * * 1) Both have no srsName (null) 2) Both have srsNames and equalsIgnoreCase is true * 3) Both CRS are decodeable and equalsIgnoreMetaData is true * * Error situations are * * 1) One attribute has a srsName, the other not 2) a CRS is not decodeable * * If we need a transformation, both geometries are transformd to WGS84 */ protected String transformOnDemand(GeometryAttribute array[]) throws GeoXACMLException { GeometryAttribute attr1 = array[0]; GeometryAttribute attr2 = array[1]; if (attr1.getSrsName() == null && attr2.getSrsName() == null) return null; if ((attr1.getSrsName() == null && attr2.getSrsName() != null) || (attr1.getSrsName() != null && attr2.getSrsName() == null)) { throw new GeoXACMLException("Missing srsName"); } if (attr1.getSrsName().equalsIgnoreCase(attr2.getSrsName())) return attr1.getSrsName(); CoordinateReferenceSystem[] crsArray = new CoordinateReferenceSystem[2]; crsArray[0] = decodeCRS(array[0].getSrsName()); crsArray[1] = decodeCRS(array[1].getSrsName()); for (int i = 0; i < crsArray.length; i++) { // check if not null if (crsArray[i] == null) throw new GeoXACMLException("Cannod decode " + array[i].getSrsName()); } if (CRS.equalsIgnoreMetadata(crsArray[0], crsArray[1])) // CRS are compatible return array[0].getSrsName(); try { for (int i = 0; i < array.length; i++) { Geometry newGeom = transformToCommonCRS(array[i].getGeometry(), array[i] .getSrsName(), crsArray[i]); if (newGeom != array[i].getGeometry()) { // geometry changed GeometryAttribute newGeomAttr = new GeometryAttribute(newGeom, COMMON_CRS_NAME, array[i].getGid(), array[i].getGmlVersion(), array[i].getType() .toString()); newGeomAttr.setSrsDimension(array[i].getSrsDimension()); array[i] = newGeomAttr; } } } catch (Exception e) { throw new GeoXACMLException(e); } return COMMON_CRS_NAME; } /** * @param g * @param srsName * @param sourceCRS * @return * @throws GeoXACMLException * * Transformation of a geomtry to WGS84 * * No transformation in the following situations * * 1) the srsName equalsIgnoreCase with EPSG:4326 is true 2) equalsIgnorMetaData * returns true */ protected Geometry transformToCommonCRS(Geometry g, String srsName, CoordinateReferenceSystem sourceCRS) throws GeoXACMLException { try { if (COMMON_CRS == null) { synchronized (COMMON_CRS_NAME) { COMMON_CRS = CRS.decode(COMMON_CRS_NAME, true); } } if (COMMON_CRS_NAME.equalsIgnoreCase(srsName)) return g; if (CRS.equalsIgnoreMetadata(sourceCRS, COMMON_CRS)) return g; MathTransform transform = CRS.findMathTransform(sourceCRS, COMMON_CRS); return JTS.transform(g, transform); } catch (Exception e) { throw new GeoXACMLException(e); } } /** * @param t * a Throwable * @return an EvaluationResult indicating a processing error */ protected EvaluationResult exceptionError(Throwable t) { Logger log = Logger.getLogger(this.getClass().getName()); log.log(Level.SEVERE, t.getMessage(), t); List<String> codeList = new ArrayList<String>(); codeList.add(Status.STATUS_PROCESSING_ERROR); return new EvaluationResult(new Status(codeList, t.getLocalizedMessage())); } /** * @param srsName * @return CoordinateRefernceSystem * @throws GeoXACMLException * * try to decode the value of the GML srsName attribute */ protected CoordinateReferenceSystem decodeCRS(String srsName) throws GeoXACMLException { URI srs = null; try { srs = new URI(srsName); } catch (URISyntaxException e) { // failed, continue on } if (srs != null) { // TODO: JD, this is a hack until GEOT-1136 has been resolved if ("http".equals(srs.getScheme()) && "www.opengis.net".equals(srs.getAuthority()) && "/gml/srs/epsg.xml".equals(srs.getPath()) && (srs.getFragment() != null)) { try { return CRS.decode("EPSG:" + srs.getFragment(), true); } catch (Exception e) { // failed, try as straight up uri try { return CRS.decode(srs.toString(), true); } catch (Exception e1) { // failed again, do nothing ,should fail below as well } } } } try { return CRS.decode(srsName, true); } catch (NoSuchAuthorityCodeException e) { // HACK HACK HACK!: remove when // http://jira.codehaus.org/browse/GEOT-1659 is fixed if (srsName.toUpperCase().startsWith("URN")) { String code = srsName.substring(srsName.lastIndexOf(":") + 1); try { return CRS.decode("EPSG:" + code, true); } catch (Exception e1) { throw new GeoXACMLException("Could not create crs: " + srs, e); } } } catch (FactoryException e) { throw new GeoXACMLException("Could not create crs: " + srs, e); } return null; } }