【C#】关于GB/T 13989-2012分幅编号及坐标计算

发布时间 2023-11-05 22:11:50作者: yzhyingcool
  1   public static class StandardSubdivisionConvertor
  2     {
  3         /// <summary>
  4         /// 通过图幅号获取四角经纬度坐标
  5         /// </summary>
  6         /// <param name="subdivCode"></param>
  7         /// <returns>返回(经度,纬度)的四角坐标,顺序是西北、东北、东南、西南</returns>
  8         public static ((string, string) northWestPoint, (string, string) northEastPoint, (string, string)southEastPoint, (string, string) southWestPoint) GetCoordinatesOfFourCorners(string subdivCode)
  9         {
 10             ((int degree, int minite, double second) lonDifference, (int degree, int minite, double second) latDifference) = GetLongitudeAndLattitudeDifference(subdivCode);
 11             (string longititude, string latitude)southWest = GetCoordinatesOfSouthWestPoint(subdivCode);
 12 
 13             int.TryParse(southWest.longititude.Split('°')[0], out int degreeLonOfSW);
 14             int.TryParse(southWest.longititude.Split('°')[1].Split('')[0], out int miniteLonOfSW);
 15             double.TryParse(southWest.longititude.Split('°')[1].Split('')[1].Split('')[0], out double secondLonOfSW);
 16 
 17             double sLon = secondLonOfSW + lonDifference.second;
 18             double sLon2Second = sLon % 60;
 19             int mLon = miniteLonOfSW + (int)sLon / 60 + lonDifference.minite;
 20             int mLon2Minite = mLon % 60;
 21             int dLon = degreeLonOfSW + lonDifference.degree + mLon / 60;
 22 
 23             string rLon = $"{dLon}°{mLon2Minite}′{sLon2Second}″";
 24 
 25 
 26             int.TryParse(southWest.latitude.Split('°')[0], out int degreeLatOfSW);
 27             int.TryParse(southWest.latitude.Split('°')[1].Split('')[0], out int miniteLatOfSW);
 28             double.TryParse(southWest.latitude.Split('°')[1].Split('')[1].Split('')[0], out double secondLatOfSW);
 29 
 30             double sLat = secondLatOfSW + latDifference.second;
 31             double sLat2Second = sLat % 60;
 32             int mLat = miniteLatOfSW + (int)sLat / 60 + latDifference.minite;
 33             int mLat2Minite = mLat % 60;
 34             int dLat = degreeLatOfSW + latDifference.degree + mLat / 60;
 35 
 36             string uLat = $"{dLat}°{mLat2Minite}′{sLat2Second}″";
 37 
 38             return new (
 39                 (southWest.longititude, uLat),
 40                 (rLon, uLat),
 41                 (rLon, southWest.latitude),
 42                 (southWest.longititude,southWest.latitude)
 43                 );
 44         }
 45         /// <summary>
 46         /// 根据图幅号计算图幅西南角的坐标。
 47         /// </summary>
 48         /// <param name="subdivCode"></param>
 49         /// <returns>度分秒格式的经纬度坐标</returns>
 50         public static (string longititude,string latitude) GetCoordinatesOfSouthWestPoint(string subdivCode)
 51         {
 52             subdivCode = subdivCode.ToUpper();
 53             //百万行号(纬度带字符码所对应数字码)
 54             int latRow = subdivCode[0] switch
 55             {
 56                 'A' => 1,
 57                 'B' => 2,
 58                 'C' => 3,
 59                 'D' => 4,
 60                 'E' => 5,
 61                 'F' => 6,
 62                 'G' => 7,
 63                 'H' => 8,
 64                 'I' => 9,
 65                 'J' => 10,
 66                 'K' => 11,
 67                 'L' => 12,
 68                 'M' => 13,
 69                 'N' => 14,
 70                 'O' => 15,
 71                 'P' => 16,
 72                 'Q' => 17,
 73                 'R' => 18,
 74                 'S' => 19,
 75                 'T' => 20,
 76                 'U' => 21,
 77                 'V' => 22,
 78                 _ => 0
 79             };
 80             if (latRow < 1) throw new ArgumentException("图幅号中百万行号错误!");
 81             //百万列号(经度带数字码)
 82             if (!int.TryParse(subdivCode.Substring(1, 2), out int lonCol)||lonCol<1) throw new ArgumentException("图幅号中百万列号错误!");
 83             //
 84             if (!int.TryParse(subdivCode.Substring(4, 3), out int row) || row < 1) throw new ArgumentException("图幅号中所在行号错误!");
 85             if (!int.TryParse(subdivCode.Substring(7, 3), out int col) || col < 1) throw new ArgumentException("图幅号中所在列号错误!");
 86 
 87             //获取经差、纬差
 88             ((int, int, double) lonDifference, (int, int, double) latDifference) lonLatDifference = GetLongitudeAndLattitudeDifference(subdivCode);
 89             (int, int, double) lonDifference = lonLatDifference.Item1;
 90             (int, int, double) latDifference = lonLatDifference.Item2;
 91 
 92             //经度
 93             double sLon = (col - 1) * lonDifference.Item3;
 94             int sLon2Degree = (int)sLon / 3600;
 95             int sLon2Minite = (int)sLon % 3600 / 60;
 96             double sLon2Second = sLon % 3600 % 60;
 97 
 98             int mLon = (col - 1) * lonDifference.Item2;
 99             int mLon2Degree = (sLon2Minite + mLon) / 60;
100             int mLon2Minite = (sLon2Minite + mLon) % 60;
101 
102             string lon = $"{(lonCol - 31) * 6 + lonDifference.Item1 + sLon2Degree + mLon2Degree}°{mLon2Minite}′{sLon2Second}″";
103 
104             //纬度
105             double sLat = row * latDifference.Item3;
106             int sLat2Degree = (int)sLat / 3600;
107             int sLat2Minite = (int)sLat % 3600 / 60;
108             double sLat2Second = (sLat % 3600) % 60;
109 
110             int mLat = row * latDifference.Item2;
111             int mLat2Degree = (sLat2Minite + mLat) / 60;
112             int mLat2Minite = (sLat2Minite + mLat) % 60;
113 
114             int dLat = row * latDifference.Item1 + sLat2Degree + mLat2Degree;
115             int degreeLat = mLat2Minite > 0 || sLat2Second > 0 ? 4 - dLat - 1 : 4 - dLat;
116             int miniteLat = sLat2Second == 0 && mLat2Minite == 0 ? 0 : sLat2Second > 0 ? 60 - mLat2Minite - 1 : 60 - mLat2Minite;
117             double secondLat = sLat2Second > 0 ? 60 - sLat2Second : 0;
118 
119             string lat = $"{(latRow - 1) * 4 + degreeLat}°{miniteLat}′{secondLat}″";
120 
121             return new (lon, lat);
122         }
123         
124         /// <summary>
125         /// 根据图幅号返回图幅的经差、纬差
126         /// </summary>
127         /// <param name="scaleCode"></param>
128         /// <returns>对应度分秒的经差和纬差</returns>
129         private static ((int degree, int minite, double second) lonDifference , (int degree, int minite, double second) latDifference) GetLongitudeAndLattitudeDifference(string subdivCode)
130         {
131             //经差
132             (int, int, double) lonDifference = subdivCode[3] switch
133             {
134                 'B' => (3, 0, 0),
135                 'C' => (1, 30, 0),
136                 'D' => (0, 30, 0),
137                 'E' => (0, 15, 0),
138                 'F' => (0, 7, 30),
139                 'G' => (0, 3, 45),
140                 'H' => (0, 1, 52.5),
141                 'I' => (0, 0, 37.5),
142                 'J' => (0, 0, 18.75),
143                 'K' => (0, 0, 9.375),
144                 _ => (-1, -1, -1)
145             };
146             if (lonDifference.Item1 < 0) throw new ArgumentException("图幅号中比例尺代码错误!");
147             //纬差
148             (int,int,double) latDifference = subdivCode[3] switch
149             {
150                 'B' => (2, 0, 0),
151                 'C' => (1, 0, 0),
152                 'D' => (0, 20, 0),
153                 'E' => (0, 10, 0),
154                 'F' => (0, 5, 0),
155                 'G' => (0, 2, 30),
156                 'H' => (0, 1, 15),
157                 'I' => (0, 0, 25),
158                 'J' => (0, 0, 12.5),
159                 'K' => (0, 0, 6.25),
160                 _ => (-1, -1, -1)
161             };
162             return new (lonDifference, latDifference);
163         }
164 
165         /// <summary>
166         /// 获取本幅八方向邻幅的图幅号
167         /// </summary>
168         /// <param name="subdivCode"></param>
169         /// <returns></returns>
170         public static ((string nw, string n, string ne) northSide, (string sw, string s, string se) southSide, string westSide, string eastSide) GetEightDirectionSubdivCode(string subdivCode)
171         {
172             //百万行号(纬度带字符码所对应数字码)
173             List<char> codeOfMillion = new List<char>() { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V' };
174             //List<int> rowOfMillion = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 };
175 
176             //本幅比例尺代码
177             char thisScaleCode = subdivCode[3];
178             //行列数
179             int thisSubdivCount = thisScaleCode switch
180             {
181                 'B' => 2,
182                 'C' => 4,
183                 'D' => 12,
184                 'E' => 24,
185                 'F' => 48,
186                 'G' => 96,
187                 'H' => 192,
188                 'I' => 576,
189                 'J' => 1152,
190                 'K' => 2304,
191                 _ => -1
192             };
193             if (thisSubdivCount == -1) throw new ArgumentException("图幅号中比例尺代码错误!");
194 
195             //本幅所在百万行号
196             char thisMRow = subdivCode[0];
197             if (codeOfMillion.IndexOf(thisMRow) < 0) throw new ArgumentException("图幅号中所在百万行号错误!");
198             //本幅所在百万列号
199             if (!int.TryParse(subdivCode.Substring(1, 2), out int thisMCol) || thisMCol < 43 || thisMCol > 53) throw new ArgumentException("图幅号中所在百万列号错误!");
200             //本幅行列号
201             if (!int.TryParse(subdivCode.Substring(4, 3), out int thisRow) || thisRow < 1 || thisRow > thisSubdivCount) throw new ArgumentException("图幅号中所在行号错误!");
202             if (!int.TryParse(subdivCode.Substring(7, 3), out int thisCol) || thisCol < 1 || thisCol > thisSubdivCount) throw new ArgumentException("图幅号中所在列号错误!");
203 
204 
205             //  ▁▁▁▁▁N▁▁▁▁▁
206             //  ▏     ▏     ▏     ▏                   
207             //  ▏     ▏     ▏     ▏
208             //  W━━━━━━━━━━E
209             //  ▏     ▏     ▏     ▏                    
210             //  ▏     ▏     ▏     ▏
211             //  ━━━━━━━━━━━  
212             //  ▏     ▏     ▏     ▏                    
213             //  ▏     ▏     ▏     ▏
214             //  ▔▔▔▔▔S▔▔▔▔▔
215             //本幅北邻幅行号
216             int rowN;
217             char mRowN;
218             if (thisRow == 1)
219             {
220                 rowN = thisSubdivCount;
221                 mRowN = codeOfMillion[codeOfMillion.IndexOf(thisMRow) + 1];
222             }
223             else
224             {
225                 rowN = thisRow - 1;
226                 mRowN = thisMRow;
227             }
228             //本幅南邻幅行号
229             int rowS;
230             char mRowS;
231             if (thisRow == thisSubdivCount)
232             {
233                 rowS = 1;
234                 mRowS = codeOfMillion[codeOfMillion.IndexOf(thisMRow) - 1];
235             }
236             else
237             {
238                 rowS = thisRow + 1;
239                 mRowS = thisMRow;
240             }
241             //本幅西邻幅列号
242             int colW, mColW;
243             if (thisCol == 1)
244             {
245                 colW = thisSubdivCount;
246                 mColW = thisMCol - 1;
247             }
248             else
249             {
250                 colW = thisCol - 1;
251                 mColW = thisMCol;
252             }
253             //本幅东邻幅列号
254             int colE, mColE;
255             if (thisCol == thisSubdivCount)
256             {
257                 colE = 1;
258                 mColE = thisMCol + 1;
259             }
260             else
261             {
262                 colE = thisCol + 1;
263                 mColE = thisMCol;
264             }
265             string NW = $"{mRowN}{mColW}{thisScaleCode}{string.Format("{0:d3}", rowN)}{string.Format("{0:d3}", colW)}";
266             string N = $"{mRowN}{thisMCol}{thisScaleCode}{string.Format("{0:d3}", rowN)}{string.Format("{0:d3}", thisCol)}";
267             string NE = $"{mRowN}{mColE}{thisScaleCode}{string.Format("{0:d3}", rowN)}{string.Format("{0:d3}", colE)}";
268             string E = $"{thisMRow}{mColE}{thisScaleCode}{string.Format("{0:d3}", thisRow)}{string.Format("{0:d3}", colE)}";
269             string SE = $"{mRowS}{mColE}{thisScaleCode}{string.Format("{0:d3}", rowS)}{string.Format("{0:d3}", colE)}";
270             string S = $"{mRowS}{thisMCol}{thisScaleCode}{string.Format("{0:d3}", rowS)}{string.Format("{0:d3}", thisCol)}";
271             string SW = $"{mRowS}{mColW}{thisScaleCode}{string.Format("{0:d3}", rowS)}{string.Format("{0:d3}", colW)}";
272             string W = $"{thisMRow}{mColW}{thisScaleCode}{string.Format("{0:d3}", thisRow)}{string.Format("{0:d3}", colW)}";
273             return new((NW, N, NE), (SW, S, SE), W, E);
274         }
275     }