package com.revolsys.geometry.cs; import java.io.InputStream; import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import com.revolsys.io.FileUtil; import com.revolsys.spring.resource.Resource; import com.revolsys.util.number.Doubles; import com.revolsys.util.number.Integers; public class WktCsParser { public static CoordinateSystem read(final String wkt) { return new WktCsParser(wkt).parse(); } private int index = 0; private final Stack<String> nameStack = new Stack<>(); private final String value; public WktCsParser(final InputStream in) { this(FileUtil.newUtf8Reader(in)); } public WktCsParser(final Reader reader) { this(FileUtil.getString(reader)); } public WktCsParser(final Resource resource) { this(resource.contentsAsString()); } public WktCsParser(final String value) { this.value = value; } public CoordinateSystem parse() { if (this.value.length() == 0) { return null; } else { return (CoordinateSystem)parseValue(); } } private String parseName() { final int startIndex = this.index; while (this.value.charAt(this.index) != '[' && this.value.charAt(this.index) != ']') { this.index++; } final String name = new String(this.value.substring(startIndex, this.index)).trim(); return name; } private double parseNumber() { final int startIndex = this.index; char currentChar = this.value.charAt(this.index); while (Character.isDigit(currentChar) || currentChar == '.' || currentChar == '-') { this.index++; currentChar = this.value.charAt(this.index); } final String string = this.value.substring(startIndex, this.index); return Double.parseDouble(string); } private String parseString() { final int startIndex = this.index; char currentChar = this.value.charAt(this.index); while (currentChar != '"') { this.index++; currentChar = this.value.charAt(this.index); } final String string = new String(this.value.substring(startIndex, this.index)); this.index++; return string; } private Object parseValue() { char currentChar = this.value.charAt(this.index); if (currentChar == '"') { this.index++; return parseString(); } else if (Character.isDigit(currentChar) || currentChar == '-') { return parseNumber(); } else { final String name = parseName(); this.nameStack.push(name); try { final List<Object> values = new ArrayList<>(); currentChar = this.value.charAt(this.index); if (currentChar == '[') { do { this.index++; currentChar = skipWhitespace(); if (currentChar != ']') { final Object value = parseValue(); values.add(value); } currentChar = skipWhitespace(); } while (currentChar == ','); this.index++; if (name.equals("AUTHORITY")) { return processAuthority(values); } else if (name.equals("AXIS")) { return processAxis(values); } else if (name.equals("DATUM")) { return processDatum(values); } else if (name.equals("GEOGCS")) { return processGeographicCoordinateSystem(values); } else if (name.equals("PRIMEM")) { return processPrimeMeridian(values); } else if (name.equals("PROJCS")) { return processProjectedCoordinateSystem(values); } else if (name.equals("PROJECTION")) { return processProjection(values); } else if (name.equals("SPHEROID")) { return processSpheroid(values); } else if (name.equals("TOWGS84")) { return processToWgs84(values); } else if (name.equals("UNIT")) { if (this.nameStack.get(this.nameStack.size() - 2).equals("GEOGCS")) { return processAngularUnit(values); } else { return processLinearUnit(values); } } else { return Collections.singletonMap(name, values); } } else { return name; } } finally { this.nameStack.pop(); } } } private AngularUnit processAngularUnit(final List<Object> values) { final String name = (String)values.get(0); final Number conversionFactor = (Number)values.get(1); Authority authority = null; if (values.size() > 2) { authority = (Authority)values.get(2); } return new AngularUnit(name, conversionFactor.doubleValue(), authority); } private Authority processAuthority(final List<Object> values) { final String name = (String)values.get(0); final String code = values.get(1).toString(); return new BaseAuthority(name, code); } private Axis processAxis(final List<Object> values) { final String name = (String)values.get(0); final String direction = (String)values.get(1); return new Axis(name, direction); } private Datum processDatum(final List<Object> values) { final String name = (String)values.get(0); Spheroid spheroid = null; Authority authority = null; ToWgs84 toWgs84 = null; for (int i = 1; i < values.size(); i++) { final Object value = values.get(i); if (value instanceof Spheroid) { spheroid = (Spheroid)value; } else if (value instanceof Authority) { authority = (Authority)value; } else if (value instanceof ToWgs84) { toWgs84 = (ToWgs84)value; } } return new Datum(name, spheroid, toWgs84, authority); } private GeographicCoordinateSystem processGeographicCoordinateSystem(final List<Object> values) { final String name = (String)values.get(0); final Datum datum = (Datum)values.get(1); final PrimeMeridian primeMeridian = (PrimeMeridian)values.get(2); final AngularUnit angularUnit = (AngularUnit)values.get(3); int index = 4; List<Axis> axis = null; if (index < values.size() && values.get(index) instanceof Axis) { axis = Arrays.asList((Axis)values.get(index++), (Axis)values.get(index++)); } Authority authority = null; if (index < values.size()) { authority = (Authority)values.get(index); } final int authorityId; if (authority == null) { authorityId = 0; } else { final String authorityCode = authority.getCode(); if (authorityCode == null) { authorityId = 0; } else { authorityId = Integer.parseInt(authorityCode); } } return new GeographicCoordinateSystem(authorityId, name, datum, primeMeridian, angularUnit, axis, authority); } private LinearUnit processLinearUnit(final List<Object> values) { final String name = (String)values.get(0); final Number conversionFactor = (Number)values.get(1); Authority authority = null; if (values.size() > 2) { authority = (Authority)values.get(2); } return new LinearUnit(name, conversionFactor.doubleValue(), authority); } private PrimeMeridian processPrimeMeridian(final List<Object> values) { final String name = (String)values.get(0); final Number longitude = (Number)values.get(1); Authority authority = null; if (values.size() > 2) { authority = (Authority)values.get(2); } return new PrimeMeridian(name, longitude.doubleValue(), authority); } @SuppressWarnings("unchecked") private ProjectedCoordinateSystem processProjectedCoordinateSystem(final List<Object> values) { int index = 0; final String name = (String)values.get(index++); final GeographicCoordinateSystem geographicCoordinateSystem = (GeographicCoordinateSystem)values .get(index++); Projection projection = null; final Map<String, Object> parameters = new HashMap<>(); LinearUnit linearUnit = null; final List<Axis> axis = new ArrayList<>(); Authority authority = null; while (index < values.size()) { final Object value = values.get(index++); if (value instanceof Projection) { projection = (Projection)value; } else if (value instanceof Map) { final Map<String, List<Object>> map = (Map<String, List<Object>>)value; final String key = map.keySet().iterator().next(); if (key.equals("PARAMETER")) { final List<Object> paramValues = map.get(key); final String paramName = (String)paramValues.get(0); final Object paramValue = paramValues.get(1); parameters.put(paramName, paramValue); } } else if (value instanceof LinearUnit) { linearUnit = (LinearUnit)value; } else if (value instanceof Axis) { axis.add((Axis)value); } else if (value instanceof Authority) { authority = (Authority)value; } } int srid = -1; if (authority != null) { final String code = authority.getCode(); if (code != null) { try { srid = Integers.toInteger(code); } catch (final Throwable e) { final Double value = Doubles.toDouble(code); if (value != null) { srid = value.intValue(); } } } } return new ProjectedCoordinateSystem(srid, name, geographicCoordinateSystem, projection, parameters, linearUnit, axis, authority); } private Projection processProjection(final List<Object> values) { final String name = (String)values.get(0); return new Projection(name); } private Spheroid processSpheroid(final List<Object> values) { final String name = (String)values.get(0); final Number semiMajorAxis = (Number)values.get(1); final Number inverseFlattening = (Number)values.get(2); Authority authority = null; if (values.size() > 3) { authority = (Authority)values.get(3); } return new Spheroid(name, semiMajorAxis.doubleValue(), inverseFlattening.doubleValue(), authority); } private ToWgs84 processToWgs84(final List<Object> values) { return new ToWgs84(values); } private char skipWhitespace() { char currentChar = this.value.charAt(this.index); while (Character.isWhitespace(currentChar)) { this.index++; currentChar = this.value.charAt(this.index); } return currentChar; } }