// ----------------------------------------------------------------------------------------------- // receives descriptor and ordinal at which file is being generated in the sequence (keeps files in order) private String generateSliceFilename( SliceDescriptor desc, int fileSequenceIndex, bool appendCoordinate ) { String filename =""; // -- sequence index -- // surely 1000 numbers will be enough...for now filename = _outputFilename + "_vs" + _sliceDirection.ToString() + "_" + fileSequenceIndex.ToString("D4"); // -- coordinate -- if ( appendCoordinate ) { if ( _sliceDirection == SliceDirectionType.NS ) { filename += "_long_" + desc.Start.Longitude().ToString("F4"); } else { filename += "_lat_" + desc.Start.Latitude().ToString("F4"); } } return filename; }
// ----------------------------------------------------------------------------------------------- private void generateSlice( SliceDescriptor sliceDesc ) { Console.WriteLine( "generating file = " + sliceDesc.filename ); System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Reset(); stopwatch.Start(); fillPixelBuffer( _backgroundColor ); // -- inits -- double currentLatitude = sliceDesc.Start.Latitude(); double currentLongitude = sliceDesc.Start.Longitude(); var startRowCol = CoordinateToRowCol( sliceDesc.Start ); var endRowCol = CoordinateToRowCol( sliceDesc.End ); // These are invariant over the slices, why aren't I doing them outside of this? // note this is elevation range in image, which includes buffers at top/bottom float elevationRange = (_maxElevationInRect - _minElevationInRect) + (BufferMeters * 2.0f); // scale elevation range to image width float pixelsPerMeter = _imageHeight / elevationRange; var yCoordinates = new int[ _imageWidth ]; // -- loop over image x axis -- for ( int currentImageX = 0; currentImageX < _imageWidth; ++currentImageX ) { // what point in data is current image column looking 'down' on ? var currentRowCol = CoordinateToRowCol( currentLatitude, currentLongitude ); var currentHeight = _data.ValueAt( currentRowCol.Item1, currentRowCol.Item2 ); // the 'y' in the bitmap of the groundline is analogous to the height data at the current point in the topo data (converted to // pixels) int groundPixelHeight = Convert.ToInt32((currentHeight - _minElevationInRect + BufferMeters) * pixelsPerMeter); /* * if this happens, it means my maths are broken again if ( _imageHeight - groundPixelHeight < 0 ) { int breakpoint = 10; breakpoint++; } * */ // note : subtract from image height because bitmap y coordinates are 0 at the top ,increasing downward int currentImageY = Math.Max( _imageHeight - groundPixelHeight, 0 ); yCoordinates[ currentImageX ] = currentImageY; _pixels[ (currentImageY * _imageWidth) + currentImageX ] = _contourLineColor; // check for vertical gaps between consecutive pixels (>1 pixel apart) // It might be better to break up the slice into a series of lines, and handle these gaps that way (by iterating // over a series of points, drawing actual lines between them), but for now we'll take advantage of the fact // that we're always only moving one pixel horizontally in screen space, and that these gaps don't appear very // often in most datasets (unless you're in an area with a lot of cliffs and bluffs e.g. the Grand Canyon) if ( currentImageX > 0 ) { if ( Math.Abs( currentImageY - yCoordinates[ currentImageX - 1 ] ) > 1 ) // found a gap { int midY = (yCoordinates[ currentImageX ] + yCoordinates[ currentImageX - 1 ]) / 2; // fill FROM previous y TO current, but half on each coordinate verticalLine( currentImageX - 1, yCoordinates[ currentImageX - 1 ], midY, _contourLineColor ); verticalLine( currentImageX, midY, yCoordinates[ currentImageX ], _contourLineColor ); } } currentLatitude += _coordinateStepPerImagePixel.Latitude(); currentLongitude += _coordinateStepPerImagePixel.Longitude(); } stopwatch.Stop(); addTiming( "generate slice", stopwatch.ElapsedMilliseconds ); SaveBitmap( sliceDesc.filename, _pixels ); }
// ----------------------------------------------------------------------------------------------- // iterates over the line between the coordinates start->end, stepping by degreesBetweenSlices, and // generating a slice descriptor at each step that goes from current location to current location + sliceDelta private List<SliceDescriptor> generateSliceDescriptors( Tuple<double,double> start, Tuple<double,double> end, double degreesBetweenSlices, Tuple<double,double> sliceDelta ) { System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Reset(); stopwatch.Start(); // determine how many slices will be generated var startEndDelta = Vector.Ops.Delta( start, end ); // length of delta double startEndDeltaDegrees = Vector.Ops.Length( startEndDelta ); // note : add 1 because the division determines the count of spaces -between- slices int numSlices = (int)(startEndDeltaDegrees / degreesBetweenSlices) + 1; var slices = new List<SliceDescriptor>( numSlices ); // need normalized length start->end delta var normalizedStartEndDelta = Vector.Ops.Normalize( startEndDelta ); // now need coincident vector, but sized to degrees step var deltaStep = Vector.Ops.Scale( normalizedStartEndDelta, degreesBetweenSlices ); // starting point of current slice double currentStartLatitude = start.Item1; double currentStartLongitude = start.Item2; for ( int currentSliceIndex = 0; currentSliceIndex < numSlices; ++currentSliceIndex ) { var slice = new SliceDescriptor(); // -- generate coordinates -- slice.Start = new Tuple<double, double>( currentStartLatitude, currentStartLongitude ); slice.End = new Tuple<double,double>( currentStartLatitude + sliceDelta.Latitude(), currentStartLongitude + sliceDelta.Longitude()); // -- generate filename -- slice.filename = generateSliceFilename( slice, currentSliceIndex, _appendCoordinatesToFilenames ); slices.Add( slice ); currentStartLatitude += deltaStep.Latitude(); currentStartLongitude += deltaStep.Longitude(); } stopwatch.Stop(); addTiming( "generate slice descriptors", stopwatch.ElapsedMilliseconds ); return slices; }
// ----------------------------------------------------------------------------------------------- // NOTE : has some dependencies private void DetermineImageDimensionsVSlice( SliceDescriptor sampleSlice ) { // note : adding buffer distance below and above min and max elevations // (not needed in ALL slices, but ones that contain min and/or max should be buffered) double elevationRange = _maxElevationInRect - _minElevationInRect + (BufferMeters * 2.0f); if ( ( false == ImageDimensionSpecified( _imageHeight ) ) && ( false == ImageDimensionSpecified( _imageWidth ) ) ) { // neither specified... // the image "width" is 1x1 with the output rect // this means each pixel's real world width is the same a single data point's (important below) _imageWidth = ( SliceDirectionType.NS == _sliceDirection ) ? rectHeight : rectWidth; // the image "height" is the vertical range of readings the slices will cover, plus some buffer at top and bottom // pixels, being square, are as tall as they are wide, which is the horizontal distance between data point cells _imageHeight = Convert.ToInt32( elevationRange / _metersPerCell ); } else if ( ( ImageDimensionSpecified( _imageHeight ) ) && ( false == ImageDimensionSpecified( _imageWidth ) ) ) { // height specified, width was not, calculate width from height // distance each pixel represents in specified height double metersPerPixel = elevationRange / _imageHeight; double sliceWidthDegrees = Vector.Ops.Length( sampleSlice.Start, sampleSlice.End ); double sliceWidthMeters = sliceWidthDegrees * MetersPerDegree; _imageWidth = Convert.ToInt32( sliceWidthMeters / metersPerPixel ); } else if ( ( ImageDimensionSpecified( _imageWidth ) ) && ( false == ImageDimensionSpecified( _imageHeight ) ) ) { // width was specified, calculate height // determine scale factor on width (i.e. what is the width of a pixel at this size) double sliceWidthDegrees = Vector.Ops.Length( sampleSlice.Start, sampleSlice.End ); double pixelsPerDegree = _imageWidth / sliceWidthDegrees; double pixelsPerMeter = pixelsPerDegree * DegreesPerMeter; _imageHeight = Convert.ToInt32( elevationRange * pixelsPerMeter ); } // whatever height was, scale it _imageHeight = (int)(_imageHeight * _imageHeightScale); }