@@ -42,6 +42,8 @@ public class SRTMDataCell : ISRTMDataCell
4242 {
4343 #region Lifecycle
4444
45+ private static readonly double _oneArcSecondAtEquator = 30.87f ;
46+
4547 /// <summary>
4648 /// Initializes a new instance of the <see cref="SRTM.SRTMDataCell"/> class.
4749 /// </summary>
@@ -95,17 +97,30 @@ public SRTMDataCell(string filepath)
9597
9698 switch ( HgtData . Length )
9799 {
100+ // https://www.esri.com/news/arcuser/0400/wdside.html#:~:text=At%20the%20equator%2C%20an%20arc,101.27%20feet%20or%2030.87%20meters).
101+ // At the equator, an arc-second of longitude approximately equals an arc-second of latitude, which is 1 / 60th of a nautical mile(or 101.27 feet or 30.87 meters).
102+ // Arc-seconds of latitude remain nearly constant, while arc-seconds of longitude decrease in a trigonometric cosine-based fashion as one moves toward the earth's poles.
103+ // E.g. at 49 degrees north latitude, along the northern boundary of the Concrete sheet, an arc-second of longitude equals 30.87 meters * 0.6561 (cos 49) or 20.250 meters.
98104 case 1201 * 1201 * 2 : // SRTM-3
99105 PointsPerCell = 1201 ;
106+ VerticalPointsPerCell = 1201 ;
107+ PointHeightInMeters = _oneArcSecondAtEquator * 3 ; // 3 arc-seconds
108+ PointWidthInMeters = _oneArcSecondAtEquator * 3 * Math . Cos ( ConvertToRadians ( Latitude ) ) ;
100109 break ;
101110 case 3601 * 3601 * 2 : // SRTM-1
102111 PointsPerCell = 3601 ;
112+ VerticalPointsPerCell = 3601 ;
113+ PointHeightInMeters = _oneArcSecondAtEquator ; // 1 arc-second
114+ PointWidthInMeters = _oneArcSecondAtEquator * Math . Cos ( ConvertToRadians ( Latitude ) ) ;
103115 break ;
104116 default :
105117 throw new ArgumentException ( "Invalid file size." , filepath ) ;
106118 }
107119 }
108-
120+ private double ConvertToRadians ( double angle )
121+ {
122+ return Math . PI / 180 * angle ;
123+ }
109124 #endregion
110125
111126 #region Properties
@@ -116,31 +131,55 @@ public SRTMDataCell(string filepath)
116131 /// <value>
117132 /// The hgt data.
118133 /// </value>
119- private byte [ ] HgtData { get ; set ; }
134+ public byte [ ] HgtData { get ; }
120135
121136 /// <summary>
122137 /// Gets or sets the points per cell.
123138 /// </summary>
124139 /// <value>
125140 /// The points per cell.
126141 /// </value>
127- private int PointsPerCell { get ; set ; }
142+ public int PointsPerCell { get ; }
143+
144+ /// <summary>
145+ /// Gets or sets the vertical points per cell.
146+ /// </summary>
147+ /// <value>
148+ /// The vertical points per cell.
149+ /// </value>
150+ public int VerticalPointsPerCell { get ; }
151+
152+ /// <summary>
153+ /// Gets the point height in meters.
154+ /// </summary>
155+ /// <value>
156+ /// The point height in meters.
157+ /// </value>
158+ public double PointHeightInMeters { get ; }
159+
160+ /// <summary>
161+ /// Gets the point width in meters.
162+ /// </summary>
163+ /// <value>
164+ /// The point width in meters.
165+ /// </value>
166+ public double PointWidthInMeters { get ; }
128167
129168 /// <summary>
130169 /// Gets or sets the latitude of the srtm data file.
131170 /// </summary>
132171 /// <value>
133172 /// The latitude.
134173 /// </value>
135- public int Latitude { get ; private set ; }
174+ public int Latitude { get ; }
136175
137176 /// <summary>
138177 /// Gets or sets the longitude of the srtm data file.
139178 /// </summary>
140179 /// <value>
141180 /// The longitude.
142181 /// </value>
143- public int Longitude { get ; private set ; }
182+ public int Longitude { get ; }
144183
145184 #endregion
146185
@@ -158,12 +197,38 @@ public SRTMDataCell(string filepath)
158197 /// Represents errors that occur during application execution.
159198 /// </exception>
160199 public int ? GetElevation ( double latitude , double longitude )
200+ {
201+ var bytesPos = GetBytePositionByCoordinate ( latitude , longitude ) ;
202+ return ReadByteData ( bytesPos ) ;
203+ }
204+
205+ /// <summary>
206+ /// Method responsible for obtaining a byte position by coordinate.
207+ /// </summary>
208+ /// <param name="localLat">Local latitude within the data cell</param>
209+ /// <param name="localLon">Local longitude within the data cell</param>
210+ /// <returns>Byte position</returns>
211+ public int GetBytePositionByCoordinate ( double latitude , double longitude )
161212 {
162213 int localLat = ( int ) ( ( latitude - Latitude ) * PointsPerCell ) ;
163- int localLon = ( int ) ( ( ( longitude - Longitude ) ) * PointsPerCell ) ;
164- return ReadByteData ( localLat , localLon ) ;
214+ int localLon = ( int ) ( ( longitude - Longitude ) * PointsPerCell ) ;
215+ return ( ( PointsPerCell - localLat - 1 ) * PointsPerCell * 2 ) + localLon * 2 ;
165216 }
166-
217+
218+ /// <summary>
219+ /// Method responsible for obtaining an elevation by byte position.
220+ /// </summary>
221+ /// <param name="bytesPos">The byte position</param>
222+ /// <returns>The elevation</returns>
223+ public int ? GetElevation ( int bytesPos )
224+ {
225+ if ( ( HgtData [ bytesPos ] == 0x80 ) && ( HgtData [ bytesPos + 1 ] == 0x00 ) )
226+ return null ;
227+
228+ // Motorola "big-endian" order with the most significant byte first
229+ return ( HgtData [ bytesPos ] ) << 8 | HgtData [ bytesPos + 1 ] ;
230+ }
231+
167232 /// <summary>
168233 /// Gets the elevation. Data is smoothed using bilinear interpolation.
169234 /// </summary>
@@ -204,7 +269,7 @@ public SRTMDataCell(string filepath)
204269 }
205270
206271 #endregion
207-
272+
208273 #region Private Methods
209274
210275 /// <summary>
@@ -217,18 +282,23 @@ public SRTMDataCell(string filepath)
217282 private int ? ReadByteData ( int localLat , int localLon )
218283 {
219284 int bytesPos = ( ( PointsPerCell - localLat - 1 ) * PointsPerCell * 2 ) + localLon * 2 ;
285+ return ReadByteData ( bytesPos ) ;
286+ }
220287
288+ /// <summary>
289+ /// Method responsible for reading byte data from data cell file.
290+ /// </summary>
291+ /// <param name="bytesPos">The byte position</param>
292+ /// <returns>Height read from data cell file</returns>
293+ private int ? ReadByteData ( int bytesPos )
294+ {
221295 if ( bytesPos < 0 || bytesPos > PointsPerCell * PointsPerCell * 2 )
222296 throw new ArgumentOutOfRangeException ( "Coordinates out of range." , "coordinates" ) ;
223297
224298 if ( bytesPos >= HgtData . Length )
225299 return null ;
226300
227- if ( ( HgtData [ bytesPos ] == 0x80 ) && ( HgtData [ bytesPos + 1 ] == 0x00 ) )
228- return null ;
229-
230- // Motorola "big-endian" order with the most significant byte first
231- return ( HgtData [ bytesPos ] ) << 8 | HgtData [ bytesPos + 1 ] ;
301+ return GetElevation ( bytesPos ) ;
232302 }
233303
234304 private double Lerp ( double start , double end , double delta )
0 commit comments