/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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.geotools.referencing.wkt;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;
import java.util.Collection;
import java.util.HashSet;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.cs.CartesianCS;
import org.opengis.referencing.operation.MathTransform;
import org.geotools.referencing.crs.DefaultProjectedCRS;
import org.geotools.referencing.ScriptRunner;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.referencing.cs.DefaultCartesianCS;
import org.geotools.referencing.operation.DefaultMathTransformFactory;
import org.geotools.test.TestData;
import org.junit.*;
import static org.junit.Assert.*;
/**
* Tests the WKT {@link Parser} implementation.
*
*
* @source $URL$
* @version $Id$
* @author Yann Cézard
* @author Remi Eve
* @author Martin Desruisseaux (IRD)
*/
public final class ParserTest {
/**
* Test a hard coded version of a WKT. This is more convenient for debugging.
*/
@Test
public void testHardCoded() throws ParseException {
final Parser parser = new Parser();
String wkt1, wkt2;
DefaultProjectedCRS crs1, crs2;
ParameterValueGroup param;
/*
* First, rather simple Mercator projection.
* Uses standard units and axis order.
*/
wkt1 = "PROJCS[\"Mercator test\",\n" +
" GEOGCS[\"WGS84\",\n" +
" DATUM[\"WGS84\",\n" +
" SPHEROID[\"WGS84\", 6378137.0, 298.257223563]],\n" +
" PRIMEM[\"Greenwich\", 0.0],\n" +
" UNIT[\"degree\", 0.017453292519943295],\n" +
" AXIS[\"Longitude\", EAST],\n" +
" AXIS[\"Latitude\", NORTH]],\n" +
" PROJECTION[\"Mercator_1SP\"],\n" +
" PARAMETER[\"central_meridian\", -20.0],\n" +
" PARAMETER[\"scale_factor\", 1.0],\n" +
" PARAMETER[\"false_easting\", 500000.0],\n" +
" PARAMETER[\"false_northing\", 0.0],\n" +
" UNIT[\"m\", 1.0],\n" +
" AXIS[\"x\", EAST],\n" +
" AXIS[\"y\", NORTH]]\n";
assertTrue(Symbols.DEFAULT.containsAxis(wkt1));
crs1 = (DefaultProjectedCRS) parser.parseObject(wkt1);
wkt2 = parser.format(crs1);
crs2 = (DefaultProjectedCRS) parser.parseObject(wkt2);
param = crs1.getConversionFromBase().getParameterValues();
assertEquals(crs1, crs2);
assertEquals("Mercator_1SP", crs1.getConversionFromBase().getMethod().getName().getCode());
assertTrue(crs1.getConversionFromBase().getMathTransform().toWKT().startsWith("PARAM_MT[\"Mercator_1SP\""));
assertFalse (wkt2.contains("semi_major"));
assertFalse (wkt2.contains("semi_minor"));
assertEquals("semi_major", 6378137.0, param.parameter("semi_major" ).doubleValue(), 1E-4);
assertEquals("semi_minor", 6356752.3, param.parameter("semi_minor" ).doubleValue(), 1E-1);
assertEquals("central_meridian", -20.0, param.parameter("central_meridian").doubleValue(), 1E-8);
assertEquals("scale_factor", 1.0, param.parameter("scale_factor" ).doubleValue(), 1E-8);
assertEquals("false_easting", 500000.0, param.parameter("false_easting" ).doubleValue(), 1E-4);
assertEquals("false_northing", 0.0, param.parameter("false_northing" ).doubleValue(), 1E-4);
/*
* Same Mercator projection as above, but
* switch longitude and latitude axis.
*/
wkt1 = "PROJCS[\"Mercator test\",\n" +
" GEOGCS[\"WGS84\",\n" +
" DATUM[\"WGS84\",\n" +
" SPHEROID[\"WGS84\", 6378137.0, 298.257223563]],\n" +
" PRIMEM[\"Greenwich\", 0.0],\n" +
" UNIT[\"degree\", 0.017453292519943295],\n" +
" AXIS[\"Latitude\", NORTH],\n" +
" AXIS[\"Longitude\", EAST]],\n" +
" PROJECTION[\"Mercator_1SP\"],\n" +
" PARAMETER[\"central_meridian\", -20.0],\n" +
" PARAMETER[\"scale_factor\", 1.0],\n" +
" PARAMETER[\"false_easting\", 500000.0],\n" +
" PARAMETER[\"false_northing\", 0.0],\n" +
" UNIT[\"m\", 1.0],\n" +
" AXIS[\"x\", EAST],\n" +
" AXIS[\"y\", NORTH]]\n";
assertTrue(Symbols.DEFAULT.containsAxis(wkt1));
crs1 = (DefaultProjectedCRS) parser.parseObject(wkt1);
wkt2 = parser.format(crs1);
crs2 = (DefaultProjectedCRS) parser.parseObject(wkt2);
param = crs1.getConversionFromBase().getParameterValues();
assertEquals(crs1, crs2);
assertEquals("Mercator_1SP", crs1.getConversionFromBase().getMethod().getName().getCode());
assertTrue(crs1.getConversionFromBase().getMathTransform().toWKT().startsWith("CONCAT_MT[PARAM_MT["));
assertFalse (wkt2.contains("semi_major"));
assertFalse (wkt2.contains("semi_minor"));
assertEquals("semi_major", 6378137.0, param.parameter("semi_major" ).doubleValue(), 1E-4);
assertEquals("semi_minor", 6356752.3, param.parameter("semi_minor" ).doubleValue(), 1E-1);
assertEquals("central_meridian", -20.0, param.parameter("central_meridian").doubleValue(), 1E-8);
assertEquals("scale_factor", 1.0, param.parameter("scale_factor" ).doubleValue(), 1E-8);
assertEquals("false_easting", 500000.0, param.parameter("false_easting" ).doubleValue(), 1E-4);
assertEquals("false_northing", 0.0, param.parameter("false_northing" ).doubleValue(), 1E-4);
/*
* Try an other projection (Transverse Mercator).
*/
wkt1 = "PROJCS[\"OSGB 1936 / British National Grid\",\n" +
" GEOGCS[\"OSGB 1936\",\n" +
" DATUM[\"OSGB_1936\",\n" +
" SPHEROID[\"Airy 1830\", 6377563.396, 299.3249646, AUTHORITY[\"EPSG\",\"7001\"]],\n" +
" TOWGS84[375.0, -111.0, 431.0, 0.0, 0.0, 0.0, 0.0],\n" +
" AUTHORITY[\"EPSG\",\"6277\"]],\n" +
" PRIMEM[\"Greenwich\",0.0, AUTHORITY[\"EPSG\",\"8901\"]],\n" +
" UNIT[\"DMSH\",0.0174532925199433, AUTHORITY[\"EPSG\",\"9108\"]],\n" +
" AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST], AUTHORITY[\"EPSG\",\"4277\"]],\n" +
" PROJECTION[\"Transverse_Mercator\"],\n" +
" PARAMETER[\"latitude_of_origin\", 49.0],\n" +
" PARAMETER[\"central_meridian\", -2.0],\n" +
" PARAMETER[\"scale_factor\", 0.999601272],\n" +
" PARAMETER[\"false_easting\", 400000.0],\n" +
" PARAMETER[\"false_northing\", -100000.0],\n" +
" UNIT[\"metre\", 1.0, AUTHORITY[\"EPSG\",\"9001\"]],\n" +
" AXIS[\"E\",EAST],\n" +
" AXIS[\"N\",NORTH],\n" +
" AUTHORITY[\"EPSG\",\"27700\"]]\n";
assertTrue(Symbols.DEFAULT.containsAxis(wkt1));
crs1 = (DefaultProjectedCRS) parser.parseObject(wkt1);
wkt2 = parser.format(crs1);
crs2 = (DefaultProjectedCRS) parser.parseObject(wkt2);
param = crs1.getConversionFromBase().getParameterValues();
assertEquals(crs1, crs2);
assertFalse (wkt2.contains("semi_major"));
assertFalse (wkt2.contains("semi_minor"));
assertEquals("Transverse_Mercator", crs1.getConversionFromBase().getMethod().getName().getCode());
assertEquals("semi_major", 6377563.396, param.parameter("semi_major" ).doubleValue(), 1E-4);
assertEquals("semi_minor", 6356256.909, param.parameter("semi_minor" ).doubleValue(), 1E-3);
assertEquals("latitude_of_origin", 49.0, param.parameter("latitude_of_origin").doubleValue(), 1E-8);
assertEquals("central_meridian", -2.0, param.parameter("central_meridian" ).doubleValue(), 1E-8);
assertEquals("scale_factor", 0.9996, param.parameter("scale_factor" ).doubleValue(), 1E-5);
assertEquals("false_easting", 400000.0, param.parameter("false_easting" ).doubleValue(), 1E-4);
assertEquals("false_northing", -100000.0, param.parameter("false_northing" ).doubleValue(), 1E-4);
/*
* Try a projection with feet units.
*/
wkt1 = "PROJCS[\"TransverseMercator\",\n" +
" GEOGCS[\"Sphere\",\n" +
" DATUM[\"Sphere\",\n" +
" SPHEROID[\"Sphere\", 6370997.0, 0.0],\n" +
" TOWGS84[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]],\n" +
" PRIMEM[\"Greenwich\", 0.0],\n" +
" UNIT[\"degree\", 0.017453292519943295],\n" +
" AXIS[\"Longitude\", EAST],\n" +
" AXIS[\"Latitude\", NORTH]],\n" +
" PROJECTION[\"Transverse_Mercator\",\n" +
" AUTHORITY[\"OGC\",\"Transverse_Mercator\"]],\n" +
" PARAMETER[\"central_meridian\", 170.0],\n" +
" PARAMETER[\"latitude_of_origin\", 50.0],\n" +
" PARAMETER[\"scale_factor\", 0.95],\n" +
" PARAMETER[\"false_easting\", 0.0],\n" +
" PARAMETER[\"false_northing\", 0.0],\n" +
" UNIT[\"feet\", 0.304800609601219],\n" +
" AXIS[\"x\", EAST],\n" +
" AXIS[\"y\", NORTH]]\n";
assertTrue(Symbols.DEFAULT.containsAxis(wkt1));
crs1 = (DefaultProjectedCRS) parser.parseObject(wkt1);
wkt2 = parser.format(crs1);
crs2 = (DefaultProjectedCRS) parser.parseObject(wkt2);
param = crs1.getConversionFromBase().getParameterValues();
assertEquals(crs1, crs2);
assertFalse (wkt2.contains("semi_major"));
assertFalse (wkt2.contains("semi_minor"));
assertEquals("Transverse_Mercator", crs1.getConversionFromBase().getMethod().getName().getCode());
assertEquals("semi_major", 6370997.0, param.parameter("semi_major" ).doubleValue(), 1E-5);
assertEquals("semi_minor", 6370997.0, param.parameter("semi_minor" ).doubleValue(), 1E-5);
assertEquals("latitude_of_origin", 50.0, param.parameter("latitude_of_origin").doubleValue(), 1E-8);
assertEquals("central_meridian", 170.0, param.parameter("central_meridian" ).doubleValue(), 1E-8);
assertEquals("scale_factor", 0.95, param.parameter("scale_factor" ).doubleValue(), 1E-8);
assertEquals("false_easting", 0.0, param.parameter("false_easting" ).doubleValue(), 1E-8);
assertEquals("false_northing", 0.0, param.parameter("false_northing" ).doubleValue(), 1E-8);
/*
* Try with a number using scientific notation.
*/
wkt1 = "GEOGCS[\"NAD83 / NFIS Seconds\",DATUM[\"North_American_Datum_1983\",\n" +
" SPHEROID[\"GRS 1980\", 6378137, 298.257222101]],\n" +
" PRIMEM[\"Greenwich\", 0],\n" +
" UNIT[\"Decimal_Second\", 4.84813681109536e-06],\n" +
" AUTHORITY[\"EPSG\", \"100001\"]]";
assertFalse(Symbols.DEFAULT.containsAxis(wkt1));
wkt2 = parser.format(parser.parseObject(wkt1));
assertFalse(wkt2.contains("semi_major"));
assertFalse(wkt2.contains("semi_minor"));
}
/**
* Tests the Oracle variant of WKT.
*/
@Test
public void testOracleWKT() throws ParseException {
final String wkt =
"PROJCS[\"Datum 73 / Modified Portuguese Grid\"," +
" GEOGCS [ \"Datum 73\"," +
" DATUM[\"Datum 73 (EPSG ID 6274)\"," +
" SPHEROID [\"International 1924 (EPSG ID 7022)\", 6378388, 297]," +
" -231, 102.6, 29.8, .6149999999999993660366746131394108039579," +
" -.1979999999999997958947342656936639661522," +
" .8809999999999990918346509498793836069706, .99999821]," +
" PRIMEM [ \"Greenwich\", 0.000000 ]," +
" UNIT [\"Decimal Degree\", 0.01745329251994328]]," +
" PROJECTION[\"Transverse_Mercator\"]," +
// " PROJECTION[\"Modified Portuguese Grid (EPSG OP 19974)\"]," +
// TODO: The real projection is "Modified Portugues", but it is not yet implemented in Geotools.
" PARAMETER[\"Latitude_Of_Origin\", 39.66666666666666666666666666666666666667]," +
" PARAMETER[\"Central_Meridian\", -8.13190611111111111111111111111111111111]," +
" PARAMETER[\"Scale_Factor\", 1]," +
" PARAMETER [\"False_Easting\", 180.598]," +
" PARAMETER[\"False_Northing\", -86.99]," +
" UNIT [\"Meter\", 1]]";
assertFalse(Symbols.DEFAULT.containsAxis(wkt));
final Parser parser = new Parser();
CoordinateReferenceSystem crs1 = parser.parseCoordinateReferenceSystem(wkt);
final String check = parser.format(crs1);
assertTrue(check.indexOf("TOWGS84[-231") >= 0);
CoordinateReferenceSystem crs2 = parser.parseCoordinateReferenceSystem(check);
assertEquals(crs1, crs2);
assertFalse(check.contains("semi_major"));
assertFalse(check.contains("semi_minor"));
}
/**
* Tests parsing with custom axis length. At the difference of the previous test,
* the WKT formatting in this test should include the axis length as parameter values.
*/
@Test
public void testCustomAxisLength() throws FactoryException, ParseException {
DefaultMathTransformFactory factory = new DefaultMathTransformFactory();
ParameterValueGroup parameters = factory.getDefaultParameters("Lambert_Conformal_Conic_2SP");
final double majorAxis = 6.3712e+6;
final double minorAxis = 6.3712e+6;
parameters.parameter("semi_major").setValue(majorAxis);
parameters.parameter("semi_minor").setValue(minorAxis);
parameters.parameter("latitude_of_origin").setValue(25.0);
parameters.parameter("standard_parallel_1").setValue(25.0);
parameters.parameter("standard_parallel_2").setValue(25.0);
parameters.parameter("longitude_of_origin").setValue(-95.0);
parameters.parameter("false_easting").setValue(0.0);
parameters.parameter("false_northing").setValue(0.0);
GeographicCRS base = DefaultGeographicCRS.WGS84;
MathTransform mt = factory.createParameterizedTransform(parameters);
CartesianCS cs = DefaultCartesianCS.PROJECTED;
CoordinateReferenceSystem crs = new DefaultProjectedCRS("Lambert", base, mt, cs);
final String wkt = crs.toWKT();
assertTrue(wkt.contains("semi_major"));
assertTrue(wkt.contains("semi_minor"));
final Parser parser = new Parser();
CoordinateReferenceSystem check = parser.parseCoordinateReferenceSystem(wkt);
assertEquals(wkt, check.toWKT());
}
/**
* Tests parsing of math transforms.
*/
@Test
public void testMathTransform() throws IOException, ParseException {
testParsing(new MathTransformParser(), "wkt/MathTransform.txt");
}
/**
* Tests parsing of coordinate reference systems.
*/
@Test
public void testCoordinateReferenceSystem() throws IOException, ParseException {
testParsing(new Parser(), "wkt/CoordinateReferenceSystem.txt");
}
/**
* Parses all elements from the specified file. Parsing creates a set of
* geographic objects. No special processing are done with them; we just
* check if the parsing work without error and produces distincts objects.
*/
private void testParsing(final AbstractParser parser, final String filename)
throws IOException, ParseException
{
final BufferedReader reader = TestData.openReader(ScriptRunner.class, filename);
if (reader == null) {
throw new FileNotFoundException(filename);
}
final Collection<Object> pool = new HashSet<Object>();
String line;
while ((line=reader.readLine()) != null) {
line = line.trim();
if (line.length()==0 || line.startsWith("#")) {
continue;
}
/*
* Parses a line. If the parse fails, then dump the WKT and rethrow the exception.
*/
final Object parsed;
try {
parsed = parser.parseObject(line);
} catch (ParseException exception) {
System.err.println();
System.err.println("-----------------------------");
System.err.println("Parse failed. Dump WKT below.");
System.err.println("-----------------------------");
System.err.println(line);
System.err.println();
throw exception;
}
assertNotNull("Parsing returns null.", parsed);
assertEquals("Inconsistent equals method", parsed, parsed);
assertSame("Parsing twice returns different objects.", parsed, parser.parseObject(line));
assertTrue("An identical object already exists.", pool.add(parsed));
assertTrue("Inconsistent hashCode or equals method.", pool.contains(parsed));
/*
* Formats the object and parse it again.
* Ensures that the result is consistent.
*/
String formatted = parser.format(parsed);
final Object again;
try {
again = parser.parseObject(formatted);
} catch (ParseException exception) {
System.err.println();
System.err.println("------------------------------------");
System.err.println("Second parse failed. Dump WKT below.");
System.err.println("------------------------------------");
System.err.println(line);
System.err.println();
System.err.println("------ Reformatted WKT below -------");
System.err.println();
System.err.println(formatted);
System.err.println();
throw exception;
}
assertEquals("Second parsing produced different objects", parsed, again);
assertTrue("Inconsistent hashCode or equals method.", pool.contains(again));
}
reader.close();
}
/**
* Test if the parser handles string and number coded Authority code-values.
* @throws IOException
* @throws ParseException
*/
@Test
public void testAuthorityCodeParsing() throws IOException, ParseException {
testParsing(new Parser(), "wkt/AuthorityCode.txt");
}
}