// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.opendata.core.io.geographic; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.AbstractDerivedCRS; import org.geotools.referencing.datum.DefaultEllipsoid; import org.geotools.referencing.operation.projection.LambertConformal; import org.geotools.referencing.operation.projection.LambertConformal1SP; import org.geotools.referencing.operation.projection.LambertConformal2SP; import org.geotools.referencing.operation.projection.MapProjection.AbstractProvider; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.referencing.operation.MathTransform; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.projection.AbstractProjection; import org.openstreetmap.josm.data.projection.Ellipsoid; import org.openstreetmap.josm.data.projection.Projection; import org.openstreetmap.josm.data.projection.proj.LambertConformalConic; import org.openstreetmap.josm.data.projection.proj.LambertConformalConic.Parameters; import org.openstreetmap.josm.data.projection.proj.LambertConformalConic.Parameters1SP; import org.openstreetmap.josm.data.projection.proj.LambertConformalConic.Parameters2SP; import org.openstreetmap.josm.gui.preferences.projection.ProjectionChoice; import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference; import org.openstreetmap.josm.plugins.opendata.core.OdConstants; import org.openstreetmap.josm.tools.Pair; public class DefaultShpHandler extends DefaultGeographicHandler implements ShpHandler { private static final List<Pair<org.opengis.referencing.datum.Ellipsoid, Ellipsoid>> ellipsoids = new ArrayList<>(); static { ellipsoids.add(new Pair<org.opengis.referencing.datum.Ellipsoid, Ellipsoid>(DefaultEllipsoid.GRS80, Ellipsoid.GRS80)); ellipsoids.add(new Pair<org.opengis.referencing.datum.Ellipsoid, Ellipsoid>(DefaultEllipsoid.WGS84, Ellipsoid.WGS84)); } protected static final Double get(ParameterValueGroup values, ParameterDescriptor<?> desc) { return (Double) values.parameter(desc.getName().getCode()).getValue(); } private static boolean equals(Double a, Double b) { boolean res = Math.abs(a - b) <= Main.pref.getDouble( OdConstants.PREF_CRS_COMPARISON_TOLERANCE, OdConstants.DEFAULT_CRS_COMPARISON_TOLERANCE); if (Main.pref.getBoolean(OdConstants.PREF_CRS_COMPARISON_DEBUG, false)) { Main.debug("Comparing "+a+" and "+b+" -> "+res); } return res; } private Charset dbfCharset = null; @Override public MathTransform findMathTransform(CoordinateReferenceSystem sourceCRS, CoordinateReferenceSystem targetCRS, boolean lenient) throws FactoryException { if (getCrsFor(sourceCRS.getName().getCode()) != null) { return CRS.findMathTransform(getCrsFor(sourceCRS.getName().getCode()), targetCRS, lenient); } else if (sourceCRS instanceof AbstractDerivedCRS && sourceCRS.getName().getCode().equalsIgnoreCase("Lambert_Conformal_Conic")) { List<MathTransform> result = new ArrayList<>(); AbstractDerivedCRS crs = (AbstractDerivedCRS) sourceCRS; MathTransform transform = crs.getConversionFromBase().getMathTransform(); if (transform instanceof LambertConformal && crs.getDatum() instanceof GeodeticDatum) { LambertConformal lambert = (LambertConformal) transform; GeodeticDatum geo = (GeodeticDatum) crs.getDatum(); for (ProjectionChoice choice : ProjectionPreference.getProjectionChoices()) { Projection p = choice.getProjection(); if (p instanceof AbstractProjection) { AbstractProjection ap = (AbstractProjection) p; if (ap.getProj() instanceof LambertConformalConic) { for (Pair<org.opengis.referencing.datum.Ellipsoid, Ellipsoid> pair : ellipsoids) { if (pair.a.equals(geo.getEllipsoid()) && pair.b.equals(ap.getEllipsoid())) { boolean ok = true; ParameterValueGroup values = lambert.getParameterValues(); Parameters params = ((LambertConformalConic) ap.getProj()).getParameters(); ok = ok ? equals(get(values, AbstractProvider.LATITUDE_OF_ORIGIN), params.latitudeOrigin) : ok; ok = ok ? equals(get(values, AbstractProvider.CENTRAL_MERIDIAN), ap.getCentralMeridian()) : ok; ok = ok ? equals(get(values, AbstractProvider.SCALE_FACTOR), ap.getScaleFactor()) : ok; ok = ok ? equals(get(values, AbstractProvider.FALSE_EASTING), ap.getFalseEasting()) : ok; ok = ok ? equals(get(values, AbstractProvider.FALSE_NORTHING), ap.getFalseNorthing()) : ok; if (lambert instanceof LambertConformal2SP && params instanceof Parameters2SP) { Parameters2SP param = (Parameters2SP) params; ok = ok ? equals(Math.min(get(values, AbstractProvider.STANDARD_PARALLEL_1), get(values, AbstractProvider.STANDARD_PARALLEL_2)), Math.min(param.standardParallel1, param.standardParallel2)) : ok; ok = ok ? equals(Math.max(get(values, AbstractProvider.STANDARD_PARALLEL_1), get(values, AbstractProvider.STANDARD_PARALLEL_2)), Math.max(param.standardParallel1, param.standardParallel2)) : ok; } else if (!(lambert instanceof LambertConformal1SP && params instanceof Parameters1SP)) { ok = false; } if (ok) { try { result.add(CRS.findMathTransform(CRS.decode(p.toCode()), targetCRS, lenient)); } catch (FactoryException e) { Main.error(e.getMessage()); } } } } } } } } if (!result.isEmpty()) { if (result.size() > 1) { Main.warn("Found multiple projections !"); // TODO: something } return result.get(0); } } return null; } @Override public void notifyFeatureParsed(Object feature, DataSet result, Set<OsmPrimitive> featurePrimitives) { // To be overriden by modules handlers } @Override public void setDbfCharset(Charset charset) { dbfCharset = charset; } @Override public Charset getDbfCharset() { return dbfCharset; } }