From befe4514bb5824750bef796c2ca65467af25b106 Mon Sep 17 00:00:00 2001 From: Sequoia Andrade <63666909+sequoiarose@users.noreply.github.com> Date: Thu, 9 Mar 2023 16:22:44 -0800 Subject: [PATCH 1/8] GDAL.scala Initial GDAL executable wrappers with classes for gdal2tiles, gdalwarp, gdalinfo, and gdaltranslate. Note that further optimization is needed to ensure parameters and commands follow expectations from GDAL. Companion driver for gdal2tiles is in separate proposed folder. --- .../main/scala/gov/nasa/race/earth/GDAL.scala | 619 ++++++++++++++++++ 1 file changed, 619 insertions(+) create mode 100644 race-earth/src/main/scala/gov/nasa/race/earth/GDAL.scala diff --git a/race-earth/src/main/scala/gov/nasa/race/earth/GDAL.scala b/race-earth/src/main/scala/gov/nasa/race/earth/GDAL.scala new file mode 100644 index 00000000..dda57b13 --- /dev/null +++ b/race-earth/src/main/scala/gov/nasa/race/earth/GDAL.scala @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2022, United States Government, as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The RACE - Runtime for Airspace Concept Evaluation platform is licensed + * under the Apache License, Version 2.0 (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gov.nasa.race.earth + +import java.io.File +import gov.nasa.race.common.ExternalProc +import gov.nasa.race.util.FileUtils + +/** + * external proc to run GDAL executables + */ + +class Gdal2Tiles (val prog: File, val inFile: File, val outputPath: File, driverPath: File, + srsName: String = "EPSG:4326", webviewer: String = "none", + tileSize: Int = 256, profile: String = "mercator", resampling: String = "average", + zoom: Option[Int] = None, resume: Boolean = false, noDataValue: Option[Double] = None, + tmsCompatible: Boolean = false, verbose: Boolean = true, kml: Boolean = false, + googleKey: Option[String] = None, bingKey: Option[String] = None, nbProcesses: Int = 1 + ) extends ExternalProc[File] { + + if (!prog.isFile) throw new RuntimeException(s"executable not found: $prog") + if (!driverPath.isFile) throw new RuntimeException(s"gdal2tiles driver not found: $driverPath") + if (!inFile.isFile) throw new RuntimeException(s"input file not found: $inFile") + if (!FileUtils.ensureWritableDir(outputPath).isDefined) throw new RuntimeException(s"cannot create gdal tiles output dir $outputPath") + + protected override def buildCommand: String = { + args = Seq( + s"$driverPath", + s"s_srs=$srsName", + s"webviewer=$webviewer", + s"tile_size=$tileSize", + s"profile=$profile", + s"resampling=$resampling", + s"zoom=$zoom", + s"resume=$resume", + s"srcnodata=$noDataValue", + s"tmscompatible=$tmsCompatible", + s"verbose=$verbose", + s"kml=$kml", + s"googlekey=$googleKey", + s"bingkey=$bingKey", + s"nbprocesses=$nbProcesses", + s"$inFile", + s"$outputPath" + ) + super.buildCommand + } + + override def getSuccessValue: File = { + if (!outputPath.isDirectory) throw new RuntimeException(s"output file not found: $outputPath") + outputPath + } + +} + +class GdalInfo (val prog: File, val inFile: File) extends ExternalProc[File] { + if (!prog.isFile) throw new RuntimeException(s"executable not found: $prog") + if (!inFile.isFile) throw new RuntimeException(s"input file not found: $inFile") + + protected override def buildCommand: String = s"${super.buildCommand} $inFile" + + override def getSuccessValue: File = { + if (!inFile.isFile) throw new RuntimeException(s"output file not found: $inFile") + inFile + } + def getJson(): this.type = { + args = args :+ "-json" + this + } + + def getMinMax(): this.type = { + args = args :+ "-mm" + this + } + + def getStats(): this.type = { + args = args :+ "-stats" + this + } + + def getStatsApprox(): this.type = { + args = args :+ "-approx_stats" + this + } + + def getHistogram(): this.type = { + args = args :+ "-hist" + this + } + + def setNoGroundControlPoints(): this.type = { + args = args :+ "-nogcp" + this + } + + def setNoMetadata(): this.type = { + args = args :+ "-nomd" + this + } + + def setNoRasterPrint(): this.type = { + args = args :+ "-norat" + this + } + + def setNoColorPrint(): this.type = { + args = args :+ "-noct" + this + } + + def setCheckSum(): this.type = { + args = args :+ "-checksum" + this + } + + def getMetadataList(): this.type = { + args = args :+ "-listmdd" + this + } + + def setMetadata(domain: String=""): this.type = { + args = args :+ s"-mdd $domain" + this + } + + def getFirstFile(): this.type = { + args = args :+ "-nofl" + this + } + + def setWKTFormat(wkt: String): this.type = { // can only be wkt1, wkt2, wkt2_205, wtk2_2018 + args = args :+ s"-wkt_format $wkt" + this + } + + def getDataset(n: String): this.type = { + args = args :+ s"-sd $n" + this + } + + def getProjection(): this.type = { + args = args :+ "-proj4" + this + } + + def setDatasetOpenOption(option: String): this.type = { + args = args :+ s"-oo $option" + this + } + + def setDriver(format: String): this.type = { + args = args :+ s"-if $format" + this + } + +} + +class GdalWarp (val prog: File, val inFile: File, val outFile: File) extends ExternalProc[File] { + + if (!prog.isFile) throw new RuntimeException(s"executable not found: $prog") + if (!inFile.isFile) throw new RuntimeException(s"input file not found: $inFile") + + protected override def buildCommand: String = s"${super.buildCommand} $inFile $outFile" + + override def getSuccessValue: File = { + if (!outFile.isFile) throw new RuntimeException(s"output file not found: $outFile") + outFile + } + + def setWarpBand(bandN: String): this.type = { + args = args :+ s"-b $bandN" // version 3.7 uses srcband, dstband + this + } + + def setTargetSrs (srsName: String): this.type = { + // could do more sanity checks here + args = args :+ s"-t_srs $srsName" + this + } + + def setSourceSrs (srsName: String): this.type = { + args = args :+ s"-s_srs $srsName" + this + } + + def setSourceEpoch(epoch: String): this.type = { + args = args :+ s"-s_coord_epoch $epoch" + this + } + + def setTargetEpoch(epoch: String): this.type = { + args = args :+ s"-t_coord_epoch $epoch" + this + } + + def setTargetBounds(xMin:String, yMin:String, xMax:String, yMax:String): this.type = { + args = args :+ s"-te $xMin $yMin $xMax $yMax" + this + } + + def setTargetBoundSrs (srsName: String): this.type = { + args = args :+ s"-te_srs $srsName" + this + } + + def setCT(projName: String): this.type = { + args = args :+ s"-CT $projName" + this + } + + def setTransformerOption(transformerName: String): this.type = { + args = args :+ s"-to $transformerName" + this + } + + def useVerticalShift(): this.type = { + args = args :+ "-vshift" + this + } + + def disableVerticalShift(): this.type = { + args = args :+ "-novshift" + this + } + + def setPolynomialOrder(pOrder: String): this.type = { + args = args :+ s"-order $pOrder" // must be between 1-3 + this + } + + def useSpline(): this.type = { + args = args :+ "-tps" + this + } + + def useRPC(): this.type = { + args = args :+ "-rpc" + this + } + + def useGeolocationArrays(): this.type = { + args = args :+ "-geoloc" + this + } + + def setErrorThreshold(eThresh: String): this.type = { + args = args :+ s"-et $eThresh" + this + } + + def refineGCPS(tolerance: String, minGCPS: String):this.type = { + args = args :+ s"-refine_gcps $tolerance $minGCPS" + this + } + + def setTargetResolution(xRes:String = "", yRes:String = "", square:String = ""): this.type = { + args = args :+ s"-tr $xRes $yRes $square" + this + } + + def targetAlignedPixels(): this.type = { + args = args :+ "-tap" + this + } + + def setTargetSize(width:String, height:String): this.type = { + args = args :+ s"-ts $width $height" + this + } + + def setOverviewLevel(level: String = ""): this.type = { //level is optional, uses AUTO by default, can input AUTO-n or NONE as well + args = args :+ s"-ovr $level" + this + } + + def setWarpOption(warpOption: String): this.type = { + args = args :+ s"-wo $warpOption" + this + } + + def setTargetBandType(bandType: String): this.type = { + args = args :+ s"-ot $bandType" + this + } + + def setWorkingPixelType(pixelType: String): this.type = { + args = args :+ s"-wt $pixelType" + this + } + + def setResamplingMethod(method: String): this.type = { + args = args :+ s"-r $method" + this + } + + def setSoruceNoDataValue(value: String = "", values: Seq[String] = Nil): this.type = { // can be a set of values or just one + args = args :+ s"-srcnodata $value" + values.mkString(" ") + this + } + + def setTargetNoDataValue(value: String = "", values: Seq[String] = Nil): this.type = { // can be a set of values or just one + args = args :+ s"-dstnodata $value" + values.mkString(" ") + this + } + + def setLastBandAlpha(): this.type = { + args = args :+ "-srcalpha" + this + } + + def setLastBandNotAlpha(): this.type = { + args = args :+ "-nosrcalpha" + this + } + + def setTargetAlpha(): this.type = { + args = args :+ "-dstalpha" + this + } + + def setMemorySize(memory: String): this.type = { + args = args :+ s"-wm $memory" + this + } + + def useMultithread(): this.type = { + args = args :+ "-multi" + this + } + + def beQuiet(): this.type = { + args = args :+ "-q" + this + } + + def setDriver(format: String): this.type = { + args = args :+ s"-of $format" + this + } + + def setCreationOptions(option: String): this.type = { + args = args :+ s"-co $option" + this + } + + def setCutline(dataSource: String): this.type = { + args = args :+ s"-cutline $dataSource" + this + } + + def setCutlineLayer(layerName: String): this.type = { + args = args :+ s"-cl $layerName" + this + } + + def setCutlineRestriction(expression: String): this.type = { + args = args :+ s"-cwhere $expression" + this + } + + def setCutlineSQL(query: String): this.type = { + args = args :+ s"-csql $query" + this + } + + def setCutlineBlend(distance: String): this.type = { + args = args :+ s"-cblend $distance" + this + } + + def setCropToCutline(): this.type = { + args = args :+ "-crop_to_cutline" + this + } + + def setOverwrite(): this.type = { + args = args :+ "-overwrite" + this + } + + def setNoCopyMetadata(): this.type = { + args = args :+ "-nomd" + this + } + + def setMetadataConflicts(conflictValue: String): this.type = { + args = args :+ s"-cvmd $conflictValue" + this + } + + def setColorInterpretation(): this.type = { + args = args :+ "-setci" + this + } + + def setSourceOpenOption(option: String): this.type = { + args = args :+ s"-oo $option" + this + } + + def setDestinationOpenOption(option: String): this.type = { + args = args :+ s"-doo $option" + this + } + +} + +class GdalTranslate (val prog: File, val inFile: File, val outFile: File) extends ExternalProc[File] { + + if (!prog.isFile) throw new RuntimeException(s"executable not found: $prog") + if (!inFile.isFile) throw new RuntimeException(s"input file not found: $inFile") + + protected override def buildCommand: String = s"${super.buildCommand} $inFile $outFile" + + override def getSuccessValue: File = { + if (!outFile.isFile) throw new RuntimeException(s"output file not found: $outFile") + outFile + } + + def setOutputType(outputType: String): this.type = { + // can only be: Byte, Int8, UInt16, Int16, UInt32, Int32, UInt64, Int64, Float32, Float64, CInt16, CInt32, CFloat32 or CFloat64 + args = args :+ s"-ot $outputType" + this + } + + def setStrict(): this.type = { + args = args :+ "=strict" + this + } + + def setInputDriver(format: String): this.type = { + args = args :+ s"-if $format" + this + } + + def setOutputDriver(format: String): this.type = { + args = args :+ s"-of $format" + this + } + + def setBand(bandN: String): this.type = { + args = args :+ s"-b $bandN" + this + } + + def setMask(bandN: String): this.type = { + args = args :+ s"-mask $bandN" + this + } + + def setExpandColorTable(colorTable: String): this.type = { + // can be gray, rgb, or rgba + args = args :+ s"-expand $colorTable" + this + } + + def setOutputSize(xSize: String = "0", ySize: String = "0"): this.type = { + // can be gray, rgb, or rgba + args = args :+ s"-outsize $xSize $ySize" + this + } + + def setTargetResolution(xRes:String, yRes:String): this.type = { + args = args :+ s"-tr $xRes $yRes" + this + } + + def setOverviewLevel(level: String = ""): this.type = { //level is optional, uses AUTO by default, can input AUTO-n or NONE as well + args = args :+ s"-ovr $level" + this + } + + def setResamplingMethod(method: String): this.type = { + args = args :+ s"-r $method" + this + } + + def setScale(src_min: String = "", src_max: String = "", dst_min: String = "", dst_max: String = ""): this.type = { + args = args :+ s"-scale $src_min $src_max $dst_min $dst_max" + this + } + + def setScaleExponent(exp: Int): this.type = { + if (!args.contains("-scale")) throw new RuntimeException(s"Scaling exponent can only be used following setScale()") + args = args :+ s"-exponent $exp" + this + } + + def setUnscale(): this.type = { + args = args :+ "-unscale" + this + } + + def setWindowPixels(xOff: String, yOff: String, xSize: String, ySize: String): this.type = { + args = args :+ s"-srcwin $xOff $yOff $xSize $ySize" + this + } + def setWindowCoords(ulx: String, uly: String, lrx: String, lry: String): this.type = { + args = args :+ s"-projwin $ulx $uly $lrx $lry" + this + } + + def setWindowSrs(srsName: String): this.type = { + args = args :+ s"-projwin_srs $srsName" + this + } + + def setPartiallyOutsideError(): this.type = { + args = args :+ "-epo" + this + } + + def setCompletelyOutsideError(): this.type = { + args = args :+ "-eco" + this + } + + def setCoordEpoch(epoch: String): this.type = { + args = args :+ s"-a_coord_epoch $epoch" + this + } + + def setBandScale(value: String): this.type = { + args = args :+ s"-a_scale $value" + this + } + + def setBandOffset(value: String): this.type = { + args = args :+ s"-a_offset $value" + this + } + + def setOutputBounds(ulx: String, uly: String, lrx: String, lry: String): this.type = { + args = args :+ s"-a_ullr $ulx $uly $lrx $lry" + this + } + + def setNoDataValue(value: String): this.type = { + args = args :+ s"-a_nodata $value" + this + } + + def setBandColorInterpretation(bandN: String, color: String): this.type = { + args = args :+ s"-colorinterp_$bandN $color" + this + } + + def setColorInterpretation(colors: Seq[String]): this.type = { + val colorsString = colors.mkString(",") + args = args :+ s"-colorinterp $colorsString" + this + } + + def setMetaData(key: String, value: String): this.type = { + args = args :+ s"-mo $key=$value" + this + } + + def setCreationOptions(option: String): this.type = { + args = args :+ s"-co $option" + this + } + + def setNoGroundControlPoints(): this.type = { + args = args :+ "-nogcp" + this + } + + def setGroundControlPoints(pixel: String, line: String, easting: String, northing: String, elevation: String): this.type = { + args = args :+ s"-gcp $pixel $line $easting $elevation" + this + } + + def beQuiet(): this.type = { + args = args :+ "-q" + this + } + + def setCopyDatasets(): this.type = { + args = args :+ "-sds" + this + } + + def getStats(): this.type = { + args = args :+ "-stats" + this + } + + def setNoRAT(): this.type = { + args = args :+ "-norat" + this + } + + def setNoXMP(): this.type = { + args = args :+ "-noxmp" + this + } + + def setDatasetOpenOption(option: String): this.type = { + args = args :+ s"-oo $option" + this + } + +} \ No newline at end of file From e183ea6a98cdb4be76614d13cf98b8a6ecc0eae6 Mon Sep 17 00:00:00 2001 From: Sequoia Andrade <63666909+sequoiarose@users.noreply.github.com> Date: Thu, 9 Mar 2023 16:35:04 -0800 Subject: [PATCH 2/8] Added README for gdal2tiles python driver --- race-earth/src/main/python/README.md | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 race-earth/src/main/python/README.md diff --git a/race-earth/src/main/python/README.md b/race-earth/src/main/python/README.md new file mode 100644 index 00000000..0e2ef76e --- /dev/null +++ b/race-earth/src/main/python/README.md @@ -0,0 +1,35 @@ +# gdal2tiles driver docs + +## Description: + +The gdal2tiles-driver.py file allows scala applications (e.g., RACE-ODIN applications) to use the python gdal2tiles packages. The core functionality of this pacakge is to reformat geotiffs into WMS tiles for data display. + +## Requirements: +- exisiting gdal installation (https://gdal.org/download.html). If on Windows, we recommend installing via Anaconda + + ex: `conda install -c conda-forge gdal` +- existing python installation (.exe) +- gdal2tiles python package (https://gdal2tiles.readthedocs.io/en/latest/installation.html) installed in default python environment (pip recommended) + + ex: `pip install gdal2tiles` + +## Usage: +- Use in scala, create a new gdal2tiles instance of the gov.nasa.race.earth.Gdal2Tiles class by passing in the driver path, python path, input file, and output path: + +``` + val tilesProg = new File("C:/mypath/Anaconda3/python.exe") + val outputPath = new File("tiles") + val driverPath = new File("C:/mypath/gdal2tiles-driver.py") + val tiles = new Gdal2Tiles(prog = tilesprog, inFile = inFile, outputPath = outputPath, driverPath = driverPath) + ``` + +Note that the driverPath is in ".../race/race-earth/src/main/python" +- execute the new Gdal2Tiles instance to produce tiles, remember the output is a future: +``` + val tilesFuture = tiles.exec + tilesFuture.onComplete{ + case Success(v) => println(v) + case Failure(e) => e.printStackTrace + } + Await.ready(tilesFuture, 60.seconds) + ``` From 9273a2305f82eea4b27cb8ff0688be52ef3598c7 Mon Sep 17 00:00:00 2001 From: Sequoia Andrade <63666909+sequoiarose@users.noreply.github.com> Date: Thu, 9 Mar 2023 16:35:27 -0800 Subject: [PATCH 3/8] Added gdal2tiles-driver.py --- .../src/main/python/gdal2tiles-driver.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 race-earth/src/main/python/gdal2tiles-driver.py diff --git a/race-earth/src/main/python/gdal2tiles-driver.py b/race-earth/src/main/python/gdal2tiles-driver.py new file mode 100644 index 00000000..a95daeb4 --- /dev/null +++ b/race-earth/src/main/python/gdal2tiles-driver.py @@ -0,0 +1,36 @@ +import gdal2tiles +import sys +import os + +def infer_type(arg): + if arg == "None": + return None + elif arg == "false": + return False + elif arg == "true": + return True + else: # numeric + try: + arg = int(arg) + except ValueError: + pass # leave as string + return arg + +def check_dir(output_folder): + if os.path.isdir(output_folder) == False: + os.makedirs(output_folder) + +def parse_args(args): + kwargs = {arg.split("=")[0]:infer_type(arg.split("=")[1]) for arg in args} + return kwargs + +def create_wms_tiles(tif_path, output_folder, tile_options={}): + check_dir(output_folder) + gdal2tiles.generate_tiles(tif_path, output_folder, **tile_options) + +if __name__ == "__main__": + kwargs = {} + if len(sys.argv) > 3: + kwargs = parse_args(sys.argv[1:-2]) + create_wms_tiles(sys.argv[-2], sys.argv[-1], tile_options=kwargs) + From 10ef4f0e7c0b6bb94ace0cca7e46a9725d94f78c Mon Sep 17 00:00:00 2001 From: Sequoia Andrade <63666909+sequoiarose@users.noreply.github.com> Date: Thu, 9 Mar 2023 16:48:16 -0800 Subject: [PATCH 4/8] Update gdal2tiles-driver.py Added copyright --- race-earth/src/main/python/gdal2tiles-driver.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/race-earth/src/main/python/gdal2tiles-driver.py b/race-earth/src/main/python/gdal2tiles-driver.py index a95daeb4..b7d5cc52 100644 --- a/race-earth/src/main/python/gdal2tiles-driver.py +++ b/race-earth/src/main/python/gdal2tiles-driver.py @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2022, United States Government, as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The RACE - Runtime for Airspace Concept Evaluation platform is licensed + * under the Apache License, Version 2.0 (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import gdal2tiles import sys import os From 1e71086e1967c0b478bae42de317d6c6d4fdc5de Mon Sep 17 00:00:00 2001 From: Sequoia Andrade <63666909+sequoiarose@users.noreply.github.com> Date: Thu, 9 Mar 2023 16:51:32 -0800 Subject: [PATCH 5/8] Rename race-earth/src/main/python/README.md to race-earth/src/main/python/gdal2tiles/README.md --- race-earth/src/main/python/{ => gdal2tiles}/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename race-earth/src/main/python/{ => gdal2tiles}/README.md (100%) diff --git a/race-earth/src/main/python/README.md b/race-earth/src/main/python/gdal2tiles/README.md similarity index 100% rename from race-earth/src/main/python/README.md rename to race-earth/src/main/python/gdal2tiles/README.md From 58bdc581131965c9cd3803132c733dca80a9a184 Mon Sep 17 00:00:00 2001 From: Sequoia Andrade <63666909+sequoiarose@users.noreply.github.com> Date: Thu, 9 Mar 2023 16:51:51 -0800 Subject: [PATCH 6/8] Rename race-earth/src/main/python/gdal2tiles-driver.py to race-earth/src/main/python/gdal2tiles/gdal2tiles-driver.py --- race-earth/src/main/python/{ => gdal2tiles}/gdal2tiles-driver.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename race-earth/src/main/python/{ => gdal2tiles}/gdal2tiles-driver.py (100%) diff --git a/race-earth/src/main/python/gdal2tiles-driver.py b/race-earth/src/main/python/gdal2tiles/gdal2tiles-driver.py similarity index 100% rename from race-earth/src/main/python/gdal2tiles-driver.py rename to race-earth/src/main/python/gdal2tiles/gdal2tiles-driver.py From 638e9733cf77e460fde828b2fa0d3f8cdb7f02d3 Mon Sep 17 00:00:00 2001 From: Sequoia Andrade <63666909+sequoiarose@users.noreply.github.com> Date: Thu, 30 Mar 2023 09:44:24 -0700 Subject: [PATCH 7/8] Update gdal2tiles-driver.py Changed license comment to use python syntax --- race-earth/src/main/python/gdal2tiles/gdal2tiles-driver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/race-earth/src/main/python/gdal2tiles/gdal2tiles-driver.py b/race-earth/src/main/python/gdal2tiles/gdal2tiles-driver.py index b7d5cc52..3ee65ea7 100644 --- a/race-earth/src/main/python/gdal2tiles/gdal2tiles-driver.py +++ b/race-earth/src/main/python/gdal2tiles/gdal2tiles-driver.py @@ -1,4 +1,4 @@ -/* +""" * Copyright (c) 2022, United States Government, as represented by the * Administrator of the National Aeronautics and Space Administration. * All rights reserved. @@ -13,7 +13,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ +""" import gdal2tiles import sys import os From 53be6df0b5a229d6a5fbf440f282e7b29374ac5f Mon Sep 17 00:00:00 2001 From: Sequoia Andrade <63666909+sequoiarose@users.noreply.github.com> Date: Thu, 30 Mar 2023 09:47:11 -0700 Subject: [PATCH 8/8] Add files via upload Converted gdal docs to rst, added more info, moved to correct documentation location in RACE --- doc/manual/design/GDAL.rst | 102 +++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 doc/manual/design/GDAL.rst diff --git a/doc/manual/design/GDAL.rst b/doc/manual/design/GDAL.rst new file mode 100644 index 00000000..f9e99253 --- /dev/null +++ b/doc/manual/design/GDAL.rst @@ -0,0 +1,102 @@ +GDAL +==== + +GDAL (https://gdal.org/index.html) is a standard library for working with georeferenced raster and vector files, such as geotiffs and netcdfs. +Race provides wrappers for native GDAL executables to enable GDAL functions from a scala programming interface. +Since GDAL performs best when the native libraries are installed according to the operating system, RACE implementations +wrappers to the commandline executables rather than a scala/java library. The wrappers provided in RACE use plain +english function names which correspond to specific executable commands. + +Requirements +------------ + +* exisiting gdal installation (https://gdal.org/download.html). If on Windows, we recommend installing via Anaconda in a dedicated environment + + + ex: + `conda create -n gdal` + `conda activate gdal` + `conda install -c conda-forge gdal` + +gdalinfo +-------- + +gdalinfo extracts information about a specified file, including the georeferenced coordinate system. + +gdalwarp +-------- + +gdalwarp provides methods for modifying (warping) images, including reprojection and cropping. + +**Usage**: + +1. To use in scala, first create a new gdalwarp instance of the gov.nasa.race.earth.Gdal2Tiles class +by specifying the driver path, python path, input file, and output path: + +.. code:: scala + val warpProg = new File("C:/mypath/gdalwarp.exe") + val inFile = new File("myinput.tif") + val outFile = new File("myoutput.tif") + val warp = new GdalWarp(prog = warpProg, inFile = inFile, outFile = outFile) + +2. Add any commands to the GdalWarp instance: +.. code:: scala + val xMin = "-127.71" + val yMin = "34.91" + val xMax = "-115.79" + val yMax = "41.25" + warp.setTargetBounds( xMin, yMin, xMax, yMax) + +2. Execute the commands, remember the output is a future: + +.. code:: scala + val warpFuture = warp.exec + warpFuture.onComplete{ + case Success(v) => println(v) + case Failure(e) => e.printStackTrace + } + +gdaltranslate +------------- + +gdaltranslate provides methods for translating vector images to raster images (e.g., netcdf to geotiff). +Usage is similar to GdalWarp. + +gdal2tiles +---------- + +The gdal2tiles-driver.py file allows scala applications (e.g., RACE-ODIN applications) to use the python gdal2tiles packages. +The core functionality of this pacakge is to reformat geotiffs into WMS tiles for data display. + +**Additional Requirements**: + +1. existing python installation (.exe) +2. gdal2tiles python package (https://gdal2tiles.readthedocs.io/en/latest/installation.html) installed in python environment which runs the exisiting executable from 1 (pip recommended) and has gdal installed + + ex: + `conda activate gdal` + `pip install gdal2tiles` + +**Usage**: + +1. To use in scala, first create a new gdal2tiles instance of the gov.nasa.race.earth.Gdal2Tiles class +by specifying the driver path, python path, input file, and output path: + +.. code:: scala + val tilesProg = new File("C:/mypath/Anaconda3/python.exe") + val outputPath = new File("tiles") + val driverPath = new File("C:/mypath/gdal2tiles-driver.py") + val tiles = new Gdal2Tiles(prog = tilesprog, inFile = inFile, outputPath = outputPath, driverPath = driverPath) + +Note that the driverPath is in ".../race/race-earth/src/main/python/gdal2tiles/gdal2tiles-driver.py" + +2. Execute the new Gdal2Tiles instance to produce tiles, remember the output is a future: + +.. code:: scala + val tilesFuture = tiles.exec + tilesFuture.onComplete{ + case Success(v) => println(v) + case Failure(e) => e.printStackTrace + } + Await.ready(tilesFuture, 60.seconds) +