4

In our current project we use a maven setup to manage dependencies and run unit tests. At some point in development one of our tests stoped working (we are not sure at what point exactly, so we don't know what exactly changed). The specific thing failing is a transformation from cartesian to geographic coordinates using geotools (9 and 9.1 tried). The juicy part is that the same test works fine in eclipse. We heavily worked on analysing the problem and created a minimal example that shows the behavior we see where we are (reasonably) sure that it is not classpath problem.

The Example

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.geotest</groupId>
    <artifactId>geotest</artifactId>
    <name>geotest</name>
    <version>1.0.0</version>
    <description></description>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <geotools.version>9.1</geotools.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-main</artifactId>
            <version>${geotools.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.example.geotest.Geotest</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net repository</name>
            <url>http://download.java.net/maven/2</url>
        </repository>
        <repository>
            <id>osgeo</id>
            <name>Open Source Geospatial Foundation Repository</name>
            <url>http://download.osgeo.org/webdav/geotools/</url>
        </repository>
    </repositories>
</project>

Tester.java

package com.example.geotest;

import java.awt.geom.Point2D;
import java.util.Collections;
import java.util.Map;

import org.geotools.referencing.CRS;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.referencing.cs.DefaultCartesianCS;
import org.geotools.referencing.factory.ReferencingFactoryContainer;
import org.geotools.referencing.operation.DefaultConversion;
import org.geotools.referencing.operation.DefiningConversion;
import org.junit.Test;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.crs.CRSFactory;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.crs.ProjectedCRS;
import org.opengis.referencing.cs.CartesianCS;
import org.opengis.referencing.operation.Conversion;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.referencing.operation.TransformException;

public class Tester {

    @Test
    public void testtesttest() throws Exception {

        // prepare actuall action
        final MathTransformFactory mtFactory = ReferencingFactoryFinder.getMathTransformFactory(null);
        final ReferencingFactoryContainer factories = new ReferencingFactoryContainer(null);
        final GeographicCRS geoCRS = DefaultGeographicCRS.WGS84;
        final CartesianCS cartCS = DefaultCartesianCS.GENERIC_2D;

        ProjectedCRS projCRS;
        MathTransform transformGeoToCrs;
        MathTransform transformCrsToGeo;


        ParameterValueGroup parameters = mtFactory.getDefaultParameters("Transverse_Mercator");
        parameters.parameter("central_meridian").setValue(9);
        parameters.parameter("latitude_of_origin").setValue(0.0);
        //0.9996 (a reduction of 1:2500); (40 cm/km)
        parameters.parameter("scale_factor").setValue(0.9996);
        //500 km for north hemisphere
        parameters.parameter("false_easting").setValue(500000.0);
        //10,0000 km for south hemisphere
        parameters.parameter("false_northing").setValue(0.0);

        Map<String, String> properties = Collections.singletonMap("name", "WGS 84 / UTM Zone 32");

        CRSFactory crsFactory = factories.getCRSFactory();
        DefiningConversion conv = new DefiningConversion("test", parameters);
        projCRS = crsFactory.createProjectedCRS(properties, geoCRS, conv, cartCS);
        transformGeoToCrs = CRS.findMathTransform(geoCRS, projCRS);
        transformCrsToGeo = CRS.findMathTransform(projCRS, geoCRS);

        // execute actual test
        double[] src = new double[] {5838597.0, 807147.75};
        double[] dest = new double[2];

        try {
            // this fails in maven
            transformCrsToGeo.transform(src, 0, dest, 0, 1);
        } catch (TransformException e) {
            throw new RuntimeException(e);
        }

        Point2D.Double geo = new Point2D.Double(dest[0], dest[1]);
    }
}

If we call a 'mvn test' on this we get the following exception:

Tests in error:
    testtesttest(com.example.geotest.Tester): org.geotools.referencing.operation.projection.ProjectionException: The transform result may be 9.478,277 meters away from the expected position. Are you sure that the input coordinates are inside this map projection area of validity? The point is located 43°20.6'E away from the central meridian and 5°19.1'N away from the latitude of origin. The projection is "Transverse_Mercator".

If we run the JUnit test from eclipse this works perfectly. Any ideas why this is happening or how we can avoid it?


As a side note: the following three lines are a workaround for the deprecated factories.createProjectedCRS(properties, geoCRS, null, parameters, cartCS) method, if anyone has a better solution be my guest :)

CRSFactory crsFactory = factories.getCRSFactory();
DefiningConversion conv = new DefiningConversion("test", parameters);
projCRS = crsFactory.createProjectedCRS(properties, geoCRS, conv, cartCS);
gsnerf
  • 573
  • 1
  • 4
  • 15
  • It might be worth checking the dependencies' versions (9.1). Maybe maven uses another version of the same jar than eclipse. Look in the target directory for more info (sure-fire reports or so). – Joop Eggen May 13 '13 at 15:31
  • Both seem to use the same jar file out of the maven repository. The only real difference we could see was eclipse calling junit directly while maven runs it through the surefire plugin. It even seems to be the same version of JUnit in both cases. – gsnerf May 13 '13 at 15:41
  • What i see is that your Tester does not fit in the naming convention of maven-surefire-plugin which is responsible for running unit tests. Or on the other hand your posted pom does not show everything what is needed. – khmarbaise May 14 '13 at 06:25

2 Answers2

4

As Werner said, the problem can be solved by disabling the assertion during surefire test run. If you want to keep assertions enabled in the rest of your code, you can only disable assertions in the MapProjection class either by modifying your POM:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
            <argLine>-da:org.geotools.referencing.operation.projection.MapProjection</argLine>
        </configuration>
    </plugin>

or in Java:

MapProjection.class.getClassLoader().setClassAssertionStatus(MapProjection.class.getName(), false);

This error can occur even if the transformed point is within 3 degrees from the central meridian of the UTM zone (and the actual transformation error is negligible) due to a bug in the way geotools estimates its accuracy ( https://osgeo-org.atlassian.net/browse/GEOT-4207 ). That bug is not relevant in the OP's case, as here the point is far out of the UTM zone (about 40 degrees from the central meridian) and the transformation error (about 10 kilometers) is not negligible (for some applications).

Leif Gruenwoldt
  • 13,561
  • 5
  • 60
  • 64
Jaan
  • 2,220
  • 20
  • 17
  • The setClassAssertionStatus method is only effective if the MapProjection class hasn't already been loaded. If you are going to disable assertions from code you need to put that line somewhere early in your program startup. – Ryan Mar 06 '15 at 19:02
  • This is the fix for "transform result may be 0.095 meters" issues. – Ryan Mar 06 '15 at 19:08
2

I had the same problem and could solve it by disabling assertions during surefire test runs. You need to add the following lines to your pom:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <enableAssertions>false</enableAssertions>
            </configuration>
        </plugin>
  • 1
    Thats what we ended up doing as well. From what we learned on the mailing list the cause might be a "bug" on geotools side which might get fixed... or not if the answer we got actually had nothing to do with it :) – gsnerf Aug 27 '13 at 15:30