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 }