package com.revolsys.geometry.cs.epsg; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import com.revolsys.collection.map.IntHashMap; import com.revolsys.geometry.cs.AngularUnit; import com.revolsys.geometry.cs.Area; import com.revolsys.geometry.cs.Authority; import com.revolsys.geometry.cs.Axis; import com.revolsys.geometry.cs.CoordinateSystem; import com.revolsys.geometry.cs.Datum; import com.revolsys.geometry.cs.GeographicCoordinateSystem; import com.revolsys.geometry.cs.LinearUnit; import com.revolsys.geometry.cs.PrimeMeridian; import com.revolsys.geometry.cs.ProjectedCoordinateSystem; import com.revolsys.geometry.cs.Projection; import com.revolsys.geometry.cs.Spheroid; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.impl.BoundingBoxDoubleXY; import com.revolsys.io.FileUtil; import com.revolsys.record.io.format.csv.CsvIterator; import com.revolsys.record.io.format.json.Json; import com.revolsys.util.Property; public final class EpsgCoordinateSystems { private static Set<CoordinateSystem> coordinateSystems; private static IntHashMap<List<CoordinateSystem>> coordinateSystemsByCoordinateSystem = new IntHashMap<>(); private static Map<Integer, CoordinateSystem> coordinateSystemsById = new TreeMap<>(); private static Map<String, CoordinateSystem> coordinateSystemsByName = new TreeMap<>(); private static boolean initialized = false; private static final IntHashMap<LinearUnit> linearUnits = new IntHashMap<>(); private static Map<String, LinearUnit> linearUnitsByName = new TreeMap<>(); private static int nextSrid = 2000000; private static Map<Integer, Projection> projectionsByCode = new TreeMap<>(); private static Map<String, Projection> projectionsByName = new TreeMap<>(); private static void addCoordinateSystem(final CoordinateSystem coordinateSystem) { final Integer id = coordinateSystem.getCoordinateSystemId(); final String name = coordinateSystem.getCoordinateSystemName(); coordinateSystemsById.put(id, coordinateSystem); final int hashCode = coordinateSystem.hashCode(); List<CoordinateSystem> coordinateSystems = coordinateSystemsByCoordinateSystem.get(hashCode); if (coordinateSystems == null) { coordinateSystems = new ArrayList<>(); coordinateSystemsByCoordinateSystem.put(hashCode, coordinateSystems); } coordinateSystems.add(coordinateSystem); coordinateSystemsByName.put(name, coordinateSystem); } public static void clear() { coordinateSystems = null; coordinateSystemsByCoordinateSystem.clear(); coordinateSystemsById.clear(); coordinateSystemsByName.clear(); } public synchronized static CoordinateSystem getCoordinateSystem( final CoordinateSystem coordinateSystem) { initialize(); if (coordinateSystem == null) { return null; } else { int srid = coordinateSystem.getCoordinateSystemId(); CoordinateSystem matchedCoordinateSystem = coordinateSystemsById.get(srid); if (matchedCoordinateSystem == null) { matchedCoordinateSystem = coordinateSystemsByName .get(coordinateSystem.getCoordinateSystemName()); if (matchedCoordinateSystem == null) { final int hashCode = coordinateSystem.hashCode(); int matchCoordinateSystemId = 0; final List<CoordinateSystem> coordinateSystems = coordinateSystemsByCoordinateSystem .get(hashCode); if (coordinateSystems != null) { for (final CoordinateSystem coordinateSystem3 : coordinateSystems) { if (coordinateSystem3.equals(coordinateSystem)) { final int srid3 = coordinateSystem3.getCoordinateSystemId(); if (matchedCoordinateSystem == null) { matchedCoordinateSystem = coordinateSystem3; matchCoordinateSystemId = srid3; } else if (srid3 < matchCoordinateSystemId) { if (!coordinateSystem3.isDeprecated() || matchedCoordinateSystem.isDeprecated()) { matchedCoordinateSystem = coordinateSystem3; matchCoordinateSystemId = srid3; } } } } } if (matchedCoordinateSystem == null) { if (srid <= 0) { srid = nextSrid++; } final String name = coordinateSystem.getCoordinateSystemName(); final List<Axis> axis = coordinateSystem.getAxis(); final Area area = coordinateSystem.getArea(); final Authority authority = coordinateSystem.getAuthority(); final boolean deprecated = coordinateSystem.isDeprecated(); if (coordinateSystem instanceof GeographicCoordinateSystem) { final GeographicCoordinateSystem geographicCs = (GeographicCoordinateSystem)coordinateSystem; final Datum datum = geographicCs.getDatum(); final PrimeMeridian primeMeridian = geographicCs.getPrimeMeridian(); final AngularUnit angularUnit = geographicCs.getAngularUnit(); final GeographicCoordinateSystem newCs = new GeographicCoordinateSystem(srid, name, datum, primeMeridian, angularUnit, axis, area, authority, deprecated); addCoordinateSystem(newCs); return newCs; } else if (coordinateSystem instanceof ProjectedCoordinateSystem) { final ProjectedCoordinateSystem projectedCs = (ProjectedCoordinateSystem)coordinateSystem; GeographicCoordinateSystem geographicCs = projectedCs.getGeographicCoordinateSystem(); geographicCs = (GeographicCoordinateSystem)getCoordinateSystem(geographicCs); final Projection projection = projectedCs.getProjection(); final Map<String, Object> parameters = projectedCs.getParameters(); final LinearUnit linearUnit = projectedCs.getLinearUnit(); final ProjectedCoordinateSystem newCs = new ProjectedCoordinateSystem(srid, name, geographicCs, area, projection, parameters, linearUnit, axis, authority, deprecated); addCoordinateSystem(newCs); return newCs; } return coordinateSystem; } } } return matchedCoordinateSystem; } } @SuppressWarnings("unchecked") public static <C extends CoordinateSystem> C getCoordinateSystem(final Geometry geometry) { return (C)getCoordinateSystem(geometry.getCoordinateSystemId()); } @SuppressWarnings("unchecked") public static <C extends CoordinateSystem> C getCoordinateSystem(final int crsId) { initialize(); final CoordinateSystem coordinateSystem = coordinateSystemsById.get(crsId); return (C)coordinateSystem; } public static CoordinateSystem getCoordinateSystem(final String name) { initialize(); return coordinateSystemsByName.get(name); } public static Set<CoordinateSystem> getCoordinateSystems() { initialize(); return coordinateSystems; } /** * Get the coordinate systems for the list of coordinate system identifiers. * Null identifiers will be ignored. If the coordinate system does not exist * then it will be ignored. * * @param coordinateSystemIds The coordinate system identifiers. * @return The list of coordinate systems. */ public static List<CoordinateSystem> getCoordinateSystems( final Collection<Integer> coordinateSystemIds) { final List<CoordinateSystem> coordinateSystems = new ArrayList<>(); for (final Integer coordinateSystemId : coordinateSystemIds) { if (coordinateSystemId != null) { final CoordinateSystem coordinateSystem = getCoordinateSystem(coordinateSystemId); if (coordinateSystem != null) { coordinateSystems.add(coordinateSystem); } } } return coordinateSystems; } public static Map<Integer, CoordinateSystem> getCoordinateSystemsById() { initialize(); return new TreeMap<>(coordinateSystemsById); } public static int getCrsId(final CoordinateSystem coordinateSystem) { final Authority authority = coordinateSystem.getAuthority(); if (authority != null) { final String name = authority.getName(); final String code = authority.getCode(); if (name.equals("EPSG")) { return Integer.parseInt(code); } } return 0; } public static String getCrsName(final CoordinateSystem coordinateSystem) { final Authority authority = coordinateSystem.getAuthority(); final String name = authority.getName(); final String code = authority.getCode(); if (name.equals("EPSG")) { return name + ":" + code; } else { return null; } } private static double getDouble(final String value) { if (value == null || value.equals("") || value.equals("NaN")) { return Double.NaN; } else { return Double.valueOf(value); } } public static List<GeographicCoordinateSystem> getGeographicCoordinateSystems() { final List<GeographicCoordinateSystem> coordinateSystems = new ArrayList<>(); for (final CoordinateSystem coordinateSystem : coordinateSystemsByName.values()) { if (coordinateSystem instanceof GeographicCoordinateSystem) { final GeographicCoordinateSystem geographicCoordinateSystem = (GeographicCoordinateSystem)coordinateSystem; coordinateSystems.add(geographicCoordinateSystem); } } return coordinateSystems; } private static Integer getInteger(final String value) { if (value == null || value.equals("")) { return null; } else { return Integer.valueOf(value); } } public static LinearUnit getLinearUnit(final int id) { initialize(); return linearUnits.get(id); } public static LinearUnit getLinearUnit(final String name) { initialize(); return linearUnitsByName.get(name); } private static Map<String, Object> getParameters(final String parametersString) { final Map<String, Object> parameters = new TreeMap<>(); final Map<String, Object> jsonParams = Json.toObjectMap(parametersString); for (final Entry<String, Object> parameter : jsonParams.entrySet()) { final String key = parameter.getKey(); final Object value = parameter.getValue(); if (value instanceof BigDecimal) { final BigDecimal decimal = (BigDecimal)value; parameters.put(key, decimal.doubleValue()); } else { parameters.put(key, value); } } return parameters; } public static List<ProjectedCoordinateSystem> getProjectedCoordinateSystems() { final List<ProjectedCoordinateSystem> coordinateSystems = new ArrayList<>(); for (final CoordinateSystem coordinateSystem : coordinateSystemsByName.values()) { if (coordinateSystem instanceof ProjectedCoordinateSystem) { final ProjectedCoordinateSystem projectedCoordinateSystem = (ProjectedCoordinateSystem)coordinateSystem; coordinateSystems.add(projectedCoordinateSystem); } } return coordinateSystems; } private static Projection getProjection(final int code, final String name) { Projection projection = projectionsByCode.get(code); if (projection == null) { final EpsgAuthority authority = new EpsgAuthority(code); projection = new Projection(name, authority); projectionsByCode.put(code, projection); projectionsByName.put(name, projection); } else { if (!projection.getName().equals(name)) { final EpsgAuthority authority = new EpsgAuthority(code); return new Projection(name, authority); } } return projection; } public static synchronized Projection getProjection(final String name) { initialize(); Projection projection = projectionsByName.get(name); if (projection == null) { projection = new Projection(name); projectionsByName.put(name, projection); } return projection; } private static String getString(final String string) { return new String(string); } public synchronized static void initialize() { if (!initialized) { try { final Map<Integer, List<Axis>> axisMap = loadAxis(); final Map<Integer, Area> areas = loadAreas(); final Map<Integer, AngularUnit> angularUnits = loadAngularUnits(); final Map<Integer, LinearUnit> linearUnits = loadLinearUnits(); loadGeographicCoordinateSystems(angularUnits, axisMap, areas); loadProjectedCoordinateSystems(axisMap, areas, linearUnits); final ProjectedCoordinateSystem worldMercator = (ProjectedCoordinateSystem)coordinateSystemsById .get(3857); coordinateSystemsById.put(900913, worldMercator); coordinateSystems = Collections .unmodifiableSet(new LinkedHashSet<>(coordinateSystemsById.values())); initialized = true; } catch (final Throwable t) { t.printStackTrace(); } } } private static Map<Integer, AngularUnit> loadAngularUnits() { final Map<Integer, AngularUnit> angularUnits = new LinkedHashMap<>(); final InputStream resource = EpsgCoordinateSystems.class .getResourceAsStream("/com/revolsys/gis/cs/epsg/angularunit.csv"); if (resource != null) { try { final java.io.Reader reader = FileUtil.newUtf8Reader(resource); final CsvIterator csv = new CsvIterator(reader); if (csv.hasNext()) { csv.next(); while (csv.hasNext()) { final List<String> values = csv.next(); final int id = getInteger(values.get(0)); final String name = values.get(1); final Integer baseId = getInteger(values.get(2)); final double conversionFactor = getDouble(values.get(3)); final boolean deprecated = Boolean.parseBoolean(values.get(4)); final AngularUnit baseUnit = angularUnits.get(baseId); final EpsgAuthority authority = new EpsgAuthority(id); final AngularUnit unit = new AngularUnit(name, baseUnit, conversionFactor, authority, deprecated); angularUnits.put(id, unit); } } } finally { FileUtil.closeSilent(resource); } } return angularUnits; } private static Map<Integer, Area> loadAreas() { final Map<Integer, Area> areas = new LinkedHashMap<>(); final InputStream resource = EpsgCoordinateSystems.class .getResourceAsStream("/com/revolsys/gis/cs/epsg/area.csv"); if (resource != null) { try { final java.io.Reader reader = FileUtil.newUtf8Reader(resource); final CsvIterator csv = new CsvIterator(reader); if (csv.hasNext()) { csv.next(); while (csv.hasNext()) { final List<String> values = csv.next(); final Integer code = getInteger(values.get(0)); final String name = getString(values.get(1)); final Double minX = getDouble(values.get(2)); final Double minY = getDouble(values.get(3)); final Double maxX = getDouble(values.get(4)); final Double maxY = getDouble(values.get(5)); final boolean deprecated = Boolean.parseBoolean(values.get(6)); final Authority authority = new EpsgAuthority(code); final Area area = new Area(name, new BoundingBoxDoubleXY(minX, minY, maxX, maxY), authority, deprecated); areas.put(code, area); } } } finally { FileUtil.closeSilent(resource); } } return areas; } private static Map<Integer, List<Axis>> loadAxis() { final Map<Integer, List<Axis>> axisMap = new LinkedHashMap<>(); final InputStream resource = EpsgCoordinateSystems.class .getResourceAsStream("/com/revolsys/gis/cs/epsg/axis.csv"); if (resource != null) { try { final java.io.Reader reader = FileUtil.newUtf8Reader(resource); final CsvIterator csv = new CsvIterator(reader); if (csv.hasNext()) { csv.next(); while (csv.hasNext()) { final List<String> values = csv.next(); final Integer id = getInteger(values.get(0)); final List<Axis> axisList = new ArrayList<>(); for (int i = 1; i < values.size(); i += 2) { final String name = values.get(i); if (Property.hasValue(name)) { final String direction = values.get(i + 1); final Axis axis = new Axis(name, direction); axisList.add(axis); } } axisMap.put(id, axisList); } } } finally { FileUtil.closeSilent(resource); } } return axisMap; } private static Map<Integer, Datum> loadDatums() { final Map<Integer, Spheroid> spheroids = loadSpheroids(); final Map<Integer, PrimeMeridian> primeMeridians = loadPrimeMeridians(); final Map<Integer, Datum> datums = new LinkedHashMap<>(); final InputStream resource = EpsgCoordinateSystems.class .getResourceAsStream("/com/revolsys/gis/cs/epsg/datum.csv"); if (resource != null) { try { final java.io.Reader reader = FileUtil.newUtf8Reader(resource); final CsvIterator csv = new CsvIterator(reader); if (csv.hasNext()) { csv.next(); while (csv.hasNext()) { final List<String> values = csv.next(); final int id = Integer.parseInt(values.get(0)); final String name = values.get(1); final int spheroidId = Integer.parseInt(values.get(2)); final int primeMeridianId = Integer.parseInt(values.get(3)); final boolean deprecated = Boolean.parseBoolean(values.get(4)); final Spheroid spheroid = spheroids.get(spheroidId); final PrimeMeridian primeMeridian = primeMeridians.get(primeMeridianId); final EpsgAuthority authority = new EpsgAuthority(id); final Datum datum = new Datum(name, spheroid, primeMeridian, authority, deprecated); datums.put(id, datum); } } } finally { FileUtil.closeSilent(resource); } } return datums; } private static void loadGeographicCoordinateSystems(final Map<Integer, AngularUnit> angularUnits, final Map<Integer, List<Axis>> axisMap, final Map<Integer, Area> areas) { final Map<Integer, Datum> datums = loadDatums(); final InputStream resource = EpsgCoordinateSystems.class .getResourceAsStream("/com/revolsys/gis/cs/epsg/geographic.csv"); if (resource != null) { try { final java.io.Reader reader = FileUtil.newUtf8Reader(resource); final CsvIterator csv = new CsvIterator(reader); if (csv.hasNext()) { csv.next(); while (csv.hasNext()) { final List<String> values = csv.next(); final int id = Integer.parseInt(values.get(0)); final String name = values.get(1); final Integer datumId = getInteger(values.get(2)); final Integer unitId = getInteger(values.get(3)); final Integer axisId = getInteger(values.get(4)); final Integer areaId = getInteger(values.get(5)); final boolean deprecated = Boolean.parseBoolean(values.get(6)); final Datum datum = datums.get(datumId); final EpsgAuthority authority = new EpsgAuthority(id); final AngularUnit angularUnit = angularUnits.get(unitId); final List<Axis> axis = axisMap.get(axisId); final Area area = areas.get(areaId); final GeographicCoordinateSystem coordinateSystem = new GeographicCoordinateSystem(id, name, datum, angularUnit, axis, area, authority, deprecated); addCoordinateSystem(coordinateSystem); } } } finally { FileUtil.closeSilent(resource); } } } private static Map<Integer, LinearUnit> loadLinearUnits() { final InputStream resource = EpsgCoordinateSystems.class .getResourceAsStream("/com/revolsys/gis/cs/epsg/linearunit.csv"); if (resource != null) { try { final java.io.Reader reader = FileUtil.newUtf8Reader(resource); final CsvIterator csv = new CsvIterator(reader); if (csv.hasNext()) { csv.next(); while (csv.hasNext()) { final List<String> values = csv.next(); final int id = getInteger(values.get(0)); final String name = values.get(1); final Integer baseId = getInteger(values.get(2)); final double conversionFactor = getDouble(values.get(3)); final boolean deprecated = Boolean.parseBoolean(values.get(4)); final LinearUnit baseUnit = linearUnits.get(baseId); final EpsgAuthority authority = new EpsgAuthority(id); final LinearUnit unit = new LinearUnit(name, baseUnit, conversionFactor, authority, deprecated); linearUnits.put(id, unit); linearUnitsByName.put(name, unit); } } } finally { FileUtil.closeSilent(resource); } } return linearUnits; } private static Map<Integer, PrimeMeridian> loadPrimeMeridians() { final Map<Integer, PrimeMeridian> primeMeridians = new LinkedHashMap<>(); final InputStream resource = EpsgCoordinateSystems.class .getResourceAsStream("/com/revolsys/gis/cs/epsg/primemeridian.csv"); if (resource != null) { try { final java.io.Reader reader = FileUtil.newUtf8Reader(resource); final CsvIterator csv = new CsvIterator(reader); if (csv.hasNext()) { csv.next(); while (csv.hasNext()) { final List<String> values = csv.next(); final int id = Integer.parseInt(values.get(0)); final String name = values.get(1); final double longitude = getDouble(values.get(2)); final boolean deprecated = Boolean.parseBoolean(values.get(3)); final EpsgAuthority authority = new EpsgAuthority(id); final PrimeMeridian primeMeridian = new PrimeMeridian(name, longitude, authority, deprecated); primeMeridians.put(id, primeMeridian); } } } finally { FileUtil.closeSilent(resource); } } return primeMeridians; } private static void loadProjectedCoordinateSystems(final Map<Integer, List<Axis>> axisMap, final Map<Integer, Area> areas, final Map<Integer, LinearUnit> linearUnits) { final InputStream resource = EpsgCoordinateSystems.class .getResourceAsStream("/com/revolsys/gis/cs/epsg/projected.csv"); if (resource != null) { try { final java.io.Reader reader = FileUtil.newUtf8Reader(resource); final CsvIterator csv = new CsvIterator(reader); if (csv.hasNext()) { csv.next(); while (csv.hasNext()) { final List<String> values = csv.next(); final int id = Integer.parseInt(values.get(0)); final String name = values.get(1); final Integer geoCsId = getInteger(values.get(2)); final Integer unitId = getInteger(values.get(3)); final Integer methodCode = getInteger(values.get(4)); final String methodName = values.get(5); final String parametersString = values.get(6); final Integer axisId = getInteger(values.get(7)); final Integer areaId = getInteger(values.get(8)); final boolean deprecated = Boolean.parseBoolean(values.get(9)); final CoordinateSystem referencedCoordinateSystem = coordinateSystemsById.get(geoCsId); if (referencedCoordinateSystem instanceof GeographicCoordinateSystem) { final GeographicCoordinateSystem geographicCoordinateSystem = (GeographicCoordinateSystem)referencedCoordinateSystem; EpsgAuthority authority = new EpsgAuthority(id); final LinearUnit linearUnit = linearUnits.get(unitId); final Projection projection = getProjection(methodCode, methodName); final Map<String, Object> parameters = getParameters(parametersString); final List<Axis> axis = axisMap.get(axisId); final Area area = areas.get(areaId); final ProjectedCoordinateSystem coordinateSystem = new ProjectedCoordinateSystem(id, name, geographicCoordinateSystem, area, projection, parameters, linearUnit, axis, authority, deprecated); addCoordinateSystem(coordinateSystem); if (id == 3857) { authority = new EpsgAuthority(102100); final ProjectedCoordinateSystem webMercator = new ProjectedCoordinateSystem(102100, name, geographicCoordinateSystem, area, projection, parameters, linearUnit, axis, authority, deprecated); addCoordinateSystem(webMercator); } } } } } finally { FileUtil.closeSilent(resource); } } } private static Map<Integer, Spheroid> loadSpheroids() { final Map<Integer, Spheroid> spheroids = new LinkedHashMap<>(); final InputStream resource = EpsgCoordinateSystems.class .getResourceAsStream("/com/revolsys/gis/cs/epsg/spheroid.csv"); if (resource != null) { try { final java.io.Reader reader = FileUtil.newUtf8Reader(resource); final CsvIterator csv = new CsvIterator(reader); if (csv.hasNext()) { csv.next(); while (csv.hasNext()) { final List<String> values = csv.next(); final int id = Integer.parseInt(values.get(0)); final String name = values.get(1); final double semiMajorAxis = getDouble(values.get(2)); final double semiMinorAxis = getDouble(values.get(3)); final double inverseFlattening = getDouble(values.get(4)); final boolean deprecated = Boolean.parseBoolean(values.get(5)); final EpsgAuthority authority = new EpsgAuthority(id); final Spheroid spheroid = new Spheroid(name, semiMajorAxis, semiMinorAxis, inverseFlattening, authority, deprecated); spheroids.put(id, spheroid); } } } finally { FileUtil.closeSilent(resource); } } return spheroids; } public static CoordinateSystem wgs84() { return EpsgCoordinateSystems.<CoordinateSystem> getCoordinateSystem(4326); } private EpsgCoordinateSystems() { } public static String toWkt(final CoordinateSystem coordinateSystem) { final StringWriter stringWriter = new StringWriter(); final PrintWriter out = new PrintWriter(stringWriter); EpsgCsWktWriter.write(out, coordinateSystem); return stringWriter.toString(); } }