Skip to content

Commit

Permalink
Bugfixing PNG writing
Browse files Browse the repository at this point in the history
  • Loading branch information
breki committed Jul 31, 2024
1 parent c82def0 commit 7715f9b
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 59 deletions.
6 changes: 5 additions & 1 deletion Demeton.Console/Program.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
open CommandLine
open BitMiracle.LibTiff.Classic
open CommandLine
open CommandLine.Common
open Demeton.Commands
open Demeton.Console
Expand Down Expand Up @@ -136,6 +137,9 @@ let main args =

Console.Out.WriteLine ""

// Disable TIFF library logging
Tiff.SetErrorHandler(Wiring.DisableErrorHandler()) |> ignore

let commandResult =
Shell.parseAndExecuteCommandLine
Console.Out.Write
Expand Down
24 changes: 24 additions & 0 deletions Demeton.Console/Wiring.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[<RequireQualifiedAccess>]
module Demeton.Console.Wiring

open System
open BitMiracle.LibTiff.Classic
open Demeton.Dem.Types
open Demeton.Dem.Funcs
open Demeton.Srtm.Downsampling
Expand Down Expand Up @@ -54,3 +56,25 @@ let fetchSrtmHeights srtmDir localCacheDir srtmLevel lonLatBounds =
let tileReader = fetchSrtmTile srtmDir localCacheDir
let srtmTilesNeeded = boundsToTiles 3600 srtmLevel lonLatBounds
fetchDemHeights tileReader srtmTilesNeeded




type DisableErrorHandler() =
inherit TiffErrorHandler()

override this.WarningHandler
(tif: Tiff, method: string, format: string, [<ParamArray>] args: obj[]) =
// do nothing, ie, do not write warnings to console
()

override this.WarningHandlerExt
(
tif: Tiff,
clientData: obj,
method: string,
format: string,
[<ParamArray>] args: obj[]
) =
// do nothing, ie, do not write warnings to console
()
5 changes: 4 additions & 1 deletion Demeton.Tests/FileSystem tests/Zip files tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ open Xunit

let copySampleResourceFileToDisk resourceFileName diskFileName =
use resourceStream = sampleFileStream resourceFileName
use zipOutputFileStream = File.OpenWrite diskFileName

use zipOutputFileStream =
File.Open(diskFileName, FileMode.Create, FileAccess.Write)

resourceStream.CopyTo(zipOutputFileStream)
zipOutputFileStream.Close()
diskFileName
Expand Down
12 changes: 6 additions & 6 deletions Demeton.Tests/Png/Reading and writing PNG files.fs
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,16 @@ let ``Can generate and read a valid 1-bit grayscale PNG`` () =

let initializer =
// Grayscale1Bit.ImageDataInitializer2D(fun _ _ -> rnd.Next(2) = 1)
Grayscale1Bit.ImageDataInitializer1D(fun _ -> rnd.Next(2) = 1)
// Grayscale1Bit.ImageDataInitializer1D(fun i -> i % 2 = 0)
// Grayscale1Bit.ImageDataInitializer1D(fun _ -> rnd.Next(2) = 1)
Grayscale1Bit.ImageDataInitializer1D(fun i -> i % 2 = 0)

let imageData =
Grayscale1Bit.createImageData imageWidth imageHeight initializer

let imageFileName = Path.GetFullPath("test-grayscale-1.png")
printfn $"Saving test image to %s{imageFileName}"

use stream = File.OpenWrite(imageFileName)
use stream = File.Open(imageFileName, FileMode.Create)

stream |> savePngToStream ihdr imageData |> ignore

Expand Down Expand Up @@ -250,7 +250,7 @@ let ``Can generate and read a valid 8-bit grayscale PNG`` () =
let imageFileName = Path.GetFullPath("test-grayscale-8.png")
printfn "Saving test image to %s" imageFileName

use stream = File.OpenWrite(imageFileName)
use stream = File.Open(imageFileName, FileMode.Create, FileAccess.Write)

stream |> savePngToStream ihdr imageData |> ignore

Expand Down Expand Up @@ -289,7 +289,7 @@ let ``Can generate and read a valid 16-bit grayscale PNG`` () =
let imageFileName = Path.GetFullPath("test-grayscale-16.png")
printfn "Saving test image to %s" imageFileName

use stream = File.OpenWrite(imageFileName)
use stream = File.Open(imageFileName, FileMode.Create, FileAccess.Write)

stream |> savePngToStream ihdr imageData |> ignore

Expand Down Expand Up @@ -372,7 +372,7 @@ let ``Can generate and read a valid 8-bit RGBA PNG`` () =
let imageFileName = Path.GetFullPath("test-rgba-8.png")
printfn "Saving test image to %s" imageFileName

use stream = File.OpenWrite(imageFileName)
use stream = File.Open(imageFileName, FileMode.Create, FileAccess.Write)

stream |> savePngToStream ihdr imageData |> ignore

Expand Down
4 changes: 3 additions & 1 deletion Demeton.Tests/Srtm/Converting HGT to PNG.fs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ let ``Can convert a HGT file into PNG image`` () =
printfn "%d Encoding heights into the PNG..." clock.ElapsedMilliseconds

let pngFileName = Path.GetFullPath(srtmTileId + "-test.png")
use pngWriteStream = File.OpenWrite(pngFileName)

use pngWriteStream =
File.Open(pngFileName, FileMode.Create, FileAccess.Write)

printfn "%d Writing image to %s ..." clock.ElapsedMilliseconds pngFileName

Expand Down
8 changes: 5 additions & 3 deletions Demeton.Tests/Srtm/decodeSrtmTileFromPngFile tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

open Demeton.Dem.Funcs
open Demeton.Srtm
open Demeton.Srtm.Funcs
open Demeton.Srtm.Png
open Png
open Png.Types
Expand Down Expand Up @@ -30,7 +29,10 @@ let ``Can decode a valid PNG-encoded SRTM tile`` () =
| false -> ()

printfn "Preparing new instance of %s..." pngFilename
use pngTempFileStream = File.OpenWrite(pngFilename)

use pngTempFileStream =
File.Open(pngFilename, FileMode.Create, FileAccess.Write)

resourceStream.CopyTo(pngTempFileStream)
pngTempFileStream.Close()

Expand Down Expand Up @@ -74,7 +76,7 @@ let ``Throws an exception if PNG image size is not of a SRTM tile`` () =
let imageData =
Grayscale16Bit.createImageData imageWidth imageHeight initializer

use stream = File.OpenWrite(imageFileName)
use stream = File.Open(imageFileName, FileMode.Create, FileAccess.Write)

stream |> savePngToStream ihdr imageData |> ignore

Expand Down
4 changes: 2 additions & 2 deletions Demeton.Tests/WorldCover/Loading WorldCover tiles.fs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ let ``Do not download tile if TIFF already in cache`` () =
let sampleTileId = demTileXYId 46 6

let sampleCachedTifFileName =
worldCoverTileCachedTifFileName cacheDir sampleTileId
worldCoverTileCachedTiffFileName cacheDir sampleTileId

let fileExists =
function
Expand All @@ -159,7 +159,7 @@ let ``Download tile file if not in cache`` () =
let sampleTileId = demTileXYId 6 -46

let expectedCachedTiffFileName =
worldCoverTileCachedTifFileName cacheDir sampleTileId
worldCoverTileCachedTiffFileName cacheDir sampleTileId

let fileExists _ = false

Expand Down
70 changes: 36 additions & 34 deletions Demeton/Bnry.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ open System.IO
/// <param name="value">The byte value to be written.</param>
/// <param name="stream">The stream the byte value should be written to.</param>
/// <returns>The same instance of the stream.</returns>
let writeByte (value: byte) (stream: Stream): Stream =
let writeByte (value: byte) (stream: Stream) : Stream =
stream.WriteByte(value)
stream

Expand All @@ -20,42 +20,45 @@ let writeByte (value: byte) (stream: Stream): Stream =
/// </summary>
let readByte (stream: Stream) =
let read = stream.ReadByte()

match read with
| -1 -> invalidOp "Unexpected EOF reached in the stream."
| _ -> (byte)read
| _ -> byte read


/// <summary>Writes the specified byte array to a stream.</summary>
/// <param name="value">The byte array to be written.</param>
/// <param name="bytes">The byte array to be written.</param>
/// <param name="stream">The stream the byte array should be written to.</param>
/// <returns>The same instance of the stream.</returns>
let writeBytes (bytes: byte[]) (stream: Stream): Stream =
stream.Write (bytes, 0, bytes.Length)
let writeBytes (bytes: byte[]) (stream: Stream) : Stream =
stream.Write(bytes, 0, bytes.Length)
stream

/// <summary>
/// Copies the specified number of bytes from one stream to another.
/// </summary>
let rec copyToStream
let rec copyToStream
length
(buffer: byte[])
(fromStream: Stream)
(toStream: Stream): unit =
(buffer: byte[])
(fromStream: Stream)
(toStream: Stream)
: unit =

let maxBytesToRead = min length buffer.Length
let bytesRead = fromStream.Read(buffer, 0, maxBytesToRead)
toStream.Write(buffer, 0, bytesRead)

let remainingLength = length - bytesRead

match remainingLength with
| 0 -> ignore()
| 0 -> ignore ()
| _ -> copyToStream remainingLength buffer fromStream toStream

/// <summary>
/// Reads the specified number of bytes from the stream and returns the
/// resulting byte array.
/// </summary>
let readBytes bufferLength length (stream: Stream): byte[] =
let readBytes bufferLength length (stream: Stream) : byte[] =
use bufferStream = new MemoryStream()

let bufferSize = min length bufferLength
Expand All @@ -65,51 +68,50 @@ let readBytes bufferLength length (stream: Stream): byte[] =
bufferStream.ToArray()

/// <summary>
/// Writes the specified 32-bit signed integer value to a stream using the
/// Writes the specified 32-bit signed integer value to a stream using the
/// big endian byte order.
//// </summary>
/// <param name="value">The integer value to be written.</param>
/// <param name="stream">The stream the integer value should be written to.
/// </param>
/// <returns>The same instance of the stream.</returns>
let writeBigEndianInt32 (value: int) (stream: Stream): Stream =
let writeBigEndianInt32 (value: int) (stream: Stream) : Stream =
stream
|> writeByte ((byte)(value >>> 24))
|> writeByte ((byte)(value >>> 16))
|> writeByte ((byte)(value >>> 8))
|> writeByte ((byte)value)
|> writeByte ((byte) (value >>> 24))
|> writeByte ((byte) (value >>> 16))
|> writeByte ((byte) (value >>> 8))
|> writeByte ((byte) value)

/// <summary>
/// Reads a big-endian encoded 32-bit signed integer from the stream.
/// </summary>
let readBigEndianInt32 (stream: Stream): int =
(((int)(readByte stream)) <<< 24)
||| (((int)(readByte stream)) <<< 16)
||| (((int)(readByte stream)) <<< 8)
||| (((int)(readByte stream)))
let readBigEndianInt32 (stream: Stream) : int =
(((int) (readByte stream)) <<< 24)
||| (((int) (readByte stream)) <<< 16)
||| (((int) (readByte stream)) <<< 8)
||| (((int) (readByte stream)))

/// <summary>
/// Writes the specified 32-bit unsigned integer value to a stream using the big
/// Writes the specified 32-bit unsigned integer value to a stream using the big
/// endian order.
//// </summary>
/// <param name="value">The 32-bit unsigned integer value to be written.</param>
/// <param name="stream">
/// The stream the 32-bit unsigned integer value should be written to.
/// </param>
/// <returns>The same instance of the stream.</returns>
let writeBigEndianUInt32 (value: uint32) (stream: Stream): Stream =
let writeBigEndianUInt32 (value: uint32) (stream: Stream) : Stream =
stream
|> writeByte ((byte)(value >>> 24))
|> writeByte ((byte)(value >>> 16))
|> writeByte ((byte)(value >>> 8))
|> writeByte ((byte)value)
|> writeByte ((byte) (value >>> 24))
|> writeByte ((byte) (value >>> 16))
|> writeByte ((byte) (value >>> 8))
|> writeByte ((byte) value)

/// <summary>
/// Reads a big-endian encoded 32-bit unsigned integer from the stream.
/// </summary>
let readBigEndianUInt32 (stream: Stream): uint32 =
(((uint32)(readByte stream)) <<< 24)
||| (((uint32)(readByte stream)) <<< 16)
||| (((uint32)(readByte stream)) <<< 8)
||| (((uint32)(readByte stream)))

let readBigEndianUInt32 (stream: Stream) : uint32 =
(((uint32) (readByte stream)) <<< 24)
||| (((uint32) (readByte stream)) <<< 16)
||| (((uint32) (readByte stream)) <<< 8)
||| (((uint32) (readByte stream)))
34 changes: 31 additions & 3 deletions Demeton/Commands/DemWithWaterBodiesCommand.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ open Demeton.WorldCover.Types
open Demeton.WorldCover.Fetch
open Demeton.WorldCover.Funcs
open Demeton.WorldCover.Coloring
open Demeton.WorldCover.WaterBodiesFetch
open Raster


Expand Down Expand Up @@ -173,6 +174,33 @@ let identifyAndSimplifyWaterBodies
let run (options: Options) : Result<unit, string> =
fetchAw3dTile options.TileId options.LocalCacheDir
|> Result.map (fun aw3dTile ->
fetchWorldCoverTile options.TileId options.LocalCacheDir
|> identifyAndSimplifyWaterBodies options.DemResolution
|> ignore)
let availableWorldCoverTiles =
ensureGeoJsonFile
options.LocalCacheDir
FileSys.fileExists
FileSys.downloadFile
|> listAllAvailableFiles FileSys.openFileToRead
|> Set.ofSeq

let result =
loadWaterBodiesTileFromCache
options.LocalCacheDir
availableWorldCoverTiles
options.TileId
|> makeNoneFileIfNeeded options.LocalCacheDir
|> extractWaterBodiesTileFromWorldCoverTileIfNeeded
options.LocalCacheDir

// todo 10: continue with the command - simplify water bodies
// and then merge it with the DEM and save it as DEM tile

())
// match result with
// | CachedTileLoaded heightsArray ->
// test <@ heightsArray |> Option.isSome @>
// | _ -> Should.fail "Unexpected result"
//
//
// fetchWorldCoverTile options.TileId options.LocalCacheDir
// |> identifyAndSimplifyWaterBodies options.DemResolution
// |> ignore)
5 changes: 3 additions & 2 deletions Demeton/FileSys.fs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ let openFileToRead: FileReader =
let openFileToWrite: FileWriter =
fun fileName ->
try
File.OpenWrite(fileName) :> Stream |> Ok
File.Open(fileName, FileMode.Create, FileAccess.Write) :> Stream
|> Ok
with ex ->
match mapFileSysException ex with
| Some error -> Error error
Expand Down Expand Up @@ -181,7 +182,7 @@ let closeStream (stream: Stream) = stream.Close()
/// Downloads a file from the specified URL and saves it to the specified
/// path on the disk.
/// </summary>
let downloadFile (url: string) (destinationPath: string): string =
let downloadFile (url: string) (destinationPath: string) : string =
Log.debug $"Downloading file from %s{url} to %s{destinationPath}..."

let httpClient = new HttpClient()
Expand Down
Loading

0 comments on commit 7715f9b

Please sign in to comment.