package com.revolsys.gis.cs; import java.io.File; import java.io.IOException; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.measure.converter.UnitConverter; import javax.measure.quantity.Angle; import javax.measure.quantity.Length; import javax.measure.unit.NonSI; import javax.measure.unit.SI; import javax.measure.unit.Unit; import com.revolsys.geometry.cs.AngularUnit; import com.revolsys.geometry.cs.Axis; import com.revolsys.geometry.cs.LinearUnit; import com.revolsys.io.PathName; import com.revolsys.io.Reader; import com.revolsys.record.Record; import com.revolsys.record.io.format.csv.Csv; import com.revolsys.record.io.format.csv.CsvWriter; import com.revolsys.record.io.format.json.Json; import com.revolsys.record.query.Query; import com.revolsys.record.schema.RecordStore; /** * Make sure to watch out for parameter value conversions. * * @author paustin */ public final class EpsgCoordinateSystemsLoader { private static NumberFormat getFormat() { return new DecimalFormat("#0.00000##########################"); } public static void main(final String[] args) { new EpsgCoordinateSystemsLoader().load(); } public static double toDecimalFromSexagesimalDegrees(final double sexagesimal) { final String string = getFormat().format(sexagesimal); final int dotIndex = string.indexOf('.'); final int degrees = Integer.parseInt(string.substring(0, dotIndex)); final int minutes = Integer.parseInt(string.substring(dotIndex + 1, dotIndex + 3)); final double seconds = Double.parseDouble( string.substring(dotIndex + 3, dotIndex + 5) + "." + string.substring(dotIndex + 5)); double decimal; if (sexagesimal < 0) { decimal = degrees - minutes / 60.0 - seconds / 3600.0; } else { decimal = degrees + minutes / 60.0 + seconds / 3600.0; } return decimal; } private final Map<Integer, Unit<Angle>> angularUnits = new HashMap<>(); private final Map<Integer, Integer> coordinateSystemUnitMap = new HashMap<>(); private final Map<Integer, Unit<Length>> linearUnits = new HashMap<>(); private final RecordStore recordStore; public EpsgCoordinateSystemsLoader() { final Map<String, Object> parameters = new HashMap<>(); parameters.put("url", "jdbc:postgresql://localhost:5432/epsg"); parameters.put("user", "epsg"); parameters.put("password", "epsg"); this.recordStore = RecordStore.newRecordStore(parameters); } protected Double getDoubleNaN(final Record object, final String name) { Double value = object.getDouble(name); if (value == null) { value = Double.NaN; } return value; } protected boolean isDeprecated(final Record object) { return object.getInteger("deprecated") == 1; } protected void load() { try { loadUnits(); loadAreas(); loadCoordinateAxises(); loadSpheroids(); loadPrimeMeridians(); loadDatums(); loadGeographicCoordinateSystems(); loadProjectedCoordinateSystems(); } catch (final Throwable t) { t.printStackTrace(); } } private void loadAreas() throws IOException { final Query query = new Query("/public/epsg_area"); query.addOrderBy("area_code", true); final Reader<Record> reader = this.recordStore.getRecords(query); final File file = new File( "../com.revolsys.open.core/src/main/resources/com/revolsys/gis/cs/epsg/area.csv"); final CsvWriter writer = Csv.plainWriter(file); try { writer.write("ID", "NAME", "MIN_X", "MIN_Y", "MAX_X", "MAX_Y", "DEPRECATED"); for (final Record object : reader) { final Integer code = object.getInteger("area_code"); final String name = object.getValue("area_name"); final Double minY = object.getDouble("area_south_bound_lat"); final Double maxY = object.getDouble("area_north_bound_lat"); final Double minX = object.getDouble("area_west_bound_lon"); final Double maxX = object.getDouble("area_east_bound_lon"); final boolean deprecated = isDeprecated(object); writer.write(code, name, minX, minY, maxX, maxY, deprecated); } } finally { reader.close(); writer.close(); } } private void loadCoordinateAxises() throws IOException { final Map<Integer, String> coordinateAxisNames = loadCoordinateAxisNames(); final Map<Integer, List<Axis>> coordinateAxises = new HashMap<>(); final Query query = new Query("/public/epsg_coordinateaxis"); query.addOrderBy("coord_sys_code", true); query.addOrderBy("coord_axis_order", true); final Reader<Record> reader = this.recordStore.getRecords(query); try { for (final Record object : reader) { final Integer coordSysCode = object.getInteger("coord_sys_code"); final Integer nameCode = object.getInteger("coord_axis_name_code"); final String direction = object.getValue("coord_axis_orientation"); final String name = coordinateAxisNames.get(nameCode); final Integer uomCode = object.getInteger("uom_code"); this.coordinateSystemUnitMap.put(coordSysCode, uomCode); final int order = object.getInteger("coord_axis_order"); List<Axis> axises = coordinateAxises.get(coordSysCode); if (axises == null) { axises = new ArrayList<>(); coordinateAxises.put(coordSysCode, axises); } while (axises.size() < order) { axises.add(null); } axises.set(order - 1, new Axis(name, direction)); } } finally { reader.close(); } final File file = new File( "../com.revolsys.open.core/src/main/resources/com/revolsys/gis/cs/epsg/axis.csv"); final CsvWriter writer = Csv.plainWriter(file); try { writer.write("ID", "NAME_1", "DIRECTION_1", "NAME_2", "DIRECTION_2", "NAME_3", "DIRECTION_3"); for (final Entry<Integer, List<Axis>> entry : coordinateAxises.entrySet()) { final List<String> values = new ArrayList<>(); final Integer id = entry.getKey(); values.add(id.toString()); final List<Axis> axisList = entry.getValue(); for (int i = 0; i < 3; i++) { if (i >= axisList.size()) { values.add(""); values.add(""); } else { final Axis axis = axisList.get(i); values.add(axis.getName()); values.add(axis.getDirection()); } } writer.write(values); } } finally { writer.close(); } } private Map<Integer, String> loadCoordinateAxisNames() { final Map<Integer, String> coordinateAxisNames = new HashMap<>(); final Reader<Record> reader = this.recordStore .getRecords(PathName.newPathName("/public/epsg_coordinateaxisname")); try { for (final Record object : reader) { final Integer code = object.getInteger("coord_axis_name_code"); final String name = object.getValue("coord_axis_name"); coordinateAxisNames.put(code, name); } } finally { reader.close(); } return coordinateAxisNames; } private Map<Integer, String> loadCoordinateOperationMethodNames() { final Map<Integer, String> coordinateOperationMethodNames = new HashMap<>(); final Reader<Record> reader = this.recordStore .getRecords(PathName.newPathName("/public/epsg_coordoperationmethod")); try { for (final Record object : reader) { final Integer code = object.getInteger("coord_op_method_code"); final String name = ((String)object.getValue("coord_op_method_name")).replace(" ", "_"); coordinateOperationMethodNames.put(code, name); } } finally { reader.close(); } return coordinateOperationMethodNames; } private Map<Integer, String> loadCoordinateOperationParamNames() { final Map<Integer, String> names = new HashMap<>(); final Reader<Record> reader = this.recordStore .getRecords(PathName.newPathName("/public/epsg_coordoperationparam")); try { for (final Record object : reader) { final Integer code = object.getInteger("parameter_code"); final String name = object.getValue("parameter_name".replace(" ", "_").toLowerCase()); names.put(code, name); } } finally { reader.close(); } return names; } private Map<Integer, Map<String, Object>> loadCoordinateOperationParamValues( final Map<Integer, String> coordinateOperationParamNames) { final Map<Integer, Map<String, Object>> coordinateOperationParamValues = new HashMap<>(); final Reader<Record> reader = this.recordStore .getRecords(PathName.newPathName("/public/epsg_coordoperationparamvalue")); try { for (final Record object : reader) { final Integer code = object.getInteger("coord_op_code"); final Integer nameCode = object.getInteger("parameter_code"); final Integer uomCode = object.getInteger("uom_code"); double value = getDoubleNaN(object, "parameter_value"); value = normalizeValue(uomCode, value); Map<String, Object> parameters = coordinateOperationParamValues.get(code); if (parameters == null) { parameters = new HashMap<>(); coordinateOperationParamValues.put(code, parameters); } String paramName = coordinateOperationParamNames.get(nameCode); paramName = paramName.toLowerCase().replace(' ', '_'); parameters.put(paramName, value); } } finally { reader.close(); } return coordinateOperationParamValues; } private Map<Integer, Integer> loadCoordinateOperations() { final Map<Integer, Integer> coordinateOperationMethods = new HashMap<>(); final Query query = new Query("/public/epsg_coordoperation"); query.addOrderBy("coord_op_code", true); final Reader<Record> reader = this.recordStore.getRecords(query); try { for (final Record object : reader) { final Integer code = object.getInteger("coord_op_code"); final Integer methodCode = object.getInteger("coord_op_method_code"); coordinateOperationMethods.put(code, methodCode); } } finally { reader.close(); } return coordinateOperationMethods; } private void loadDatums() throws IOException { final Query query = new Query("/public/epsg_datum"); query.addOrderBy("datum_code", true); final Reader<Record> reader = this.recordStore.getRecords(query); final File file = new File( "../com.revolsys.open.core/src/main/resources/com/revolsys/gis/cs/epsg/datum.csv"); final CsvWriter writer = Csv.plainWriter(file); try { writer.write("ID", "NAME", "SPHEROID_ID", "PRIME_MERIDIAN_ID", "DEPRECATED"); for (final Record object : reader) { final String datumType = object.getValue("datum_type"); final boolean deprecated = isDeprecated(object); if (datumType.equals("geodetic")) { final Integer code = object.getInteger("datum_code"); final String name = object.getValue("datum_name"); final Integer spheroidCode = object.getInteger("ellipsoid_code"); final Integer primeMeridianCode = object.getInteger("prime_meridian_code"); writer.write(code, name, spheroidCode, primeMeridianCode, deprecated); } } } finally { reader.close(); writer.close(); } } private void loadGeographicCoordinateSystems() throws IOException { final Reader<Record> reader = this.recordStore .getRecords(PathName.newPathName("/public/epsg_coordinatereferencesystem")); final File file = new File( "../com.revolsys.open.core/src/main/resources/com/revolsys/gis/cs/epsg/geographic.csv"); final CsvWriter writer = Csv.plainWriter(file); try { writer.write("ID", "NAME", "DATUM_ID", "UNIT_ID", "AXIS_ID", "AREA_ID", "DEPRECATED"); for (final Record object : reader) { final String type = object.getValue("coord_ref_sys_kind"); final boolean deprecated = isDeprecated(object); if (type.equals("geographic 2D")) { final Integer datumCode = object.getInteger("datum_code"); final Integer code = object.getInteger("coord_ref_sys_code"); final String name = object.getValue("coord_ref_sys_name"); final Integer coordSysCode = object.getInteger("coord_sys_code"); final Integer areaCode = object.getInteger("area_of_use_code"); final Integer unitId = this.coordinateSystemUnitMap.get(coordSysCode); final Integer axisId = coordSysCode; writer.write(code, name, datumCode, unitId, axisId, areaCode, deprecated); } } } finally { reader.close(); writer.close(); } } private void loadPrimeMeridians() throws IOException { final Query query = new Query("/public/epsg_primemeridian"); query.addOrderBy("prime_meridian_code", true); final Reader<Record> reader = this.recordStore.getRecords(query); final File file = new File( "../com.revolsys.open.core/src/main/resources/com/revolsys/gis/cs/epsg/primemeridian.csv"); final CsvWriter writer = Csv.plainWriter(file); try { writer.write("ID", "NAME", "LONGITUDE", "DEPRECATED"); for (final Record object : reader) { final Integer code = object.getInteger("prime_meridian_code"); final int uomCode = object.getInteger("uom_code"); final String name = object.getValue("prime_meridian_name"); final boolean deprecated = isDeprecated(object); double longitude = getDoubleNaN(object, "greenwich_longitude"); longitude = toDegrees(uomCode, longitude); writer.write(code, name, longitude, deprecated); } } finally { reader.close(); writer.close(); } } private void loadProjectedCoordinateSystems() throws IOException { final Map<Integer, String> coordinateOperationMethodNames = loadCoordinateOperationMethodNames(); final Map<Integer, String> coordinateOperationParamNames = loadCoordinateOperationParamNames(); final Map<Integer, Map<String, Object>> coordinateOperationParamValues = loadCoordinateOperationParamValues( coordinateOperationParamNames); final Map<Integer, Integer> coordinateOperationMethods = loadCoordinateOperations(); final Reader<Record> reader = this.recordStore .getRecords(PathName.newPathName("/public/epsg_coordinatereferencesystem")); final File file = new File( "../com.revolsys.open.core/src/main/resources/com/revolsys/gis/cs/epsg/projected.csv"); final CsvWriter writer = Csv.plainWriter(file); try { writer.write("ID", "NAME", "GEO_CS_ID", "UNIT_ID", "PROJECTION_ID", "PROJECTION_NAME", "PARAMETERS", "AXIS_ID", "AREA_ID", "DEPRECATED"); for (final Record object : reader) { final String type = object.getValue("coord_ref_sys_kind"); if (type.equals("projected")) { final Integer code = object.getInteger("coord_ref_sys_code"); final String name = object.getValue("coord_ref_sys_name"); final Integer coordSysCode = object.getInteger("coord_sys_code"); final Integer areaCode = object.getInteger("area_of_use_code"); final Integer sourceGeogcrsCode = object.getInteger("source_geogcrs_code"); final Integer projectionConvCode = object.getInteger("projection_conv_code"); final boolean deprecated = isDeprecated(object); final Map<String, Object> parameters = coordinateOperationParamValues .get(projectionConvCode); final Integer methodCode = coordinateOperationMethods.get(projectionConvCode); final String methodName = coordinateOperationMethodNames.get(methodCode); if (methodCode == null) { System.err.println("Unknown coordinate operation:\n" + object); } else { final Integer unitId = this.coordinateSystemUnitMap.get(coordSysCode); final Integer axisId = coordSysCode; final String parametersString = Json.toString(parameters); writer.write(code, name, sourceGeogcrsCode, unitId, methodCode, methodName, parametersString, axisId, areaCode, deprecated); } } } } finally { reader.close(); writer.close(); } } private void loadSpheroids() throws IOException { final Query query = new Query("/public/epsg_ellipsoid"); query.addOrderBy("ellipsoid_code", true); final Reader<Record> reader = this.recordStore.getRecords(query); final File file = new File( "../com.revolsys.open.core/src/main/resources/com/revolsys/gis/cs/epsg/spheroid.csv"); final CsvWriter writer = Csv.plainWriter(file); try { writer.write("ID", "NAME", "SEMI_MAJOR_AXIS", "SEMI_MINOR_AXIS", "INVERSE_FLATTENING", "DEPRECATED"); for (final Record object : reader) { final Integer code = object.getInteger("ellipsoid_code"); final boolean deprecated = isDeprecated(object); final String name = object.getValue("ellipsoid_name"); final Integer uomCode = object.getInteger("ellipsoid_code"); final double semiMajorAxis = normalizeValue(uomCode, getDoubleNaN(object, "semi_major_axis")); final double inverseFlattening = normalizeValue(uomCode, getDoubleNaN(object, "inv_flattening")); final double semiMinorAxis = normalizeValue(uomCode, getDoubleNaN(object, "semi_minor_axis")); writer.write(code, name, semiMajorAxis, semiMinorAxis, inverseFlattening, deprecated); } } finally { reader.close(); writer.close(); } } private void loadUnits() throws IOException { final Query query = new Query("/public/epsg_unitofmeasure"); query.addOrderBy("uom_code", true); final Reader<Record> reader = this.recordStore.getRecords(query); final File linearFile = new File( "../com.revolsys.open.core/src/main/resources/com/revolsys/gis/cs/epsg/linearunit.csv"); final CsvWriter linearWriter = Csv.plainWriter(linearFile); final File angularFile = new File( "../com.revolsys.open.core/src/main/resources/com/revolsys/gis/cs/epsg/angularunit.csv"); final CsvWriter angularWriter = Csv.plainWriter(angularFile); try { linearWriter.write("ID", "NAME", "BASE_ID", "CONVERSION_FACTOR", "DEPRECATED"); angularWriter.write("ID", "NAME", "BASE_ID", "CONVERSION_FACTOR", "DEPRECATED"); for (final Record object : reader) { final Integer code = object.getInteger("uom_code"); final String name = object.getValue("unit_of_meas_name"); final String type = object.getValue("unit_of_meas_type"); final Integer baseUnitCode = object.getInteger("target_uom_code"); Double conversionFactor = object.getDouble("factor_b"); final Double conversionFactorC = object.getDouble("factor_c"); final boolean deprecated = isDeprecated(object); if (conversionFactor == null) { conversionFactor = Double.NaN; } if (conversionFactorC != null) { conversionFactor = conversionFactor / conversionFactorC; } if (type.equals("angle")) { Unit<Angle> baseUnit = null; if (!code.equals(baseUnitCode)) { baseUnit = this.angularUnits.get(baseUnitCode); } final Unit<Angle> unit = AngularUnit.getUnit(baseUnit, conversionFactor); this.angularUnits.put(code, unit); angularWriter.write(code, name, baseUnitCode, conversionFactor, deprecated); } else if (type.equals("length")) { Unit<Length> baseUnit = null; if (!code.equals(baseUnitCode)) { baseUnit = this.linearUnits.get(baseUnitCode); } final Unit<Length> unit = LinearUnit.getUnit(baseUnit, conversionFactor); this.linearUnits.put(code, unit); linearWriter.write(code, name, baseUnitCode, conversionFactor, deprecated); } } } finally { reader.close(); linearWriter.close(); angularWriter.close(); } } private double normalizeValue(final Integer sourceUomCode, final double value) { if (sourceUomCode == null) { return value; } else if (sourceUomCode < 9000) { return value; } else if (sourceUomCode < 9100) { return toMetres(sourceUomCode, value); } else if (sourceUomCode < 9200) { return toDegrees(sourceUomCode, value); } else { return value; } } private double toDegrees(final int sourceUomCode, final double value) { double degrees; if (sourceUomCode == 9101) { degrees = Math.toDegrees(value); } else if (sourceUomCode == 9102) { degrees = value; } else if (sourceUomCode == 9110) { degrees = toDecimalFromSexagesimalDegrees(value); } else { final Unit<Angle> angularUnit = this.angularUnits.get(sourceUomCode); if (angularUnit == null) { throw new IllegalArgumentException("Angular unit of measure not found " + sourceUomCode); } else { final UnitConverter converter = angularUnit.getConverterTo(NonSI.DEGREE_ANGLE); degrees = converter.convert(value); } } return degrees; } private double toMetres(final int sourceUomCode, final double value) { double metres; if (sourceUomCode == 9001) { metres = value; } else { final Unit<Length> linearUnit = this.linearUnits.get(sourceUomCode); if (linearUnit == null) { throw new IllegalArgumentException("Linear unit of measure not found " + sourceUomCode); } else { final UnitConverter converter = linearUnit.getConverterTo(SI.METRE); metres = converter.convert(value); } } return metres; } }