source: josm/src/org/openstreetmap/josm/data/projection/UTM.java@ 1

Last change on this file since 1 was 1, checked in by imi, 19 years ago

wiki 19:37, 27. Sep 2005

File size: 9.6 KB
Line 
1package org.openstreetmap.josm.data.projection;
2
3import java.awt.event.ActionEvent;
4import java.awt.event.ActionListener;
5
6import javax.swing.BorderFactory;
7import javax.swing.Box;
8import javax.swing.JComboBox;
9import javax.swing.JComponent;
10import javax.swing.JLabel;
11import javax.swing.JSpinner;
12import javax.swing.SpinnerNumberModel;
13import javax.swing.border.Border;
14import javax.swing.event.ChangeEvent;
15import javax.swing.event.ChangeListener;
16
17import org.openstreetmap.josm.data.Bounds;
18import org.openstreetmap.josm.data.GeoPoint;
19import org.openstreetmap.josm.data.osm.DataSet;
20
21/**
22 * A java port of a ruby port of a C port of a Projection from the
23 * Defense Mapping agency. ;-)
24 *
25 * The whole dataset is interpreted as beeing in one zone. This
26 * zone is initially taken from the center lat/lon value but can
27 * be configured to any other zone. Same is for the hemisphere.
28 *
29 * C code by Chuck Gantz
30 * Ruby port by Ben Gimpert
31 * modified Java port by imi (myself)
32 *
33 * @author imi
34 */
35public class UTM extends Projection {
36
37 public final static double DEG_TO_RAD = Math.PI / 180;
38 public final static double RAD_TO_DEG = 180 / Math.PI;
39
40 /**
41 * A reference ellipsoid used in Projections
42 */
43 public static class Ellipsoid {
44 String name;
45 double a, ecc_squared;
46 Ellipsoid(String name, double a, double ecc_squared) {
47 this.name = name;
48 this.a = a;
49 this.ecc_squared = ecc_squared;
50 }
51 public String toString() {
52 return name;
53 }
54 }
55
56 /**
57 * All available reference ellipsoids.
58 */
59 public final static Ellipsoid[] allEllipsoids = new Ellipsoid[] {
60 new Ellipsoid("Airy", 6377563, 0.00667054),
61 new Ellipsoid("Australian National", 6378160, 0.006694542),
62 new Ellipsoid("Bessel 1841", 6377397, 0.006674372),
63 new Ellipsoid("Clarke 1866", 6378206, 0.006768658),
64 new Ellipsoid("Clarke 1880", 6378249, 0.006803511),
65 new Ellipsoid("Everest", 6377276, 0.006637847),
66 new Ellipsoid("Fischer Mercury 1960", 6378166, 0.006693422),
67 new Ellipsoid("Fischer 1968", 6378150, 0.006693422),
68 new Ellipsoid("GRS 1967", 6378160, 0.006694605),
69 new Ellipsoid("GRS 1980", 6378137, 0.00669438),
70 new Ellipsoid("Helmert 1906", 6378200, 0.006693422),
71 new Ellipsoid("Hough", 6378270, 0.00672267),
72 new Ellipsoid("Krassovsky", 6378245, 0.006693422),
73 new Ellipsoid("WGS-60", 6378165, 0.006693422),
74 new Ellipsoid("WGS-66", 6378145, 0.006694542),
75 new Ellipsoid("WGS-72", 6378135, 0.006694318),
76 new Ellipsoid("WGS-84", 6378137, 0.00669438)
77 };
78
79 private enum Hemisphere {north, south};
80
81 /**
82 * What hemisphere the whole map is in.
83 */
84 private Hemisphere hemisphere = Hemisphere.north;
85 /**
86 * What zone the whole map is in.
87 */
88 private int zone = 0; // 0 means not initialized
89 /**
90 * Reference ellipsoid used in projection
91 */
92 protected Ellipsoid ellipsoid = allEllipsoids[allEllipsoids.length-1];
93
94
95 @Override
96 public void latlon2xy(GeoPoint p) {
97 // converts lat/long to UTM coords. Equations from USGS Bulletin 1532
98 // North latitudes are positive, South latitudes are negative
99 // East longitudes are positive, West longitudes are negative
100 // lat and long are in decimal degrees
101 // Written by Chuck Gantz- chuck.gantz@globalstar.com
102 // ported to Ruby by Ben Gimpert- ben@somethingmodern.com
103 double k0 = 0.9996012717;
104
105 double lat_rad = p.lat * DEG_TO_RAD;
106 double long_temp = (p.lon + 180) - (Math.floor((p.lon + 180) / 360) * 360) - 180;
107 double long_rad = long_temp * DEG_TO_RAD;
108
109 double long_origin = (zone - 1)*6 - 180 + 3; // +3 puts origin in middle of zone
110 double long_origin_rad = long_origin * DEG_TO_RAD;
111
112 double ecc_prime_squared = ellipsoid.ecc_squared / (1 - ellipsoid.ecc_squared);
113
114 double n = ellipsoid.a / Math.sqrt(1 - ellipsoid.ecc_squared * Math.sin(lat_rad) * Math.sin(lat_rad));
115 double t = Math.tan(lat_rad) * Math.tan(lat_rad);
116 double c = ecc_prime_squared * Math.cos(lat_rad) * Math.cos(lat_rad);
117 double a = Math.cos(lat_rad) * (long_rad - long_origin_rad);
118
119 double e2 = ellipsoid.ecc_squared*ellipsoid.ecc_squared;
120 double e3 = e2*ellipsoid.ecc_squared;
121 double m = ellipsoid.a * (((1 - ellipsoid.ecc_squared/4 - 3*e2/64 - 5*e3/256)*lat_rad) - ((3*ellipsoid.ecc_squared/8 + 3*e2/32 + 45*e3/1024)*Math.sin(2*lat_rad)) + ((15*e2/256 + 45*e3/1024)*Math.sin(4*lat_rad)) - ((35*e3/3072)*Math.sin(6*lat_rad)));
122
123 p.x = k0*n*(a+(1-t+c)*a*a*a/6 + (5-18*t+t*t+72*c-58*ecc_prime_squared)*a*a*a*a*a/120) + 500000.0;
124 p.y = k0*(m+n*Math.tan(lat_rad)*(a*a/2+(5-t+9*c+4*c*c)*a*a*a*a/24 + (61-58*t+t*t+600*c-330*ecc_prime_squared)*a*a*a*a*a*a/720));
125 if (p.lat < 0)
126 p.y += 10000000.0; // offset for southern hemisphere
127 }
128
129 @Override
130 public void xy2latlon(GeoPoint p) {
131 // converts UTM coords to lat/long. Equations from USGS Bulletin 1532
132 // East longitudes are positive, West longitudes are negative.
133 // North latitudes are positive, South latitudes are negative
134 // lat and long are in decimal degrees.
135 // Written by Chuck Gantz- chuck.gantz@globalstar.com
136 // ported to Ruby by Ben Gimpert- ben@somethingmodern.com
137 double k0 = 0.9996;
138 double e1 = (1-Math.sqrt(1-ellipsoid.ecc_squared))/(1+Math.sqrt(1-ellipsoid.ecc_squared));
139 double x = p.x - 500000.0;
140 double y = p.y;
141 if (hemisphere == Hemisphere.south)
142 y -= 10000000.0;
143
144 double long_origin = (zone - 1)*6 - 180 + 3; // +3 puts origin in middle of zone
145 double ecc_prime_squared = ellipsoid.ecc_squared / (1 - ellipsoid.ecc_squared);
146 double m = y / k0;
147 double mu = m / (ellipsoid.a*(1-ellipsoid.ecc_squared/4-3*ellipsoid.ecc_squared*ellipsoid.ecc_squared/64-5*ellipsoid.ecc_squared*ellipsoid.ecc_squared*ellipsoid.ecc_squared/256));
148
149 double phi1_rad = mu + (3*e1/2-27*e1*e1*e1/32)*Math.sin(2*mu) + (21*e1*e1/16-55*e1*e1*e1*e1/32)*Math.sin(4*mu) +(151*e1*e1*e1/96)*Math.sin(6*mu);
150
151 double n1 = ellipsoid.a/Math.sqrt(1-ellipsoid.ecc_squared*Math.sin(phi1_rad)*Math.sin(phi1_rad));
152 double t1 = Math.tan(phi1_rad)*Math.tan(phi1_rad);
153 double c1 = ecc_prime_squared*Math.cos(phi1_rad)*Math.cos(phi1_rad);
154 double r1 = ellipsoid.a*(1-ellipsoid.ecc_squared)/Math.pow((1-ellipsoid.ecc_squared*Math.sin(phi1_rad)*Math.sin(phi1_rad)), 1.5);
155 double d = x / (n1*k0);
156
157 p.lat = phi1_rad - (n1*Math.tan(phi1_rad)/r1)*(d*d/2-(5+3*t1+10*c1-4*c1*c1-9*ecc_prime_squared)*d*d*d*d/24 + (61+90*t1+298*c1+45*t1*t1-252*ecc_prime_squared-3*c1*c1)*d*d*d*d*d*d/720);
158 p.lat = p.lat * RAD_TO_DEG;
159
160 p.lon = (d-(1+2*t1+c1)*d*d*d/6+(5-2*c1+28*t1-3*c1*c1+8*ecc_prime_squared+24*t1*t1)*d*d*d*d*d/120)/Math.cos(phi1_rad);
161 p.lon = long_origin + (p.lon * RAD_TO_DEG);
162 }
163
164 @Override
165 public String toString() {
166 return "UTM";
167 }
168
169 @Override
170 public String description() {
171 return "UTM projection ported from Ben Gimpert's ruby port.\n" +
172 "http://www.openstreetmap.org/websvn/filedetails.php?repname=" +
173 "OpenStreetMap&path=%2Futils%2Ftiger_import%2Ftiger%2Futm.rb";
174 }
175
176 /**
177 * If the zone is not already set, calculate it from this dataset.
178 * If the dataset span over more than one zone, take the middle one
179 * (the zone of the middle lat/lon).
180 * Also, calculate the hemisphere (northern/southern).
181 */
182 @Override
183 public void init(DataSet dataSet) {
184 if (zone == 0) {
185 Bounds b = dataSet.getBoundsLatLon();
186 if (b == null)
187 return;
188 GeoPoint center = b.centerLatLon();
189 double lat = center.lat;
190 double lon = center.lon;
191 // make sure the longitude is between -180.00 .. 179.9
192 double long_temp = (lon + 180) - (Math.floor((lon + 180) / 360) * 360) - 180;
193
194 zone = (int)((long_temp + 180) / 6) + 1;
195 if ((lat >= 56.0) && (lat < 64.0) && (long_temp >= 3.0) && (long_temp < 12.0))
196 zone = 32;
197 // special zones for Svalbard
198 if ((lat >= 72.0) && (lat < 84.0))
199 {
200 if ((long_temp >= 0.0) && (long_temp < 9.0))
201 zone = 31;
202 else if ((long_temp >= 9.0) && (long_temp < 21.0))
203 zone = 33;
204 else if ((long_temp >= 21.0) && (long_temp < 33.0))
205 zone = 35;
206 else if ((long_temp >= 33.0) && (long_temp < 42.0))
207 zone = 37;
208 }
209 hemisphere = lat > 0 ? Hemisphere.north : Hemisphere.south;
210 }
211 super.init(dataSet);
212 }
213
214 @Override
215 public JComponent getConfigurationPanel() {
216 Border border = BorderFactory.createEmptyBorder(5,0,0,0);
217 Box panel = Box.createVerticalBox();
218
219 // ellipsoid
220 Box ellipsoidPanel = Box.createHorizontalBox();
221 ellipsoidPanel.add(new JLabel("Ellipsoid"));
222 final JComboBox ellipsoidCombo = new JComboBox(allEllipsoids);
223 ellipsoidPanel.add(ellipsoidCombo);
224 ellipsoidCombo.setSelectedItem(ellipsoid);
225 ellipsoidCombo.addActionListener(new ActionListener(){
226 public void actionPerformed(ActionEvent e) {
227 ellipsoid = (Ellipsoid)ellipsoidCombo.getSelectedItem();
228 fireStateChanged();
229 }
230 });
231 ellipsoidPanel.setBorder(border);
232 panel.add(ellipsoidPanel);
233
234 // zone
235 Box zonePanel = Box.createHorizontalBox();
236 zonePanel.add(new JLabel("Zone"));
237 final JSpinner zoneSpinner = new JSpinner(new SpinnerNumberModel(zone,1,60,1));
238 zonePanel.add(zoneSpinner);
239 zoneSpinner.addChangeListener(new ChangeListener(){
240 public void stateChanged(ChangeEvent e) {
241 zone = (Integer)zoneSpinner.getValue();
242 fireStateChanged();
243 }
244 });
245 zonePanel.setBorder(border);
246 panel.add(zonePanel);
247
248 // hemisphere
249 Box hemispherePanel = Box.createHorizontalBox();
250 hemispherePanel.add(new JLabel("Hemisphere"));
251 final JComboBox hemisphereCombo = new JComboBox(Hemisphere.values());
252 hemispherePanel.add(hemisphereCombo);
253 hemisphereCombo.setSelectedItem(hemisphere);
254 hemisphereCombo.addActionListener(new ActionListener(){
255 public void actionPerformed(ActionEvent e) {
256 hemisphere = (Hemisphere)hemisphereCombo.getSelectedItem();
257 fireStateChanged();
258 }
259 });
260 hemispherePanel.setBorder(border);
261 panel.add(hemispherePanel);
262
263 return panel;
264 }
265}
Note: See TracBrowser for help on using the repository browser.