Hi Paul, I have some questions and remarks about your proposition :
1 - why do you use a setX setY method in the interface instead of something like Coordinate transform(Coordinate) Coordinate inverseTransform(Coordinate) or double[] transform(double[]) double[] inverseTransform(double[]) I you don't want to be Coordinate implementation dependant I also think that having separate setX, forward() and getX method may not be very safe from a programmer point of view, because if you do those operations in different places in the code, it may be hard to know if the transformation has been done when comes the time to use the getX/getY method. I'm curious to know why you separated set, transform and get methods 2 - second point is about semantic Projection is not appropriate as it is reserved to mathematical transformation from the geographic coordinates to a plane. In GeoAPI (geotools, epsg...), things are generally named as follows : CoordinateOperation : any operation on coordinates (it should be the name of your interface) * Coordinate transformation (involving a Datum cahnge - transformation is based on estimated parameters) : many useful transformations involve a datum change (transformations from old local datums to new international datum) * Coordinate conversion (not involving any Datum change - conversion is a mathematic transformation) o Projection : most conversion operations are projections 3 - JTS and OpenJUMP can use 3D coordinates, and I think that the coordinate operation package must be able to deal with those 3D coordinates (transform altitudes to ellpsoidal heights for example). my 2 cents Michael Paul Austin a écrit : >I've come up with a very simple interface for projection algorithms. The >purpose of this is to be model neutral (not tied to JTS), as such it is >not for use by end users but instead for use in higher level libraries >that would work on JTS geometries. > >public interface Projection { > void forward(); > > void reverse(); > > double getX(); > void setX(double x); > > double getY(); > void setY(double y); > >} > > >To perform a forward projection the code would be as follows. > > projection.setX(lonDegrees); > projection.setY(latDegrees); > projection.forward(); > x = projection.getX(); > y = projection.getY(); > > > >Reverse projection would be similar. > > projection.setX(xMetres); > projection.setY(yMetres); > projection.reverse(); > lonDegrees = projection.getX(); > latDegrees = projection.getY(); > >NOTE: The interface is not thread safe. > >The design goals were it must be simple and not require any temporary >objects. > >A JTS wrapper for this would have the following two basic methods > > > public Coordinate forward(double x, double y, double z) { > projection.setX(x); > projection.setY(y); > projection.forward(); > Coordinate coordinate = new Coordinate(projection.getX(), >projection.getY(),z); > forwardPrecisionModel.makePrecise(coordinate); > return coordinate; > } > > public Coordinate reverse(double x, double y, double z) { > projection.setX(x); > projection.setY(y); > projection.reverse(); > Coordinate coordinate = new Coordinate(projection.getX(), >projection.getY(),z); > reversePrecisionModel.makePrecise(coordinate); > return coordinate; > } > >plus a number of other methods to deal with all the JTS Geometry types, >see attached for full implementation. Note that there could be a >performance optimization for coordinate sequences to directly deal with >the Projection interface. > >Most projections go to/from Geographics to a Cartesian system to go from >one Cartesian to another Cartesian you would need a chained projection >which goes from one to another. > >The last piece in the puzzle is to have a ProjectionFactory that can >create projections from EPSG srid codes. There can be many factory >implementations, e.g. one to wrap FME, one to wrap the Java Projection >Library, one for JUMP specific projections and one for GeoTools. You >would then interact with an instance of the factory to create your >projections and then use either the Projection interface or the >JtsProjection wrapper. > >What do you guys think? > >Paul > > >------------------------------------------------------------------------ > >package com.revolsys.gis.projection.jts; > >import com.revolsys.gis.projection.Projection; >import com.vividsolutions.jts.geom.Coordinate; >import com.vividsolutions.jts.geom.CoordinateSequence; >import com.vividsolutions.jts.geom.Envelope; >import com.vividsolutions.jts.geom.Geometry; >import com.vividsolutions.jts.geom.GeometryFactory; >import com.vividsolutions.jts.geom.LineString; >import com.vividsolutions.jts.geom.LinearRing; >import com.vividsolutions.jts.geom.MultiLineString; >import com.vividsolutions.jts.geom.MultiPoint; >import com.vividsolutions.jts.geom.MultiPolygon; >import com.vividsolutions.jts.geom.Point; >import com.vividsolutions.jts.geom.Polygon; >import com.vividsolutions.jts.geom.PrecisionModel; >import com.vividsolutions.jts.geom.impl.CoordinateArraySequence; > >public class JtsProjection { > private GeometryFactory forwardGeometryFactory; > > private GeometryFactory reverseGeometryFactory; > > private Projection projection; > > private PrecisionModel forwardPrecisionModel; > > private PrecisionModel reversePrecisionModel; > > public JtsProjection(Projection projection) { > this(new GeometryFactory(), new GeometryFactory(), projection); > } > > public JtsProjection(GeometryFactory forwardGeometryFactory, GeometryFactory > reverseGeometryFactory, Projection projection) { > this.forwardGeometryFactory = forwardGeometryFactory; > this.reverseGeometryFactory = reverseGeometryFactory; > this.projection = projection; > forwardPrecisionModel = forwardGeometryFactory.getPrecisionModel(); > reversePrecisionModel = reverseGeometryFactory.getPrecisionModel(); > } > > public JtsProjection(GeometryFactory forwardGeometryFactory, Projection > projection) { > this(forwardGeometryFactory, new GeometryFactory(), projection); > } > > public Coordinate forward(double x, double y, double z) { > projection.setX(x); > projection.setY(y); > projection.forward(); > Coordinate coordinate = new Coordinate(projection.getX(), > projection.getY(),z); > forwardPrecisionModel.makePrecise(coordinate); > return coordinate; > } > > public Coordinate reverse(double x, double y, double z) { > projection.setX(x); > projection.setY(y); > projection.reverse(); > Coordinate coordinate = new Coordinate(projection.getX(), > projection.getY(),z); > reversePrecisionModel.makePrecise(coordinate); > return coordinate; > } > > public Envelope forward(final Envelope envelope) { > Coordinate min = forward(envelope.getMinX(), envelope.getMinY()); > Coordinate max = forward(envelope.getMaxX(), envelope.getMaxY()); > return new Envelope(min, max); > } > > public Geometry forward(Geometry geometry) { > if (geometry instanceof Point) { > Point point = (Point)geometry; > return forward(point); > } else if (geometry instanceof LineString) { > LineString line = (LineString)geometry; > return forward(line); > } else if (geometry instanceof Polygon) { > Polygon polygon = (Polygon)geometry; > return forward(polygon); > } else if (geometry instanceof MultiPoint) { > MultiPoint point = (MultiPoint)geometry; > return forward(point); > } else if (geometry instanceof MultiLineString) { > MultiLineString line = (MultiLineString)geometry; > return forward(line); > } else if (geometry instanceof MultiPolygon) { > MultiPolygon polygon = (MultiPolygon)geometry; > return forward(polygon); > } else { > return geometry; > } > } > > public Point forward(final Point point) { > if (point != null) { > Coordinate newCoordinate = forward(point.getCoordinate()); > Point newPoint = forwardGeometryFactory.createPoint(newCoordinate); > addUserData(point, newPoint); > return newPoint; > } else { > return null; > } > } > > public LineString forward(final LineString line) { > if (line != null) { > Coordinate[] newCoordinates = forward(line.getCoordinates()); > LineString newLine = > forwardGeometryFactory.createLineString(newCoordinates); > addUserData(line, newLine); > return newLine; > } else { > return null; > } > } > > public Polygon forward(final Polygon polygon) { > > LinearRing shell = (LinearRing)polygon.getExteriorRing(); > LinearRing newShell = forward(shell); > LinearRing[] newHoles = new LinearRing[polygon.getNumInteriorRing()]; > for (int i = 0; i < newHoles.length; i++) { > LinearRing hole = (LinearRing)polygon.getInteriorRingN(i); > newHoles[i] = forward(hole); > } > Polygon newPolygon = forwardGeometryFactory.createPolygon(newShell, > newHoles); > addUserData(newPolygon, polygon); > return newPolygon; > > } > > public MultiPoint forward(final MultiPoint multiPoint) { > if (multiPoint != null) { > Point[] newPoints = new Point[multiPoint.getNumGeometries()]; > for (int i = 0; i < multiPoint.getNumGeometries(); i++) { > Point point = (Point)multiPoint.getGeometryN(i); > Point newPoint = forward(point); > addUserData(point, newPoint); > newPoints[i] = newPoint; > } > MultiPoint newMultiPoint = > forwardGeometryFactory.createMultiPoint(newPoints); > addUserData(multiPoint, newMultiPoint); > return newMultiPoint; > } else { > return null; > } > } > > public MultiLineString forward(final MultiLineString multiLineString) { > if (multiLineString != null) { > LineString[] newLineStrings = new > LineString[multiLineString.getNumGeometries()]; > for (int i = 0; i < multiLineString.getNumGeometries(); i++) { > LineString line = (LineString)multiLineString.getGeometryN(i); > LineString newLineString = forward(line); > addUserData(line, newLineString); > newLineStrings[i] = newLineString; > } > MultiLineString newMultiLineString = > forwardGeometryFactory.createMultiLineString(newLineStrings); > addUserData(multiLineString, newMultiLineString); > return newMultiLineString; > } else { > return null; > } > } > > public MultiPolygon forward(final MultiPolygon multiPolygon) { > if (multiPolygon != null) { > Polygon[] newPolygons = new Polygon[multiPolygon.getNumGeometries()]; > for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { > Polygon polygon = (Polygon)multiPolygon.getGeometryN(i); > Polygon newPolygon = forward(polygon); > addUserData(polygon, newPolygon); > newPolygons[i] = newPolygon; > } > MultiPolygon newMultiPolygon = > forwardGeometryFactory.createMultiPolygon(newPolygons); > addUserData(multiPolygon, newMultiPolygon); > return newMultiPolygon; > } else { > return null; > } > } > > public LinearRing forward(final LinearRing ring) { > if (ring != null) { > CoordinateSequence newCoordinates = > forward(ring.getCoordinateSequence()); > LinearRing newRing = > forwardGeometryFactory.createLinearRing(newCoordinates); > addUserData(ring, newRing); > return newRing; > } else { > return null; > } > } > > public Coordinate[] forward(final Coordinate[] coordinates) { > Coordinate[] newCoordinates = new Coordinate[coordinates.length]; > for (int i = 0; i < coordinates.length; i++) { > Coordinate coordinate = coordinates[i]; > newCoordinates[i] = forward(coordinate); > } > return newCoordinates; > } > > public CoordinateSequence forward(final CoordinateSequence coordinates) { > CoordinateSequence newCoordinates = new CoordinateArraySequence( > coordinates.size()); > for (int i = 0; i < coordinates.size(); i++) { > Coordinate coordinate = coordinates.getCoordinate(i); > Coordinate projectedCoordinate = forward(coordinate); > newCoordinates.setOrdinate(i, 0, projectedCoordinate.x); > newCoordinates.setOrdinate(i, 1, projectedCoordinate.y); > newCoordinates.setOrdinate(i, 2, projectedCoordinate.z); > } > return newCoordinates; > } > > public Coordinate forward(final Coordinate coordinate) { > return forward(coordinate.x, coordinate.y, coordinate.z); > } > > public Coordinate forward(double x, double y) { > return forward(x, y, Double.NaN); > } > > public Envelope reverse(final Envelope envelope) { > Coordinate min = reverse(envelope.getMinX(), envelope.getMinY()); > Coordinate max = reverse(envelope.getMaxX(), envelope.getMaxY()); > return new Envelope(min, max); > } > > public Geometry reverse(Geometry geometry) { > if (geometry instanceof Point) { > Point point = (Point)geometry; > return projectInverse(point); > } else if (geometry instanceof LineString) { > LineString line = (LineString)geometry; > return reverse(line); > } else if (geometry instanceof Polygon) { > Polygon polygon = (Polygon)geometry; > return reverse(polygon); > } else if (geometry instanceof MultiPoint) { > MultiPoint point = (MultiPoint)geometry; > return projectInverse(point); > } else if (geometry instanceof MultiLineString) { > MultiLineString line = (MultiLineString)geometry; > return projectInverse(line); > } else if (geometry instanceof MultiPolygon) { > MultiPolygon polygon = (MultiPolygon)geometry; > return projectInverse(polygon); > } else { > return geometry; > } > } > > public Point projectInverse(final Point point) { > if (point != null) { > Coordinate newCoordinate = reverse(point.getCoordinate()); > Point newPoint = reverseGeometryFactory.createPoint(newCoordinate); > addUserData(point, newPoint); > return newPoint; > } else { > return null; > } > } > > public LineString reverse(final LineString line) { > if (line != null) { > Coordinate[] newCoordinates = reverse(line.getCoordinates()); > LineString newLine = > reverseGeometryFactory.createLineString(newCoordinates); > addUserData(line, newLine); > return newLine; > } else { > return null; > } > } > > public Polygon reverse(final Polygon polygon) { > LinearRing shell = (LinearRing)polygon.getExteriorRing(); > LinearRing newShell = reverse(shell); > LinearRing[] newHoles = new LinearRing[polygon.getNumInteriorRing()]; > for (int i = 0; i < newHoles.length; i++) { > LinearRing hole = (LinearRing)polygon.getInteriorRingN(i); > newHoles[i] = reverse(hole); > } > Polygon newPolygon = reverseGeometryFactory.createPolygon(newShell, > newHoles); > addUserData(newPolygon, polygon); > return newPolygon; > > } > > public MultiPoint projectInverse(final MultiPoint multiPoint) { > if (multiPoint != null) { > Point[] newPoints = new Point[multiPoint.getNumGeometries()]; > for (int i = 0; i < multiPoint.getNumGeometries(); i++) { > Point point = (Point)multiPoint.getGeometryN(i); > Point newPoint = projectInverse(point); > addUserData(point, newPoint); > newPoints[i] = newPoint; > } > MultiPoint newMultiPoint = > reverseGeometryFactory.createMultiPoint(newPoints); > addUserData(multiPoint, newMultiPoint); > return newMultiPoint; > } else { > return null; > } > } > > public MultiLineString projectInverse(final MultiLineString multiLineString) > { > if (multiLineString != null) { > LineString[] newLineStrings = new > LineString[multiLineString.getNumGeometries()]; > for (int i = 0; i < multiLineString.getNumGeometries(); i++) { > LineString line = (LineString)multiLineString.getGeometryN(i); > LineString newLineString = reverse(line); > addUserData(line, newLineString); > newLineStrings[i] = newLineString; > } > MultiLineString newMultiLineString = > reverseGeometryFactory.createMultiLineString(newLineStrings); > addUserData(multiLineString, newMultiLineString); > return newMultiLineString; > } else { > return null; > } > } > > public MultiPolygon projectInverse(final MultiPolygon multiPolygon) { > if (multiPolygon != null) { > Polygon[] newPolygons = new Polygon[multiPolygon.getNumGeometries()]; > for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { > Polygon polygon = (Polygon)multiPolygon.getGeometryN(i); > Polygon newPolygon = reverse(polygon); > addUserData(polygon, newPolygon); > newPolygons[i] = newPolygon; > } > MultiPolygon newMultiPolygon = > reverseGeometryFactory.createMultiPolygon(newPolygons); > addUserData(multiPolygon, newMultiPolygon); > return newMultiPolygon; > } else { > return null; > } > } > > public LinearRing reverse(final LinearRing ring) { > if (ring != null) { > CoordinateSequence newCoordinates = > reverse(ring.getCoordinateSequence()); > LinearRing newRing = > reverseGeometryFactory.createLinearRing(newCoordinates); > addUserData(ring, newRing); > return newRing; > } else { > return null; > } > } > > public Coordinate[] reverse(final Coordinate[] coordinates) { > Coordinate[] newCoordinates = new Coordinate[coordinates.length]; > for (int i = 0; i < coordinates.length; i++) { > Coordinate coordinate = coordinates[i]; > newCoordinates[i] = reverse(coordinate); > } > return newCoordinates; > } > > public CoordinateSequence reverse(final CoordinateSequence coordinates) { > CoordinateSequence newCoordinates = new CoordinateArraySequence( > coordinates.size()); > for (int i = 0; i < coordinates.size(); i++) { > Coordinate coordinate = coordinates.getCoordinate(i); > Coordinate projectedCoordinate = reverse(coordinate); > newCoordinates.setOrdinate(i, 0, projectedCoordinate.x); > newCoordinates.setOrdinate(i, 1, projectedCoordinate.y); > newCoordinates.setOrdinate(i, 2, projectedCoordinate.z); > } > return newCoordinates; > } > > public Coordinate reverse(final Coordinate coordinate) { > return reverse(coordinate.x, coordinate.y, coordinate.z); > } > > public Coordinate reverse(double x, double y) { > return reverse(x, y, Double.NaN); > } > > public GeometryFactory getReverseGeometryFactory() { > return reverseGeometryFactory; > } > > public GeometryFactory getForwardGeometryFactory() { > return forwardGeometryFactory; > } > > private void addUserData(Geometry oldGeometry, Geometry newGeometry) { > Object userData = oldGeometry.getUserData(); > if (userData != null) { > // TODO clone data? > newGeometry.setUserData(userData); > } > } > > >} > > >------------------------------------------------------------------------ > >package com.revolsys.gis.projection.rs; > >import com.revolsys.gis.projection.Projection; > > >public class ChainedProjection implements Projection { > > private Projection sourceProjection; > > private Projection targetProjection; > > private double y; > > private double x; > > public ChainedProjection(Projection sourceProjection, > Projection targetProjection) { > this.sourceProjection = sourceProjection; > this.targetProjection = targetProjection; > } > > public void forward() { > sourceProjection.setX(x); > sourceProjection.setY(y); > sourceProjection.forward(); > > targetProjection.setX(sourceProjection.getX()); > targetProjection.setY(sourceProjection.getY()); > targetProjection.reverse(); > x = targetProjection.getX(); > y = targetProjection.getY(); > } > > public void reverse() { > sourceProjection.setX(x); > sourceProjection.setY(y); > sourceProjection.reverse(); > > targetProjection.setX(sourceProjection.getX()); > targetProjection.setY(sourceProjection.getY()); > targetProjection.forward(); > x = targetProjection.getX(); > y = targetProjection.getY(); > } > > public double getX() { > return x; > } > > public void setX(double x) { > this.x= x; > } > > public double getY() { > return y; > } > > public void setY(double y) { > this.y = y; > } > >} > > >------------------------------------------------------------------------ > >------------------------------------------------------------------------- >This SF.net email is sponsored by: Splunk Inc. >Still grepping through log files to find problems? Stop. >Now Search log events and configuration files using AJAX and a browser. >Download your FREE copy of Splunk now >> http://get.splunk.com/ > >------------------------------------------------------------------------ > >_______________________________________________ >Jump-pilot-devel mailing list >Jump-pilot-devel@lists.sourceforge.net >https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel > > ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2005. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ Jump-pilot-devel mailing list Jump-pilot-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel