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 }