├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── doc └── intro.md ├── examples └── repl-session.clj ├── project.clj ├── resources └── images │ ├── clj-gdal-LogoColor-x1000.png │ └── clj-gdal-LogoColor-x250.png ├── src └── gdal │ ├── band.clj │ ├── core.clj │ ├── dataset.clj │ ├── dev.clj │ ├── driver.clj │ ├── libpath.clj │ ├── proj.clj │ └── util.clj └── test └── gdal ├── band_test.clj ├── core_test.clj ├── dataset_test.clj ├── driver_test.clj ├── proj_test.clj └── util_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | *.sw[po] 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "clj-gdal-test-data"] 2 | path = test/data 3 | url = git@github.com:jmorton/clj-gdal-test-data.git 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 4 | 5 | ## 0.0.1 - 2015-10-10 6 | 7 | ### Added 8 | - Band 9 | - Dataset 10 | - Driver 11 | 12 | ### Changed 13 | - none 14 | 15 | ### Removed 16 | - none 17 | 18 | ### Fixed 19 | - none 20 | 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 – Element 84, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test-data: 2 | git submodule update --init --recursive 3 | 4 | deploy: 5 | lein deploy clojars 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clj-gdal 2 | 3 | *Clojure-idiomatic GDAL wrapper* 4 | 5 | [![][clj-gdal-logo]][clj-gdal-logo-large] 6 | 7 | [clj-gdal-logo]: resources/images/clj-gdal-LogoColor-x250.png 8 | [clj-gdal-logo-large]: resources/images/clj-gdal-LogoColor-x1000.png 9 | 10 | 11 | #### Contents 12 | 13 | * [About](#about-) 14 | * [Dependencies](#dependencies-) 15 | * [Usage](#usage-) 16 | * [License](#license-) 17 | 18 | 19 | ## About [↟](#contents) 20 | 21 | [GDAL](http://www.gdal.org/) (Geospatial Data Abstraction Library) is a library 22 | for reading and writing raster and vector geospatial data formats. GDAL 23 | supports more than 120 data formats such as NOAA GTX, NASA ELAS, HF2/HFZ, ESRI 24 | HDR, NetCDF, GeoTIFF, etc. 25 | 26 | The clj-gdal project offers a Clojure wrapper around parts of the Java GDAL 27 | library. 28 | 29 | 30 | ## Dependencies [↟](#contents) 31 | 32 | * Java 1.7 or higher (uses java.nio via the nio Clojure wrapper) 33 | * Java GDAL 1.11 library (included) 34 | 35 | The Java library downloaded from Maven still requires that you have GDAL 2.x 36 | compiled on your system, as it references libraries which GDAL builds. 37 | 38 | 39 | ## Usage [↟](#contents) 40 | 41 | Add clj-gdal a dependency to your lein project: 42 | 43 | [![Clojars Project](http://clojars.org/element84/clj-gdal/latest-version.svg)](http://clojars.org/element84/clj-gdal) 44 | 45 | Then start up the Clojure REPL: 46 | 47 | ```bash 48 | $ lein repl 49 | ``` 50 | 51 | The following examples use the development convenience namespace ``gdal-dev``. This 52 | namespace has the following requires already done for you: 53 | 54 | ```clojure 55 | (ns gdal.dev 56 | (:require [clojure.pprint :as pp] 57 | [clojure.reflect :as reflect] 58 | [clojure.tools.logging :as log] 59 | [clojure.tools.namespace.repl :as repl] 60 | [gdal.core :as gdal] 61 | [gdal.band :as band] 62 | [gdal.dataset :as dataset]) 63 | (:import [java.nio ByteBuffer] 64 | [org.gdal.gdalconst gdalconst])) 65 | ``` 66 | 67 | 68 | ### Top-level GDAL Functions 69 | 70 | ```clojure 71 | gdal.dev=> (gdal/init) 72 | nil 73 | gdal.dev=> (def data (gdal/open "LC80290302015263LGN00_B2.TIF")) 74 | #'gdal.dev/tiff-file 75 | gdal.dev=> (def tiff-data (gdal/open tiff-file)) 76 | #'gdal.dev/tiff-data 77 | ``` 78 | 79 | 80 | ### Working with Dataset Objects 81 | 82 | ```clojure 83 | gdal.dev=> (dataset/get-size tiff-data) 84 | [7681 7811] 85 | gdal.dev=> (dataset/count-bands tiff-data) 86 | 1 87 | gdal.dev=> (def band-data (dataset/get-band tiff-data 1)) 88 | #'gdal.dev/band-data 89 | ``` 90 | 91 | 92 | ### Working with Band Objects 93 | 94 | ```clojure 95 | gdal.dev=> (band/get-checksum band-data) 96 | 60155 97 | 98 | ``` 99 | 100 | 101 | ### Working with Projection Objects 102 | 103 | Top-level data: 104 | 105 | ```clojure 106 | 107 | ``` 108 | 109 | 110 | Datum Functions: 111 | 112 | ```clojure 113 | gdal.dev=> (proj/get-datum proj-data) 114 | #object[org.apache.sis.referencing.datum.DefaultGeodeticDatum 0x659718b5 "GeodeticDatum[\"WGS_1984\",\n Ellipsoid[\"WGS 84\", 6378137.0, 298.257223563],\n Id[\"EPSG\", 6326, URI[\"urn:ogc:def:datum:EPSG::6326\"]]]"] 115 | gdal.dev=> (proj/get-ellipsoid proj-data) 116 | #object[org.apache.sis.referencing.datum.DefaultEllipsoid 0x1b798fdd "Ellipsoid[\"WGS 84\", 6378137.0, 298.257223563, Id[\"EPSG\", 7030, URI[\"urn:ogc:def:ellipsoid:EPSG::7030\"]]]"] 117 | gdal.dev=> (proj/get-prime-meridian proj-data) 118 | #object[org.apache.sis.referencing.datum.DefaultPrimeMeridian 0x4a616ee4 "PrimeMeridian[\"Greenwich\", 0.0]"] 119 | gdal.dev=> (proj/get-bw-params proj-data) 120 | #object["[Lorg.apache.sis.referencing.datum.BursaWolfParameters;" 0x5b736d8 "[Lorg.apache.sis.referencing.datum.BursaWolfParameters;@5b736d8"] 121 | 122 | ``` 123 | 124 | Coordinate Systems and Axes: 125 | 126 | ```clojure 127 | gdal.dev=> (proj/get-coord-system proj-data) 128 | #object[org.apache.sis.referencing.cs.DefaultCartesianCS 0xaeb1198 "CS[Cartesian, 2]"] 129 | gdal.dev=> (proj/get-coord-name proj-data) 130 | "Cartesian CS: East (m), North (m)." 131 | gdal.dev=> (proj/get-coord-dim proj-data) 132 | 2 133 | gdal.dev=> (proj/get-axes proj-data) 134 | (#object[org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis 0x1c2152ac "Axis[\"Easting (E)\", east, Unit[\"metre\", 1]]"] #object[org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis 0x4fbaa8a1 "Axis[\"Northing (N)\", north, Unit[\"metre\", 1]]"]) 135 | gdal.dev=> (proj/get-axis proj-data 1) 136 | #object[org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis 0x4fbaa8a1 "Axis[\"Northing (N)\", north, Unit[\"metre\", 1]]"] 137 | gdal.dev=> (proj/get-axis-name proj-data 1) 138 | "Northing" 139 | gdal.dev=> (proj/get-axis-unit proj-data 1) 140 | "m" 141 | 142 | ``` 143 | 144 | 145 | ## License [↟](#contents) 146 | 147 | Copyright © 2015-2016 Jonathan Morton, Duncan McGreggor 148 | 149 | Distributed under the Eclipse Public License either version 1.0 or (at 150 | your option) any later version. 151 | -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to clj-gdal 2 | 3 | This library provides idiomatic Clojure support for GDAL. The project 4 | version number will track the GDAL version number. Currently, only GDAL 5 | 1.11.2 is supported. 6 | 7 | 8 | ## Add Dependency 9 | 10 | For Leiningen projects, updatet project.clj 11 | 12 | [oubiwann/clj-gdal "0.0.1"]] 13 | 14 | _This versioning scheme will probably change to reflect the version 15 | of GDAL that is supported._ 16 | 17 | 18 | ## Configure GDAL 19 | 20 | To use this library you must compile GDAL's Java SWIG bindings and 21 | set LD_LIBRARY_PATH to reference the directory that contains them. 22 | 23 | 24 | ### Get GDAL 25 | 26 | The compilation and installation of GDAL on Linux, *BSD, and Mac can be a 27 | bit tricky. This version of the Clojure library has only been tested 28 | against GDAL 1.11.2 on Ubuntu from the UbuntuGIS reposotiory. 29 | -------------------------------------------------------------------------------- /examples/repl-session.clj: -------------------------------------------------------------------------------- 1 | (gdal/init) 2 | (def data-dir "test/data/l8") 3 | (def scene-id "LC80290302015263LGN00") 4 | (def tiff-file (format "%s/%s/%s_B2.TIF" data-dir scene-id scene-id)) 5 | (def tiff-data (gdal/open tiff-file)) 6 | 7 | (dataset/get-size tiff-data) 8 | (dataset/count-bands tiff-data) 9 | 10 | (def band-data (dataset/get-band tiff-data 1)) 11 | (band/get-checksum band-data) 12 | 13 | (def proj-data (dataset/get-projection tiff-data)) 14 | (proj/get-name proj-data) 15 | (proj/get-ids proj-data) 16 | (:code-space (proj/get-id proj-data)) 17 | (:code (proj/get-id proj-data)) 18 | 19 | (proj/get-datum proj-data) 20 | (proj/get-datum-name proj-data) 21 | (proj/get-ellipsoid proj-data) 22 | (proj/get-ellipsoid-name proj-data) 23 | (proj/get-eccentricity proj-data) 24 | (proj/get-prime-meridian proj-data) 25 | (proj/get-bw-params proj-data) 26 | 27 | (proj/get-coord-system proj-data) 28 | (proj/get-coord-name proj-data) 29 | (proj/get-coord-dim proj-data) 30 | (proj/get-axes proj-data) 31 | (proj/get-axis proj-data 1) 32 | (proj/get-axis-name proj-data 1) 33 | (proj/get-axis-unit proj-data 1) 34 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject clj-gdal "0.4.0-ALPHA" 2 | :description "Clojure-idiomatic GDAL wrapper" 3 | :url "http://github.com/Element84/clj-gdal" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :repositories [["geotoolkit" "http://maven.geotoolkit.org/"]] 7 | :dependencies [[org.clojure/clojure "1.7.0"] 8 | [org.clojure/tools.logging "0.3.1"] 9 | [org.clojure/tools.namespace "0.2.11"] 10 | [org.gdal/gdal "1.11.2"] 11 | [org.apache.sis.core/sis-referencing "0.6"] 12 | [nio "1.0.3"]] 13 | :repl-options {:init-ns gdal.dev} 14 | :profiles {:dev {}}) 15 | -------------------------------------------------------------------------------- /resources/images/clj-gdal-LogoColor-x1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/clj-gdal/7e3ded6935d8b06c49e90be911ca9648b5a84205/resources/images/clj-gdal-LogoColor-x1000.png -------------------------------------------------------------------------------- /resources/images/clj-gdal-LogoColor-x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Element84/clj-gdal/7e3ded6935d8b06c49e90be911ca9648b5a84205/resources/images/clj-gdal-LogoColor-x250.png -------------------------------------------------------------------------------- /src/gdal/band.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.band 2 | (:require [gdal.core] 3 | [gdal.util] 4 | [nio.core :as nio]) 5 | (:import [java.nio ByteBuffer] 6 | [org.gdal.gdalconst gdalconst])) 7 | 8 | (defn checksum 9 | "Compute checksum for whole image" 10 | ([band] (.Checksum band)) 11 | ([band x y xs ys] (.Checksum band x y xs ys))) 12 | 13 | (defn compute-band-stats 14 | "Compute mean and standard deviation values" 15 | ([band] (gdal.util/not-yet)) 16 | ([band samplestep] (gdal.util/not-yet))) 17 | 18 | (defn compute-raster-min-max 19 | "Compute min/max values for a band" 20 | ([band] (gdal.util/not-yet)) 21 | ([band approx-ok] (gdal.util/not-yet))) 22 | 23 | (defn compute-statistics 24 | "Compute image statistics" 25 | ([band approx-ok & opts] (gdal.util/not-yet))) 26 | 27 | (defn create-mask-band 28 | "Add a mask band to the current band" 29 | [band flags] 30 | (gdal.util/not-yet)) 31 | 32 | (defn fill 33 | "Fill band with a constant value" 34 | ([band real-fill & opts] (.Fill band real-fill))) 35 | 36 | (defn flush-cache 37 | "Flush raster data cache" 38 | [band] 39 | (.FlushCache band)) 40 | 41 | (defn get-band 42 | "Fetch the band number" 43 | [band] 44 | (.GetBand band)) 45 | 46 | (defn get-block-x-size 47 | "Fetch the natural block width of this band" 48 | [band] 49 | (.GetBlockXSize band)) 50 | 51 | (defn get-block-y-size 52 | "Fetch the natural block heigh of this band" 53 | [band] 54 | (.GetBlockYSize band)) 55 | 56 | (defn get-block-size 57 | "Fetch the natural block size of this band" 58 | [band] 59 | ;; this side-steps working with the actual GDAL function 60 | ;; because creating and reading array buffer arguments is 61 | ;; not cool. 62 | [(get-block-x-size band) (get-block-y-size band)]) 63 | 64 | (defn get-category-names 65 | "Fetch the list of category names for this raster" 66 | [band] 67 | (.GetCategoryNames band)) 68 | 69 | ; shall we use a symbol instead of an int constant?? 70 | (defn get-color-interpretation 71 | "How should this band be interpreted as color?" 72 | [band] 73 | ; I wonder if a symbol ought to be returned. 74 | (.GetColorInterpretation band)) 75 | 76 | (defn get-color-table 77 | "Fetch the color table associated with band" 78 | [band] 79 | (.GetColorTable band)) 80 | 81 | (defn get-dataset 82 | "Get the dataset to which the band belongs" 83 | [band] 84 | (.GetDataset band)) 85 | 86 | (defn get-data-type 87 | "Return GDAL data type of the band" 88 | [band] 89 | (.getDataType band)) 90 | 91 | (def java-type->gdal-type 92 | {java.lang.Byte gdalconst/GDT_Byte 93 | java.lang.Short gdalconst/GDT_Int16 94 | java.lang.Integer gdalconst/GDT_Int32 95 | java.lang.Float gdalconst/GDT_Float32}) 96 | 97 | (defn get-gdal-type 98 | "Get the GDAL type needed to convert rasters to a Java type" 99 | [java-type] (java-type->gdal-type java-type)) 100 | 101 | (def gdal-type->java-type 102 | {gdalconst/GDT_Byte java.lang.Byte 103 | gdalconst/GDT_Int16 java.lang.Short 104 | gdalconst/GDT_Int32 java.lang.Integer 105 | gdalconst/GDT_Float32 java.lang.Float}) 106 | 107 | (defn get-java-type 108 | "Get the best Java type for the given GDAL band" 109 | [band] 110 | (let [gdal-type (get-data-type band)] 111 | (gdal-type->java-type gdal-type))) 112 | 113 | (def java-type->buffer-type 114 | {java.lang.Byte nio/byte-buffer 115 | java.lang.Short nio/short-buffer 116 | java.lang.Integer nio/int-buffer 117 | java.lang.Float nio/float-buffer}) 118 | 119 | (defn get-buffer-type 120 | "Get the nio buffer converter for the Java type" 121 | [java-type] (java-type->buffer-type java-type)) 122 | 123 | (defn get-data-type-size 124 | "Number of bytes per pixel" 125 | [band] 126 | (let [gdal-type (get-data-type band) 127 | byte-size (get-in gdal-type->java-type [gdal-type :size])] 128 | (/ byte-size 8))) 129 | 130 | ; unsigned integers are too big for the signed java types... 131 | ; but they require special handling to make fit. 132 | ; gdalconst/GDT_UInt16 nio/int-buffer ; wrong 133 | ; gdalconst/GDT_UInt32 nio/long-buffer ; wrong 134 | 135 | ; explain what this does -- comment repeats function name 136 | (defn get-default-histogram 137 | "Fetch the default raster histogram" 138 | ;; XXX handle overloaded functions with opts 139 | ([band min max & opts] (gdal.util/not-yet))) 140 | 141 | (defn get-default-rat 142 | "Fetch the default raster attribute table" 143 | [band] 144 | (.GetDefaultRAT band)) 145 | 146 | (defn get-histogram 147 | "Compute raster histogram" 148 | ;; XXX handle overloaded functions with opts 149 | [band & opts] 150 | (.GetHistogram band)) 151 | 152 | (defn get-mask-band 153 | "Return the mask band associated with band" 154 | [band] 155 | (.GetMaskBand band)) 156 | 157 | (defn get-mask-flags 158 | "Return the status flags of the mask band associated with the band" 159 | [band] 160 | (.GetMaskFlags band)) 161 | 162 | (defn get-maximum 163 | "Fetch maximum value for band" 164 | [band] 165 | (let [result (make-array java.lang.Double 1) 166 | safely #(cond % (short %))] 167 | (.GetMaximum band result) 168 | (-> result first safely))) 169 | 170 | (defn get-minimum 171 | "Fetch minimum value for band" 172 | [band] 173 | (let [result (make-array java.lang.Double 1) 174 | safely #(cond % (short %))] 175 | (.GetMinimum band result) 176 | (-> result first safely))) 177 | 178 | (defn get-no-data-value 179 | "Fetch the no data value for this band" 180 | [band] 181 | (let [result (make-array java.lang.Double 1) 182 | safely #(cond % (short %))] 183 | (.GetNoDataValue band result) 184 | (-> result first safely))) 185 | 186 | (defn get-offset 187 | "Fetch the raster value offset" 188 | [band i] 189 | (let [result (make-array java.lang.Double 1) 190 | safely #(cond % (short %))] 191 | (.GetOffset band result) 192 | (-> result first safely))) 193 | 194 | (defn get-overview 195 | "Fetch overview raster band" 196 | [band index] 197 | (.GetOverview index)) 198 | 199 | (defn get-overview-count 200 | "Number of overview layers available" 201 | [band] 202 | (.GetOverviewCount band)) 203 | 204 | (defn get-raster-category-names 205 | "" 206 | [band] 207 | (.GetRasterCategoryNames band)) 208 | 209 | (defn get-raster-color-interpretation 210 | "" 211 | [band] 212 | (.GetRasterColorInterpretation band)) 213 | 214 | (defn get-raster-color-table 215 | "" 216 | [band] 217 | (.GetRasterColorTable band)) 218 | 219 | (defn get-raster-data-type 220 | "" 221 | [band] 222 | (.GetRasterDataType band)) 223 | 224 | (defn get-scale 225 | "Fetch the raster value scale" 226 | [band] 227 | (let [result (make-array java.lang.Double 1) 228 | safely #(cond % (short %))] 229 | (.GetScale band result) 230 | (-> result first safely))) 231 | 232 | (defn get-statistics 233 | "Fetch image statistics" 234 | [band approx-ok force] 235 | (gdal.util/not-yet)) 236 | 237 | (defn get-unit-type 238 | "Fetch raster unit type" 239 | [band] 240 | (.GetUnitType band)) 241 | 242 | (defn get-byte-count 243 | "Count the number of bytes used by java-type" 244 | [java-type] 245 | (/ (eval `(. ~java-type SIZE)) 8)) 246 | 247 | (defn get-x-size 248 | "Fetch number of pixels along x axis" 249 | [band] 250 | (.GetXSize band)) 251 | 252 | (defn get-y-size 253 | "Fetch number of pixels along y axis" 254 | [band] 255 | (.GetYSize band)) 256 | 257 | (defn get-size 258 | "Fetch number of pixels along x and y axis" 259 | [band] 260 | [(get-x-size band) (get-y-size band)]) 261 | 262 | (defn has-arbitrary-overviews 263 | "Check for arbitrary overviews" 264 | [band] 265 | (.HasArbitraryOverviews band)) 266 | 267 | (defn read-block-direct 268 | "Read a block of image data efficiently" 269 | [band xoff yoff buffer] 270 | (.ReadBlock_Direct band xoff yoff buffer)) 271 | 272 | (defn read-raster-direct 273 | "Read a region of image data" 274 | ([band xoff yoff xsize ysize] 275 | (let [buf-type (get-data-type band)] 276 | (.ReadRaster_Direct band xoff yoff xsize ysize buf-type))) 277 | ([band xoff yoff xsize ysize buf-xsize buf-ysize buf-type buffer] 278 | (.ReadRaster_Direct band xoff yoff xsize ysize buf-xsize buf-ysize buf-type buffer))) 279 | 280 | (defn raster-seq 281 | "Read an entire raster in blocks without converting buffer to array. 282 | 283 | This function is for power users that intend to use byte buffer contents 284 | directly. It avoids the overhead raster-seq incurs when it converts the 285 | buffer contents into a vector." 286 | [band & {:keys [xstart ystart xstop ystop xstep ystep java-type] 287 | :or {xstart 0 288 | ystart 0 289 | xstop (get-x-size band) 290 | ystop (get-y-size band) 291 | xstep (get-block-x-size band) 292 | ystep (get-block-y-size band) 293 | :as args}}] 294 | (let [reader #(read-raster-direct band %1 %2 xstep ystep)] 295 | (for [x (range xstart xstop xstep) 296 | y (range xstart ystop ystep)] 297 | {:x x :y y :data (reader x y)}))) 298 | 299 | (defn read-raster 300 | "" 301 | [band x y xs ys array & opts] 302 | ;; XXX handle overloaded function calls with opts 303 | (.ReadRaster band x y xs ys array)) 304 | 305 | (defn set-category-names 306 | "Set the category names for this band" 307 | [band names] 308 | (.GetCategoryNames band names)) 309 | 310 | (defn set-color-interpretation 311 | "Set color interpretation of band" 312 | [band interpretation] 313 | (.SetColorInterpretation band interpretation)) 314 | 315 | (defn set-color-table 316 | "Set color table associated with band" 317 | [band color-table] 318 | (gdal.util/not-yet)) 319 | 320 | (defn set-default-histogram 321 | "Set default histogram" 322 | [band min max histogram] 323 | (gdal.util/not-yet)) 324 | 325 | (defn set-default-rat 326 | "Set raster color table" 327 | [band table] 328 | (gdal.util/not-yet)) 329 | 330 | (defn set-no-data-value 331 | "Set the no data value for this band" 332 | [band no-data] 333 | (.SetNoDataValue band no-data)) 334 | 335 | (defn set-offset 336 | "Set scaling offset" 337 | [band offset] 338 | (.SetOffset band offset)) 339 | 340 | (defn set-scale 341 | "Set scaling ratio" 342 | [band scale] 343 | (.SetScale band scale)) 344 | 345 | (defn set-statistics 346 | "Set statistics on band" 347 | [band min max mean stddev] 348 | (gdal.util/not-yet)) 349 | 350 | (defn set-unit-type 351 | "Set unit type" 352 | [band unit-type] 353 | (.SetUnitType band unit-type)) 354 | 355 | (defn write-block-direct 356 | "Write a block of image data efficiently" 357 | [band xoff yoff data] 358 | (gdal.util/not-yet)) 359 | 360 | (defn write-raster-direct 361 | "Write a region of image data for this band. Buffer must be a direct allocated byte buffer." 362 | [band xoff yoff xsize ysize buffer] 363 | (let [gdal-type (gdal.util/type-map buffer)] 364 | (.WriteRaster_Direct band xoff yoff xsize ysize buffer))) 365 | 366 | (defn write-raster 367 | "Write a region of image data for this band" 368 | [band xoff yoff xsize ysize data] 369 | (let [gdal-type (gdal.util/array->gdal-type data)] 370 | (.WriteRaster band xoff yoff xsize ysize gdal-type data))) 371 | -------------------------------------------------------------------------------- /src/gdal/core.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.core 2 | (:require [clojure.string :refer [split join]] 3 | [gdal.libpath])) 4 | 5 | ;; This reduces the amount of configuration required 6 | ;; by developers by addin directories to the load path 7 | ;; that contain the GDAL JNI libraries. Without this, 8 | ;; one would likely need to set LD_LIBRARY_PATH when 9 | ;; invoking something that uses clj-gdal (yikes). 10 | (try 11 | (gdal.libpath/amend) 12 | (catch RuntimeException e 13 | (binding [*out* *err*] 14 | (println (str "Could not update paths to native libraries. " 15 | "You may need to set LD_LIBRARY_PATH to the " 16 | "directory containing libgdaljni.so")))) 17 | (finally 18 | (import org.gdal.gdal.gdal))) 19 | 20 | (defn init 21 | "Load all available GDAL drivers" 22 | [] 23 | (gdal/AllRegister)) 24 | 25 | (defmulti open class) 26 | 27 | (defmethod open java.lang.String [path] 28 | (gdal/Open path)) 29 | 30 | (defmethod open java.io.File [file] 31 | (gdal/Open (.getAbsolutePath file))) 32 | 33 | (defn close 34 | "Frees native resources associated to dataset, closes file." 35 | [dataset] 36 | (.delete dataset)) 37 | 38 | (defmacro with-dataset 39 | [[binding path] & body] 40 | `(let [dataset# (open ~path) 41 | ~binding dataset#] 42 | (try 43 | (do ~@body) 44 | (finally 45 | (close dataset#))))) 46 | 47 | (defn version-info 48 | "Get information about the current version of GDAL" 49 | [& request] 50 | (gdal/VersionInfo)) 51 | 52 | ;;; Driver related functions 53 | 54 | (defn get-driver 55 | "Get driver using a numeric index" 56 | [idx] 57 | (gdal/GetDriver idx)) 58 | 59 | (defn get-driver-by-name 60 | "Get driver using a string name" 61 | [name] 62 | (gdal/GetDriverByName name)) 63 | 64 | (defn get-driver-count 65 | "Get number of available drivers" 66 | [] 67 | (gdal/GetDriverCount)) 68 | 69 | (defn get-drivers 70 | "Get all available drivers" 71 | [] 72 | (let [ix (range 0 (get-driver-count))] 73 | (map get-driver ix))) 74 | -------------------------------------------------------------------------------- /src/gdal/dataset.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.dataset 2 | (:require [gdal.core] 3 | [gdal.util]) 4 | (:import [org.gdal.gdal Dataset] 5 | [org.gdal.gdalconst gdalconst] 6 | [java.nio ByteBuffer] 7 | [java.text ParsePosition] 8 | [org.apache.sis.io.wkt WKTFormat] 9 | [gdal.util]) 10 | (:refer-clojure :exclude [read])) 11 | 12 | (defn add-band 13 | "Add a band to a dataset" 14 | ([dataset] 15 | (.AddBand dataset)) 16 | ([dataset index & options] 17 | (.AddBand dataset index))) 18 | 19 | (defn build-overviews 20 | "Build raster overview(s)." 21 | [dataset] 22 | (gdal.util/not-yet)) 23 | 24 | (defn commit-transaction 25 | "" 26 | [dataset] 27 | (gdal.util/not-yet)) 28 | 29 | (defn create-layer 30 | "" 31 | [dataset] 32 | (gdal.util/not-yet)) 33 | 34 | (defn create-mask-band 35 | "" 36 | [dataset] 37 | (gdal.util/not-yet)) 38 | 39 | (defn delete 40 | "" 41 | [dataset] 42 | (.delete dataset)) 43 | 44 | (defn create-mask-band 45 | "" 46 | [dataset] 47 | (gdal.util/not-yet)) 48 | 49 | (defn delete-layer 50 | "" 51 | [dataset] 52 | (gdal.util/not-yet)) 53 | 54 | (defn execute-sql 55 | "" 56 | [dataset] 57 | (gdal.util/not-yet)) 58 | 59 | (defn flush-cache 60 | "Write dataset to file" 61 | [dataset] 62 | (.FlushCache dataset)) 63 | 64 | (defn get-driver 65 | "" 66 | [dataset] 67 | (.GetDriver dataset)) 68 | 69 | (defn get-file-list 70 | "" 71 | [dataset] 72 | (.GetFileList dataset)) 73 | 74 | (defn get-gcp-count 75 | "Get number of GCPs" 76 | [dataset] 77 | (.GetGCPCount dataset)) 78 | 79 | (defn get-gcp-projection 80 | "Get output projection for GCPs" 81 | [dataset] 82 | (.GetGCPProjection dataset)) 83 | 84 | (defn get-gcps 85 | "Fetch GCPs" 86 | [dataset] 87 | (.GetGCPs dataset)) 88 | 89 | (defn get-geo-transform 90 | "Get the affine transformation coefficients of the dataset. 91 | 92 | These are used for transforming between pixel/line (P,L) raster space, 93 | and projection coordinates (Xp,Yp) space. 94 | 95 | Xp = geoTransformArray[0] + P*geoTransformArray[1] + L*geoTransformArray[2]; 96 | Yp = geoTransformArray[3] + P*geoTransformArray[4] + L*geoTransformArray[5]; 97 | 98 | Ultimately, you shouldn't need to use these to retrieve raster data using 99 | clj-gdal." 100 | [dataset] 101 | (vec (.GetGeoTransform dataset))) 102 | 103 | (defn get-projection-str 104 | "Get the projection definition WKT string for dataset" 105 | [dataset] 106 | (.GetProjection dataset)) 107 | 108 | (defn get-projection 109 | "Get a parsed WKT object for the dataset's projection definition" 110 | [dataset & {:keys [locale timezone start-index] 111 | :or {locale nil timezone nil start-index 0}}] 112 | (let [wkt (new WKTFormat locale timezone) 113 | parse-index (new ParsePosition start-index)] 114 | (.parse wkt (get-projection-str dataset) parse-index))) 115 | 116 | (defn get-raster-band 117 | "Get the nth raster band, starts with index of 1 in keeping with GDAL conventions" 118 | [dataset band-id] 119 | (.GetRasterBand dataset band-id)) 120 | 121 | (defn get-raster-count 122 | "Get the number of raster bands in the dataset" 123 | [dataset] 124 | (.GetRasterCount dataset)) 125 | 126 | (defn get-raster-x-size 127 | "Raster width in pixels" 128 | [dataset] 129 | (.GetRasterXSize dataset)) 130 | 131 | (defn get-raster-y-size 132 | "Raster height in pixels" 133 | [dataset] 134 | (.GetRasterYSize dataset)) 135 | 136 | (defn get-size 137 | "" 138 | [dataset] 139 | [(get-raster-x-size dataset) (get-raster-y-size dataset)]) 140 | 141 | (defn get-style-table 142 | "" 143 | [dataset] 144 | (.GetStyleTable dataset)) 145 | 146 | (defn read-raster 147 | "Read a region of image data from multiple bands" 148 | [dataset xoff yoff xsize ysize & opts] 149 | (gdal.util/not-yet)) 150 | 151 | (defn read-raster-direct 152 | "Read a region of image data from multiple bands" 153 | [dataset xoff yoff xsize ysize & opts] 154 | (gdal.util/not-yet)) 155 | 156 | (defn release-result-set 157 | "" 158 | [dataset layer] 159 | (gdal.util/not-yet)) 160 | 161 | (defn rollback-transaction 162 | "" 163 | [dataset] 164 | (gdal.util/not-yet)) 165 | 166 | (defn set-gcps 167 | "" 168 | [dataset gcp-seq gcp-projection] 169 | (gdal.util/not-yet)) 170 | 171 | (defn set-geo-transform 172 | "" 173 | [dataset affine] 174 | (.SetGeoTransform dataset (into-array Double/TYPE affine))) 175 | 176 | (defn set-projection-str 177 | "" 178 | [dataset wkt] 179 | (.SetProjection dataset wkt)) 180 | 181 | (defn set-style-table 182 | "" 183 | [dataset style-table] 184 | (.SetStyleTable dataset style-table)) 185 | 186 | (defn start-transaction 187 | "" 188 | [dataset] 189 | (gdal.util/not-yet)) 190 | 191 | (defn test-capability 192 | "" 193 | [dataset capability] 194 | (.TestCapability dataset capability)) 195 | 196 | (defn write-raster 197 | "" 198 | [dataset xoff yoff xsize ysize & opts] 199 | (gdal.util/not-yet)) 200 | 201 | (defn write-raster-direct 202 | "" 203 | [dataset xoff yoff xsize ysize & opts] 204 | (gdal.util/not-yet)) 205 | 206 | 207 | ;;; Aliases 208 | 209 | (def get-proj #'get-projection) 210 | (def count-bands #'get-raster-count) 211 | (def count-gcps #'get-gcp-count) 212 | (def read #'read-raster) 213 | 214 | (def get-band #'get-raster-band) 215 | (def get-band-count #'get-raster-count) 216 | (def get-x-size #'get-raster-x-size) 217 | (def get-y-size #'get-raster-y-size) 218 | -------------------------------------------------------------------------------- /src/gdal/dev.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.dev 2 | (:require [clojure.pprint :as pp] 3 | [clojure.reflect :as reflect] 4 | [clojure.tools.logging :as log] 5 | [clojure.tools.namespace.repl :as repl] 6 | [gdal.band :as band] 7 | [gdal.dataset :as dataset] 8 | [gdal.driver :as driver] 9 | [gdal.proj :as proj] 10 | [gdal.util :as util] 11 | [gdal.core :as gdal]) 12 | (:import [java.nio ByteBuffer] 13 | [org.gdal.gdalconst gdalconst])) 14 | 15 | (def reload #'repl/refresh) 16 | 17 | (defn show-members [java-object] 18 | (pp/print-table (:members (reflect/reflect java-object)))) 19 | 20 | (defn describe [it] 21 | {:type (class it) 22 | :driver (-> it dataset/get-driver driver/get-short-name) 23 | :band-count (dataset/count-bands it) 24 | :projection (dataset/get-projection-str it) 25 | :affine (dataset/get-geo-transform it) 26 | :xs (dataset/get-x-size it) 27 | :ys (dataset/get-y-size it)}) 28 | 29 | (defn copy [dataset driver-name file-name] 30 | (let [driver (gdal/get-driver-by-name driver-name) 31 | extension (-> driver driver/get-metadata :dmd-extension)] 32 | (driver/create-copy driver (str file-name "." extension) dataset))) 33 | 34 | (comment 35 | "copy and compare" 36 | (let [src (gdal/open "test/data/sample.tif") 37 | dup (copy src "netCDF" "sample-meow")] 38 | (merge-with = (describe src) (describe dup)) 39 | (dataset/get-file-list dup) 40 | (dataset/flush-cache dup))) 41 | 42 | (comment 43 | "playing with drivers" 44 | (def drivers (gdal/get-drivers)) 45 | (def names (sort (map driver/get-short-name drivers))) 46 | (def mem (gdal/get-driver-by-name "MEM")) 47 | (def tiff (gdal/get-driver-by-name "GTiff")) 48 | (def netcdf (gdal/get-driver-by-name "netCDF")) 49 | (def hdf4 (gdal/get-driver-by-name "hdf4")) 50 | (def hdf5 (gdal/get-driver-by-name "hdf5"))) 51 | 52 | ;; in memory raster does not support subdataset 53 | ;; 54 | 55 | (comment 56 | (driver/create? netcdf) 57 | (driver/get-metadata netcdf) 58 | (driver/create-datatypes tiff) 59 | (driver/copy? tiff) 60 | (driver/virtual-io? tiff) 61 | (driver/mime-type netcdf)) 62 | -------------------------------------------------------------------------------- /src/gdal/driver.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.driver 2 | (:require [gdal.core]) 3 | (:import [org.gdal.gdal Driver])) 4 | 5 | (defn copy-files 6 | "Not explained in GDAL Java bindings" 7 | [driver new-name old-name] 8 | ;; XXX understand and explain intended use. 9 | (.CopyFiles driver new-name old-name)) 10 | 11 | (defn create 12 | "Create a new dataset" 13 | ([driver name xsize ysize] 14 | (.Create driver name xsize ysize)) 15 | ([driver name xsize ysize bands] 16 | (.Create driver name xsize ysize bands)) 17 | ([driver name xsize ysize bands etype] 18 | (.Create driver name xsize ysize bands etype))) 19 | 20 | (defn create-copy 21 | "Create a copy of a dataset" 22 | [driver name src-dataset] 23 | (.CreateCopy driver name src-dataset)) 24 | 25 | (defn deregister 26 | "Deregister the driver" 27 | [driver] 28 | (.Deregister driver)) 29 | 30 | (defn get-help-topic 31 | "URL to the help describing this driver" 32 | [driver] 33 | (.getHelpTopic driver)) 34 | 35 | (defn get-long-name 36 | "Full name of a driver" 37 | [driver] 38 | (.getLongName driver)) 39 | 40 | (defn get-short-name 41 | "Succint name of a driver" 42 | [driver] 43 | (.getShortName driver)) 44 | 45 | (defn ->keyword-kebab-case [text] 46 | (-> text 47 | (clojure.string/lower-case) 48 | (clojure.string/replace #"_" "-") 49 | (keyword))) 50 | 51 | (defn get-metadata 52 | [driver] 53 | (into {} 54 | (for [[k v] (.GetMetadata_Dict driver)] 55 | [(->keyword-kebab-case k) v]))) 56 | 57 | (defn delete 58 | "Delete named dataset" 59 | [driver dataset-name] 60 | (.Delete driver dataset-name)) 61 | 62 | (defn register 63 | "Register a driver for use" 64 | [driver] 65 | (.Register driver)) 66 | 67 | (defn rename 68 | "Change the name of a dataset" 69 | [driver new-name old-name] 70 | (.Rename driver new-name old-name)) 71 | 72 | ;; metadata related functions 73 | 74 | (defn copy? 75 | [driver] 76 | (-> driver get-metadata :dcap-createcopy (= "YES"))) 77 | 78 | (defn create? 79 | [driver] 80 | (-> driver get-metadata :dcap-create (= "YES"))) 81 | 82 | (defn create-datatypes 83 | [driver] 84 | (-> driver 85 | get-metadata 86 | :dmd-creationdatatypes 87 | (clojure.string/split #" "))) 88 | 89 | (defn file-ext 90 | [driver] 91 | (-> driver get-metadata :dmd-extension)) 92 | 93 | (defn mime-type 94 | [driver] 95 | (-> driver get-metadata :dmd-mimetype)) 96 | 97 | (defn virtual-io? 98 | [driver] 99 | (-> driver get-metadata :dcap-virtualio (= "YES"))) 100 | -------------------------------------------------------------------------------- /src/gdal/libpath.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.libpath) 2 | 3 | (def gdal-paths 4 | [;; CentOS yum install location 5 | "/usr/lib/java/gdal" 6 | ;; Ubuntu install location 7 | "/usr/lib/jni"]) 8 | 9 | (defn add-usr-path 10 | "" 11 | [& paths] 12 | (let [field (.getDeclaredField ClassLoader "usr_paths")] 13 | (try (.setAccessible field true) 14 | (let [original (vec (.get field nil)) 15 | updated (distinct (concat original paths))] 16 | (.set field nil (into-array updated))) 17 | (finally 18 | (.setAccessible field false))))) 19 | 20 | (defn get-usr-path 21 | "" 22 | [& paths] 23 | (let [field (.getDeclaredField ClassLoader "usr_paths")] 24 | (try (.setAccessible field true) 25 | (vec (.get field nil)) 26 | (finally 27 | (.setAccessible field false))))) 28 | 29 | (defn amend 30 | "" 31 | [] 32 | (apply add-usr-path gdal-paths)) 33 | -------------------------------------------------------------------------------- /src/gdal/proj.clj: -------------------------------------------------------------------------------- 1 | ;;;; This namespace projides wrappers for getting projection information 2 | ;;;; returned by GDAL. Since GDAL only gives this information as text (WKT 3 | ;;;; format), this namespace provides wrappers for object-access to 4 | ;;;; the projection data (using the Apache Spatial Information System 5 | ;;;; library). 6 | ;;;; 7 | ;;;; Note that this is for standard formats of WKT as found in Landsat 8 image 8 | ;;;; metadata. It is not meant to parse custom WKT with unsupported or 9 | ;;;; non-standard fields 10 | ;;;; 11 | (ns gdal.proj 12 | (:require [gdal.core] 13 | [gdal.util :refer :all]) 14 | (:import [org.gdal.gdal Dataset] 15 | [org.gdal.gdalconst gdalconst] 16 | [java.nio ByteBuffer] 17 | [java.text ParsePosition] 18 | [org.apache.sis.io.wkt WKTFormat])) 19 | 20 | ;;; Top-level 21 | 22 | (defn get-name [proj] 23 | (-> proj 24 | (.getName) 25 | (.toString))) 26 | 27 | (defn get-id-objs [proj] 28 | (.getIdentifiers proj)) 29 | 30 | (defn- -get-id [id-obj] 31 | {:code-space (obj->str (.getCodeSpace id-obj) "") 32 | :code (obj->str (.getCode id-obj) "") 33 | :authority (obj->str (.getAuthority id-obj) "") 34 | :version (obj->str (.getVersion id-obj) "")}) 35 | 36 | (defn get-ids [proj] 37 | (into [] (map -get-id (get-id-objs proj)))) 38 | 39 | (defn get-id [proj] 40 | (first (get-ids proj))) 41 | 42 | ;;; Datum 43 | 44 | (defn get-datum [proj] 45 | (.getDatum proj)) 46 | 47 | (defn get-datum-name [proj] 48 | (-> (get-datum proj) 49 | (.getName) 50 | (.toString))) 51 | 52 | (defn get-ellipsoid [proj] 53 | (-> (get-datum proj) 54 | (.getEllipsoid))) 55 | 56 | (defn get-ellipsoid-name [proj] 57 | (-> (get-ellipsoid proj) 58 | (.getName) 59 | (.toString))) 60 | 61 | (defn get-eccentricity [proj] 62 | (-> (get-ellipsoid proj) 63 | (.getEccentricity))) 64 | 65 | (defn get-distance [proj lat1 long1 lat2 long2] 66 | (-> (get-ellipsoid proj) 67 | (.orthodromicDistance lat1 long1 lat2 long2))) 68 | 69 | (defn get-prime-meridian [proj] 70 | (-> (get-datum proj) 71 | (.getPrimeMeridian))) 72 | 73 | (defn get-bw-params [proj] 74 | (-> (get-datum proj) 75 | (.getBursaWolfParameters))) 76 | 77 | ;;; Coordinate system and axes 78 | 79 | (defn get-coord-system [proj] 80 | (.getCoordinateSystem proj)) 81 | 82 | (defn get-coord-name [proj] 83 | (-> (get-coord-system proj) 84 | (.getName) 85 | (.toString))) 86 | 87 | (defn get-coord-dim [proj] 88 | (-> (get-coord-system proj) 89 | (.getDimension))) 90 | 91 | (defn get-axis [proj index] 92 | (-> (get-coord-system proj) 93 | (.getAxis index))) 94 | 95 | (defn get-axis-name [proj index] 96 | (-> (get-axis proj index) 97 | (.getName) 98 | (.toString))) 99 | 100 | (defn get-axis-unit [proj index] 101 | (-> (get-axis proj index) 102 | (.getUnit) 103 | (.toString))) 104 | 105 | (defn get-axes [proj] 106 | (map #(get-axis proj %) (range (get-coord-dim proj)))) 107 | 108 | ;;; Aliases 109 | 110 | (def get-coordinate-system #'get-coord-system) 111 | (def get-coord-system-name #'get-coord-name) 112 | (def get-coord-system-dimension #'get-coord-dim) 113 | (def get-bursa-wolf-parameters #'get-bw-params) 114 | (def get-ellipsoid-eccentricity #'get-ellipsoid) 115 | (def get-orthodromic-distance #'get-distance) 116 | -------------------------------------------------------------------------------- /src/gdal/util.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.util 2 | (:import [org.gdal.gdalconst gdalconst] 3 | [java.nio ByteBuffer ShortBuffer IntBuffer FloatBuffer DoubleBuffer HeapByteBuffer DirectByteBuffer])) 4 | 5 | (defn obj->str 6 | ([obj] 7 | (obj->str obj "")) 8 | ([obj default] 9 | (.toString (or obj default)))) 10 | 11 | (defn print-obj [obj] 12 | (println (obj->str obj))) 13 | 14 | (def type-map {;; Primitive Types 15 | Byte/TYPE gdalconst/GDT_Byte 16 | Short/TYPE gdalconst/GDT_Int16 17 | Integer/TYPE gdalconst/GDT_Int32 18 | Float/TYPE gdalconst/GDT_Float32 19 | Double/TYPE gdalconst/GDT_Float64 20 | ;; Buffer Types 21 | ByteBuffer gdalconst/GDT_Byte 22 | ShortBuffer gdalconst/GDT_Int16 23 | IntBuffer gdalconst/GDT_Int32 24 | FloatBuffer gdalconst/GDT_Float32 25 | DoubleBuffer gdalconst/GDT_Float64 26 | ;; java.nio Buffers 27 | HeapByteBuffer gdalconst/GDT_Byte 28 | DirectByteBuffer gdalconst/GDT_Byte}) 29 | 30 | (defn array->gdal-type 31 | "" 32 | [array] 33 | (-> array 34 | (.getClass) 35 | (.getComponentType) 36 | type-map)) 37 | 38 | (defn not-yet 39 | "" 40 | [] 41 | (throw (UnsupportedOperationException. "this function is not yet supported"))) 42 | -------------------------------------------------------------------------------- /test/gdal/band_test.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.band-test 2 | (:require [clojure.test :refer :all] 3 | [nio.core :as nio] 4 | [gdal.core :as gdal] 5 | [gdal.band :as band] 6 | [gdal.dataset :as dataset] 7 | [gdal.driver :as driver] 8 | [gdal.util :as util]) 9 | (:import [org.gdal.gdalconst gdalconst])) 10 | 11 | ;; BEWARE: IF YOU CHANGE THE TEST FILE THERE IS A GOOD CHANCE THE 12 | ;; THE TESTS WILL FAIL. These tests don't just ensure the definition 13 | ;; of functions, they check the properties and values contained in 14 | ;; the file too! 15 | 16 | ;;; Fixture to to wrap all tests in the namespace, called just once 17 | 18 | (defn setup-once [] 19 | (gdal/init)) 20 | 21 | (defn teardown-once []) 22 | 23 | (defn fixture-once [test-fn] 24 | (setup-once) 25 | (test-fn) 26 | (teardown-once)) 27 | 28 | (use-fixtures :once fixture-once) 29 | 30 | ;;; Fixture to to wrap each test in the namespace, called for every test 31 | 32 | (def test-data-path "test/data/sample.tif") 33 | (def ^:dynamic *band*) 34 | 35 | (defn setup-each [test-fn] 36 | (let [band-data (-> test-data-path 37 | (gdal/open) 38 | (dataset/get-band 1))] 39 | (with-redefs [*band* band-data] 40 | (test-fn)))) 41 | 42 | (defn teardown-each []) 43 | 44 | (defn fixture-each [test-fn] 45 | (setup-each test-fn) 46 | (teardown-each)) 47 | 48 | (use-fixtures :each fixture-each) 49 | 50 | ;;; Tests 51 | 52 | (deftest test-geotiff-handling 53 | (testing "getting properties" 54 | ;; Exactly how GeoTIFF files are handled is explained in detail here: 55 | ;; http://www.gdal.org/frmt_gtiff.html 56 | (testing "width" 57 | (is (= 1000 (band/get-x-size *band*)))) 58 | (testing "height" 59 | (is (= 1000 (band/get-y-size *band*)))) 60 | ;; Block size is not simply the pixel width + 1 pixel high... 61 | ;; but the GDAL GeoTIFF does aren't exactly clear on how this 62 | ;; is calculated. This test exists to make sure the function 63 | ;; has been implemented. 64 | (testing "x block size" 65 | (is (= 1000 (band/get-block-x-size *band*)))) 66 | (testing "y block size" 67 | (is (= 4 (band/get-block-y-size *band*)))) 68 | (testing "get x and y block size" 69 | (is (= [1000 4] (band/get-block-size *band*)))) 70 | ;; The GDAL type corresponds to a Java type and some related 71 | ;; kind of NIO buffer function. 72 | (testing "GDAL data type" 73 | (is (= gdalconst/GDT_Int16 (band/get-data-type *band*)))) 74 | (testing "Java data type" 75 | (is (= java.lang.Short (band/get-java-type *band*)))) 76 | (testing "Java NIO buffer type" 77 | (is (= nio/short-buffer))) 78 | (testing "Get band number" 79 | (is (= 1 (band/get-band *band*)))) 80 | (testing "Get color interpretation" 81 | (is (= 1 (band/get-color-interpretation *band*)))) 82 | (testing "Get color table" 83 | (not (= nil (band/get-color-table *band*)))) 84 | (testing "Get default raster attribute table" 85 | (not (= nil (band/get-default-rat *band*)))) 86 | (testing "Get unit type (blank for test data)" 87 | (is (= "" (band/get-unit-type *band*)))) 88 | (testing "Does not have arbitrary overviews" 89 | (is (= false (band/has-arbitrary-overviews *band*)))) 90 | (testing "Get mask band (which is the 0th band in the same dataset)" 91 | (is (= 0 (-> *band* 92 | (band/get-mask-band) 93 | (band/get-band))))) 94 | (testing "Get mask flags (0x08 / GMF_NODATA)" 95 | (is (= 8 (band/get-mask-flags *band*)))) 96 | ;;(testing "Get dataset" 97 | ;; (not (= nil (band/get-dataset *band*)))) 98 | (testing "Get no-data value" 99 | (is (= -9999 (band/get-no-data-value *band*)))) 100 | (testing "Get minimum (which geotiffs don't know)" 101 | (is (= nil (band/get-minimum *band*)))) 102 | (testing "Get maximum (which geotiffs don't know)" 103 | (is (= nil (band/get-maximum *band*))))) 104 | (testing "Raster sequence (seq-ing many blocks as vectors)" 105 | (testing "implicit call" 106 | ;; The test image is 1000x1000 pixels. The natural block 107 | ;; size is 1000 pixels wide and 4 pixel high, so there 108 | ;; should be exactly 250 blocks. 109 | (let [blocks (band/raster-seq *band*)] 110 | (is (= 250 (count blocks))))) 111 | (testing "band with block size call" 112 | (let [blocks (band/raster-seq *band* :xstep 100 :ystep 100)] 113 | (is (= 100 (count blocks))))) 114 | (testing "explicit call" 115 | ;; The same 5000x5000 image is parsed in blocks of 1000x1000 116 | ;; so there should be five blocks along the x-axis and ten 117 | ;; blocks along the y-axis, yielding exactly 50 blocks. 118 | (let [blocks (band/raster-seq *band* :xstep 100 :ystep 50)] 119 | (is (= 200 (count blocks)))))) 120 | (testing "Raster sequence of byte buffer" 121 | (let [blocks (band/raster-seq *band* :xstep 500 :ystep 500)] 122 | (is (= 4 (count blocks))) 123 | (is (every? #(= (type (:data %)) java.nio.DirectByteBuffer) blocks))))) 124 | 125 | (defn make-band [xs ys] 126 | (-> (gdal/get-driver-by-name "MEM") 127 | (driver/create "test-dataset" xs ys) 128 | (dataset/get-band 1))) 129 | 130 | (defn make-data [xs ys] 131 | (into-array Byte/TYPE (take (* xs ys) (cycle (range Byte/MIN_VALUE Byte/MAX_VALUE))))) 132 | 133 | (deftest test-write 134 | (testing "write an array (not a byte buffer)" 135 | (let [test-band (make-band 128 128) 136 | test-data (make-data 128 128)] 137 | (band/write-raster test-band 0 0 128 128 test-data) 138 | (band/read-raster-direct test-band 0 0 4 4))) 139 | (testing "write an allocated direct buffer" 140 | (let [test-band (make-band 128 128) 141 | test-data (make-data 128 128) 142 | buff (java.nio.ByteBuffer/allocateDirect (* 128 128))] 143 | (band/write-raster-direct test-band 0 0 128 128 buff))) 144 | (testing "write a buffer wrapped array generates an exception" 145 | (let [test-band (make-band 128 128) 146 | test-data (make-data 128 128) 147 | buff (java.nio.ByteBuffer/wrap test-data)] 148 | ;; XXX thrown-with-msg? doesn't seem to handle the 149 | ;; raised exception, that's odd... 150 | #_(is (thrown-with-msg? 151 | java.lang.RuntimeException 152 | #"Buffer must be allocated direct" 153 | (band/write-raster-direct test-band 0 0 128 128 buff)))))) 154 | -------------------------------------------------------------------------------- /test/gdal/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.core-test 2 | (:require [clojure.test :refer :all] 3 | [gdal.core :as gdal])) 4 | 5 | (deftest test-drivers 6 | ;; Drivers are loaded during GDAL initailization. Before calling 7 | ;; init there are no available driveres. However, this test only 8 | ;; makes sense if initialization must be invoked explicitly. 9 | (testing "Initialize GDAL" 10 | (let [_ (gdal/init)] 11 | (is (< 0 (gdal/get-driver-count))))) 12 | 13 | ;; GDAL has a few built in drivers, a geotiff driver is one of them. 14 | (testing "Loading a driver by name" 15 | (let [driver (gdal/get-driver-by-name "GTiff")] 16 | (not (= driver nil)))) 17 | 18 | ;; I don't know if it makes sense to make a guess at the name of the 19 | ;; 0th driver, so the assertion is pretty vague. 20 | (testing "Loading a driver by index" 21 | (let [driver (gdal/get-driver 0)] 22 | (not (= driver nil))))) 23 | 24 | (deftest test-open 25 | (testing "Opening a geotiff" 26 | (let [_ (gdal/init) ; fixture? 27 | path "test/data/sample.tif" 28 | tiff (gdal/open path)] 29 | (not (= tiff nil))))) 30 | -------------------------------------------------------------------------------- /test/gdal/dataset_test.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.dataset-test 2 | (:require [clojure.test :refer :all] 3 | [gdal.dataset :as dataset])) 4 | -------------------------------------------------------------------------------- /test/gdal/driver_test.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.driver-test 2 | (:require [clojure.test :refer :all] 3 | [gdal.core :as gdal] 4 | [gdal.driver :as driver])) 5 | 6 | (deftest create-test 7 | (testing "in memory raster" 8 | (let [mem (gdal/get-driver-by-name "MEM") 9 | dataset (driver/create mem "test" 128 128)] 10 | (is (some? dataset))))) 11 | 12 | (deftest register-test 13 | (testing "register a driver" 14 | (comment "I don't know how to test this..."))) 15 | 16 | (deftest deregister-test 17 | (testing "deregister a driver" 18 | (comment "I don't know how to test this..."))) 19 | -------------------------------------------------------------------------------- /test/gdal/proj_test.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.proj-test 2 | (:require [clojure.test :refer :all] 3 | [nio.core :as nio] 4 | [gdal.core :as gdal] 5 | [gdal.band :as band] 6 | [gdal.dataset :as dataset] 7 | [gdal.proj :as proj]) 8 | (:import [org.gdal.gdalconst gdalconst])) 9 | 10 | ;;; Fixture to to wrap all tests in the namespace, called just once 11 | 12 | (defn setup-once [] 13 | (gdal/init)) 14 | 15 | (defn teardown-once []) 16 | 17 | (defn fixture-once [test-fn] 18 | (setup-once) 19 | (test-fn) 20 | (teardown-once)) 21 | 22 | (use-fixtures :once fixture-once) 23 | 24 | ;;; Fixture to to wrap each test in the namespace, called for every test 25 | 26 | (def test-data-path "test/data/sample.tif") 27 | (def ^:dynamic *proj*) 28 | 29 | (defn setup-each [test-fn] 30 | (let [proj-data (-> test-data-path 31 | (gdal/open) 32 | (dataset/get-projection))] 33 | (with-redefs [*proj* proj-data] 34 | (test-fn)))) 35 | 36 | (defn teardown-each []) 37 | 38 | (defn fixture-each [test-fn] 39 | (setup-each test-fn) 40 | (teardown-each)) 41 | 42 | (use-fixtures :each fixture-each) 43 | 44 | ;;; Tests 45 | 46 | (deftest test-top-level-funcs 47 | (testing "name" 48 | ;; Exactly how GeoTIFF files are handled is explained in detail here: 49 | ;; http://www.gdal.org/frmt_gtiff.html 50 | (is (= "WGS 84 / UTM zone 14N" (proj/get-name *proj*)))) 51 | (testing "ids" 52 | (is (= [{:code-space "EPSG", :code "32614", :authority "IdentifierSpace[“EPSG”]", :version ""}] 53 | (proj/get-ids *proj*))) 54 | (is (= {:code-space "EPSG", :code "32614", :authority "IdentifierSpace[“EPSG”]", :version ""} 55 | (proj/get-id *proj*))))) 56 | 57 | (deftest test-datum-funcs 58 | (testing "name" 59 | (is (= "WGS_1984" (proj/get-datum-name *proj*)))) 60 | (testing "ellipsoid" 61 | (is (= "WGS 84" (proj/get-ellipsoid-name *proj*))) 62 | (is (= 0.08181919084262128 (proj/get-eccentricity *proj*)))) 63 | ; (testing "distance" 64 | ; (is (= "" (get-distance *proj* lat1 long1 lat2 long2)))) 65 | (testing "meridian" 66 | (is (= "Greenwich" (-> *proj* 67 | (proj/get-prime-meridian) 68 | (.getName) 69 | (.toString))))) 70 | ; (testing "bursa-wolf" 71 | ; ) 72 | ) 73 | 74 | (deftest test-coordinate-system-funcs 75 | (testing "coords" 76 | (is (= "Cartesian CS: East (m), North (m)." 77 | (proj/get-coord-name *proj*))) 78 | (is (= 2 (proj/get-coord-dim *proj*)))) 79 | (testing "axes" 80 | (is (= 2 (count (proj/get-axes *proj*)))) 81 | (is (= "Northing" (proj/get-axis-name *proj* 1))) 82 | (is (= "m" (proj/get-axis-unit *proj* 1))) 83 | (is (= "Easting" (proj/get-axis-name *proj* 0))) 84 | (is (= "m" (proj/get-axis-unit *proj* 0))))) 85 | -------------------------------------------------------------------------------- /test/gdal/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns gdal.util-test 2 | (:require [clojure.test :refer :all] 3 | [gdal.util :as util])) 4 | 5 | (deftest test-gdal-type 6 | (testing "a short array (in more ways than one)" 7 | (let [xs (into-array Short/TYPE [42])] 8 | (is (= 3 (util/array->gdal-type xs)))))) 9 | --------------------------------------------------------------------------------