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 }

Back to equidistant projectionGo to article index