Skip to content

Commit 173e13c

Browse files
committed
refactor: make some logic and properties public
This refactoring allows helix-toolkit library to generate 3D models from HGT files using SRTM library
1 parent 15d2e0c commit 173e13c

File tree

7 files changed

+220
-31
lines changed

7 files changed

+220
-31
lines changed

src/SRTM/EmptySRTMDataCell.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,31 @@ public EmptySRTMDataCell(string filepath)
3131

3232
public int Longitude { get; private set; }
3333

34+
public byte[] HgtData => Array.Empty<byte>();
35+
36+
public int PointsPerCell => -1;
37+
38+
public int VerticalPointsPerCell => -1;
39+
40+
public double PointHeightInMeters => -1;
41+
42+
public double PointWidthInMeters => -1;
43+
44+
public int GetBytePositionByCoordinate(double latitude, double longitude)
45+
{
46+
return 0;
47+
}
48+
3449
public int? GetElevation(double latitude, double longitude)
3550
{
3651
return null;
3752
}
3853

54+
public int? GetElevation(int bytesPos)
55+
{
56+
return null;
57+
}
58+
3959
public double? GetElevationBilinear(double latitude, double longitude)
4060
{
4161
return null;

src/SRTM/ISRTMData.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,15 @@ public interface ISRTMData
4444
/// Represents errors that occur during application execution.
4545
/// </exception>
4646
int? GetElevation(double latitude, double longitude);
47-
47+
48+
/// <summary>
49+
/// Method responsible for identifying the correct data cell and either retrieving it from cache ir downloading it from source.
50+
/// </summary>
51+
/// <param name="latitude"></param>
52+
/// <param name="longitude"></param>
53+
/// <returns>Elevation data cell. Must be an instance of the <see cref="SRTM.ISRTMDataCell"/> class.</returns>
54+
ISRTMDataCell GetDataCell(double latitude, double longitude);
55+
4856
/// <summary>
4957
/// Gets the elevation. Data is smoothed using bilinear interpolation.
5058
/// </summary>

src/SRTM/ISRTMDataCell.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,28 @@ public interface ISRTMDataCell
66

77
int Longitude { get; }
88

9+
byte[] HgtData { get; }
10+
11+
int PointsPerCell { get; }
12+
13+
/// <summary>
14+
/// Gets the number of vertical points per cell.
15+
/// Useful for custom data cells that must have square shape.
16+
/// On majority of earth places it is feasible only when the number of horizontal and vertical points are different.
17+
/// It's because the size of vertical arc-second is constant while the size of horizontal one is different on different latitudes.
18+
/// </summary>
19+
int VerticalPointsPerCell { get; }
20+
21+
double PointHeightInMeters { get; }
22+
23+
double PointWidthInMeters { get; }
24+
925
int? GetElevation(double latitude, double longitude);
1026

1127
double? GetElevationBilinear(double latitude, double longitude);
28+
29+
int GetBytePositionByCoordinate(double latitude, double longitude);
30+
31+
int? GetElevation(int bytesPos);
1232
}
1333
}

src/SRTM/SRTM.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
<ApplicationIcon />
77
<StartupObject />
88
<LangVersion>latest</LangVersion>
9+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
910
</PropertyGroup>
1011

1112
<PropertyGroup>
1213
<DefineConstants>LIBLOG_PORTABLE</DefineConstants>
13-
<Version>0.0.7</Version>
14+
<Version>0.0.8</Version>
1415
<Authors>Ben Abelshausen</Authors>
1516
<Company>Itinero</Company>
1617
<Description>A library to read SRTM data, loads missing files on-the-fly.</Description>

src/SRTM/SRTMData.cs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public SRTMData(string dataDirectory, Func<(string path, string name), bool> get
104104
/// <value>
105105
/// The SRTM data cells.
106106
/// </value>
107-
private List<ISRTMDataCell> DataCells { get; set; }
107+
protected List<ISRTMDataCell> DataCells { get; private set; }
108108

109109
#region Public methods
110110

@@ -168,7 +168,7 @@ public void Unload()
168168
/// <param name="latitude"></param>
169169
/// <param name="longitude"></param>
170170
/// <returns>Elevation data cell. Must be an instance of the <see cref="SRTM.ISRTMDataCell"/> class.</returns>
171-
private ISRTMDataCell GetDataCell(double latitude, double longitude)
171+
public ISRTMDataCell GetDataCell(double latitude, double longitude)
172172
{
173173
int cellLatitude = (int)Math.Floor(Math.Abs(latitude));
174174
if (latitude < 0)
@@ -228,16 +228,9 @@ private ISRTMDataCell GetDataCell(double latitude, double longitude)
228228
}
229229
}
230230
}
231-
232-
if (File.Exists(filePath))
233-
{
234-
dataCell = new SRTMDataCell(filePath);
235-
}
236-
else if(File.Exists(zipFilePath))
237-
{
238-
dataCell = new SRTMDataCell(zipFilePath);
239-
}
240-
else
231+
232+
if (!TryReadExistingFile(filePath, out dataCell) &&
233+
!TryReadExistingFile(zipFilePath, out dataCell))
241234
{
242235
if (count < 0)
243236
{
@@ -259,6 +252,27 @@ private ISRTMDataCell GetDataCell(double latitude, double longitude)
259252

260253
return dataCell;
261254
}
255+
256+
protected bool TryReadExistingFile(string filePath, out ISRTMDataCell dataCell)
257+
{
258+
dataCell = null;
259+
260+
try
261+
{
262+
if (File.Exists(filePath))
263+
{
264+
dataCell = new SRTMDataCell(filePath);
265+
return true;
266+
}
267+
}
268+
catch
269+
{
270+
// probably the file is corrupted.
271+
File.Delete(filePath);
272+
}
273+
274+
return false;
275+
}
262276

263277
#endregion
264278
}

src/SRTM/SRTMDataCell.cs

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)