001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.data.projection; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import org.openstreetmap.josm.data.Bounds; 007 import org.openstreetmap.josm.data.coor.LatLon; 008 import org.openstreetmap.josm.data.projection.datum.NTV2Datum; 009 import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper; 010 import org.openstreetmap.josm.data.projection.proj.LambertConformalConic; 011 import org.openstreetmap.josm.data.projection.proj.ProjParameters; 012 013 /** 014 * Lambert conic conform 4 zones using the French geodetic system NTF. 015 * This newer version uses the grid translation NTF<->RGF93 provided by IGN for a submillimetric accuracy. 016 * (RGF93 is the French geodetic system similar to WGS84 but not mathematically equal) 017 * 018 * Source: http://professionnels.ign.fr/DISPLAY/000/526/700/5267002/transformation.pdf 019 * @author Pieren 020 */ 021 public class Lambert extends AbstractProjection { 022 023 /** 024 * Lambert I, II, III, and IV latitude origin 025 */ 026 private static final double lat_0s[] = { 49.5, 46.8, 44.1, 42.165 }; 027 028 /** 029 * Lambert I, II, III, and IV latitude of first standard parallel 030 */ 031 private static final double lat_1s[] = { 032 convertDegreeMinuteSecond(48, 35, 54.682), 033 convertDegreeMinuteSecond(45, 53, 56.108), 034 convertDegreeMinuteSecond(43, 11, 57.449), 035 convertDegreeMinuteSecond(41, 33, 37.396)}; 036 037 /** 038 * Lambert I, II, III, and IV latitude of second standard parallel 039 */ 040 private static final double lat_2s[] = { 041 convertDegreeMinuteSecond(50, 23, 45.282), 042 convertDegreeMinuteSecond(47, 41, 45.652), 043 convertDegreeMinuteSecond(44, 59, 45.938), 044 convertDegreeMinuteSecond(42, 46, 3.588)}; 045 046 /** 047 * Lambert I, II, III, and IV false east 048 */ 049 private static final double x_0s[] = { 600000.0, 600000.0, 600000.0, 234.358 }; 050 051 /** 052 * Lambert I, II, III, and IV false north 053 */ 054 private static final double y_0s[] = { 200000.0, 200000.0, 200000.0, 185861.369 }; 055 056 /** 057 * France is divided in 4 Lambert projection zones (1,2,3 + 4th for Corsica) 058 */ 059 public static final double cMaxLatZone1Radian = Math.toRadians(57 * 0.9); 060 public static final double cMinLatZone1Radian = Math.toRadians(46.1 * 0.9);// lowest latitude of Zone 4 (South Corsica) 061 062 public static final double[][] zoneLimitsDegree = { 063 {Math.toDegrees(cMaxLatZone1Radian), (53.5 * 0.9)}, // Zone 1 (reference values in grad *0.9) 064 {(53.5 * 0.9), (50.5 * 0.9)}, // Zone 2 065 {(50.5 * 0.9), (47.0 * 0.9)}, // Zone 3 066 {(47.51963 * 0.9), Math.toDegrees(cMinLatZone1Radian)} // Zone 4 067 }; 068 069 public static final double cMinLonZonesRadian = Math.toRadians(-4.9074074074074059 * 0.9); 070 071 public static final double cMaxLonZonesRadian = Math.toRadians(10.2 * 0.9); 072 073 /** 074 * Allow some extension beyond the theoretical limits 075 */ 076 public static final double cMaxOverlappingZonesDegree = 1.5; 077 078 public static final int DEFAULT_ZONE = 0; 079 080 private int layoutZone; 081 082 public Lambert() { 083 this(DEFAULT_ZONE); 084 } 085 086 public Lambert(final int layoutZone) { 087 if (layoutZone < 0 || layoutZone >= 4) 088 throw new IllegalArgumentException(); 089 this.layoutZone = layoutZone; 090 ellps = Ellipsoid.clarkeIGN; 091 datum = new NTV2Datum("ntf_rgf93Grid", null, ellps, NTV2GridShiftFileWrapper.ntf_rgf93); 092 x_0 = x_0s[layoutZone]; 093 y_0 = y_0s[layoutZone]; 094 lon_0 = 2.0 + 20.0 / 60 + 14.025 / 3600; // 0 grade Paris 095 if (proj == null) { 096 proj = new LambertConformalConic(); 097 } 098 proj = new LambertConformalConic(); 099 try { 100 proj.initialize(new ProjParameters() {{ 101 ellps = Lambert.this.ellps; 102 lat_0 = lat_0s[layoutZone]; 103 lat_1 = lat_1s[layoutZone]; 104 lat_2 = lat_2s[layoutZone]; 105 }}); 106 } catch (ProjectionConfigurationException e) { 107 throw new RuntimeException(e); 108 } 109 } 110 111 @Override 112 public String toString() { 113 return tr("Lambert 4 Zones (France)"); 114 } 115 116 @Override 117 public Integer getEpsgCode() { 118 return 27561+layoutZone; 119 } 120 121 @Override 122 public int hashCode() { 123 return getClass().getName().hashCode()+layoutZone; // our only real variable 124 } 125 126 @Override 127 public String getCacheDirectoryName() { 128 return "lambert"; 129 } 130 131 @Override 132 public Bounds getWorldBoundsLatLon() 133 { 134 Bounds b= new Bounds( 135 new LatLon(Math.max(zoneLimitsDegree[layoutZone][1] - cMaxOverlappingZonesDegree, Math.toDegrees(cMinLatZone1Radian)), Math.toDegrees(cMinLonZonesRadian)), 136 new LatLon(Math.min(zoneLimitsDegree[layoutZone][0] + cMaxOverlappingZonesDegree, Math.toDegrees(cMaxLatZone1Radian)), Math.toDegrees(cMaxLonZonesRadian)), 137 false); 138 return b; 139 } 140 141 public int getLayoutZone() { 142 return layoutZone; 143 } 144 145 }