package com.revolsys.geometry.cs.projection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.measure.quantity.Angle;
import javax.measure.quantity.Length;
import javax.measure.unit.NonSI;
import javax.measure.unit.SI;
import javax.measure.unit.Unit;
import com.revolsys.geometry.cs.CoordinateSystem;
import com.revolsys.geometry.cs.GeographicCoordinateSystem;
import com.revolsys.geometry.cs.ProjectedCoordinateSystem;
import com.revolsys.geometry.cs.Projection;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.Point;
public final class ProjectionFactory {
/** The map from projection names to projection classes. */
private static final Map<String, Class<? extends CoordinatesProjection>> projectionClasses = new HashMap<>();
static {
registerCoordinatesProjection(Projection.ALBERS_EQUAL_AREA, AlbersConicEqualArea.class);
registerCoordinatesProjection(Projection.TRANSVERSE_MERCATOR, TransverseMercator.class);
registerCoordinatesProjection(Projection.MERCATOR, Mercator1SP.class);
registerCoordinatesProjection(Projection.POPULAR_VISUALISATION_PSEUDO_MERCATOR,
WebMercator.class);
registerCoordinatesProjection(Projection.MERCATOR_1SP, Mercator1SP.class);
registerCoordinatesProjection(Projection.MERCATOR_2SP, Mercator2SP.class);
registerCoordinatesProjection(Projection.MERCATOR_1SP_SPHERICAL, Mercator1SPSpherical.class);
registerCoordinatesProjection(Projection.LAMBERT_CONIC_CONFORMAL_1SP,
LambertConicConformal1SP.class);
registerCoordinatesProjection(Projection.LAMBERT_CONIC_CONFORMAL_2SP,
LambertConicConformal.class);
registerCoordinatesProjection(Projection.LAMBERT_CONIC_CONFORMAL_2SP_BELGIUM,
LambertConicConformal.class);
}
public static Point convert(final Point point, final GeometryFactory sourceGeometryFactory,
final GeometryFactory targetGeometryFactory) {
if (point == null) {
return null;
} else if (sourceGeometryFactory == targetGeometryFactory) {
return point;
} else if (sourceGeometryFactory == null) {
return point;
} else if (targetGeometryFactory == null) {
return point;
} else {
return sourceGeometryFactory.point(point).convertGeometry(targetGeometryFactory);
}
}
public static CoordinatesOperation getCoordinatesOperation(
final CoordinateSystem fromCoordinateSystem, final CoordinateSystem toCoordinateSystem) {
if (fromCoordinateSystem == null || toCoordinateSystem == null
|| fromCoordinateSystem == toCoordinateSystem) {
return null;
} else {
final List<CoordinatesOperation> operations = new ArrayList<>();
if (fromCoordinateSystem instanceof ProjectedCoordinateSystem) {
final ProjectedCoordinateSystem pcs1 = (ProjectedCoordinateSystem)fromCoordinateSystem;
final CoordinatesOperation inverseOperation = getInverseCoordinatesOperation(pcs1);
if (inverseOperation == null) {
return null;
}
final Unit<Length> linearUnit1 = pcs1.getLengthUnit();
if (!linearUnit1.equals(SI.METRE)) {
operations.add(new UnitConverstionOperation(linearUnit1, SI.METRE));
}
operations.add(inverseOperation);
} else if (fromCoordinateSystem instanceof GeographicCoordinateSystem) {
final GeographicCoordinateSystem gcs1 = (GeographicCoordinateSystem)fromCoordinateSystem;
final Unit<Angle> angularUnit1 = gcs1.getUnit();
if (toCoordinateSystem instanceof GeographicCoordinateSystem) {
final GeographicCoordinateSystem gcs2 = (GeographicCoordinateSystem)toCoordinateSystem;
// TODO Datum shift
final Unit<Angle> angularUnit2 = gcs2.getUnit();
if (!angularUnit1.equals(angularUnit2)) {
return new UnitConverstionOperation(angularUnit1, angularUnit2, 2);
} else {
return null;
}
} else {
if (!angularUnit1.equals(SI.RADIAN)) {
CoordinatesOperation converstionOperation;
if (angularUnit1.equals(NonSI.DEGREE_ANGLE)) {
converstionOperation = DegreesToRadiansOperation.INSTANCE;
} else {
converstionOperation = new UnitConverstionOperation(angularUnit1, SI.RADIAN, 2);
}
operations.add(converstionOperation);
}
}
} else {
return null;
}
if (toCoordinateSystem instanceof ProjectedCoordinateSystem) {
final ProjectedCoordinateSystem pcs2 = (ProjectedCoordinateSystem)toCoordinateSystem;
final CoordinatesOperation projectOperation = getProjectCoordinatesOperation(pcs2);
if (projectOperation != null) {
operations.add(projectOperation);
}
final Unit<Length> linearUnit2 = pcs2.getLengthUnit();
if (!linearUnit2.equals(SI.METRE)) {
operations.add(new UnitConverstionOperation(SI.METRE, linearUnit2));
}
} else if (toCoordinateSystem instanceof GeographicCoordinateSystem) {
final GeographicCoordinateSystem gcs2 = (GeographicCoordinateSystem)toCoordinateSystem;
final Unit<Angle> angularUnit2 = gcs2.getUnit();
if (!angularUnit2.equals(SI.RADIAN)) {
operations.add(new UnitConverstionOperation(SI.RADIAN, angularUnit2, 2));
}
}
switch (operations.size()) {
case 0:
return null;
case 1:
return operations.get(0);
default:
return new ChainedCoordinatesOperation(operations);
}
}
}
public static CoordinatesProjection getCoordinatesProjection(
final CoordinateSystem coordinateSystem) {
return coordinateSystem.getCoordinatesProjection();
}
/**
* Get the operation to convert coordinates to geographics coordinates.
*
* @param coordinateSystem The coordinate system.
* @return The coordinates operation.
*/
public static CoordinatesOperation getInverseCoordinatesOperation(
final CoordinateSystem coordinateSystem) {
final CoordinatesProjection projection = getCoordinatesProjection(coordinateSystem);
if (projection == null) {
return null;
} else {
return projection.getInverseOperation();
}
}
/**
* Get the operation to convert geographics coordinates to projected
* coordinates.
*
* @param coordinateSystem The coordinate system.
* @return The coordinates operation.
*/
public static CoordinatesOperation getProjectCoordinatesOperation(
final CoordinateSystem coordinateSystem) {
final CoordinatesProjection projection = getCoordinatesProjection(coordinateSystem);
if (projection == null) {
return new CopyOperation();
} else {
return projection.getProjectOperation();
}
}
public static CoordinatesProjection newCoordinatesProjection(
final ProjectedCoordinateSystem coordinateSystem) {
final Projection projection = coordinateSystem.getProjection();
final String projectionName = projection.getNormalizedName();
synchronized (projectionClasses) {
final Class<? extends CoordinatesProjection> projectionClass = projectionClasses
.get(projectionName);
if (projectionClass == null) {
return null;
} else {
try {
final Constructor<? extends CoordinatesProjection> constructor = projectionClass
.getConstructor(ProjectedCoordinateSystem.class);
final CoordinatesProjection coordinateProjection = constructor
.newInstance(coordinateSystem);
return coordinateProjection;
} catch (final NoSuchMethodException e) {
throw new IllegalArgumentException("Constructor " + projectionClass + "("
+ ProjectedCoordinateSystem.class.getName() + ") does not exist");
} catch (final InstantiationException e) {
throw new IllegalArgumentException(projectionClass + " cannot be instantiated", e);
} catch (final IllegalAccessException e) {
throw new IllegalArgumentException(projectionClass + " cannot be instantiated", e);
} catch (final InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException)cause;
} else if (cause instanceof Error) {
throw (Error)cause;
} else {
throw new IllegalArgumentException(projectionClass + " cannot be instantiated", cause);
}
}
}
}
}
/**
* Register a projection for the named projection.
*
* @param name The name.
* @param projectionClass The projection class.
*/
public static void registerCoordinatesProjection(final String name,
final Class<? extends CoordinatesProjection> projectionClass) {
projectionClasses.put(name, projectionClass);
}
private ProjectionFactory() {
}
}