Skip to content

Commit

Permalink
Make BasicCoordinateTransform thread-safe to increase performance in
Browse files Browse the repository at this point in the history
multi-threaded environments by reusing the same instance.
  • Loading branch information
sebasbaumh committed May 13, 2019
1 parent 9478958 commit 5e67736
Showing 1 changed file with 26 additions and 36 deletions.
62 changes: 26 additions & 36 deletions src/main/java/org/locationtech/proj4j/BasicCoordinateTransform.java
Expand Up @@ -15,8 +15,6 @@
*/
package org.locationtech.proj4j;

import java.util.Arrays;

import org.locationtech.proj4j.datum.*;

/**
Expand All @@ -41,9 +39,6 @@
* <pre>
* [ SrcProjCRS {InverseProjection} ] SrcGeoCRS [ {Datum Conversion} ] TgtGeoCRS [ {Projection} TgtProjCRS ]
* </pre>
* <tt>BasicCoordinateTransform</tt> objects are stateful,
* and thus are not thread-safe.
* However, they may be reused any number of times within a single thread.
* <p>
* Information about the transformation procedure is pre-computed
* and cached in this object for efficient computation.
Expand All @@ -53,17 +48,14 @@
*/
public class BasicCoordinateTransform implements CoordinateTransform {

private CoordinateReferenceSystem srcCRS;
private CoordinateReferenceSystem tgtCRS;

// temporary variable for intermediate results
private ProjCoordinate geoCoord = new ProjCoordinate(0, 0);
private final CoordinateReferenceSystem srcCRS;
private final CoordinateReferenceSystem tgtCRS;

// precomputed information
private boolean doInverseProjection = true;
private boolean doForwardProjection = true;
private boolean doDatumTransform = false;
private boolean transformViaGeocentric = false;
private final boolean doInverseProjection;
private final boolean doForwardProjection;
private final boolean doDatumTransform;
private final boolean transformViaGeocentric;
private GeocentricConverter srcGeoConv;
private GeocentricConverter tgtGeoConv;

Expand All @@ -82,19 +74,16 @@ public BasicCoordinateTransform(CoordinateReferenceSystem srcCRS,
// compute strategy for transformation at initialization time, to make transformation more efficient
// this may include precomputing sets of parameters

doInverseProjection = (srcCRS != null && srcCRS != CoordinateReferenceSystem.CS_GEO);
doForwardProjection = (tgtCRS != null && tgtCRS != CoordinateReferenceSystem.CS_GEO);
doInverseProjection = (srcCRS != CoordinateReferenceSystem.CS_GEO);
doForwardProjection = (tgtCRS != CoordinateReferenceSystem.CS_GEO);
doDatumTransform = doInverseProjection && doForwardProjection
&& srcCRS.getDatum() != tgtCRS.getDatum();

if (doDatumTransform) {

boolean isEllipsoidEqual = srcCRS.getDatum().getEllipsoid().isEqual(tgtCRS.getDatum().getEllipsoid());
if (!isEllipsoidEqual)
transformViaGeocentric = true;
if (srcCRS.getDatum().hasTransformToWGS84()
|| tgtCRS.getDatum().hasTransformToWGS84())
transformViaGeocentric = true;
transformViaGeocentric = ! isEllipsoidEqual || srcCRS.getDatum().hasTransformToWGS84()
|| tgtCRS.getDatum().hasTransformToWGS84();

if (transformViaGeocentric) {
srcGeoConv = new GeocentricConverter(srcCRS.getDatum().getEllipsoid());
Expand All @@ -109,14 +98,18 @@ public BasicCoordinateTransform(CoordinateReferenceSystem srcCRS,
}
}

} else {
transformViaGeocentric=false;
}
}

public CoordinateReferenceSystem getSourceCRS() {
@Override
public CoordinateReferenceSystem getSourceCRS() {
return srcCRS;
}

public CoordinateReferenceSystem getTargetCRS() {
@Override
public CoordinateReferenceSystem getTargetCRS() {
return tgtCRS;
}

Expand All @@ -131,35 +124,32 @@ public CoordinateReferenceSystem getTargetCRS() {
* @throws Proj4jException if a computation error is encountered
*/
// transform corresponds to the pj_transform function in proj.4
public ProjCoordinate transform(ProjCoordinate src, ProjCoordinate tgt)
@Override
public ProjCoordinate transform(ProjCoordinate src, ProjCoordinate tgt)
throws Proj4jException {
geoCoord.setValue(src);
srcCRS.getProjection().getAxisOrder().toENU(geoCoord);
tgt.setValue(src);
srcCRS.getProjection().getAxisOrder().toENU(tgt);

// NOTE: this method may be called many times, so needs to be as efficient as possible
if (doInverseProjection) {
// inverse project to geographic
ProjCoordinate coord = new ProjCoordinate();
coord.setValue(geoCoord);
srcCRS.getProjection().inverseProjectRadians(coord, geoCoord);
srcCRS.getProjection().inverseProjectRadians(tgt, tgt);
}

srcCRS.getProjection().getPrimeMeridian().toGreenwich(geoCoord);
srcCRS.getProjection().getPrimeMeridian().toGreenwich(tgt);

// fixes bug where computed Z value sticks around
geoCoord.clearZ();
tgt.clearZ();

if (doDatumTransform) {
datumTransform(geoCoord);
datumTransform(tgt);
}

tgtCRS.getProjection().getPrimeMeridian().fromGreenwich(geoCoord);
tgtCRS.getProjection().getPrimeMeridian().fromGreenwich(tgt);

if (doForwardProjection) {
// project from geographic to planar
tgtCRS.getProjection().projectRadians(geoCoord, tgt);
} else {
tgt.setValue(geoCoord);
tgtCRS.getProjection().projectRadians(tgt, tgt);
}

tgtCRS.getProjection().getAxisOrder().fromENU(tgt);
Expand Down

0 comments on commit 5e67736

Please sign in to comment.