|
Part 3- Orthographic Projection
Oct 12, 2003 -- Developing Map Projections
with Java. In the third article of a multi-part series, the orthographic
projection, is presented. VR
The orthographic projection was used for astronomical purposes
as early the 200 B.C. by the Egyptian and Greek cultures. It is still
used for that purpose today. It can be used to display a map of an astronomical
object such as Earth so that appears very much like it would in space.
The equations for the orthographic projection are :

The inverse equations are:
|

Orthographic map projection of Earth utilizing images from NASA's
Blue Marble web site.
In the code below, the method getLocationForCoordinate implements
the forward equations and the getCoordinateForLocation the inverse equation.
|
1 /*
2 * OrthographicMapProjection.java
3 *
4 * Copyright (c) 2002, 2003, Raben Systems, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * Neither the name of Raben Systems, Inc. nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 * Created on June 7, 2002, 4:31 PM
34 */
35
36 package com.raben.projection.map;
37 import java.awt.geom.Point2D;
38 import java.awt.geom.GeneralPath;
39 /***
40 * Orthographic Map projection computation
41 * @author Vern Raben
42 * @version $Revision: 1.11 $ $Date: 2002/08/26 03:49:21 $
43 * Copyright (c) Raben Systems, Inc., 2002
44 * All rights reserved
45 */
46 public final class OrthographicMapProjection extends AbstractMapProjection {
47
48 /*** Creates new OrthographicMapProjection */
49 public OrthographicMapProjection() {
50 }
51
52 /***
53 * Get Screen location for specified coordinate in radians
54 * @param coordinate Point2D Longitude and latitude of coordinate in radians
55 * @return Point2D Screen location of the coordinate
56 */
57 public Point2D getLocationForCoordinate(Point2D coordinate) {
58 Point2D.Double loc = new Point2D.Double(Double.NaN, Double.NaN);
59 Point2D centerPoint = getCenterPoint();
60 Point2D centerCoordinate = getCenterCoordinate();
61 double radius = getRadius();
62 double cosLatCenter = getCosLatCenter();
63 double sinLatCenter = getSinLatCenter();
64
65 if ((!Double.isNaN(coordinate.getX()))
66 && (!Double.isNaN(coordinate.getY()))) {
67 double latitude = coordinate.getY();
68 double longitude = coordinate.getX();
69
70 double sinLat = Math.sin(normalizeLatitude(latitude));
71 double cosLat = Math.cos(latitude);
72 double lonDiff = normalizeLongitude(longitude
73 - centerCoordinate.getX());
74 double cosLonDiff = Math.cos(lonDiff);
75 double cosC = (sinLatCenter * sinLat)
76 + (cosLatCenter * cosLat * cosLonDiff);
77
78 if (cosC >= 0.0) {
79 double sinLonDiff = Math.sin(lonDiff);
80 double x = (radius * cosLat * sinLonDiff) + centerPoint.getX();
81 double y = (radius * ((cosLatCenter * sinLat)
82 - (sinLatCenter * cosLat * cosLonDiff)))
83 + centerPoint.getY();
84 loc.setLocation(x, y);
85 }
86 }
87
88 return loc;
89 }
90
91
92
93 /***
94 * Get coordinate for a given point on the screen
95 * @param loc Point2D Screen location of the point
96 * @return Point2D Coordinate of the point in radians
97 */
98 public Point2D getCoordinateForLocation(Point2D loc) {
99 Point2D.Double coordinate
100 = new Point2D.Double(Double.NaN, Double.NaN);
101 Point2D centerPoint = getCenterPoint();
102 Point2D centerCoordinate = getCenterCoordinate();
103 double sinLatCenter = getSinLatCenter();
104 double cosLatCenter = getCosLatCenter();
105 double radius = getRadius();
106
107 if ((!Double.isNaN(loc.getX()))
108 && (!Double.isNaN(loc.getY()))) {
109 double x = loc.getX() - centerPoint.getX();
110 double y = loc.getY() - centerPoint.getY();
111 double rho = Math.sqrt((x * x) + (y * y));
112
113
114 if ((rho > 0.0) & (rho <= radius)) {
115 double sinC = rho / radius;
116 double cosC = Math.sqrt(1.0 - (sinC * sinC));
117 double latitude = Math.asin(cosC * sinLatCenter)
118 + (y * sinC * cosLatCenter / rho);
119 double longitude = Double.NaN;
120
121 if (centerCoordinate.getY()
122 == MapProjectionConstants.PI_OVER_2) {
123 longitude = centerCoordinate.getX()
124 + Math.atan2(x, -y);
125 } else if (centerCoordinate.getY()
126 == -MapProjectionConstants.PI_OVER_2) {
127 longitude = centerCoordinate.getX() + Math.atan2(x, y);
128 } else {
129 longitude = centerCoordinate.getX()
130 + Math.atan2((x * sinC), (rho * cosLatCenter * cosC)
131 - (y * sinLatCenter * sinC));
132 }
133
134 longitude = normalizeLongitude(longitude);
135 latitude = normalizeLatitude(latitude);
136 coordinate.setLocation(longitude, latitude);
137 } else if (rho == 0.0) {
138 coordinate.setLocation(centerCoordinate.getX(),
139 centerCoordinate.getY());
140 }
141
142 }
143
144 return coordinate;
145 }
146
147
148 /***
149 * Get overlay grid for map as a path
150 * @return GeneralPath to draw mapOverlay.
151 */
152 public GeneralPath getOverlayGridPath() {
153 GeneralPath overlayGridPath = new GeneralPath();
154 double sinLat = 0.0;
155 double cosLat = 0.0;
156 double cosLonDiff = 0.0;
157 double sinLonDiff = 0.0;
158 double lonDiff = 0.0;
159 double cosC = 0.0;
160 float x, y;
161 float mark = (float) getRadius() / 360.0F;
162 Point2D centerPoint = getCenterPoint();
163 Point2D centerCoordinate = getCenterCoordinate();
164 double radius = getRadius();
165 double cosLatCenter = getCosLatCenter();
166 double sinLatCenter = getSinLatCenter();
167 double overlayGridIncrement = getOverlayGridIncrement();
168 double overlayGridLongitudeIncrement
169 = getOverlayGridLongitudeIncrement();
170 double overlayGridLatitudeIncrement = getOverlayGridLatitudeIncrement();
171
172 // Create latitude lines
173 for (double lat = -MapProjectionConstants.PI_OVER_2;
174 lat <= MapProjectionConstants.PI_OVER_2;
175 lat += overlayGridLatitudeIncrement) {
176 sinLat = Math.sin(lat);
177 cosLat = Math.cos(lat);
178
179
180 for (double lon = -Math.PI; lon <= Math.PI;
181 lon += overlayGridIncrement) {
182 lonDiff = lon - centerCoordinate.getX();
183 cosLonDiff = Math.cos(lonDiff);
184 cosC = (sinLatCenter * sinLat)
185 + (cosLatCenter * cosLat * cosLonDiff);
186
187 if (cosC >= 0.0) {
188 sinLonDiff = Math.sin(lonDiff);
189 x = (float) ((radius * cosLat * sinLonDiff)
190 + centerPoint.getX());
191 y = (float) ((radius * ((cosLatCenter * sinLat)
192 - (sinLatCenter * cosLat * cosLonDiff)))
193 + centerPoint.getY());
194 overlayGridPath.moveTo(x - mark, y);
195 overlayGridPath.lineTo(x + mark, y);
196 overlayGridPath.moveTo(x, y - mark);
197 overlayGridPath.lineTo(x, y + mark);
198 }
199 }
200 }
201
202 // Create longitude lines
203 for (double lon = -Math.PI; lon <= Math.PI;
204 lon += overlayGridLongitudeIncrement) {
205 lonDiff = lon - centerCoordinate.getX();
206 cosLonDiff = Math.cos(lonDiff);
207
208 for (double lat = -MapProjectionConstants.PI_OVER_2;
209 lat <= MapProjectionConstants.PI_OVER_2;
210 lat += overlayGridIncrement) {
211 sinLat = Math.sin(lat);
212 cosLat = Math.cos(lat);
213 cosC = (sinLatCenter * sinLat)
214 + (cosLatCenter * cosLat * cosLonDiff);
215
216 if (cosC >= 0.0) {
217 sinLonDiff = Math.sin(lonDiff);
218 x = (float) ((radius * cosLat * sinLonDiff)
219 + centerPoint.getX());
220 y = (float) ((radius * ((cosLatCenter * sinLat)
221 - (sinLatCenter * cosLat * cosLonDiff)))
222 + centerPoint.getY());
223 overlayGridPath.moveTo(x - mark, y);
224 overlayGridPath.lineTo(x + mark, y);
225 overlayGridPath.moveTo(x, y - mark);
226 overlayGridPath.lineTo(x, y + mark);
227 }
228 }
229 }
230
231 return overlayGridPath;
232 }
233
234 /***
235 * Get projection name
236 * @return ProjectionName
237 */
238 public ProjectionName getProjectionName() {
239 return ProjectionName.ORTHOGRAPHIC;
240 }
241
242
243
244
245
246 }

|