├── .gitignore ├── CITATION.cff ├── CMakeLists.txt ├── LICENSE ├── NEWS ├── NOTES ├── README.md ├── cmake ├── GetGitRevisionDescription.cmake └── GetGitRevisionDescription.cmake.in ├── docker ├── Dockerfile ├── README.md └── docker-compose.yaml ├── evaluation └── main.cpp ├── include └── arc_raster_rescue │ └── arc_raster_rescue.hpp └── src └── arr.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | build/ 3 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Barnes" 5 | given-names: "Richard" 6 | orcid: "https://orcid.org/0000-0002-0204-6040" 7 | title: "ArcRasterRescue" 8 | doi: 10.5281/zenodo.4128479 9 | date-released: 2016-08-29 10 | url: "https://github.com/r-barnes/ArcRasterRescue" -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(arc_raster_rescue LANGUAGES CXX) 4 | 5 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") 6 | include(GetGitRevisionDescription) 7 | get_git_head_revision(GIT_REFSPEC GIT_HASH) 8 | 9 | string(TIMESTAMP MY_TIMESTAMP "%Y-%m-%d %H:%M:%S") 10 | 11 | find_package(GDAL REQUIRED) 12 | find_package(ZLIB REQUIRED) 13 | 14 | option(EXPLORE "Whether or not to print exploratory debugging info useful for building new features.") 15 | 16 | add_library(arc_raster_rescue 17 | src/arr.cpp 18 | ) 19 | target_include_directories(arc_raster_rescue PUBLIC ${GDAL_INCLUDE_DIR} include) 20 | target_link_libraries(arc_raster_rescue PRIVATE ZLIB::ZLIB ${GDAL_LIBRARY}) 21 | target_compile_options(arc_raster_rescue PRIVATE 22 | -Wall -pedantic -Wno-unused-variable 23 | ) 24 | target_compile_features(arc_raster_rescue PRIVATE cxx_constexpr) 25 | target_compile_definitions(arc_raster_rescue PUBLIC 26 | GIT_HASH=\"${GIT_HASH}\" 27 | COMPILE_TIME=\"${MY_TIMESTAMP}\" 28 | ) 29 | if(EXPLORE) 30 | target_compile_definitions(arc_raster_rescue PRIVATE EXPLORE) 31 | endif() 32 | 33 | 34 | 35 | add_executable(arc_raster_rescue.exe 36 | evaluation/main.cpp 37 | ) 38 | target_link_libraries(arc_raster_rescue.exe PRIVATE arc_raster_rescue) 39 | target_compile_options(arc_raster_rescue.exe PRIVATE 40 | -Wall -pedantic -Wno-unused-variable 41 | ) 42 | target_compile_features(arc_raster_rescue.exe PRIVATE cxx_constexpr) 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Richard Barnes 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | 2020-10-25 (v0.1) 2 | ================= 3 | 4 | * Added NEWS. 5 | * Improved geotransform calculations. 6 | * Switched to cmake build system. -------------------------------------------------------------------------------- /NOTES: -------------------------------------------------------------------------------- 1 | JPEG compression only works with 8-bit data. 2 | JPEG2000 compression only works with 8- or 16-bit data. 3 | 4 | band_types with compression 5 | =========================== 6 | 7 | uncompressed, float 8 | band_types = 0 0 2 1 00000000 00000000 00000010 00000001 9 | 10 | LZ77, float32 11 | band_types = 0 4 2 1 00000000 00000100 00000010 00000001 12 | 13 | JPEG, 75% quality, uint8_t 14 | band_types = 0 8 40 0 00000000 00001000 01000000 00000000 15 | 16 | JPEG, 23% quality, uint8_t 17 | band_types = 0 8 40 0 00000000 00001000 01000000 00000000 18 | 19 | JPEG2000 75% quality, int16_t 20 | band_types = 0 c 81 0 00000000 00001100 10000001 00000000 21 | 22 | JPEG2000 23% quality, int16_t 23 | band_types = 0 c 81 0 00000000 00001100 10000001 00000000 24 | 25 | 26 | LibJPEG examples: 27 | * https://github.com/LuaDist/libjpeg/blob/master/example.c 28 | 29 | * http://www.andrewewhite.net/wordpress/2008/09/02/very-simple-jpeg-writer-in-c-c/ 30 | 31 | * http://www.aaronmr.com/en/2010/03/test/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![DOI](https://zenodo.org/badge/66634837.svg)](https://zenodo.org/badge/latestdoi/66634837) 2 | 3 | ArcRasterRescue 4 | =============== 5 | 6 | **Seeking someone knowledgeable in the ways of crafting GDAL drivers to help with that part of the effort.** 7 | 8 | The enclosed program extracts (rescues!) raster data from an ArcGIS File 9 | Geodatabase into a GeoTIFF file. 10 | 11 | List the numbers and names of rasters in the geodatabase using 12 | 13 | ./arc_raster_rescue.exe 14 | 15 | Extract to a GeoTIFF using 16 | 17 | ./arc_raster_rescue.exe 18 | 19 | The geodatabase path must end with a slash! 20 | 21 | Requirements 22 | ============ 23 | 24 | * [C++11](https://en.wikipedia.org/wiki/C%2B%2B11) 25 | * [zlib](http://www.zlib.net/) 26 | * [GDAL](http://www.gdal.org/) 27 | * [Cmake](https://cmake.org/) 28 | 29 | On an Ubuntu/Debian system you can obtain these with: 30 | 31 | sudo apt install cmake libgdal-dev zlib1g-dev g++ 32 | 33 | Compilation 34 | =========== 35 | 36 | To compile use the standard cmake sequence: 37 | 38 | mkdir build 39 | cd build 40 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. 41 | make 42 | 43 | Add `-DEXPLORE=ON` to print additional information useful for development to 44 | stderr as the program runs. 45 | 46 | An executable called `arc_raster_rescue.exe` is produced. 47 | 48 | TODO 49 | ==== 50 | 51 | * **Turn this into a GDAL driver.** 52 | * Improve endian checking. 53 | * Check for other kinds of compressions. 54 | * Improve calculation of raster dimensions. 55 | * Improve geotransform calculations: they're kind of experimental at the moment. 56 | * Switch some identification strings to ENUM values so things run quicker 57 | 58 | 59 | Credits 60 | ======= 61 | 62 | * Even Roualt did much of the work figuring out the FileGeodatabase specification. His notes are [here](https://github.com/rouault/dump_gdbtable/wiki/FGDB-Spec) and a program he wrote for extractin FGDB data is [here](https://github.com/rouault/dump_gdbtable). 63 | 64 | * James Ramm did some exploratory work that resulted in determining that the raster data (at least in cases he tested) had been compressed using zlib. ([Link](http://lists.osgeo.org/pipermail/gdal-dev/2016-July/044761.html)) 65 | 66 | * Richard Barnes converted code by Even Roualt into C++ as a base for Arc Raster Rescue and, starting from some notes by James Ramm, continued deciphering the format. He reorganized the code extensively to increase its modularity, determined how to connect the various raster tables together, drafted geotransform and WKT projection extraction capabilities, determined how data types were specified, and produced a working executable to extract raster data into GeoTIFFs. He also wrote this paragraph. :-) 67 | 68 | 69 | 70 | Cite This 71 | ========= 72 | 73 | Please cite this software: 74 | 75 | Barnes, Richard. 2020. Arc Raster Rescue. Software. doi: 10.5281/zenodo.4128479. -------------------------------------------------------------------------------- /cmake/GetGitRevisionDescription.cmake: -------------------------------------------------------------------------------- 1 | # - Returns a version string from Git 2 | # 3 | # These functions force a re-configure on each git commit so that you can 4 | # trust the values of the variables in your build system. 5 | # 6 | # get_git_head_revision( [ ...]) 7 | # 8 | # Returns the refspec and sha hash of the current head revision 9 | # 10 | # git_describe( [ ...]) 11 | # 12 | # Returns the results of git describe on the source tree, and adjusting 13 | # the output so that it tests false if an error occurs. 14 | # 15 | # git_get_exact_tag( [ ...]) 16 | # 17 | # Returns the results of git describe --exact-match on the source tree, 18 | # and adjusting the output so that it tests false if there was no exact 19 | # matching tag. 20 | # 21 | # git_local_changes() 22 | # 23 | # Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. 24 | # Uses the return code of "git diff-index --quiet HEAD --". 25 | # Does not regard untracked files. 26 | # 27 | # Requires CMake 2.6 or newer (uses the 'function' command) 28 | # 29 | # Original Author: 30 | # 2009-2010 Ryan Pavlik 31 | # http://academic.cleardefinition.com 32 | # Iowa State University HCI Graduate Program/VRAC 33 | # 34 | # Copyright Iowa State University 2009-2010. 35 | # Distributed under the Boost Software License, Version 1.0. 36 | # (See accompanying file LICENSE_1_0.txt or copy at 37 | # http://www.boost.org/LICENSE_1_0.txt) 38 | 39 | if(__get_git_revision_description) 40 | return() 41 | endif() 42 | set(__get_git_revision_description YES) 43 | 44 | # We must run the following at "include" time, not at function call time, 45 | # to find the path to this module rather than the path to a calling list file 46 | get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) 47 | 48 | function(get_git_head_revision _refspecvar _hashvar) 49 | set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 50 | set(GIT_DIR "${GIT_PARENT_DIR}/.git") 51 | while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories 52 | set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") 53 | get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) 54 | if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) 55 | # We have reached the root directory, we are not in git 56 | set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) 57 | set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) 58 | return() 59 | endif() 60 | set(GIT_DIR "${GIT_PARENT_DIR}/.git") 61 | endwhile() 62 | # check if this is a submodule 63 | if(NOT IS_DIRECTORY ${GIT_DIR}) 64 | file(READ ${GIT_DIR} submodule) 65 | string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) 66 | get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) 67 | get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) 68 | endif() 69 | set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") 70 | if(NOT EXISTS "${GIT_DATA}") 71 | file(MAKE_DIRECTORY "${GIT_DATA}") 72 | endif() 73 | 74 | if(NOT EXISTS "${GIT_DIR}/HEAD") 75 | return() 76 | endif() 77 | set(HEAD_FILE "${GIT_DATA}/HEAD") 78 | configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) 79 | 80 | configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" 81 | "${GIT_DATA}/grabRef.cmake" 82 | @ONLY) 83 | include("${GIT_DATA}/grabRef.cmake") 84 | 85 | set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) 86 | set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) 87 | endfunction() 88 | 89 | function(git_describe _var) 90 | if(NOT GIT_FOUND) 91 | find_package(Git QUIET) 92 | endif() 93 | get_git_head_revision(refspec hash) 94 | if(NOT GIT_FOUND) 95 | set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) 96 | return() 97 | endif() 98 | if(NOT hash) 99 | set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) 100 | return() 101 | endif() 102 | 103 | # TODO sanitize 104 | #if((${ARGN}" MATCHES "&&") OR 105 | # (ARGN MATCHES "||") OR 106 | # (ARGN MATCHES "\\;")) 107 | # message("Please report the following error to the project!") 108 | # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") 109 | #endif() 110 | 111 | #message(STATUS "Arguments to execute_process: ${ARGN}") 112 | 113 | execute_process(COMMAND 114 | "${GIT_EXECUTABLE}" 115 | describe 116 | ${hash} 117 | ${ARGN} 118 | WORKING_DIRECTORY 119 | "${CMAKE_CURRENT_SOURCE_DIR}" 120 | RESULT_VARIABLE 121 | res 122 | OUTPUT_VARIABLE 123 | out 124 | ERROR_QUIET 125 | OUTPUT_STRIP_TRAILING_WHITESPACE) 126 | if(NOT res EQUAL 0) 127 | set(out "${out}-${res}-NOTFOUND") 128 | endif() 129 | 130 | set(${_var} "${out}" PARENT_SCOPE) 131 | endfunction() 132 | 133 | function(git_get_exact_tag _var) 134 | git_describe(out --exact-match ${ARGN}) 135 | set(${_var} "${out}" PARENT_SCOPE) 136 | endfunction() 137 | 138 | function(git_local_changes _var) 139 | if(NOT GIT_FOUND) 140 | find_package(Git QUIET) 141 | endif() 142 | get_git_head_revision(refspec hash) 143 | if(NOT GIT_FOUND) 144 | set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) 145 | return() 146 | endif() 147 | if(NOT hash) 148 | set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) 149 | return() 150 | endif() 151 | 152 | execute_process(COMMAND 153 | "${GIT_EXECUTABLE}" 154 | diff-index --quiet HEAD -- 155 | WORKING_DIRECTORY 156 | "${CMAKE_CURRENT_SOURCE_DIR}" 157 | RESULT_VARIABLE 158 | res 159 | OUTPUT_VARIABLE 160 | out 161 | ERROR_QUIET 162 | OUTPUT_STRIP_TRAILING_WHITESPACE) 163 | if(res EQUAL 0) 164 | set(${_var} "CLEAN" PARENT_SCOPE) 165 | else() 166 | set(${_var} "DIRTY" PARENT_SCOPE) 167 | endif() 168 | endfunction() 169 | -------------------------------------------------------------------------------- /cmake/GetGitRevisionDescription.cmake.in: -------------------------------------------------------------------------------- 1 | # 2 | # Internal file for GetGitRevisionDescription.cmake 3 | # 4 | # Requires CMake 2.6 or newer (uses the 'function' command) 5 | # 6 | # Original Author: 7 | # 2009-2010 Ryan Pavlik 8 | # http://academic.cleardefinition.com 9 | # Iowa State University HCI Graduate Program/VRAC 10 | # 11 | # Copyright Iowa State University 2009-2010. 12 | # Distributed under the Boost Software License, Version 1.0. 13 | # (See accompanying file LICENSE_1_0.txt or copy at 14 | # http://www.boost.org/LICENSE_1_0.txt) 15 | 16 | set(HEAD_HASH) 17 | 18 | file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) 19 | 20 | string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) 21 | if(HEAD_CONTENTS MATCHES "ref") 22 | # named branch 23 | string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") 24 | if(EXISTS "@GIT_DIR@/${HEAD_REF}") 25 | configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) 26 | else() 27 | configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) 28 | file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) 29 | if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") 30 | set(HEAD_HASH "${CMAKE_MATCH_1}") 31 | endif() 32 | endif() 33 | else() 34 | # detached HEAD 35 | configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) 36 | endif() 37 | 38 | if(NOT HEAD_HASH) 39 | file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) 40 | string(STRIP "${HEAD_HASH}" HEAD_HASH) 41 | endif() 42 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | LABEL author="Austin Raney" 4 | LABEL author-email="aaraney@crimson.ua.edu" 5 | 6 | ARG URL="https://api.github.com/repos/r-barnes/ArcRasterRescue/releases/latest" 7 | ARG FILENAME="ArcRasterRescue.tar.gz" 8 | 9 | RUN apt-get update && \ 10 | apt-get install -y\ 11 | cmake libgdal-dev \ 12 | zlib1g-dev g++ \ 13 | wget && \ 14 | mkdir build && \ 15 | # Get url of latest release 16 | wget -O "${FILENAME}" \ 17 | `wget -qO- "${URL}" | grep -o "https://api.github.com/repos/r-barnes/ArcRasterRescue/tarball/[^\"]*"` && \ 18 | # Extract and build 19 | tar xf "${FILENAME}" -C build/ --strip-components=1 && \ 20 | cd build && \ 21 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo . &&\ 22 | make && \ 23 | # Soft link to /bin 24 | ln -s `realpath arc_raster_rescue.exe` /bin/arc_raster_rescue.exe && \ 25 | # Clean up: remove unneeded packages and clear cache 26 | apt-get remove -y \ 27 | cmake g++ \ 28 | wget &&\ 29 | apt-get clean 30 | 31 | WORKDIR /home -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | Docker must be installed. Its recommended that `docker-compose` is also installed for 4 | ease of use. The built container is ~800 mb in size. 5 | 6 | 1. Create a clone of the repository. Then, change to the directory named `docker/`. 7 | 8 | ```shell 9 | git clone https://github.com/r-barnes/ArcRasterRescue.git 10 | cd ArcRasterRescue/docker 11 | ``` 12 | 13 | 2. Build the docker image. This will download and build the latest release 14 | version of ArcRasterRescue. The image is built using an Ubuntu 18.04 bionic base. 15 | 16 | ```shell 17 | docker-compose build 18 | ``` 19 | 20 | You can verify that the build was successful using `docker images`. Look for an image 21 | named `arc_raster_rescue`. 22 | 23 | 3. Once the image is built it's ready to use. Before you use it, its important to 24 | note that the docker container that is spun up from docker image created in the last 25 | step will be mounted locally to the `./docker/` directory. This means the geodatabase 26 | for which you are extracting rasters from must be in the `./docker/` directory. If 27 | you would like to change this skip down to the second example below. 28 | 29 | ```shell 30 | docker-compose run --rm arc_raster_rescue 31 | root@:/home# arc_raster_rescue.exe 32 | ``` 33 | 34 | If you would like to change the directory that the container is mounted to, you will 35 | need to make a tiny modification to the `docker-compose.yaml` file. The first code 36 | snippet shows the original docker-compose yaml. In this let's say instead the 37 | geodatabase of interest was in the `~/Documents` directory or somewhere beneath it's 38 | hierarchy. 39 | 40 | ```yaml 41 | # Original docker-compose.yaml 42 | version: "3.8" 43 | services: 44 | arc_raster_rescue: 45 | build: . 46 | image: arc_raster_rescue 47 | volumes: 48 | - .:/home/ 49 | entrypoint: "/bin/bash" 50 | ``` 51 | Notice the small modification to the line after `volumes`. Once this small change has 52 | been made, you can re-compose the container using `docker-compose run --rm 53 | arc_raster_rescue` as shown prior. 54 | 55 | ```yaml 56 | # Modified docker-compose.yaml 57 | version: "3.8" 58 | services: 59 | arc_raster_rescue: 60 | build: . 61 | image: arc_raster_rescue 62 | volumes: 63 | - ~/Downloads:/home/ 64 | entrypoint: "/bin/bash" 65 | ``` 66 | -------------------------------------------------------------------------------- /docker/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | services: 3 | arc_raster_rescue: 4 | build: . 5 | image: arc_raster_rescue 6 | volumes: 7 | - .:/home/ 8 | entrypoint: "/bin/bash" -------------------------------------------------------------------------------- /evaluation/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char **argv){ 8 | std::string operation; 9 | for(int i=0;i"< "<\n"; 21 | std::cerr<<"\nNOTE: The geodatabase path must end with a slash!\n"; 22 | std::cerr<<"EXAMPLE: ./arc_raster_rescue.exe path/to/geodatabase.gdb/ dem03 /z/out.tif\n"; 23 | return -1; 24 | } 25 | 26 | std::string basename = argv[1]; 27 | 28 | MasterTable mt(basename+"a00000001.gdbtable"); 29 | 30 | if(argc==2){ 31 | std::cout<<"Rasters found: \n"; 32 | for(unsigned int r=0;r=mt.rasters.size()){ //Note: Don't need <0 check because raster_num is unsigned 66 | std::cerr<<"Invalid raster number! Must be 0-"<<(mt.rasters.size()-1)<<"."< 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef GIT_HASH 15 | #pragma message "Compiling without a git hash!" 16 | const std::string git_hash = "NO HASH SPECIFIED!"; 17 | #else 18 | const std::string git_hash = std::string(GIT_HASH).substr(0,16); 19 | #endif 20 | 21 | #ifndef COMPILE_TIME 22 | #pragma message "Compiling without UTC compile time falling back to local!" 23 | const std::string compilation_datetime = __DATE__ " " __TIME__; 24 | #else 25 | const std::string compilation_datetime = COMPILE_TIME; 26 | #endif 27 | 28 | const std::string program_url = "github.com/r-barnes/ArcRasterRescue"; 29 | 30 | ///Richdem vX.X.X 31 | const std::string program_name = "Arc Raster Rescue"; 32 | 33 | ///Richdem vX.X.X (hash=GIT HASH, compiled=COMPILATION DATE TIME) 34 | const std::string program_identifier = program_name + " (url="+program_url+", hash=" + git_hash + ", compiled="+compilation_datetime + ")"; 35 | 36 | struct RasterFields { 37 | double raster_mtolerance; 38 | double raster_xytolerance; 39 | double raster_zorig; 40 | double raster_morig; 41 | double raster_mscale; 42 | double raster_zscale; 43 | double raster_xorig; 44 | double raster_yorig; 45 | double raster_xyscale; 46 | double raster_ztolerance; 47 | bool raster_has_m; 48 | bool raster_has_z; 49 | std::string wkt; 50 | std::string raster_column; 51 | }; 52 | 53 | struct Shape { 54 | double ymax; 55 | double xmax; 56 | double xmin; 57 | double ymin; 58 | double morig; 59 | double zorig; 60 | double zscale; 61 | double mscale; 62 | double xyscale; 63 | double xorig; 64 | double yorig; 65 | bool has_z; 66 | bool has_m; 67 | double mtolerance; 68 | double ztolerance; 69 | double xytolerance; 70 | std::string wkt; 71 | }; 72 | 73 | struct Field { 74 | std::string name; 75 | std::string alias; 76 | int8_t type; 77 | bool nullable; 78 | RasterFields raster; 79 | Shape shape; 80 | void print() const; 81 | }; 82 | 83 | 84 | class BaseTable { 85 | public: 86 | std::ifstream gdbtable, gdbtablx; 87 | int32_t nfeaturesx; 88 | int32_t size_tablx_offsets; 89 | std::vector fields; 90 | bool has_flags; 91 | int nullable_fields; 92 | 93 | std::vector flags; 94 | 95 | std::string getFilenameX(std::string filename); 96 | 97 | void getFlags(); 98 | 99 | bool skipField(const Field &field, uint8_t &ifield_for_flag_test); 100 | 101 | BaseTable(std::string filename); 102 | }; 103 | 104 | 105 | 106 | class MasterTable : public BaseTable { 107 | public: 108 | std::vector< std::pair > rasters; 109 | 110 | MasterTable(std::string filename); 111 | }; 112 | 113 | class RasterBase : public BaseTable { 114 | private: 115 | std::string bandTypeToDataTypeString(std::vector< uint8_t > &band_types) const; 116 | std::string bandTypeToCompressionTypeString(std::vector &band_types) const; 117 | public: 118 | int32_t block_width; //Pixel width of tiles 119 | int32_t block_height; //Pixel height of tiles 120 | int32_t band_width; //Pixel width of the band 121 | int32_t band_height; //Pixel height of the band 122 | double eminx; //Minimum X coordinate 123 | double eminy; //Minimum Y coordinate 124 | double emaxx; //Maximum X coordinate 125 | double emaxy; //Maximum Y coordinate 126 | double block_origin_x; 127 | double block_origin_y; 128 | std::string data_type; 129 | std::string compression_type; 130 | std::string name; //Name of the band 131 | int32_t cdate; //Creation date 132 | int32_t mdate; //Last modification date 133 | std::vector< uint8_t > band_types; 134 | std::array geotransform; 135 | 136 | RasterBase(std::string filename); 137 | }; 138 | 139 | class RasterProjection : public BaseTable { 140 | public: 141 | RasterProjection(std::string filename); 142 | }; 143 | 144 | class Raster; 145 | 146 | template 147 | class RasterData : public BaseTable { 148 | public: 149 | std::vector geodata; 150 | 151 | int minpx = 0; 152 | int minpy = 0; 153 | int maxpx = 0; 154 | int maxpy = 0; 155 | 156 | RasterData(std::string filename, const RasterBase &rb); 157 | 158 | void getDimensionsFromData(std::string filename, const RasterBase &rb); 159 | 160 | T no_data; 161 | std::array geotransform; 162 | std::string projection; 163 | int width; 164 | int height; 165 | void resize(int64_t width, int64_t height, T no_data_val); 166 | bool in_raster(int x, int y) const; 167 | T& operator()(int64_t x, int64_t y); 168 | T operator()(int64_t x, int64_t y) const; 169 | void setAll(T val); 170 | 171 | GDALDataType myGDALType() const { 172 | if(typeid(T)==typeid(uint8_t)) 173 | return GDT_Byte; 174 | else if(typeid(T)==typeid(int8_t)) 175 | return GDT_Byte; 176 | else if(typeid(T)==typeid(uint16_t)) 177 | return GDT_UInt16; 178 | else if(typeid(T)==typeid(int16_t)) 179 | return GDT_Int16; 180 | else if(typeid(T)==typeid(uint32_t)) 181 | return GDT_UInt32; 182 | else if(typeid(T)==typeid(int32_t)) 183 | return GDT_Int32; 184 | else if(typeid(T)==typeid(float)) 185 | return GDT_Float32; 186 | else if(typeid(T)==typeid(double)) 187 | return GDT_Float64; 188 | else { 189 | std::cerr<<"Could not map native type '"<GetDriverByName("GTiff"); 209 | if(poDriver==NULL){ 210 | std::cerr<<"Could not open GDAL driver!"<Create(filename.c_str(), width, height, 1, myGDALType(), papszOptions); 214 | if(fout==NULL){ 215 | std::cerr<<"Could not open file '"<GetRasterBand(1); 220 | oband->SetNoDataValue(no_data); 221 | 222 | //This could be used to copy metadata 223 | //poDstDS->SetMetadata( poSrcDS->GetMetadata() ); 224 | 225 | //TIFFTAG_SOFTWARE 226 | //TIFFTAG_ARTIST 227 | { 228 | std::time_t the_time = std::time(nullptr); 229 | char time_str[64]; 230 | std::strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S UTC", std::gmtime(&the_time)); 231 | fout->SetMetadataItem("TIFFTAG_DATETIME", time_str); 232 | fout->SetMetadataItem("TIFFTAG_SOFTWARE", program_identifier.c_str()); 233 | 234 | auto out_processing_history = std::string(time_str) + " | " + program_identifier + " | "; 235 | if(!metadata.empty()) 236 | out_processing_history += metadata; 237 | else 238 | out_processing_history += "Unspecified Operation"; 239 | 240 | fout->SetMetadataItem("PROCESSING_HISTORY", out_processing_history.c_str()); 241 | } 242 | 243 | //The geotransform maps each grid cell to a point in an affine-transformed 244 | //projection of the actual terrain. The geostransform is specified as follows: 245 | // Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2) 246 | // Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5) 247 | //In case of north up images, the GT(2) and GT(4) coefficients are zero, and 248 | //the GT(1) is pixel width, and GT(5) is pixel height. The (GT(0),GT(3)) 249 | //position is the top left corner of the top left pixel of the raster. 250 | 251 | if(!geotransform.empty()){ 252 | if(geotransform.size()!=6){ 253 | std::cerr<<"Geotransform of output is not the right size. Found "<SetGeoTransform(geotransform.data()); 258 | } 259 | 260 | if(!projection.empty()) 261 | fout->SetProjection(projection.c_str()); 262 | 263 | #ifdef DEBUG 264 | std::cerr<<"Filename: "<RasterIO(GF_Write, 0, 0, width, height, geodata.data(), width, height, myGDALType(), 0, 0); 268 | if(temp!=CE_None) 269 | std::cerr<<"Error writing file! Continuing in the hopes that some work can be salvaged."< 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | //////////////////////////////////////////////////////////// 17 | //UTILITY FUNCTIONS FOR READING AND MANIPULATING BINARY DATA 18 | 19 | 20 | void bitsetToString(const std::vector< uint8_t > &bs){ 21 | for(const auto &v: bs){ 22 | std::bitset<8> t(v); 23 | std::cerr< 35 | T ReadThing(std::ifstream &fin){ 36 | T v; 37 | fin.read( reinterpret_cast (&v), sizeof( T ) ); 38 | return v; 39 | } 40 | 41 | uint8_t ReadByte(std::ifstream &fin){ 42 | return ReadThing(fin); 43 | } 44 | 45 | std::vector ReadBytes(std::ifstream &fin, int count){ 46 | std::vector ret(count); 47 | fin.read( reinterpret_cast (ret.data()), sizeof( uint8_t )*count ); 48 | return ret; 49 | } 50 | 51 | std::string ReadBytesAsString(std::ifstream &fin, int count){ 52 | auto v = ReadBytes(fin,count); 53 | return std::string(v.begin(),v.end()); 54 | } 55 | 56 | int16_t ReadInt16(std::ifstream &fin){ 57 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 58 | return ReadThing(fin); 59 | #else 60 | #pragma message "Big-endian unimplemented" 61 | #endif 62 | } 63 | 64 | int32_t ReadInt32(std::ifstream &fin){ 65 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 66 | return ReadThing(fin); 67 | #else 68 | #pragma message "Big-endian unimplemented" 69 | #endif 70 | } 71 | 72 | uint64_t ReadIndex40(std::ifstream& fin){ 73 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 74 | uint64_t value = ReadThing(fin); 75 | uint64_t byte5 = ReadThing(fin); 76 | value |= (byte5 << 32); 77 | return value; 78 | #else 79 | #pragma message "Big-endian unimplemented" 80 | #endif 81 | } 82 | 83 | float ReadFloat32(std::ifstream &fin){ 84 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 85 | return ReadThing(fin); 86 | #else 87 | #pragma message "Big-endian unimplemented" 88 | #endif 89 | } 90 | 91 | double ReadFloat64(std::ifstream &fin){ 92 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 93 | return ReadThing(fin); 94 | #else 95 | #pragma message "Big-endian unimplemented" 96 | #endif 97 | } 98 | 99 | uint64_t ReadVarUint(std::ifstream &fin){ 100 | uint64_t shift = 0; 101 | uint64_t ret = 0; 102 | while(true){ 103 | uint8_t b = ReadByte(fin); 104 | ret |= ((b & 0x7F) << shift); 105 | if( (b & 0x80)==0) 106 | break; 107 | shift += 7; 108 | } 109 | return ret; 110 | } 111 | 112 | void AdvanceBytes(std::ifstream &fin, int64_t count){ 113 | fin.seekg(count, std::ios_base::cur); 114 | } 115 | 116 | void GotoPosition(std::ifstream &fin, int64_t pos){ 117 | fin.seekg(pos); 118 | } 119 | 120 | std::string GetString(std::ifstream &fin, int nbcar=-1){ 121 | std::string temp; 122 | 123 | if(nbcar==-1) 124 | nbcar = ReadByte(fin); 125 | 126 | for(int j=0;j &src, std::vector &dst) { 150 | z_stream strm = {0}; 151 | strm.total_in = strm.avail_in = src.size(); 152 | strm.total_out = strm.avail_out = dst.size(); 153 | strm.next_in = (Bytef *) src.data(); 154 | strm.next_out = (Bytef *) dst.data(); 155 | 156 | strm.zalloc = Z_NULL; 157 | strm.zfree = Z_NULL; 158 | strm.opaque = Z_NULL; 159 | 160 | int err = -1; 161 | int ret = -1; 162 | 163 | err = inflateInit2(&strm, (15 + 32)); //15 window bits, and the +32 tells zlib to to detect if using gzip or zlib 164 | if(err!=Z_OK){ 165 | inflateEnd(&strm); 166 | throw std::runtime_error("zlib inflateInit2 error: "+std::to_string(err)); 167 | } 168 | 169 | err = inflate(&strm, Z_FINISH); 170 | if (err!=Z_STREAM_END) { 171 | inflateEnd(&strm); 172 | throw std::runtime_error("zlib inflate error: "+std::to_string(err)); 173 | } 174 | 175 | ret = strm.total_out; 176 | inflateEnd(&strm); 177 | dst.resize(ret); 178 | } 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | void Field::print() const { 191 | std::cout<<"Name = "< 206 | std::vector Unpack(std::vector &packed, const int block_width, const int block_height){ 207 | std::vector output(block_width*block_height); 208 | packed.resize(sizeof(T)*block_width*block_height); 209 | 210 | if(std::is_same::value){ 211 | #if __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__ 212 | for(unsigned int i=0;i::value){ 218 | #if __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__ 219 | for(unsigned int i=0;i::value){ 227 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 228 | for(unsigned int i=0;i::value){ 232 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 233 | for(unsigned int i=0;i::value){ 239 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 240 | for(unsigned int i=0;i::value){ 244 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 245 | for(unsigned int i=0;i::value || std::is_same::value){ 251 | //No special unpacking needs to be done for these 252 | } else { 253 | std::cerr<<"Unimplemented type conversion for '"<0){ 273 | auto tempflag = ReadByte(gdbtable); 274 | flags.push_back(tempflag); 275 | nremainingflags -= 8; 276 | } 277 | } 278 | } 279 | 280 | bool BaseTable::skipField(const Field &field, uint8_t &ifield_for_flag_test){ 281 | if(has_flags && field.nullable){ 282 | uint8_t test = (flags[ifield_for_flag_test >> 3] & (1 << (ifield_for_flag_test % 8))); 283 | ifield_for_flag_test++; 284 | return test!=0; 285 | } 286 | return false; 287 | } 288 | 289 | BaseTable::BaseTable(std::string filename){ 290 | std::string filenamex = getFilenameX(filename); 291 | gdbtablx.open(filenamex, std::ios_base::in | std::ios_base::binary); 292 | 293 | #ifdef EXPLORE 294 | std::cerr<<"Opening BaseTable as '"<0){ 428 | //auto default_value = ; 429 | AdvanceBytes(gdbtable, default_value_length); 430 | } 431 | 432 | } else if(field.type==8){ //TODO: What is this? 433 | AdvanceBytes(gdbtable,1); 434 | const auto flag = ReadByte(gdbtable); 435 | if( (flag&1)==0 ) 436 | field.nullable = false; 437 | 438 | } else if(field.type==9) { //Raster 439 | AdvanceBytes(gdbtable,1); 440 | const auto flag = ReadByte(gdbtable); 441 | if( (flag & 1)==0 ) 442 | field.nullable = false; 443 | 444 | field.raster.raster_column = GetString(gdbtable); 445 | 446 | const auto wkt_len = GetCount(gdbtable); 447 | field.raster.wkt = GetString(gdbtable,wkt_len/2); 448 | 449 | #ifdef EXPLORE 450 | std::cerr<<"field.raster.wkt: "<0){ 458 | field.raster.raster_has_m = false; 459 | field.raster.raster_has_z = false; 460 | 461 | if(magic_byte3==5){ 462 | field.raster.raster_has_z = true; 463 | } else if(magic_byte3==7){ 464 | field.raster.raster_has_m = true; 465 | field.raster.raster_has_z = true; 466 | } 467 | 468 | field.raster.raster_xorig = ReadFloat64(gdbtable); 469 | field.raster.raster_yorig = ReadFloat64(gdbtable); 470 | field.raster.raster_xyscale = ReadFloat64(gdbtable); 471 | 472 | if(field.raster.raster_has_m){ 473 | field.raster.raster_morig = ReadFloat64(gdbtable); 474 | field.raster.raster_mscale = ReadFloat64(gdbtable); 475 | } 476 | 477 | if(field.raster.raster_has_z){ 478 | field.raster.raster_zorig = ReadFloat64(gdbtable); 479 | field.raster.raster_zscale = ReadFloat64(gdbtable); 480 | } 481 | 482 | field.raster.raster_xytolerance = ReadFloat64(gdbtable); 483 | if(field.raster.raster_has_m) 484 | field.raster.raster_mtolerance = ReadFloat64(gdbtable); 485 | if(field.raster.raster_has_z) 486 | field.raster.raster_ztolerance = ReadFloat64(gdbtable); 487 | } 488 | 489 | AdvanceBytes(gdbtable,1); 490 | 491 | } else if(field.type==11 || field.type==10 || field.type==12){ //UUID or XML 492 | const auto width = ReadByte(gdbtable); 493 | const auto flag = ReadByte(gdbtable); 494 | if( (flag&1)==0 ) 495 | field.nullable = false; 496 | } else { 497 | const auto width = ReadByte(gdbtable); 498 | const auto flag = ReadByte(gdbtable); 499 | if( (flag&1)==0 ) 500 | field.nullable = false; 501 | 502 | const auto default_value_length = ReadByte(gdbtable); 503 | 504 | //TODO: What is this? 505 | if( (flag&4)!=0 ){ 506 | if(field.type==0 && default_value_length==2) 507 | auto default_value = ReadInt16(gdbtable); 508 | else if(field.type==1 && default_value_length==4) 509 | auto default_value = ReadInt32(gdbtable); 510 | else if(field.type==2 && default_value_length==4) 511 | auto default_value = ReadFloat32(gdbtable); 512 | else if(field.type==3 && default_value_length==8) 513 | auto default_value = ReadFloat64(gdbtable); 514 | else if(field.type==5 && default_value_length==8) 515 | auto default_value = ReadFloat64(gdbtable); 516 | else 517 | AdvanceBytes(gdbtable, default_value_length); 518 | } 519 | } 520 | 521 | if(field.nullable){ 522 | has_flags = true; 523 | nullable_fields += 1; 524 | } 525 | 526 | if(field.type!=6) 527 | fields.push_back(field); 528 | 529 | //std::cout<<"\n\nField Number = "<<(fields.size()-1)<<"\n"; 530 | //field.print(); 531 | } 532 | 533 | 534 | } 535 | 536 | 537 | MasterTable::MasterTable(std::string filename) : BaseTable(filename) { 538 | #ifdef EXPLORE 539 | std::cerr<<"gdbtables found:\n"; 540 | #endif 541 | for(int f=0;f(band_width-1); 727 | const double ph = (emaxx-eminx)/static_cast(band_width-1); 728 | geotransform[0] = eminx+(pw*0.5); 729 | geotransform[1] = pw; 730 | geotransform[2] = 0; 731 | geotransform[3] = emaxy+(ph*0.5); 732 | geotransform[4] = 0; 733 | geotransform[5] = -ph; //Arc really doesn't seem to like rasters with this value positive. 734 | 735 | std::cerr<<"Using geotransform (this is experimental): "; 736 | for(const auto &x: geotransform) 737 | std::cerr< &band_types) const { 754 | if(band_types[2]==0x08 && band_types[3]==0x00) //00000000 00000100 00001000 00000000 755 | return "1bit"; 756 | if(band_types[2]==0x20 && band_types[3]==0x00) //00000000 00000100 00100000 00000000 757 | return "4bit"; 758 | if(band_types[2]==0x41 && band_types[3]==0x00) //00000000 00000100 01000001 00000000 759 | return "int8_t"; 760 | if(band_types[2]==0x40 && band_types[3]==0x00) //00000000 00000100 01000000 0000000 761 | return "uint8_t"; 762 | if(band_types[2]==0x81 && band_types[3]==0x00) //00000000 00000100 10000001 00000000 763 | return "int16_t"; 764 | if(band_types[2]==0x80 && band_types[3]==0x00) //00000000 00000100 10000000 00000000 765 | return "uint16_t"; 766 | if(band_types[2]==0x01 && band_types[3]==0x01) //00000000 00000100 00000001 00000001 767 | return "int32_t"; 768 | if(band_types[2]==0x02 && band_types[3]==0x01) //00000000 00000100 00000010 00000001 769 | return "float32"; 770 | if(band_types[2]==0x00 && band_types[3]==0x01) //00000000 00000100 00000000 00000001 771 | return "uint32_t"; 772 | if(band_types[2]==0x00 && band_types[3]==0x02) //00000000 00000100 00000000 00000010 773 | return "64bit"; 774 | 775 | std::cerr<<"Unrecognised band data type!"< &band_types) const { 800 | if(band_types[1]==0x00) //band_types = 0 0 2 1 00000000 00000000 00000010 00000001 801 | return "uncompressed"; 802 | if(band_types[1]==0x04) //band_types = 0 4 2 1 00000000 00000100 00000010 00000001 803 | return "lz77"; 804 | if(band_types[1]==0x08) //band_types = 0 8 40 0 00000000 00001000 01000000 00000000 805 | return "jpeg"; 806 | if(band_types[1]==0x0C) //band_types = 0 c 81 0 00000000 00001100 10000001 00000000 807 | return "jpeg2000"; 808 | 809 | std::cerr<<"Unrecognised band compression type!"< 5 922 | polygon 923 | nb_total_points: 5 924 | nb_geoms: 1 925 | minx = 421568.000000000000000 926 | miny = 4872699.000000001862645 927 | maxx = 428822.000000000640284 928 | maxy = 4877607.000000003725290 929 | nb_points[0] = 5 930 | [1] 421568.000000000000000 4872699.000000001862645 931 | [2] 421568.000000000000000 4877607.000000003725290 932 | [3] 428822.000000000000000 4877607.000000003725290 933 | [4] 428822.000000000000000 4872699.000000001862645 934 | [5] 421568.000000000000000 4872699.000000001862645 935 | 936 | cur_offset = 2189 937 | Type: 3 938 | Field FOOTPRINT_Length : 24324.000000 939 | Type: 3 940 | Field FOOTPRINT_Area : 35602632.000014 941 | set([9, 3, 6, 7]) 942 | */ 943 | RasterProjection::RasterProjection(std::string filename) : BaseTable(filename){ 944 | #ifdef EXPLORE 945 | std::cerr<<"Opening RasterProjection as '"< 951 | RasterData::RasterData(std::string filename, const RasterBase &rb) : BaseTable(filename){ 952 | //Determine maximum and minimum pixel coordinates from the data itself, since 953 | //extracting them from the metadata is not yet reliable. 954 | getDimensionsFromData(filename,rb); 955 | 956 | //NOTE: In theory, the dimensions are given by 957 | // resize(rb.band_width, rb.band_height, -9999); 958 | //However, the pixels themselves seem to have a non-obvious coordinate scheme 959 | //which often takes them outside of this area. Therefore, we use 960 | //getDimensionsFromData() to determine the range 961 | 962 | //////////////////////////////////////// 963 | // Original code (above) 964 | // resize(maxpx-minpx, maxpy-minpy, -9999); 965 | // runs, but "shrink wraps" output to non-null data bounding box, 966 | // which is "no good" because the exported layers will not be immediately 967 | // "stackable". The new resize line below fixes this. 968 | resize(rb.band_width, rb.band_height, -9999); 969 | 970 | // TODO: there should be an option to export ALL FGDBR raster layers to a single-band GeoTIFF. 971 | 972 | //NOTE: Calculating pixel coordinates as, e.g., `col_nbr*block_width+x` may 973 | //yield pixel values that are not in the range [0,band_width). The following 974 | //offsets seemed close to working on my test data: 975 | // const int xoffset = -std::abs(rb.eminx-rb.block_origin_x); 976 | // const int yoffset = -std::abs(rb.emaxy-rb.block_origin_y); 977 | //However, there were still pixels out of range even with this offset applied 978 | //and the offset calculation seems sufficiently ridiculous that I don't trust 979 | //it. Therefore, I have set the offsets to values generated by 980 | //getDimensionsFromData() 981 | #ifdef EXPLORE 982 | std::cout<<"xxx minpx "<(std::abs((rb.eminx-rb.block_origin_x-(int)((rb.eminx-rb.block_origin_x)/pw)-pw)/pw)); 1009 | const int yoffset = static_cast(std::abs((rb.emaxy-rb.block_origin_y-(int)((rb.emaxy-rb.block_origin_y)/ph)+pw)/ph)); 1010 | /////////////////////////////////////////////////////////////////////////////////////////////////////////// 1011 | 1012 | #ifdef EXPLORE 1013 | std::cout<<"xxx xoffset "< unpacked; 1085 | 1086 | //These two magic bytes indicate zlib compression... unless they don't, 1087 | //since it is possible, if unlikely, for uncompressed data to begin a 1088 | //block with these values. The `band_types` field has bits which 1089 | //indicate compression, but appear to do so non-uniquely (lz77, lzw, 1090 | //maybe others map to the same compression bits). Therefore, since the 1091 | //compression indicators have not yet been entirely figured out, this 1092 | //if- clause checks for the magic bytes and checks the length of the 1093 | //field to determine probabilistically if compression is being used. The 1094 | //check should be fairly robust, though, since we expect some degree of 1095 | //compression for any non-pathological data. 1096 | if(rb.compression_type=="lz77"){ 1097 | #ifdef EXPLOREUNPACK 1098 | std::cerr<<"Decompressing with zlib"< decompressed(1000000); 1101 | Zinflate(val, decompressed); 1102 | decompressed.resize(sizeof(T)*rb.block_width*rb.block_height); //Drop trailer 1103 | unpacked = Unpack(decompressed, rb.block_width, rb.block_height); 1104 | 1105 | #ifdef EXPLOREUNPACK 1106 | std::cout<<"Decompressed: "; 1107 | for(unsigned int i=0;i<10;i++) 1108 | std::cout<(val, rb.block_width, rb.block_height); 1117 | } else { 1118 | std::cerr<<"Unimplemented compression type!"<=10); 1128 | std::cout<<"Unpacked: "; 1129 | for(unsigned int i=0;i<10;i++) 1130 | if(std::is_same::value || std::is_same::value) 1131 | std::cout<<(int)unpacked[i]<<" "; 1132 | else 1133 | std::cout<(&arc_no_data); 1173 | 1174 | no_data = -9999; //TODO: This cannot always be NoData. 1175 | 1176 | for(uint64_t i=0;i 1185 | void RasterData::getDimensionsFromData(std::string filename, const RasterBase &rb){ 1186 | minpx = std::numeric_limits::max(); 1187 | minpy = std::numeric_limits::max(); 1188 | maxpx = std::numeric_limits::min(); 1189 | maxpy = std::numeric_limits::min(); 1190 | 1191 | for(int f=0;f 1247 | void RasterData::resize(int64_t width, int64_t height, T no_data_val){ 1248 | this->width = width; 1249 | this->height = height; 1250 | std::cerr<<"Allocating "< 1256 | bool RasterData::in_raster(int x, int y) const { 1257 | //std::cerr< 1262 | T& RasterData::operator()(int64_t x, int64_t y){ 1263 | return geodata[y*width+x]; 1264 | } 1265 | 1266 | template 1267 | T RasterData::operator()(int64_t x, int64_t y) const { 1268 | return geodata[y*width+x]; 1269 | } 1270 | 1271 | template 1272 | void RasterData::setAll(T val){ 1273 | std::fill(geodata.begin(),geodata.end(),val); 1274 | } 1275 | 1276 | 1277 | 1278 | 1279 | template 1280 | void ExportTypedRasterToGeoTIFF(std::string operation, std::string basename, int raster_num, std::string outputname){ 1281 | #if EXPLORE 1282 | std::cerr<<"Using internal datatype '"< rd(basename+hexify(raster_num+3), rb); 1289 | 1290 | // for(int y=0;y<300;y++){ 1291 | // for(int x=0;x<300;x++) 1292 | // std::cerr<(operation, basename, raster_num, outputname); 1317 | else if(rb.data_type=="float32") 1318 | ExportTypedRasterToGeoTIFF(operation, basename, raster_num, outputname); 1319 | else if(rb.data_type=="uint8_t") 1320 | ExportTypedRasterToGeoTIFF(operation, basename, raster_num, outputname); 1321 | else if(rb.data_type=="int16_t") 1322 | ExportTypedRasterToGeoTIFF(operation, basename, raster_num, outputname); 1323 | else if(rb.data_type=="int32_t") 1324 | ExportTypedRasterToGeoTIFF(operation, basename, raster_num, outputname); 1325 | else if(rb.data_type=="int8_t") 1326 | ExportTypedRasterToGeoTIFF(operation, basename, raster_num, outputname); 1327 | else if(rb.data_type=="uint16_t") 1328 | ExportTypedRasterToGeoTIFF(operation, basename, raster_num, outputname); 1329 | else if(rb.data_type=="uint32_t") 1330 | ExportTypedRasterToGeoTIFF(operation, basename, raster_num, outputname); 1331 | else if(rb.data_type=="4bit") 1332 | ExportTypedRasterToGeoTIFF(operation, basename, raster_num, outputname); 1333 | else if(rb.data_type=="1bit") 1334 | ExportTypedRasterToGeoTIFF(operation, basename, raster_num, outputname); 1335 | else 1336 | std::cerr<<"Unrecognised raster data type: "<