001    // License: GPL. Copyright 2007 by Immanuel Scholz and others
002    package org.openstreetmap.josm.data.projection;
003    
004    import java.io.BufferedReader;
005    import java.io.IOException;
006    import java.io.InputStream;
007    import java.io.InputStreamReader;
008    import java.util.HashMap;
009    import java.util.Map;
010    import java.util.regex.Matcher;
011    import java.util.regex.Pattern;
012    
013    import org.openstreetmap.josm.Main;
014    import org.openstreetmap.josm.data.coor.EastNorth;
015    import org.openstreetmap.josm.data.coor.LatLon;
016    import org.openstreetmap.josm.data.projection.datum.Datum;
017    import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
018    import org.openstreetmap.josm.data.projection.datum.WGS84Datum;
019    import org.openstreetmap.josm.data.projection.proj.ClassProjFactory;
020    import org.openstreetmap.josm.data.projection.proj.LambertConformalConic;
021    import org.openstreetmap.josm.data.projection.proj.LonLat;
022    import org.openstreetmap.josm.data.projection.proj.Proj;
023    import org.openstreetmap.josm.data.projection.proj.ProjFactory;
024    import org.openstreetmap.josm.data.projection.proj.SwissObliqueMercator;
025    import org.openstreetmap.josm.data.projection.proj.TransverseMercator;
026    import org.openstreetmap.josm.io.MirroredInputStream;
027    
028    /**
029     * Class to handle projections
030     *
031     */
032    public class Projections {
033    
034        public static EastNorth project(LatLon ll) {
035            if (ll == null) return null;
036            return Main.getProjection().latlon2eastNorth(ll);
037        }
038    
039        public static LatLon inverseProject(EastNorth en) {
040            if (en == null) return null;
041            return Main.getProjection().eastNorth2latlon(en);
042        }
043    
044        /*********************************
045         * Registry for custom projection
046         *
047         * should be compatible to PROJ.4
048         */
049        public static Map<String, ProjFactory> projs = new HashMap<String, ProjFactory>();
050        public static Map<String, Ellipsoid> ellipsoids = new HashMap<String, Ellipsoid>();
051        public static Map<String, Datum> datums = new HashMap<String, Datum>();
052        public static Map<String, NTV2GridShiftFileWrapper> nadgrids = new HashMap<String, NTV2GridShiftFileWrapper>();
053        public static Map<String, String> inits = new HashMap<String, String>();
054    
055        static {
056            registerBaseProjection("lonlat", LonLat.class, "core");
057            registerBaseProjection("josm:smerc", org.openstreetmap.josm.data.projection.proj.Mercator.class, "core");
058            registerBaseProjection("lcc", LambertConformalConic.class, "core");
059            registerBaseProjection("somerc", SwissObliqueMercator.class, "core");
060            registerBaseProjection("tmerc", TransverseMercator.class, "core");
061    
062            ellipsoids.put("intl", Ellipsoid.hayford);
063            ellipsoids.put("GRS80", Ellipsoid.GRS80);
064            ellipsoids.put("WGS84", Ellipsoid.WGS84);
065            ellipsoids.put("bessel", Ellipsoid.Bessel1841);
066    
067            datums.put("WGS84", WGS84Datum.INSTANCE);
068    
069            nadgrids.put("BETA2007.gsb", NTV2GridShiftFileWrapper.BETA2007);
070            nadgrids.put("ntf_r93_b.gsb", NTV2GridShiftFileWrapper.ntf_rgf93);
071    
072            loadInits();
073        }
074    
075        /**
076         * Plugins can register additional base projections.
077         *
078         * @param id The "official" PROJ.4 id. In case the projection is not supported
079         * by PROJ.4, use some prefix, e.g. josm:myproj or gdal:otherproj.
080         * @param fac The base projection factory.
081         * @param origin Multiple plugins may implement the same base projection.
082         * Provide plugin name or similar string, so it be differentiated.
083         */
084        public static void registerBaseProjection(String id, ProjFactory fac, String origin) {
085            projs.put(id, fac);
086        }
087    
088        public static void registerBaseProjection(String id, Class<? extends Proj> projClass, String origin) {
089            registerBaseProjection(id, new ClassProjFactory(projClass), origin);
090        }
091    
092        public static Proj getBaseProjection(String id) {
093            ProjFactory fac = projs.get(id);
094            if (fac == null) return null;
095            return fac.createInstance();
096        }
097    
098        public static Ellipsoid getEllipsoid(String id) {
099            return ellipsoids.get(id);
100        }
101    
102        public static Datum getDatum(String id) {
103            return datums.get(id);
104        }
105    
106        public static NTV2GridShiftFileWrapper getNTV2Grid(String id) {
107            return nadgrids.get(id);
108        }
109    
110        public static String getInit(String id) {
111            return inits.get(id);
112        }
113    
114        /**
115         * Load +init "presets" from file
116         */
117        private static void loadInits() {
118            Pattern epsgPattern = Pattern.compile("\\A<(\\d+)>(.*)<>\\Z");
119            try {
120                InputStream in = new MirroredInputStream("resource://data/epsg");
121                BufferedReader r = new BufferedReader(new InputStreamReader(in));
122                String line;
123                while ((line = r.readLine()) != null) {
124                    line = line.trim();
125                    if (!line.startsWith("#") && !line.isEmpty()) {
126                        Matcher m = epsgPattern.matcher(line);
127                        if (m.matches()) {
128                            inits.put("epsg:" + m.group(1), m.group(2).trim());
129                        } else {
130                            System.err.println("Warning: failed to parse line from the epsg projection definition: "+line);
131                        }
132                    }
133                }
134            } catch (IOException ex) {
135                throw new RuntimeException();
136            }
137        }
138    }