QuadTile的CreateElevatedMesh()方法中:
1 //获取地形瓦片
2 TerrainTile tile = QuadTileSet.World.TerrainAccessor.GetElevationArray(North + degreePerSample, South - degreePerSample, West - degreePerSample, East + degreePerSample, vertexCountElevated + 3); 3 float[,] heightData = tile.ElevationData;
调用了World的TerrainAccessor属性的GetElevationArray()方法。
实际调用的是TerrainAccessor子类NltTerrainAccessor的GetElevationArray方法。
GetElevationArray对高清影像进行递归调用。如下是NltTerrainAccessor的GetElevationArray方法。
1 /// <summary>
2 /// Builds a terrain array with specified boundaries 3 /// </summary>
4 /// <param name="north">North edge in decimal degrees.</param>
5 /// <param name="south">South edge in decimal degrees.</param>
6 /// <param name="west">West edge in decimal degrees.</param>
7 /// <param name="east">East edge in decimal degrees.</param>
8 /// <param name="samples"></param>
9 public override TerrainTile GetElevationArray(double north, double south, double west, double east, 10 int samples) 11 { 12 TerrainTile res = null; 13
14 if (m_higherResolutionSubsets != null) 15 { 16 // TODO: Support more than 1 level of higher resolution sets and allow user selections
17 foreach (TerrainAccessor higherResSub in m_higherResolutionSubsets) 18 { 19 if (north <= higherResSub.North && south >= higherResSub.South &&
20 west >= higherResSub.West && east <= higherResSub.East) 21 { 22 res = higherResSub.GetElevationArray(north, south, west, east, samples); 23 return res; 24 } 25 } 26 } 27
28 res = new TerrainTile(m_terrainTileService); 29 res.North = north; 30 res.South = south; 31 res.West = west; 32 res.East = east; 33 res.SamplesPerTile = samples; 34 res.IsInitialized = true; 35 res.IsValid = true; 36
37 double samplesPerDegree = (double)samples / (double)(north - south); 38 double latrange = Math.Abs(north - south); 39 double lonrange = Math.Abs(east - west); 40 TerrainTileCacheEntry ttce = null; 41
42 float[,] data = new float[samples, samples]; 43
44 if(samplesPerDegree < World.Settings.MinSamplesPerDegree) 45 { 46 res.ElevationData = data; 47 return res; 48 } 49
50 double scaleFactor = (double)1 / (samples - 1); 51 for (int x = 0; x < samples; x++) 52 { 53 for (int y = 0; y < samples; y++) 54 { 55 double curLat = north - scaleFactor * latrange * x; 56 double curLon = west + scaleFactor * lonrange * y; 57
58 // Wrap lat/lon to fit range 90/-90 and -180/180 (PM 2006-11-17)
59 if (curLat > 90) 60 { 61 curLat = 90 - (curLat - 90); 62 curLon += 180; 63 } 64 if (curLat < -90) 65 { 66 curLat = -90 - (curLat + 90); 67 curLon += 180; 68 } 69 if (curLon > 180) 70 { 71 curLon -= 360; 72 } 73 if (curLon < -180) 74 { 75 curLon += 360; 76 } 77
78 if (ttce == null ||
79 curLat < ttce.TerrainTile.South ||
80 curLat > ttce.TerrainTile.North ||
81 curLon < ttce.TerrainTile.West ||
82 curLon > ttce.TerrainTile.East) 83 { 84 TerrainTile tt = m_terrainTileService.GetTerrainTile(curLat, curLon, samplesPerDegree); 85 ttce = (TerrainTileCacheEntry)m_tileCache[tt.TerrainTileFilePath]; 86 if (ttce == null) 87 { 88 ttce = new TerrainTileCacheEntry(tt); 89 AddToCache(ttce); 90 } 91 if (!ttce.TerrainTile.IsInitialized) 92 ttce.TerrainTile.Initialize(); 93 ttce.LastAccess = DateTime.Now; 94 if (!tt.IsValid) 95 res.IsValid = false; 96 } 97
98 data[x, y] = ttce.TerrainTile.GetElevationAt(curLat, curLon); 99 } 100 } 101 res.ElevationData = data; 102
103 return res; 104 }
最后查看TerrainTile类的GetElevationAt方法获取了高程数据。
1 public class TerrainTile : IDisposable 2 { 3 public string TerrainTileFilePath; 4 public double TileSizeDegrees; 5 public int SamplesPerTile; 6 public double South; 7 public double North; 8 public double West; 9 public double East; 10 public int Row; 11 public int Col; 12 public int TargetLevel; 13 public TerrainTileService m_owner; 14 public bool IsInitialized; 15 public bool IsValid; 16
17 public float[,] ElevationData; 18 protected TerrainDownloadRequest request; 19
20 public TerrainTile( TerrainTileService owner ) 21 { 22 m_owner = owner; 23 } 24 /// <summary>
25 /// This method initializes the terrain tile add switches to 26 /// Initialize floating point/int 16 tiles 27 /// </summary>
28 public void Initialize() 29 { 30 if(IsInitialized) 31 return; 32
33 if(!File.Exists(TerrainTileFilePath)) 34 { 35 // Download elevation
36 if(request==null) 37 { 38 using( request = new TerrainDownloadRequest(this, m_owner, Row, Col, TargetLevel) ) 39 { 40 request.SaveFilePath = TerrainTileFilePath; 41 request.DownloadInForeground(); 42 } 43 } 44 } 45
46 if(ElevationData==null) 47 ElevationData = new float[SamplesPerTile, SamplesPerTile]; 48
49 if(File.Exists(TerrainTileFilePath)) 50 { 51 // Load elevation file
52 try
53 { 54 // TerrainDownloadRequest's FlagBadTile() creates empty files 55 // as a way to flag "bad" terrain tiles. 56 // Remove the empty 'flag' files after preset time.
57 try
58 { 59 FileInfo tileInfo = new FileInfo(TerrainTileFilePath); 60 if(tileInfo.Length == 0) 61 { 62 TimeSpan age = DateTime.Now.Subtract( tileInfo.LastWriteTime ); 63 if(age < m_owner.TerrainTileRetryInterval) 64 { 65 // This tile is still flagged bad
66 IsInitialized = true; 67 } 68 else
69 { 70 // remove the empty 'flag' file
71 File.Delete(TerrainTileFilePath); 72 } 73 return; 74 } 75 } 76 catch
77 { 78 // Ignore any errors in the above block, and continue. 79 // For example, if someone had the empty 'flag' file 80 // open, the delete would fail.
81 } 82
83 using( Stream s = File.OpenRead(TerrainTileFilePath)) 84 { 85 BinaryReader reader = new BinaryReader(s); 86 if(m_owner.DataType=="Int16") 87 { 88 /*
89 byte[] tfBuffer = new byte[SamplesPerTile*SamplesPerTile*2]; 90 if (s.Read(tfBuffer,0,tfBuffer.Length) < tfBuffer.Length) 91 throw new IOException(string.Format("End of file error while reading terrain file '{0}'.", TerrainTileFilePath) ); 92
93 int offset = 0; 94 for(int y = 0; y < SamplesPerTile; y++) 95 for(int x = 0; x < SamplesPerTile; x++) 96 ElevationData[x,y] = tfBuffer[offset++] + (short)(tfBuffer[offset++]<<8); 97 */
98 for(int y = 0; y < SamplesPerTile; y++) 99 for(int x = 0; x < SamplesPerTile; x++) 100 ElevationData[x,y] = reader.ReadInt16(); 101 } 102 if(m_owner.DataType=="Float32") 103 { 104 /*
105 byte[] tfBuffer = new byte[SamplesPerTile*SamplesPerTile*4]; 106 if (s.Read(tfBuffer,0,tfBuffer.Length) < tfBuffer.Length) 107 throw new IOException(string.Format("End of file error while reading terrain file '{0}'.", TerrainTileFilePath) ); 108 */
109 for(int y = 0; y < SamplesPerTile; y++) 110 for(int x = 0; x < SamplesPerTile; x++) 111 { 112 ElevationData[x,y] = reader.ReadSingle(); 113 } 114 } 115 IsInitialized = true; 116 IsValid = true; 117 } 118 return; 119 } 120 catch(IOException) 121 { 122 // If there is an IO exception when reading the terrain tile, 123 // then either something is wrong with the file, or with 124 // access to the file, so try and remove it.
125 try
126 { 127 File.Delete(TerrainTileFilePath); 128 } 129 catch(Exception ex) 130 { 131 throw new ApplicationException(String.Format("Error while trying to delete corrupt terrain tile {0}", TerrainTileFilePath), ex); 132 } 133 } 134 catch(Exception ex) 135 { 136 // Some other type of error when reading the terrain tile.
137 throw new ApplicationException(String.Format("Error while trying to read terrain tile {0}", TerrainTileFilePath), ex); 138 } 139 } 140 } 141 //根据经纬度从DEM瓦片中获取高程
142 public float GetElevationAt(double latitude, double longitude) 143 { 144 try
145 { 146 double deltaLat = North - latitude; 147 double deltaLon = longitude - West; 148
149 double df2 = (SamplesPerTile-1) / TileSizeDegrees; 150 float lat_pixel = (float)(deltaLat * df2); 151 float lon_pixel = (float)(deltaLon * df2); 152
153 int lat_min = (int)lat_pixel; 154 int lat_max = (int)Math.Ceiling(lat_pixel); 155 int lon_min = (int)lon_pixel; 156 int lon_max = (int)Math.Ceiling(lon_pixel); 157
158 if(lat_min >= SamplesPerTile) 159 lat_min = SamplesPerTile - 1; 160 if(lat_max >= SamplesPerTile) 161 lat_max = SamplesPerTile - 1; 162 if(lon_min >= SamplesPerTile) 163 lon_min = SamplesPerTile - 1; 164 if(lon_max >= SamplesPerTile) 165 lon_max = SamplesPerTile - 1; 166
167 if(lat_min < 0) 168 lat_min = 0; 169 if(lat_max < 0) 170 lat_max = 0; 171 if(lon_min < 0) 172 lon_min = 0; 173 if(lon_max < 0) 174 lon_max = 0; 175
176 float delta = lat_pixel - lat_min; 177 float westElevation =
178 ElevationData[lon_min, lat_min]*(1-delta) +
179 ElevationData[lon_min, lat_max]*delta; 180
181 float eastElevation =
182 ElevationData[lon_max, lat_min]*(1-delta) +
183 ElevationData[lon_max, lat_max]*delta; 184
185 delta = lon_pixel - lon_min; 186 float interpolatedElevation =
187 westElevation*(1-delta) +
188 eastElevation*delta; 189
190 return interpolatedElevation; 191 } 192 catch
193 { 194 } 195 return 0; 196 } 197 #region IDisposable Members
198
199 public void Dispose() 200 { 201 if(request != null) 202 { 203 request.Dispose(); 204 request = null; 205 } 206
207 GC.SuppressFinalize(this); 208 } 209
210 #endregion
211 }
TerrainAccessor在ConfigLoader中构建,赋值给World。