diff --git a/.gitmodules b/.gitmodules index ee4ef6b..f8d7356 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "oss-internship-2020/curl/curl_wrapper/curl"] path = oss-internship-2020/curl/curl_wrapper/curl url = https://github.com/curl/curl +[submodule "oss-internship-2020/gdal/gdal"] + path = oss-internship-2020/gdal/gdal + url = https://github.com/OSGeo/gdal/tree/master/gdal diff --git a/oss-internship-2020/gdal/CANYrelief1-geo.tif b/oss-internship-2020/gdal/CANYrelief1-geo.tif new file mode 100644 index 0000000..f9d2ef0 Binary files /dev/null and b/oss-internship-2020/gdal/CANYrelief1-geo.tif differ diff --git a/oss-internship-2020/gdal/CMakeLists.txt b/oss-internship-2020/gdal/CMakeLists.txt new file mode 100644 index 0000000..708952f --- /dev/null +++ b/oss-internship-2020/gdal/CMakeLists.txt @@ -0,0 +1,86 @@ +# Copyright 2020 Google LLC +# +# 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. + +cmake_minimum_required(VERSION 3.10) + +project(test CXX C) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +find_package(PNG REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(libpcre REQUIRED IMPORTED_TARGET libpcre) +pkg_check_modules(proj REQUIRED IMPORTED_TARGET proj) + +set(SAPI_ROOT "${PROJECT_SOURCE_DIR}/../.." CACHE PATH "Path to the Sandboxed API source tree") +# cmake .. -G Ninja -DSAPI_ROOT=$HOME/sapi_root + +set(SAPI_ENABLE_EXAMPLES OFF CACHE BOOL "") +set(SAPI_ENABLE_TESTS OFF CACHE BOOL "") +add_subdirectory("${SAPI_ROOT}" + "${CMAKE_BINARY_DIR}/sandboxed-api-build" + # Omit this to have the full Sandboxed API in IDE + EXCLUDE_FROM_ALL) + +add_library(libgdal STATIC IMPORTED) +set_property(TARGET libgdal PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libgdal.a") + +target_link_libraries(libgdal INTERFACE + crypto + expat + jpeg + PkgConfig::libpcre + PkgConfig::proj + sqlite3 + tiff + z + pthread + m + rt + dl + curl + PNG::PNG +) + +add_sapi_library(gdal_sapi + FUNCTIONS GDALOpen + GDALAllRegister + GDALGetDatasetDriver + GDALGetDriverShortName + GDALGetDriverLongName + GDALGetGeoTransform + GDALGetRasterBand + GDALGetBlockSize + GDALGetRasterBandXSize + GDALGetRasterBandYSize + GDALRasterIO + INPUTS "/usr/include/gdal/gdal.h" + LIBRARY libgdal + LIBRARY_NAME GDAL + NAMESPACE "" +) + +target_include_directories(gdal_sapi INTERFACE + "${PROJECT_BINARY_DIR}" +) + +add_executable(raster + raster.cc +) + +target_link_libraries(raster + gdal_sapi + sapi::sapi +) diff --git a/oss-internship-2020/gdal/README.md b/oss-internship-2020/gdal/README.md new file mode 100644 index 0000000..d3713b7 --- /dev/null +++ b/oss-internship-2020/gdal/README.md @@ -0,0 +1,88 @@ +# GDAL Raster GeoTIFF Workflow + +``` +Build Tools: CMake/Ninja +OS: Linux +``` + +### For installing GDAL: + +``` +sudo apt-get install python3.6-dev +sudo add-apt-repository ppa:ubuntugis/ppa && sudo apt update +sudo apt-get install gdal-bin +sudo apt-get install libgdal-dev +``` + +### Dependencies: + +PNG: `sudo apt-get install libpng-dev` + +PCRE: `sudo apt-get install libpcre3 libpcre3-dev` + +PROJ: `sudo apt-get install libproj-dev` + +OBS! You may need to set `export LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib`. +It is required for libproj.so to be found into /usr/local/lib/. You can also fix +this by typing `locate libproj.so` which will give you + and then `cp +/usr/local/lib/`. + +### Initializing GDAL submodule: + +`git submodule add https://github.com/OSGeo/gdal/tree/master/gdal` + +### Building GDAL statically + +GNUmakefile from gdal/gdal can handle building the static library. + +`cd gdal/gdal && make static-lib` + +`cd ../.. && mkdir lib` + +`cp gdal/gdal/libgdal.a lib/` + +OBS! The file is huge! It may take a while. + +### For testing: + +`mkdir build && cd build` + +`cmake .. -G Ninja` + +`ninja` + +`./raster ` + +## About the project + +GDAL is a translator library for raster and vector geospatial data format. The +project consist in rastering a GeoTIFF file format using GDAL functionalities +and sandboxed methods. + +## Implementation + +*Sandboxing...* + +The purpose of sandboxing is to limit the permissions and capabilities of +library’s methods, in order to secure the usage of them. After obtaining the +sandbox, the functions will be called through an Sandbox API (being called api +in the current test) and so, the operations, system calls or namspaces access +may be controlled. + +*Raster process...* + +Useful functions from the `gdal.h` header are added to the SAPI library built +with CMake. + +One .tiff file is manipulated with GDALOpen functionality, which extracts a +pointer to the data set containg a list of raster bands, all pertaining to the +same area. Metadata, a coordinate system, a georeferencing transform, size of +raster and various other information are kept into the data set that corresponds +to the image. + +To create an array containing the image information, the dimentions needed are +extracted using some specific GDAL(X/Y)Size functions applied to the block. +GDALRasterBand function takes care of data type conversion, one more step +following: placing the converted data (with RasterIO method) into the created +and well allocated structure. diff --git a/oss-internship-2020/gdal/raster.cc b/oss-internship-2020/gdal/raster.cc new file mode 100644 index 0000000..f5865f8 --- /dev/null +++ b/oss-internship-2020/gdal/raster.cc @@ -0,0 +1,195 @@ +// Copyright 2020 Google LLC +// +// 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. + +#include +#include + +#include +#include + +#include +#include "gdal_sapi.sapi.h" // NOLINT(build/include) +#include "sandboxed_api/sandbox2/util/fileops.h" + +class GdalSapiSandbox : public GDALSandbox { + public: + GdalSapiSandbox(std::string path) + : GDALSandbox(), file_path_(std::move(path)) {} + + std::unique_ptr ModifyPolicy( + sandbox2::PolicyBuilder*) override { + return sandbox2::PolicyBuilder() + .AllowDynamicStartup() + .AllowRead() + .AllowSystemMalloc() + .AllowWrite() + .AllowExit() + .AllowStat() + .AllowOpen() + .AllowSyscalls({ + __NR_futex, + __NR_close, + __NR_recvmsg, + __NR_getdents64, + __NR_lseek, + __NR_getpid, + __NR_sysinfo, + __NR_prlimit64, + __NR_ftruncate, + __NR_unlink, + }) + .AddFile(file_path_) + .BuildOrDie(); + } + + private: + std::string file_path_; +}; + +absl::Status GdalMain(std::string filename) { + // Reading GDALDataset from a (local, specific) file. + GdalSapiSandbox sandbox(filename); + + SAPI_RETURN_IF_ERROR(sandbox.Init()); + GDALApi api(&sandbox); + + sapi::v::CStr s(filename.data()); + + SAPI_RETURN_IF_ERROR(api.GDALAllRegister()); + auto open = api.GDALOpen(s.PtrBoth(), GDALAccess::GA_ReadOnly); + + LOG(INFO) << "Dataset pointer adress: " << open.value() << std::endl; + sapi::v::RemotePtr ptr_dataset(open.value()); + + LOG(INFO) << ptr_dataset.ToString() << std::endl; + if (!open.value()) { + return absl::AbortedError("NULL pointer for Dataset.\n"); + } + + // Printing some general information about the dataset. + auto driver = api.GDALGetDatasetDriver(&ptr_dataset); + sapi::v::RemotePtr ptr_driver(driver.value()); + + auto driver_short_name = api.GDALGetDriverShortName(&ptr_driver); + auto driver_long_name = api.GDALGetDriverLongName(&ptr_driver); + + sapi::v::RemotePtr ptr_driver_short_name(driver_short_name.value()); + sapi::v::RemotePtr ptr_driver_long_name(driver_long_name.value()); + + LOG(INFO) << "Driver short name: " + << sandbox.GetCString(ptr_driver_short_name).value().c_str(); + LOG(INFO) << "Driver long name: " + << sandbox.GetCString(ptr_driver_long_name).value().c_str(); + + // Checking that GetGeoTransform is valid. + std::vector adf_geo_transform(6); + sapi::v::Array adf_geo_transform_array(&adf_geo_transform[0], + adf_geo_transform.size()); + + // For this function that returns CPLErr, the error-handling must be done + // analyzing the returning object. + // Same for GDALReturnsIO from below. + CPLErr err; + SAPI_ASSIGN_OR_RETURN(err, api.GDALGetGeoTransform( + &ptr_dataset, adf_geo_transform_array.PtrBoth())); + + // If GDALGetGeoTransform generates an error. + if (err != CE_None) { + return absl::InternalError("GDAL rasterization failed."); + } + + LOG(INFO) << "Origin = (" << adf_geo_transform[0] << ", " + << adf_geo_transform[3] << ")" << std::endl; + LOG(INFO) << "Pixel Size = (" << adf_geo_transform[0] << ", " + << adf_geo_transform[3] << ")" << std::endl; + + std::vector n_blockX_size(1); + std::vector n_blockY_size(1); + + sapi::v::Array nBlockXSizeArray(&n_blockX_size[0], n_blockX_size.size()); + sapi::v::Array nBlockYSizeArray(&n_blockY_size[0], n_blockY_size.size()); + + auto band = api.GDALGetRasterBand(&ptr_dataset, 1); + LOG(INFO) << "Band pointer adress: " << band.value() << std::endl; + if (!band.value()) { + return absl::AbortedError("NULL pointer for Band.\n"); + } + + sapi::v::RemotePtr ptr_band(band.value()); + SAPI_RETURN_IF_ERROR(api.GDALGetBlockSize(&ptr_band, nBlockXSizeArray.PtrBoth(), + nBlockYSizeArray.PtrBoth())); + + LOG(INFO) << "Block = " << n_blockX_size[0] << " x " << n_blockY_size[0] + << std::endl; + + std::vector b_got_min(1); + std::vector b_got_max(1); + + sapi::v::Array b_got_min_array(&b_got_min[0], b_got_min.size()); + sapi::v::Array b_got_max_array(&b_got_max[0], b_got_max.size()); + + auto nX_size = api.GDALGetRasterBandXSize(&ptr_band); + auto nY_size = api.GDALGetRasterBandYSize(&ptr_band); + + std::vector raster_data(nX_size.value() * nY_size.value(), -1); + sapi::v::Array raster_data_array(&raster_data[0], raster_data.size()); + + // We will use CPLErr type of returning value, as before with + // GDALGetGeoTransorm. + SAPI_ASSIGN_OR_RETURN( + err, api.GDALRasterIO(&ptr_band, GF_Read, 0, 0, nX_size.value(), + nY_size.value(), raster_data_array.PtrBoth(), + nX_size.value(), nY_size.value(), GDT_Byte, 0, 0)); + + // If GDALRasterIO generates an error. + if (err != CE_None) { + return absl::InternalError("GDAL rasterization failed."); + } + + std::cout << "Raster data info: " << raster_data_array.ToString() + << std::endl; + + // To print the data content: `std::cout << raster_data_array.GetData() << + // std::endl;` + + return absl::OkStatus(); +} + +int main(int argc, char** argv) { + // The file to be converted should be specified in the first argument while + // running the program. + if (argc < 2) { + std::cout << "You need to provide a file name: ./raster " + "your_tiff_file_absolute_path\n" + "Example: ./raster /usr/home/username/file.tiff" + << std::endl; + return EXIT_FAILURE; + } + + std::ifstream aux_file; + aux_file.open(argv[1]); + if (!aux_file.is_open()) { + std::cout << "Your file name is not valid.\nUnable to open the file." + << std::endl; + return EXIT_FAILURE; + } + std::string filename(argv[1]); + + if (absl::Status status = GdalMain(filename); !status.ok()) { + LOG(ERROR) << "Initialization failed: " << status.ToString(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +}