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.Collection;
009 import java.util.HashMap;
010 import java.util.Map;
011 import java.util.regex.Matcher;
012 import java.util.regex.Pattern;
013
014 import org.openstreetmap.josm.Main;
015 import org.openstreetmap.josm.data.coor.EastNorth;
016 import org.openstreetmap.josm.data.coor.LatLon;
017 import org.openstreetmap.josm.data.projection.datum.Datum;
018 import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
019 import org.openstreetmap.josm.data.projection.datum.WGS84Datum;
020 import org.openstreetmap.josm.data.projection.proj.ClassProjFactory;
021 import org.openstreetmap.josm.data.projection.proj.LambertConformalConic;
022 import org.openstreetmap.josm.data.projection.proj.LonLat;
023 import org.openstreetmap.josm.data.projection.proj.Proj;
024 import org.openstreetmap.josm.data.projection.proj.ProjFactory;
025 import org.openstreetmap.josm.data.projection.proj.SwissObliqueMercator;
026 import org.openstreetmap.josm.data.projection.proj.TransverseMercator;
027 import org.openstreetmap.josm.gui.preferences.projection.ProjectionChoice;
028 import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
029 import org.openstreetmap.josm.io.MirroredInputStream;
030 import org.openstreetmap.josm.tools.Pair;
031
032 /**
033 * Class to handle projections
034 *
035 */
036 public class Projections {
037
038 public static EastNorth project(LatLon ll) {
039 if (ll == null) return null;
040 return Main.getProjection().latlon2eastNorth(ll);
041 }
042
043 public static LatLon inverseProject(EastNorth en) {
044 if (en == null) return null;
045 return Main.getProjection().eastNorth2latlon(en);
046 }
047
048 /*********************************
049 * Registry for custom projection
050 *
051 * should be compatible to PROJ.4
052 */
053 final public static Map<String, ProjFactory> projs = new HashMap<String, ProjFactory>();
054 final public static Map<String, Ellipsoid> ellipsoids = new HashMap<String, Ellipsoid>();
055 final public static Map<String, Datum> datums = new HashMap<String, Datum>();
056 final public static Map<String, NTV2GridShiftFileWrapper> nadgrids = new HashMap<String, NTV2GridShiftFileWrapper>();
057 final public static Map<String, Pair<String, String>> inits = new HashMap<String, Pair<String, String>>();
058
059 static {
060 registerBaseProjection("lonlat", LonLat.class, "core");
061 registerBaseProjection("josm:smerc", org.openstreetmap.josm.data.projection.proj.Mercator.class, "core");
062 registerBaseProjection("lcc", LambertConformalConic.class, "core");
063 registerBaseProjection("somerc", SwissObliqueMercator.class, "core");
064 registerBaseProjection("tmerc", TransverseMercator.class, "core");
065
066 ellipsoids.put("intl", Ellipsoid.hayford);
067 ellipsoids.put("GRS80", Ellipsoid.GRS80);
068 ellipsoids.put("WGS84", Ellipsoid.WGS84);
069 ellipsoids.put("bessel", Ellipsoid.Bessel1841);
070
071 datums.put("WGS84", WGS84Datum.INSTANCE);
072
073 nadgrids.put("BETA2007.gsb", NTV2GridShiftFileWrapper.BETA2007);
074 nadgrids.put("ntf_r93_b.gsb", NTV2GridShiftFileWrapper.ntf_rgf93);
075
076 loadInits();
077 }
078
079 /**
080 * Plugins can register additional base projections.
081 *
082 * @param id The "official" PROJ.4 id. In case the projection is not supported
083 * by PROJ.4, use some prefix, e.g. josm:myproj or gdal:otherproj.
084 * @param fac The base projection factory.
085 * @param origin Multiple plugins may implement the same base projection.
086 * Provide plugin name or similar string, so it be differentiated.
087 */
088 public static void registerBaseProjection(String id, ProjFactory fac, String origin) {
089 projs.put(id, fac);
090 }
091
092 public static void registerBaseProjection(String id, Class<? extends Proj> projClass, String origin) {
093 registerBaseProjection(id, new ClassProjFactory(projClass), origin);
094 }
095
096 public static Proj getBaseProjection(String id) {
097 ProjFactory fac = projs.get(id);
098 if (fac == null) return null;
099 return fac.createInstance();
100 }
101
102 public static Ellipsoid getEllipsoid(String id) {
103 return ellipsoids.get(id);
104 }
105
106 public static Datum getDatum(String id) {
107 return datums.get(id);
108 }
109
110 public static NTV2GridShiftFileWrapper getNTV2Grid(String id) {
111 return nadgrids.get(id);
112 }
113
114 public static String getInit(String id) {
115 return inits.get(id.toLowerCase()).b;
116 }
117
118 /**
119 * Load +init "presets" from file
120 */
121 private static void loadInits() {
122 Pattern epsgPattern = Pattern.compile("<(\\d+)>(.*)<>");
123 try {
124 InputStream in = new MirroredInputStream("resource://data/epsg");
125 BufferedReader r = new BufferedReader(new InputStreamReader(in));
126 String line, lastline = "";
127 while ((line = r.readLine()) != null) {
128 line = line.trim();
129 if (!line.startsWith("#") && !line.isEmpty()) {
130 if (!lastline.startsWith("#")) throw new AssertionError();
131 String name = lastline.substring(1).trim();
132 Matcher m = epsgPattern.matcher(line);
133 if (m.matches()) {
134 inits.put("epsg:" + m.group(1), Pair.create(name, m.group(2).trim()));
135 } else {
136 System.err.println("Warning: failed to parse line from the epsg projection definition: "+line);
137 }
138 }
139 lastline = line;
140 }
141 } catch (IOException ex) {
142 throw new RuntimeException();
143 }
144 }
145
146 private final static Map<String, ProjectionChoice> allCodesPC = new HashMap<String, ProjectionChoice>();
147 private final static Map<String, Projection> allCodes = new HashMap<String, Projection>();
148
149 static {
150 // FIXME: use {@link #inits}, because it may contain more codes in future
151 // than exposed by the ProjectionChoices
152 for (ProjectionChoice pc : ProjectionPreference.getProjectionChoices()) {
153 for (String code : pc.allCodes()) {
154 allCodesPC.put(code, pc);
155 }
156 }
157 }
158
159 public static Projection getProjectionByCode(String code) {
160 Projection p = allCodes.get(code);
161 if (p != null) return p;
162 ProjectionChoice pc = allCodesPC.get(code);
163 if (pc == null) return null;
164 Collection<String> pref = pc.getPreferencesFromCode(code);
165 pc.setPreferences(pref);
166 p = pc.getProjection();
167 allCodes.put(code, p);
168 return p;
169 }
170
171 }