/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2009-2014, 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; * 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.geotoolkit.display2d.primitive; import com.bric.geom.Clipper; import com.vividsolutions.jts.geom.Envelope; import java.awt.Shape; import java.util.Arrays; import java.util.logging.Level; import org.geotoolkit.display2d.container.stateless.StatelessContextParams; import org.geotoolkit.display2d.primitive.jts.JTSGeometryJ2D; import org.geotoolkit.geometry.isoonjts.JTSUtils; import org.geotoolkit.geometry.jts.JTS; import org.geotoolkit.geometry.jts.transform.CoordinateSequenceMathTransformer; import org.geotoolkit.geometry.jts.transform.CoordinateSequenceWrapTransformer; import org.geotoolkit.geometry.jts.transform.GeometryCSTransformer; import org.geotoolkit.internal.referencing.CRSUtilities; import org.apache.sis.referencing.CRS; import org.opengis.geometry.Geometry; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform2D; import org.opengis.referencing.operation.TransformException; import org.apache.sis.util.logging.Logging; /** * convenient class to manipulate geometry in the 2d engine. * The geometry may be asked in different format depending of the needs. * <br/> * For example it is interesting to use the java2d shape for painting and the * ISO/JTS geometries for intersections tests. * * @author Johann Sorel (Geomatys) * @module */ public class ProjectedGeometry { private final StatelessContextParams params; private MathTransform2D dataToObjective; private MathTransform2D dataToDisplay; //Geometry is data CRS private com.vividsolutions.jts.geom.Geometry dataGeometryJTS = null; private Geometry dataGeometryISO = null; private Shape dataShape = null; //Geometry in objective CRS private com.vividsolutions.jts.geom.Geometry[] objectiveGeometryJTS = null; private Geometry[] objectiveGeometryISO = null; private Shape[] objectiveShape = null; //Geometry in display CRS private com.vividsolutions.jts.geom.Geometry[] displayGeometryJTS = null; private Geometry[] displayGeometryISO = null; private Shape[] displayShape = null; private boolean geomSet = false; private CoordinateReferenceSystem dataCRS = null; public ProjectedGeometry(final StatelessContextParams params){ this.params = params; } public ProjectedGeometry(final ProjectedGeometry copy){ this.params = copy.params; this.dataToObjective = copy.dataToObjective; this.dataToDisplay = copy.dataToDisplay; this.dataGeometryJTS = copy.dataGeometryJTS; this.dataGeometryISO = copy.dataGeometryISO; this.dataShape = copy.dataShape; this.objectiveGeometryJTS = copy.objectiveGeometryJTS; this.objectiveGeometryISO = copy.objectiveGeometryISO; this.objectiveShape = copy.objectiveShape; this.displayGeometryJTS = null; this.displayGeometryISO = null; this.displayShape = null; this.geomSet = copy.geomSet; } public void setDataGeometry(final com.vividsolutions.jts.geom.Geometry geom, CoordinateReferenceSystem dataCRS){ clearDataCache(); this.dataGeometryJTS = geom; this.geomSet = this.dataGeometryJTS != null; try { if(dataCRS == null){ //try to extract data crs from geometry dataCRS = JTS.findCoordinateReferenceSystem(geom); } if(dataCRS != null && this.dataCRS!=dataCRS){ this.dataCRS = dataCRS; dataCRS = CRSUtilities.getCRS2D(dataCRS); dataToObjective = (MathTransform2D) CRS.findOperation(dataCRS, params.context.getObjectiveCRS2D(), null).getMathTransform(); dataToDisplay = (MathTransform2D) CRS.findOperation(dataCRS, params.displayCRS, null).getMathTransform(); } } catch (Exception ex) { Logging.getLogger("org.geotoolkit.display2d.primitive").log(Level.WARNING, null, ex); } } public MathTransform2D getDataToDisplay() { return dataToDisplay; } public MathTransform2D getDataToObjective() { return dataToObjective; } public boolean isSet(){ return this.dataGeometryJTS != null; } public void clearAll(){ clearDataCache(); } public void clearDataCache(){ clearObjectiveCache(); dataGeometryISO = null; dataGeometryJTS = null; dataShape = null; } public void clearObjectiveCache(){ clearDisplayCache(); objectiveGeometryISO = null; objectiveGeometryJTS = null; objectiveShape = null; } public void clearDisplayCache(){ displayGeometryISO = null; displayGeometryJTS = null; displayShape = null; } public Geometry getDataGeometryISO() { return dataGeometryISO; } public com.vividsolutions.jts.geom.Geometry getDataGeometryJTS() { return dataGeometryJTS; } public Shape getDataShape() { if(dataShape == null && geomSet){ dataShape = new JTSGeometryJ2D(dataGeometryJTS); } return dataShape; } /** * Get a JTS representation of the geometry in objective CRS. * * @return JTS Geometry * @throws TransformException if geometry could not be reprojected. */ public com.vividsolutions.jts.geom.Geometry[] getObjectiveGeometryJTS() throws TransformException { if(objectiveGeometryJTS == null && geomSet){ objectiveGeometryJTS = new com.vividsolutions.jts.geom.Geometry[1]; com.vividsolutions.jts.geom.Geometry objBase; if(dataToObjective == null){ //we assume data and objective are in the same crs objBase = dataGeometryJTS; }else{ final GeometryCSTransformer transformer = new GeometryCSTransformer(new CoordinateSequenceMathTransformer(dataToObjective)); objBase = transformer.transform(getDataGeometryJTS()); } if(params.context.wraps != null){ com.vividsolutions.jts.geom.Envelope objBounds = objBase.getEnvelopeInternal(); final double dx = params.context.wraps.wrapPoints[1].getOrdinate(0) - params.context.wraps.wrapPoints[0].getOrdinate(0); final double dy = params.context.wraps.wrapPoints[1].getOrdinate(1) - params.context.wraps.wrapPoints[0].getOrdinate(1); // fix the geometry if some points wrap around the meridian // we expect the warp points to be axis aligned, TODO handle other cases if(dx!=0 && dy!=0){ throw new TransformException("Coordinate Reference System, wrap around points are not axis aligned."); } if( (dx>0 && (objBounds.getWidth() > (dx/2.0))) || (dy>0 && (objBounds.getHeight() > (dy/2.0)))){ // this is a possible wrap around geometry final double[] wrapTranslate = new double[]{dx,dy}; final CoordinateSequenceWrapTransformer cstrs = new CoordinateSequenceWrapTransformer(wrapTranslate); final GeometryCSTransformer transformer = new GeometryCSTransformer(cstrs); objBase = transformer.transform(objBase); objBounds = objBase.getEnvelopeInternal(); } //check if the geometry overlaps the meridian int nbIncRep = params.context.wraps.wrapIncNb; int nbDecRep = params.context.wraps.wrapDecNb; com.vividsolutions.jts.geom.Geometry objBoundsGeom = JTS.toGeometry(objBounds); // geometry cross the far east meridian, geometry is like : // POLYGON(-179,10, 181,10, 181,-10, 179,-10) if(objBoundsGeom.intersects(params.context.wraps.wrapIncLine)){ //duplicate geometry on the other warp line nbDecRep++; } // geometry cross the far west meridian, geometry is like : // POLYGON(-179,10, -181,10, -181,-10, -179,-10) else if(objBoundsGeom.intersects(params.context.wraps.wrapDecLine)){ //duplicate geometry on the other warp line nbIncRep++; } objectiveGeometryJTS = new com.vividsolutions.jts.geom.Geometry[nbIncRep+nbDecRep+1]; int n=0; for(int i=0;i<nbIncRep;i++){ //check that the futur geometry will intersect the visible area final com.vividsolutions.jts.geom.Envelope candidate = JTS.transform(objBounds, params.context.wraps.wrapIncObj[i]); if(candidate.intersects(params.objectiveJTSEnvelope)){ objectiveGeometryJTS[n++] = JTS.transform(objBase, params.context.wraps.wrapIncObj[i]); } } if(objBounds.intersects(params.objectiveJTSEnvelope)){ objectiveGeometryJTS[n++] = objBase; } for(int i=0;i<nbDecRep;i++){ //check that the futur geometry will intersect the visible area final com.vividsolutions.jts.geom.Envelope candidate = JTS.transform(objBounds, params.context.wraps.wrapDecObj[i]); if(candidate.intersects(params.objectiveJTSEnvelope)){ objectiveGeometryJTS[n++] = JTS.transform(objBase, params.context.wraps.wrapDecObj[i]); } } if(n!=objectiveGeometryJTS.length){ //some of the wrapped geometries do not intersect the visible area objectiveGeometryJTS = Arrays.copyOf(objectiveGeometryJTS, n); } }else{ //geometry is valid with no modifications or repetition objectiveGeometryJTS = new com.vividsolutions.jts.geom.Geometry[1]; objectiveGeometryJTS[0] = objBase; } } return objectiveGeometryJTS; } /** * Get a JTS representation of the geometry in display CRS. * * @return JTS Geometry * @throws TransformException if geometry could not be reprojected. */ public com.vividsolutions.jts.geom.Geometry[] getDisplayGeometryJTS() throws TransformException{ if(displayGeometryJTS == null && geomSet){ getObjectiveGeometryJTS(); displayGeometryJTS = new com.vividsolutions.jts.geom.Geometry[objectiveGeometryJTS.length]; for(int i=0;i<displayGeometryJTS.length;i++){ displayGeometryJTS[i] = params.objToDisplayTransformer.transform(objectiveGeometryJTS[i]); } } return displayGeometryJTS; } /** * Get a Java2D representation of the geometry in objective CRS. * * @return Java2D shape * @throws TransformException if geometry could not be reprojected. */ public Shape[] getObjectiveShape() throws TransformException{ if(objectiveShape == null && geomSet){ getObjectiveGeometryJTS(); objectiveShape = new Shape[objectiveGeometryJTS.length]; for(int i=0;i<objectiveShape.length;i++){ objectiveShape[i] = new JTSGeometryJ2D(objectiveGeometryJTS[i]); } } return objectiveShape; } /** * Get a Java2D representation of the geometry in display CRS. * * @return Java2D shape * @throws TransformException if geometry could not be reprojected. */ public Shape[] getDisplayShape() throws TransformException{ if(displayShape == null && geomSet){ getDisplayGeometryJTS(); displayShape = new Shape[displayGeometryJTS.length]; for(int i=0;i<displayShape.length;i++){ displayShape[i] = new JTSGeometryJ2D(displayGeometryJTS[i]); if(params.displayClipRect!=null){ //check envelopes final Envelope env = displayGeometryJTS[i].getEnvelopeInternal(); if(!env.isNull() && !params.displayClip.getEnvelopeInternal().contains(env)){ //clip to display bounds displayShape[i] = Clipper.clipToRect(displayShape[i], params.displayClipRect); } } //TODO find a way to reactive curves is there is no transformation //displayShape = ProjectedShape.wrap(shape, dataToDisplay); } } return displayShape; } /** * Get an ISO representation of the geometry in objective CRS. * * @return ISO Geometry * @throws TransformException if geometry could not be reprojected. */ public Geometry[] getObjectiveGeometry() throws TransformException { if(objectiveGeometryISO == null && geomSet){ getObjectiveGeometryJTS(); objectiveGeometryISO = new Geometry[objectiveGeometryJTS.length]; for(int i=0;i<objectiveGeometryISO.length;i++){ objectiveGeometryISO[i] = JTSUtils.toISO(objectiveGeometryJTS[i], params.objectiveCRS); } } return objectiveGeometryISO; } /** * Get a ISO representation of the geometry in display CRS. * * @return ISO Geometry * @throws TransformException if geometry could not be reprojected. */ public Geometry[] getDisplayGeometry() throws TransformException { if(displayGeometryISO == null && geomSet){ getDataGeometryJTS(); displayGeometryISO = new Geometry[displayGeometryJTS.length]; for(int i=0;i<displayGeometryISO.length;i++){ displayGeometryISO[i] = JTSUtils.toISO(displayGeometryJTS[i], params.displayCRS); } } return displayGeometryISO; } }