├── .travis.yml ├── AUTHORS.txt ├── CHANGES.txt ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── benchmarks └── ndarray.py ├── docs ├── cli.rst ├── colormaps.rst ├── concurrency.rst ├── datasets.rst ├── features.rst ├── georeferencing.rst ├── masks.rst ├── options.rst ├── reproject.rst ├── tags.rst └── windowed-rw.rst ├── examples ├── async-rasterio.py ├── concurrent-cpu-bound.py ├── decimate.py ├── features.ipynb ├── introduction.ipynb ├── polygonize.py ├── rasterio_polygonize.py ├── rasterize_geometry.py ├── reproject.py ├── sieve.py └── total.py ├── rasterio ├── __init__.py ├── _base.pxd ├── _base.pyx ├── _copy.pyx ├── _drivers.pyx ├── _err.pyx ├── _example.pyx ├── _features.pxd ├── _features.pyx ├── _gdal.pxd ├── _io.pxd ├── _io.pyx ├── _ogr.pxd ├── _warp.pyx ├── coords.py ├── crs.py ├── dtypes.py ├── enums.py ├── features.py ├── five.py ├── rio │ ├── __init__.py │ ├── bands.py │ ├── cli.py │ ├── features.py │ ├── info.py │ ├── main.py │ ├── merge.py │ └── rio.py ├── tool.py ├── transform.py └── warp.py ├── requirements-dev.txt ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── conftest.py ├── data ├── README.rst ├── RGB.byte.tif ├── float.tif ├── float_nan.tif └── shade.tif ├── test_band.py ├── test_blocks.py ├── test_colorinterp.py ├── test_colormap.py ├── test_coords.py ├── test_copy.py ├── test_crs.py ├── test_driver_management.py ├── test_dtypes.py ├── test_features_bounds.py ├── test_features_rasterize.py ├── test_features_shapes.py ├── test_features_sieve.py ├── test_indexing.py ├── test_no_georef.py ├── test_nodata.py ├── test_pad.py ├── test_png.py ├── test_read.py ├── test_read_boundless.py ├── test_read_resample.py ├── test_revolvingdoor.py ├── test_rio_bands.py ├── test_rio_features.py ├── test_rio_info.py ├── test_rio_merge.py ├── test_rio_rio.py ├── test_tags.py ├── test_tool.py ├── test_transform.py ├── test_update.py ├── test_warp.py └── test_write.py /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.3" 5 | - "3.4" 6 | before_install: 7 | - sudo add-apt-repository -y ppa:ubuntugis/ppa 8 | - sudo apt-get update -qq 9 | - sudo apt-get install -y libgdal1h gdal-bin libgdal-dev 10 | install: 11 | - pip install --install-option="--no-cython-compile" cython 12 | - "pip install -r requirements-dev.txt" 13 | - "pip install pytest" 14 | - "pip install coveralls" 15 | - "pip install -e ." 16 | script: 17 | - coverage run --source=rasterio --omit='*.pxd,*.pyx,*/tests/*,*/docs/*,*/examples/*,*/benchmarks/*,*/rio/main.py,*/rio/__init__.py' -m py.test 18 | after_success: 19 | - coveralls 20 | -------------------------------------------------------------------------------- /AUTHORS.txt: -------------------------------------------------------------------------------- 1 | Authors 2 | ======= 3 | 4 | Sean Gillies 5 | Brendan Ward https://github.com/brendan-ward 6 | Asger Skovbo Petersen https://github.com/AsgerPetersen 7 | James Seppi https://github.com/jseppi 8 | Chrisophe Gohlke https://github.com/cgohlke 9 | Robin Wilson https://github.com/robintw 10 | Mike Toews https://github.com/mwtoews 11 | Amit Kapadia https://github.com/kapadia 12 | Alessandro Amici https://github.com/alexamici 13 | 14 | See also https://github.com/mapbox/rasterio/graphs/contributors. 15 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | Changes 2 | ======= 3 | 4 | 0.17.1 (2015-01-20) 5 | ------------------- 6 | - Properly handle metadata tags with values that contain "=" (#254). 7 | 8 | 0.17.0 (2015-01-15) 9 | ------------------- 10 | - Enhancements to rio-merge: relaxation of same-extent and same-resolution 11 | constraints, addition of --bounds and --res options (#242, 247). 12 | - Data files in support of binary wheels (#239). 13 | - Fix for reading bands with undefined nodata (#237, #240). 14 | 15 | 0.16.0 (2014-12-16) 16 | ------------------- 17 | - More graceful, slice-like handling of windows (#191). 18 | - Addition of optional z coordinate to warp.transform() (#199). 19 | - Relax excessively strict transform guard, allowing translation of rasters 20 | with no georeferencing (#210). 21 | - Removal of setuptools from the package's install_requires (#222). 22 | 23 | 0.15.1 (2014-11-03) 24 | ------------------- 25 | - Fix incorrect use of output.dtype (#196). 26 | 27 | 0.15 (2014-10-11) 28 | ----------------- 29 | - Support for more data types in seive() (#159). 30 | - Handle unexpected PROJ.4 values like "+no_defs=True" (#173). 31 | - Support for writing PNG, JPEG, etc using GDALCreateCopy (#177). 32 | - New rio-stack command (#180). 33 | - Moved rio CLI main entry point to rasterio/rio/main:cli. 34 | - Add rio-env command and --version option to rio. 35 | - Make -f and --format aliases for --driver in CLI options (#183). 36 | - Remove older rio_* scripts (#184). 37 | - `out` keyword arg supercedes `output` in rasterio.features (#179). 38 | 39 | 0.14.1 (2014-10-02) 40 | ------------------- 41 | - Allow update of nodata values in r+ mode (#167). 42 | 43 | 0.14 (2014-10-01) 44 | ----------------- 45 | - Fixed tag update crasher (#145). 46 | - Add --mask and --bidx options to rio shapes (#150). 47 | - Faster geometry transforms and antimeridian cutting (#163). 48 | - Support for more data types in shapes() and rasterize() (#155, #158). 49 | - Switch to Cython 0.20+ for development (#151). 50 | 51 | 0.13.2 (2014-09-23) 52 | ------------------- 53 | - Add enum34 to requirements (#149). 54 | - Make rasterize() more robust (#146). 55 | - Pin Cython>=0.20 and Numpy>=1.8 (#151). 56 | 57 | 0.13.1 (2014-09-13) 58 | ------------------- 59 | - Read unprojected images with less flailing (#117). 60 | 61 | 0.13 (2014-09-09) 62 | ----------------- 63 | - Add single value options to rio info command (#139, #143). 64 | - Switch to console scripts entry points for rio, &c (#137). 65 | - Avoid unnecessary imports of Numpy in info command, elsewhere (#140). 66 | 67 | 0.12.1 (2014-09-02) 68 | ------------------- 69 | - Add missing rasterio.rio package (#135). 70 | 71 | 0.12 (2014-09-02) 72 | ----------------- 73 | - Add --mercator option for rio bounds (#126). 74 | - Add option for RS as a JSON text sequence separator (#127). 75 | - Add rio merge command (#131). 76 | - Change layout of tests (#134). 77 | 78 | 0.11.1 (2014-08-19) 79 | ------------------- 80 | - Add --bbox option for rio bounds (#124). 81 | 82 | 0.11 (2014-08-06) 83 | ----------------- 84 | - Add rio shapes command (#115). 85 | - Accept CRS strings like 'EPSG:3857' (#116). 86 | - Write multiple bands at a time (#95). 87 | 88 | 0.10.1 (2014-07-21) 89 | ------------------- 90 | - Numpy.require C-contiguous data when writing bands (#108). 91 | 92 | 0.10 (2014-07-18) 93 | ----------------- 94 | - Add rio bounds command (#111). 95 | - Add rio transform command (#112). 96 | 97 | 0.9 (2014-07-16) 98 | ---------------- 99 | - Add meta and tag dumping options to rio_insp. 100 | - Leave GDAL finalization to the DLL's destructor (#91). 101 | - Add pad() function (#84). 102 | - New read() method, returns 3D arrays (#83). 103 | - New affine attribute and AffineMatrix object (#80, #86). 104 | - Removal of rasterio.insp script (#51). 105 | - Read_band() is now a special case of read() (#96). 106 | - Add support for multi-band reprojection (#98). 107 | - Support for GDAL CInt16 datasets (#97). 108 | - Fix loss of projection information (#102). 109 | - Fix for loss of nodata values (#109). 110 | - Permit other than C-contiguous arrays (#108). 111 | 112 | 0.8 (2014-03-31) 113 | ---------------- 114 | - Add rasterize(), the inverse of shapes() (#45, #62). 115 | - Change the sense of mask for shapes(). Masks are always positive in 116 | rasterio, so we extract shapes only where mask is True. 117 | 118 | 0.7.3 (2014-03-22) 119 | ------------------ 120 | - Fix sieve() bug (#57). 121 | 122 | 0.7.2 (2014-03-20) 123 | ------------------ 124 | - Add rio_insp, deprecation warning in rasterio.insp (#50, #52). 125 | - Fix transform bug in shapes() (#54). 126 | 127 | 0.7.1 (2014-03-15) 128 | ------------------ 129 | - Source distribution bug fix (#48). 130 | 131 | 0.7 (2014-03-14) 132 | ---------------- 133 | - Add a Band object, providing a shortcut for shapes() and sieve() functions 134 | (#34). 135 | - Reprojection of rasters (#12). 136 | - Enhancements to the rasterio.insp console: module aliases, shortcut for 137 | show(). 138 | - Add index() method. 139 | - Reading and writing of GDAL mask bands (#41). 140 | - Add rio_cp program. 141 | - Enable r+ mode for GeoTIFFs (#46). 142 | 143 | 0.6 (2014-02-10) 144 | ---------------- 145 | - Add support for dataset and band tags (#32). 146 | - Add testing dependence on pytest (#33). 147 | - Add support for simple RGBA colormaps (#34). 148 | - Fix for a crasher that occurs when a file is sent through a write-read 149 | revolving door. 150 | - New docs for tags and colormaps. 151 | 152 | 0.5.1 (2014-02-02) 153 | ------------------ 154 | - Add mask option to shapes() function (#26). 155 | - Add rasterio.insp interactive interpreter. 156 | 157 | 0.5 (2014-01-22) 158 | ---------------- 159 | - Access to shapes of raster features via GDALPolygonize (#20). 160 | - Raster feature sieving (#21). 161 | - Registration and de-registration of drivers via context managers (#22). 162 | 163 | 0.4 (2013-12-19) 164 | ---------------- 165 | - Add nodatavals property (#13). 166 | - Allow nodata to be set when opening file to write (#17). 167 | 168 | 0.3 (2013-12-15) 169 | ---------------- 170 | - Drop six dependency (#9) 171 | - Add crs_wkt attribute (#10). 172 | - Add bounds attribute and ul() method (#11). 173 | - Add block_windows property (#7). 174 | - Enable windowed reads and writes (#6). 175 | - Use row,column ordering in window tuples as in Numpy (#13). 176 | - Add documentation on windowed reading and writing. 177 | 178 | 0.2 (2013-11-24) 179 | ---------------- 180 | - Band indexes start at 1 (#2). 181 | - Decimation or replication of pixels on read and write (#3). 182 | - Add rasterio.copy() (#5). 183 | 184 | 0.1 (2013-11-07) 185 | ---------------- 186 | - Reading and writing of GeoTIFFs, with examples. 187 | 188 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, MapBox 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Mapbox nor the names of its contributors may 15 | be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, 22 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 27 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | exclude *.rst *.txt *.py 2 | include CHANGES.txt AUTHORS.txt LICENSE.txt VERSION.txt README.rst setup.py 3 | recursive-include examples *.py 4 | recursive-include tests *.py *.rst 5 | recursive-exclude tests/data *.tif 6 | recursive-include tests/data *.txt 7 | recursive-include docs *.rst 8 | exclude MANIFEST.in 9 | -------------------------------------------------------------------------------- /benchmarks/ndarray.py: -------------------------------------------------------------------------------- 1 | # Benchmark for read of raster data to ndarray 2 | 3 | import timeit 4 | 5 | import rasterio 6 | from osgeo import gdal 7 | 8 | # GDAL 9 | s = """ 10 | src = gdal.Open('rasterio/tests/data/RGB.byte.tif') 11 | arr = src.GetRasterBand(1).ReadAsArray() 12 | src = None 13 | """ 14 | 15 | n = 100 16 | 17 | t = timeit.timeit(s, setup='from osgeo import gdal', number=n) 18 | print("GDAL:") 19 | print("%f msec\n" % (1000*t/n)) 20 | 21 | # Rasterio 22 | s = """ 23 | with rasterio.open('rasterio/tests/data/RGB.byte.tif') as src: 24 | arr = src.read_band(1) 25 | """ 26 | 27 | t = timeit.timeit(s, setup='import rasterio', number=n) 28 | print("Rasterio:") 29 | print("%f msec\n" % (1000*t/n)) 30 | 31 | # GDAL Extras 32 | s = """ 33 | src = gdal.Open('rasterio/tests/data/RGB.byte.tif') 34 | transform = src.GetGeoTransform() 35 | srs = osr.SpatialReference() 36 | srs.ImportFromWkt(src.GetProjectionRef()) 37 | wkt = srs.ExportToWkt() 38 | proj = srs.ExportToProj4() 39 | arr = src.GetRasterBand(1).ReadAsArray() 40 | src = None 41 | """ 42 | 43 | n = 1000 44 | 45 | t = timeit.timeit(s, setup='from osgeo import gdal; from osgeo import osr', number=n) 46 | print("GDAL + Extras:\n") 47 | print("%f usec\n" % (t/n)) 48 | 49 | # Rasterio 50 | s = """ 51 | with rasterio.open('rasterio/tests/data/RGB.byte.tif') as src: 52 | transform = src.transform 53 | proj = src.crs 54 | wkt = src.crs_wkt 55 | arr = src.read_band(1) 56 | """ 57 | 58 | t = timeit.timeit(s, setup='import rasterio', number=n) 59 | print("Rasterio:\n") 60 | print("%f usec\n" % (t/n)) 61 | 62 | 63 | import pstats, cProfile 64 | 65 | s = """ 66 | with rasterio.open('rasterio/tests/data/RGB.byte.tif') as src: 67 | arr = src.read_band(1, window=(10, 10, 10, 10)) 68 | """ 69 | 70 | cProfile.runctx(s, globals(), locals(), "Profile.prof") 71 | 72 | s = pstats.Stats("Profile.prof") 73 | s.strip_dirs().sort_stats("time").print_stats() 74 | -------------------------------------------------------------------------------- /docs/cli.rst: -------------------------------------------------------------------------------- 1 | Command Line Interface 2 | ====================== 3 | 4 | Rasterio's new command line interface is a program named "rio". 5 | 6 | .. code-block:: console 7 | 8 | $ rio 9 | Usage: rio [OPTIONS] COMMAND [ARGS]... 10 | 11 | Rasterio command line interface. 12 | 13 | Options: 14 | -v, --verbose Increase verbosity. 15 | -q, --quiet Decrease verbosity. 16 | --help Show this message and exit. 17 | 18 | Commands: 19 | bounds Write bounding boxes to stdout as GeoJSON. 20 | info Print information about a data file. 21 | insp Open a data file and start an interpreter. 22 | merge Merge a stack of raster datasets. 23 | shapes Write the shapes of features. 24 | stack Stack a number of bands into a multiband dataset. 25 | transform Transform coordinates. 26 | 27 | It is developed using the ``click`` package. 28 | 29 | 30 | bounds 31 | ------ 32 | 33 | New in 0.10. 34 | 35 | The bounds command writes the bounding boxes of raster datasets to GeoJSON for 36 | use with, e.g., `geojsonio-cli `__. 37 | 38 | .. code-block:: console 39 | 40 | $ rio bounds tests/data/RGB.byte.tif --indent 2 41 | { 42 | "features": [ 43 | { 44 | "geometry": { 45 | "coordinates": [ 46 | [ 47 | [ 48 | -78.898133, 49 | 23.564991 50 | ], 51 | [ 52 | -76.599438, 53 | 23.564991 54 | ], 55 | [ 56 | -76.599438, 57 | 25.550874 58 | ], 59 | [ 60 | -78.898133, 61 | 25.550874 62 | ], 63 | [ 64 | -78.898133, 65 | 23.564991 66 | ] 67 | ] 68 | ], 69 | "type": "Polygon" 70 | }, 71 | "properties": { 72 | "id": "0", 73 | "title": "tests/data/RGB.byte.tif" 74 | }, 75 | "type": "Feature" 76 | } 77 | ], 78 | "type": "FeatureCollection" 79 | } 80 | 81 | Shoot the GeoJSON into a Leaflet map using geojsonio-cli by typing 82 | ``rio bounds tests/data/RGB.byte.tif | geojsonio``. 83 | 84 | info 85 | ---- 86 | 87 | Rio's info command intends to serve some of the same uses as gdalinfo. 88 | 89 | .. code-block:: console 90 | 91 | $ rio info tests/data/RGB.byte.tif 92 | { 'affine': Affine(300.0379266750948, 0.0, 101985.0, 93 | 0.0, -300.041782729805, 2826915.0), 94 | 'count': 3, 95 | 'crs': { 'init': u'epsg:32618'}, 96 | 'driver': u'GTiff', 97 | 'dtype': , 98 | 'height': 718, 99 | 'nodata': 0.0, 100 | 'transform': ( 101985.0, 101 | 300.0379266750948, 102 | 0.0, 103 | 2826915.0, 104 | 0.0, 105 | -300.041782729805), 106 | 'width': 791} 107 | 108 | insp 109 | ---- 110 | 111 | The insp command opens a dataset and an interpreter. 112 | 113 | .. code-block:: console 114 | 115 | $ rio insp tests/data/RGB.byte.tif 116 | Rasterio 0.9 Interactive Inspector (Python 2.7.5) 117 | Type "src.meta", "src.read_band(1)", or "help(src)" for more information. 118 | >>> import pprint 119 | >>> pprint.pprint(src.meta) 120 | {'affine': Affine(300.0379266750948, 0.0, 101985.0, 121 | 0.0, -300.041782729805, 2826915.0), 122 | 'count': 3, 123 | 'crs': {'init': u'epsg:32618'}, 124 | 'driver': u'GTiff', 125 | 'dtype': , 126 | 'height': 718, 127 | 'nodata': 0.0, 128 | 'transform': (101985.0, 129 | 300.0379266750948, 130 | 0.0, 131 | 2826915.0, 132 | 0.0, 133 | -300.041782729805), 134 | 'width': 791} 135 | 136 | merge 137 | ----- 138 | 139 | The merge command can be used to flatten a stack of identically structured 140 | datasets. 141 | 142 | .. code-block:: console 143 | 144 | $ rio merge rasterio/tests/data/R*.tif merged.tif 145 | 146 | rasterize 147 | --------- 148 | 149 | New in 0.18. 150 | 151 | The rasterize command rasterizes GeoJSON features into a new or existing 152 | raster. 153 | 154 | .. code-block:: console 155 | 156 | $ rio rasterize test.tif --res 0.0167 < input.geojson 157 | 158 | The resulting file will have an upper left coordinate determined by the bounds 159 | of the GeoJSON (in EPSG:4326, which is the default), with a 160 | pixel size of approximately 30 arc seconds. Pixels whose center is within the 161 | polygon or that are selected by brezenhams line algorithm will be burned in 162 | with a default value of 1. 163 | 164 | It is possible to rasterize into an existing raster and use an alternative 165 | default value: 166 | 167 | .. code-block:: console 168 | 169 | $ rio rasterize existing.tif --default_value 10 < input.geojson 170 | 171 | It is also possible to rasterize using a template raster, which will be used 172 | to determine the transform, dimensions, and coordinate reference system of the 173 | output raster: 174 | 175 | .. code-block:: console 176 | 177 | $ rio rasterize test.tif --like tests/data/shade.tif < input.geojson 178 | 179 | GeoJSON features may be provided using stdin or specified directly as first 180 | argument, and dimensions may be provided in place of pixel resolution: 181 | 182 | .. code-block:: console 183 | 184 | $ rio rasterize input.geojson test.tif --dimensions 1024 1024 185 | 186 | Other options are available, see: 187 | 188 | .. code-block:: console 189 | 190 | $ rio rasterize --help 191 | 192 | 193 | shapes 194 | ------ 195 | 196 | New in 0.11. 197 | 198 | The shapes command extracts and writes features of a specified dataset band out 199 | as GeoJSON. 200 | 201 | .. code-block:: console 202 | 203 | $ rio shapes tests/data/shade.tif --bidx 1 --precision 6 > shade.geojson 204 | 205 | The resulting file, uploaded to Mapbox, looks like this: `sgillies.j1ho338j `__. 206 | 207 | Using the ``--mask`` option you can write out the shapes of a dataset's valid 208 | data region. 209 | 210 | .. code-block:: console 211 | 212 | $ rio shapes --mask --precision 6 tests/data/RGB.byte.tif | geojsonio 213 | 214 | See http://bl.ocks.org/anonymous/raw/ef244954b719dba97926/. 215 | 216 | stack 217 | ----- 218 | 219 | New in 0.15. 220 | 221 | The rio-stack command stack a number of bands from one or more input files into 222 | a multiband dataset. Input datasets must be of a kind: same data type, 223 | dimensions, etc. The output is cloned from the first input. By default, 224 | rio-stack will take all bands from each input and write them in same order to 225 | the output. Optionally, bands for each input may be specified using a simple 226 | syntax: 227 | 228 | - ``--bidx N`` takes the Nth band from the input (first band is 1). 229 | - ``--bidx M,N,O`` takes bands M, N, and O. 230 | - ``--bidx M..O`` takes bands M-O, inclusive. 231 | - ``--bidx ..N`` takes all bands up to and including N. 232 | - ``--bidx N..`` takes all bands from N to the end. 233 | 234 | Examples using the Rasterio testing dataset that produce a copy of it. 235 | 236 | .. code-block:: console 237 | 238 | $ rio stack RGB.byte.tif stacked.tif 239 | $ rio stack RGB.byte.tif --bidx 1,2,3 stacked.tif 240 | $ rio stack RGB.byte.tif --bidx 1..3 stacked.tif 241 | $ rio stack RGB.byte.tif --bidx ..2 RGB.byte.tif --bidx 3.. stacked.tif 242 | 243 | transform 244 | --------- 245 | 246 | New in 0.10. 247 | 248 | The transform command reads a JSON array of coordinates, interleaved, and 249 | writes another array of transformed coordinates to stdout. 250 | 251 | To transform a longitude, latitude point (EPSG:4326 is the default) to 252 | another coordinate system with 2 decimal places of output precision, do the 253 | following. 254 | 255 | .. code-block:: console 256 | 257 | $ echo "[-78.0, 23.0]" | rio transform - --dst_crs EPSG:32618 --precision 2 258 | [192457.13, 2546667.68] 259 | 260 | To transform a longitude, latitude bounding box to the coordinate system of 261 | a raster dataset, do the following. 262 | 263 | .. code-block:: console 264 | 265 | $ echo "[-78.0, 23.0, -76.0, 25.0]" | rio transform - --dst_crs tests/data/RGB.byte.tif --precision 2 266 | [192457.13, 2546667.68, 399086.97, 2765319.94] 267 | 268 | Suggestions for other commands are welcome! 269 | -------------------------------------------------------------------------------- /docs/colormaps.rst: -------------------------------------------------------------------------------- 1 | Colormaps 2 | ========= 3 | 4 | Writing colormaps 5 | ----------------- 6 | 7 | Mappings from 8-bit (rasterio.uint8) pixel values to RGBA values can be attached 8 | to bands using the ``write_colormap()`` method. 9 | 10 | .. code-block:: python 11 | 12 | import rasterio 13 | 14 | with rasterio.drivers(): 15 | 16 | with rasterio.open('tests/data/shade.tif') as src: 17 | shade = src.read_band(1) 18 | meta = src.meta 19 | 20 | with rasterio.open('/tmp/colormap.tif', 'w', **meta) as dst: 21 | dst.write_band(1, shade) 22 | dst.write_colormap( 23 | 1, { 24 | 0: (255, 0, 0, 255), 25 | 255: (0, 0, 255, 255) }) 26 | cmap = dst.colormap(1) 27 | # True 28 | assert cmap[0] == (255, 0, 0, 255) 29 | # True 30 | assert cmap[255] == (0, 0, 255, 255) 31 | 32 | subprocess.call(['open', '/tmp/colormap.tif']) 33 | 34 | The program above (on OS X, another viewer is needed with a different OS) 35 | yields the image below: 36 | 37 | .. image:: http://farm8.staticflickr.com/7391/12443115173_80ecca89db_d.jpg 38 | :width: 500 39 | :height: 500 40 | 41 | Reading colormaps 42 | ----------------- 43 | 44 | As shown above, the ``colormap()`` returns a dict holding the colormap for the 45 | given band index. For TIFF format files, the colormap will have 256 items, and 46 | all but two of those would map to (0, 0, 0, 0) in the example above. 47 | 48 | -------------------------------------------------------------------------------- /docs/concurrency.rst: -------------------------------------------------------------------------------- 1 | Concurrent processing 2 | ===================== 3 | 4 | Rasterio affords concurrent processing of raster data. The Python GIL is 5 | released when calling GDAL's ``GDALRasterIO()`` function, which means that 6 | datasets can read and write concurrently with other threads. 7 | 8 | The Numpy library also releases the GIL as much as it can, e.g., in applying 9 | universal functions to arrays, and this makes it possible to distribute 10 | processing of an array across cores of a processor. The Cython function below, 11 | included in Rasterio's ``_example`` module, simulates such a GIL-releasing 12 | raster processing function. 13 | 14 | .. code-block:: python 15 | 16 | import numpy 17 | cimport numpy 18 | 19 | def compute( 20 | unsigned char[:, :, :] input, 21 | unsigned char[:, :, :] output): 22 | # Given input and output uint8 arrays, fakes an CPU-intensive 23 | # computation. 24 | cdef int I, J, K 25 | cdef int i, j, k, l 26 | cdef double val 27 | I = input.shape[0] 28 | J = input.shape[1] 29 | K = input.shape[2] 30 | with nogil: 31 | for i in range(I): 32 | for j in range(J): 33 | for k in range(K): 34 | val = input[i, j, k] 35 | for l in range(2000): 36 | val += 1.0 37 | val -= 2000.0 38 | output[~i, j, k] = val 39 | 40 | 41 | Here is the program in examples/concurrent-cpu-bound.py. 42 | 43 | .. code-block:: python 44 | 45 | """concurrent-cpu-bound.py 46 | 47 | Operate on a raster dataset window-by-window using a ThreadPoolExecutor. 48 | 49 | Simulates a CPU-bound thread situation where multiple threads can improve performance. 50 | 51 | With -j 4, the program returns in about 1/4 the time as with -j 1. 52 | """ 53 | 54 | import concurrent.futures 55 | import multiprocessing 56 | import time 57 | 58 | import numpy 59 | import rasterio 60 | from rasterio._example import compute 61 | 62 | def main(infile, outfile, num_workers=4): 63 | 64 | with rasterio.drivers(): 65 | 66 | # Open the source dataset. 67 | with rasterio.open(infile) as src: 68 | 69 | # Create a destination dataset based on source params. 70 | # The destination will be tiled, and we'll "process" the tiles 71 | # concurrently. 72 | meta = src.meta 73 | del meta['transform'] 74 | meta.update(affine=src.affine) 75 | meta.update(blockxsize=256, blockysize=256, tiled='yes') 76 | with rasterio.open(outfile, 'w', **meta) as dst: 77 | 78 | # Define a generator for data, window pairs. 79 | # We use the new read() method here to a 3D array with all 80 | # bands, but could also use read_band(). 81 | def jobs(): 82 | for ij, window in dst.block_windows(): 83 | data = src.read(window=window) 84 | result = numpy.zeros(data.shape, dtype=data.dtype) 85 | yield data, result, window 86 | 87 | # Submit the jobs to the thread pool executor. 88 | with concurrent.futures.ThreadPoolExecutor( 89 | max_workers=num_workers) as executor: 90 | 91 | # Map the futures returned from executor.submit() 92 | # to their destination windows. 93 | # 94 | # The _example.compute function modifies no Python 95 | # objects and releases the GIL. It can execute 96 | # concurrently. 97 | future_to_window = { 98 | executor.submit(compute, data, res): (res, window) 99 | for data, res, window in jobs()} 100 | 101 | # As the processing jobs are completed, get the 102 | # results and write the data to the appropriate 103 | # destination window. 104 | for future in concurrent.futures.as_completed( 105 | future_to_window): 106 | 107 | result, window = future_to_window[future] 108 | 109 | # Since there's no multiband write() method yet in 110 | # Rasterio, we use write_band for each part of the 111 | # 3D data array. 112 | for i, arr in enumerate(result, 1): 113 | dst.write_band(i, arr, window=window) 114 | 115 | 116 | if __name__ == '__main__': 117 | 118 | import argparse 119 | 120 | parser = argparse.ArgumentParser( 121 | description="Concurrent raster processing demo") 122 | parser.add_argument( 123 | 'input', 124 | metavar='INPUT', 125 | help="Input file name") 126 | parser.add_argument( 127 | 'output', 128 | metavar='OUTPUT', 129 | help="Output file name") 130 | parser.add_argument( 131 | '-j', 132 | metavar='NUM_JOBS', 133 | type=int, 134 | default=multiprocessing.cpu_count(), 135 | help="Number of concurrent jobs") 136 | args = parser.parse_args() 137 | 138 | main(args.input, args.output, args.j) 139 | 140 | The code above simulates a fairly CPU-intensive process that runs faster when 141 | spread over multiple cores using the ``ThreadPoolExecutor`` from Python 3's 142 | ``concurrent.futures`` module. Compared to the case of one concurrent job 143 | (``-j 1``) 144 | 145 | .. code-block:: console 146 | 147 | $ time python examples/concurrent-cpu-bound.py tests/data/RGB.byte.tif /tmp/threads.tif -j 1 148 | 149 | real 0m3.474s 150 | user 0m3.426s 151 | sys 0m0.043s 152 | 153 | we get an almost 3x speed up with four concurrent jobs. 154 | 155 | .. code-block:: console 156 | 157 | $ time python examples/concurrent-cpu-bound.py tests/data/RGB.byte.tif /tmp/threads.tif -j 4 158 | 159 | real 0m1.335s 160 | user 0m3.400s 161 | sys 0m0.043s 162 | 163 | -------------------------------------------------------------------------------- /docs/datasets.rst: -------------------------------------------------------------------------------- 1 | Datasets and ndarrays 2 | ===================== 3 | 4 | Dataset objects provide read, read-write, and write access to raster data files 5 | and are obtained by calling ``rasterio.open()``. That function mimics Python's 6 | built-in ``open()`` and the dataset objects it returns mimic Python ``file`` 7 | objects. 8 | 9 | .. code-block:: pycon 10 | 11 | >>> import rasterio 12 | >>> dataset = rasterio.open('tests/data/RGB.byte.tif') 13 | >>> dataset 14 | 15 | >>> dataset.name 16 | 'tests/data/RGB.byte.tif' 17 | >>> dataset.mode 18 | r 19 | >>> dataset.closed 20 | False 21 | 22 | If you attempt to access a nonexistent path, ``rasterio.open()`` does the same 23 | thing as ``open()``, raising an exception immediately. 24 | 25 | .. code-block:: pycon 26 | 27 | >>> open('/lol/wut.tif') 28 | Traceback (most recent call last): 29 | File "", line 1, in 30 | IOError: [Errno 2] No such file or directory: '/lol/wut.tif' 31 | >>> rasterio.open('/lol/wut.tif') 32 | Traceback (most recent call last): 33 | File "", line 1, in 34 | IOError: no such file or directory: '/lol/wut.tif' 35 | 36 | Attributes 37 | ---------- 38 | 39 | In addition to the file-like attributes shown above, a dataset has a number 40 | of other read-only attributes that help explain its role in spatial information 41 | systems. The ``driver`` attribute gives you the name of the GDAL format 42 | driver used. The ``height`` and ``width`` are the number of rows and columns of 43 | the raster dataset and ``shape`` is a ``height, width`` tuple as used by 44 | Numpy. The ``count`` attribute tells you the number of bands in the dataset. 45 | 46 | .. code-block:: pycon 47 | 48 | >>> dataset.driver 49 | u'GTiff' 50 | >>> dataset.height, dataset.width 51 | (718, 791) 52 | >>> dataset.shape 53 | (718, 791) 54 | >>> dataset.count 55 | 3 56 | 57 | What makes geospatial raster datasets different from other raster files is 58 | that their pixels map to regions of the Earth. A dataset has a coordinate 59 | reference system and an affine transformation matrix that maps pixel 60 | coordinates to coordinates in that reference system. 61 | 62 | .. code-block:: pycon 63 | 64 | >>> dataset.crs 65 | {u'units': u'm', u'no_defs': True, u'ellps': u'WGS84', u'proj': u'utm', u'zone': 18} 66 | >>> dataset.affine 67 | Affine(300.0379266750948, 0.0, 101985.0, 68 | 0.0, -300.041782729805, 2826915.0) 69 | 70 | To get the ``x, y`` world coordinates for the upper left corner of any pixel, 71 | take the product of the affine transformation matrix and the tuple ``(col, 72 | row)``. 73 | 74 | .. code-block:: pycon 75 | 76 | >>> col, row = 0, 0 77 | >>> src.affine * (col, row) 78 | (101985.0, 2826915.0) 79 | >>> col, row = src.width, src.height 80 | >>> src.affine * (col, row) 81 | (339315.0, 2611485.0) 82 | 83 | Reading data 84 | ------------ 85 | 86 | Datasets generally have one or more bands (or layers). Following the GDAL 87 | convention, these are indexed starting with the number 1. The first band of 88 | a file can be read like this: 89 | 90 | .. code-block:: pycon 91 | 92 | >>> dataset.read_band(1) 93 | array([[0, 0, 0, ..., 0, 0, 0], 94 | [0, 0, 0, ..., 0, 0, 0], 95 | [0, 0, 0, ..., 0, 0, 0], 96 | ..., 97 | [0, 0, 0, ..., 0, 0, 0], 98 | [0, 0, 0, ..., 0, 0, 0], 99 | [0, 0, 0, ..., 0, 0, 0]], dtype=uint8) 100 | 101 | The returned object is a 2-dimensional Numpy ndarray. The representation of 102 | that array at the Python prompt is just a summary; the GeoTIFF file that 103 | Rasterio uses for testing has 0 values in the corners, but has nonzero values 104 | elsewhere. 105 | 106 | .. code-block:: 107 | 108 | >>> from matplotlib import pyplot 109 | >>> pyplot.imshow(dataset.read_band(1), cmap='pink') 110 | 111 | >>> pyplot.show() 112 | 113 | .. image:: http://farm6.staticflickr.com/5032/13938576006_b99b23271b_o_d.png 114 | 115 | The indexes, Numpy data types, and nodata values of all a dataset's bands can 116 | be had from its ``indexes``, ``dtypes``, and ``nodatavals`` attributes. 117 | 118 | .. code-block:: pycon 119 | 120 | >>> for i, dtype, ndval in zip(src.indexes, src.dtypes, src.nodatavals): 121 | ... print i, dtype, nodataval 122 | ... 123 | 1 0.0 124 | 2 0.0 125 | 3 0.0 126 | 127 | To close a dataset, call its ``close()`` method. 128 | 129 | .. code-block:: pycon 130 | 131 | >>> dataset.close() 132 | >>> dataset 133 | 134 | 135 | After it's closed, data can no longer be read. 136 | 137 | .. code-block:: pycon 138 | 139 | >>> dataset.read_band(1) 140 | Traceback (most recent call last): 141 | File "", line 1, in 142 | ValueError: can't read closed raster file 143 | 144 | This is the same behavior as Python's ``file``. 145 | 146 | .. code-block:: pycon 147 | 148 | >>> f = open('README.rst') 149 | >>> f.close() 150 | >>> f.read() 151 | Traceback (most recent call last): 152 | File "", line 1, in 153 | ValueError: I/O operation on closed file 154 | 155 | As Python ``file`` objects can, Rasterio datasets can manage the entry into 156 | and exit from runtime contexts created using a ``with`` statement. This 157 | ensures that files are closed no matter what exceptions may be raised within 158 | the the block. 159 | 160 | .. code-block:: pycon 161 | 162 | >>> with rasterio.open('tests/data/RGB.byte.tif', 'r') as one: 163 | ... with rasterio.open('tests/data/RGB.byte.tif', 'r') as two: 164 | print two 165 | ... print one 166 | ... print two 167 | >>> print one 168 | 169 | 170 | 171 | 172 | 173 | Writing data 174 | ------------ 175 | 176 | Opening a file in writing mode is a little more complicated than opening 177 | a text file in Python. The dimensions of the raster dataset, the 178 | data types, and the specific format must be specified. 179 | 180 | .. code-block:: pycon 181 | 182 | >>> with rasterio.oepn 183 | 184 | Writing data mostly works as with a Python file. There are a few format- 185 | specific differences. TODO: details. 186 | 187 | -------------------------------------------------------------------------------- /docs/features.rst: -------------------------------------------------------------------------------- 1 | Features 2 | ======== 3 | 4 | Rasterio's ``features`` module provides functions to extract shapes of raster 5 | features and to create new features by "burning" shapes into rasters: 6 | ``shapes()`` and ``rasterize()``. These functions expose GDAL functions in 7 | a very general way, using iterators over GeoJSON-like Python objects instead of 8 | GIS layers. 9 | 10 | Extracting shapes of raster features 11 | ------------------------------------ 12 | 13 | Consider the Python logo. 14 | 15 | .. image:: https://farm8.staticflickr.com/7018/13547682814_f2e459f7a5_o_d.png 16 | 17 | The shapes of the foreground features can be extracted like this: 18 | 19 | .. code-block:: python 20 | 21 | import pprint 22 | import rasterio 23 | from rasterio import features 24 | 25 | with rasterio.open('13547682814_f2e459f7a5_o_d.png') as src: 26 | blue = src.read_band(3) 27 | 28 | mask = blue != 255 29 | shapes = features.shapes(blue, mask=mask) 30 | pprint.pprint(next(shapes)) 31 | 32 | # Output 33 | # pprint.pprint(next(shapes)) 34 | # ({'coordinates': [[(71.0, 6.0), 35 | # (71.0, 7.0), 36 | # (72.0, 7.0), 37 | # (72.0, 6.0), 38 | # (71.0, 6.0)]], 39 | # 'type': 'Polygon'}, 40 | # 253) 41 | 42 | The shapes iterator yields ``geometry, value`` pairs. The second item is the 43 | value of the raster feature corresponding to the shape and the first is its 44 | geometry. The coordinates of the geometries in this case are in pixel units 45 | with origin at the upper left of the image. If the source dataset was 46 | georeferenced, you would get similarly georeferenced geometries like this: 47 | 48 | .. code-block:: python 49 | 50 | shapes = features.shapes(blue, mask=mask, transform=src.transform) 51 | 52 | Burning shapes into a raster 53 | ---------------------------- 54 | 55 | To go the other direction, use ``rasterize()`` to burn values into the pixels 56 | intersecting with geometries. 57 | 58 | .. code-block:: python 59 | 60 | image = features.rasterize( 61 | ((g, 255) for g, v in shapes), 62 | out_shape=src.shape) 63 | 64 | Again, to burn in georeferenced shapes, pass an appropriate transform for the 65 | image to be created. 66 | 67 | .. code-block:: python 68 | 69 | image = features.rasterize( 70 | ((g, 255) for g, v in shapes), 71 | out_shape=src.shape, 72 | transform=src.transform) 73 | 74 | The values for the input shapes are replaced with ``255`` in a generator 75 | expression. The resulting image, written to disk like this, 76 | 77 | .. code-block:: python 78 | 79 | with rasterio.open( 80 | '/tmp/rasterized-results.tif', 'w', 81 | driver='GTiff', 82 | dtype=rasterio.uint8, 83 | count=1, 84 | width=src.width, 85 | height=src.height) as dst: 86 | dst.write_band(1, image) 87 | 88 | has a black background and white foreground features. 89 | 90 | .. image:: https://farm4.staticflickr.com/3728/13547425455_79bdb5eaeb_o_d.png 91 | 92 | -------------------------------------------------------------------------------- /docs/georeferencing.rst: -------------------------------------------------------------------------------- 1 | Georeferencing 2 | ============== 3 | 4 | There are two parts to the georeferencing of raster datasets: the definition 5 | of the local, regional, or global system in which a raster's pixels are 6 | located; and the parameters by which pixel coordinates are transformed into 7 | coordinates in that system. 8 | 9 | Coordinate Reference System 10 | --------------------------- 11 | 12 | The coordinate reference system of a dataset is accessed from its ``crs`` 13 | attribute. Type ``rio insp tests/data/RGB.byte.tif`` from the 14 | Rasterio distribution root to see. 15 | 16 | .. code-block:: pycon 17 | 18 | Rasterio 0.9 Interactive Inspector (Python 3.4.1) 19 | Type "src.meta", "src.read_band(1)", or "help(src)" for more information. 20 | >>> src 21 | 22 | >>> src.crs 23 | {'init': 'epsg:32618'} 24 | 25 | Rasterio follows pyproj and uses PROJ.4 syntax in dict form as its native 26 | CRS syntax. If you want a WKT representation of the CRS, see the ``crs_wkt`` 27 | attribute. 28 | 29 | .. code-block:: pycon 30 | 31 | >>> src.crs_wkt 32 | 'PROJCS["UTM Zone 18, Northern Hemisphere",GEOGCS["Unknown datum based upon the WGS 84 ellipsoid",DATUM["Not_specified_based_on_WGS_84_spheroid",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-75],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32618"]]' 33 | 34 | When opening a new file for writing, you may also use a CRS string as an 35 | argument. 36 | 37 | .. code-block:: pycon 38 | 39 | >>> with rasterio.open('/tmp/foo.tif', 'w', crs='EPSG:3857', ...) as dst: 40 | ... # write data to this Web Mercator projection dataset. 41 | 42 | Coordinate Transformation 43 | ------------------------- 44 | 45 | A dataset's pixel coordinate system has its orgin at the "upper left" (imagine 46 | it displayed on your screen). Column index increases to the right, and row 47 | index increases downward. The mapping of these coordinates to "world" 48 | coordinates in the dataset's reference system is done with an affine 49 | transformation matrix. 50 | 51 | .. code-block:: pycon 52 | 53 | >>> src.affine 54 | Affine(300.0379266750948, 0.0, 101985.0, 55 | 0.0, -300.041782729805, 2826915.0) 56 | 57 | The ``Affine`` object is a named tuple with elements ``a, b, c, d, e, f`` 58 | corresponding to the elements in the matrix equation below, in which 59 | a pixel's image coordinates are ``x, y`` and its world coordinates are 60 | ``x', y'``.:: 61 | 62 | | x' | | a b c | | x | 63 | | y' | = | d e f | | y | 64 | | 1 | | 0 0 1 | | 1 | 65 | 66 | The ``Affine`` class has a number of useful properties and methods 67 | described at https://github.com/sgillies/affine. 68 | 69 | The ``affine`` attribute is new. Previous versions of Rasterio had only a 70 | ``transform`` attribute. As explained in the warning below, Rasterio is in 71 | a transitional phase. 72 | 73 | .. code-block:: pycon 74 | 75 | >>> src.transform 76 | /usr/local/Cellar/python3/3.4.1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/code.py:90: FutureWarning: The value of this property will change in version 1.0. Please see https://github.com/mapbox/rasterio/issues/86 for details. 77 | [101985.0, 300.0379266750948, 0.0, 2826915.0, 0.0, -300.041782729805] 78 | 79 | In Rasterio 1.0, the value of a ``transform`` attribute will be an instance 80 | of ``Affine`` and the ``affine`` attribute will remain as an alias. 81 | 82 | -------------------------------------------------------------------------------- /docs/masks.rst: -------------------------------------------------------------------------------- 1 | Masks 2 | ===== 3 | 4 | Reading masks 5 | ------------- 6 | 7 | There are a few different ways for raster datasets to carry valid data masks. 8 | Rasterio subscribes to GDAL's abstract mask band interface, so although the 9 | module's main test dataset has no mask band, GDAL will build one based upon 10 | its declared nodata value of 0. 11 | 12 | .. code-block:: python 13 | 14 | import rasterio 15 | 16 | with rasterio.open('tests/data/RGB.byte.tif') as src: 17 | mask = src.read_mask() 18 | print mask.any() 19 | count = mask.shape[0] * mask.shape[1] 20 | print float((mask > 0).sum())/count 21 | print float((mask == 0).sum())/count 22 | 23 | Some of the elements of the mask evaluate to ``True``, meaning that there is some 24 | valid data. Just over 2/3 of the dataset's pixels (use of sum being a neat trick for 25 | computing the number of pixels in a selection) have valid data. 26 | 27 | .. code-block:: console 28 | 29 | True 30 | 0.673974976142 31 | 0.326025023858 32 | 33 | Writing masks 34 | ------------- 35 | 36 | Writing a mask is just as straightforward: pass an ndarray with ``True`` (or values 37 | that evaluate to ``True`` to indicate valid data and ``False`` to indicate no data 38 | to ``write_mask()``. 39 | 40 | .. code-block:: python 41 | 42 | import os 43 | import shutil 44 | import tempfile 45 | 46 | import numpy 47 | import rasterio 48 | 49 | tempdir = tempfile.mkdtemp() 50 | 51 | with rasterio.open( 52 | os.path.join(tempdir, 'example.tif'), 53 | 'w', 54 | driver='GTiff', 55 | count=1, 56 | dtype=rasterio.uint8, 57 | width=10, 58 | height=10) as dst: 59 | 60 | dst.write_band(1, numpy.ones(dst.shape, dtype=rasterio.uint8)) 61 | 62 | mask = numpy.zeros(dst.shape, rasterio.uint8) 63 | mask[5:,5:] = 255 64 | dst.write_mask(mask) 65 | 66 | print os.listdir(tempdir) 67 | shutil.rmtree(tempdir) 68 | 69 | The code above masks out all of the file except the lower right quadrant and 70 | writes a file with a sidecar TIFF to hold the mask. 71 | 72 | .. code-block:: console 73 | 74 | ['example.tif', 'example.tif.msk'] 75 | 76 | To use an internal TIFF mask, use the ``drivers()`` option shown below: 77 | 78 | .. code-block:: python 79 | 80 | tempdir = tempfile.mkdtemp() 81 | tiffname = os.path.join(tempdir, 'example.tif') 82 | 83 | with rasterio.drivers(GDAL_TIFF_INTERNAL_MASK=True): 84 | 85 | with rasterio.open( 86 | tiffname, 87 | 'w', 88 | driver='GTiff', 89 | count=1, 90 | dtype=rasterio.uint8, 91 | width=10, 92 | height=10) as dst: 93 | 94 | dst.write_band(1, numpy.ones(dst.shape, dtype=rasterio.uint8)) 95 | 96 | mask = numpy.zeros(dst.shape, rasterio.uint8) 97 | mask[5:,5:] = 255 98 | dst.write_mask(mask) 99 | 100 | print os.listdir(tempdir) 101 | print subprocess.check_output(['gdalinfo', tiffname]) 102 | 103 | The output: 104 | 105 | .. code-block:: console 106 | 107 | ['example.tif'] 108 | Driver: GTiff/GeoTIFF 109 | Files: /var/folders/jh/w0mgrfqd1t37n0bcqzt16bnc0000gn/T/tmpcnGV_r/example.tif 110 | Size is 10, 10 111 | Coordinate System is `' 112 | Image Structure Metadata: 113 | INTERLEAVE=BAND 114 | Corner Coordinates: 115 | Upper Left ( 0.0, 0.0) 116 | Lower Left ( 0.0, 10.0) 117 | Upper Right ( 10.0, 0.0) 118 | Lower Right ( 10.0, 10.0) 119 | Center ( 5.0, 5.0) 120 | Band 1 Block=10x10 Type=Byte, ColorInterp=Gray 121 | Mask Flags: PER_DATASET 122 | 123 | -------------------------------------------------------------------------------- /docs/options.rst: -------------------------------------------------------------------------------- 1 | Options 2 | ======= 3 | 4 | GDAL's format drivers have many [configuration 5 | options](https://trac.osgeo.org/gdal/wiki/ConfigOptions) The way to set options 6 | for rasterio is via ``rasterio.drivers()``. Options set on entering are deleted 7 | on exit. 8 | 9 | .. code-block:: python 10 | 11 | import rasterio 12 | 13 | with rasterio.drivers(GDAL_TIFF_INTERNAL_MASK=True): 14 | # GeoTIFFs written here will have internal masks, not the 15 | # .msk sidecars. 16 | ... 17 | 18 | # Option is gone and the default (False) returns. 19 | 20 | Use native Python forms (``True`` and ``False``) for boolean options. Rasterio 21 | will convert them GDAL's internal forms. 22 | 23 | -------------------------------------------------------------------------------- /docs/reproject.rst: -------------------------------------------------------------------------------- 1 | Reprojection 2 | ============ 3 | 4 | Rasterio can map the pixels of a destination raster with an associated 5 | coordinate reference system and transform to the pixels of a source image with 6 | a different coordinate reference system and transform. This process is known as 7 | reprojection. 8 | 9 | Rasterio's ``rasterio.warp.reproject()`` is a very geospatial-specific analog 10 | to SciPy's ``scipy.ndimage.interpolation.geometric_transform()`` [1]_. 11 | 12 | The code below reprojects between two arrays, using no pre-existing GIS 13 | datasets. ``rasterio.warp.reproject()`` has two positional arguments: source 14 | and destination. The remaining keyword arguments parameterize the reprojection 15 | transform. 16 | 17 | .. code-block:: python 18 | 19 | import numpy 20 | import rasterio 21 | from rasterio import Affine as A 22 | from rasterio.warp import reproject, RESAMPLING 23 | 24 | with rasterio.drivers(): 25 | 26 | # As source: a 512 x 512 raster centered on 0 degrees E and 0 27 | # degrees N, each pixel covering 15". 28 | rows, cols = src_shape = (512, 512) 29 | d = 1.0/240 # decimal degrees per pixel 30 | # The following is equivalent to 31 | # A(d, 0, -cols*d/2, 0, -d, rows*d/2). 32 | src_transform = A.translation(-cols*d/2, rows*d/2) * A.scale(d, -d) 33 | src_crs = {'init': 'EPSG:4326'} 34 | source = numpy.ones(src_shape, numpy.uint8)*255 35 | 36 | # Destination: a 1024 x 1024 dataset in Web Mercator (EPSG:3857) 37 | # with origin at 0.0, 0.0. 38 | dst_shape = (1024, 1024) 39 | dst_transform = [-237481.5, 425.0, 0.0, 237536.4, 0.0, -425.0] 40 | dst_crs = {'init': 'EPSG:3857'} 41 | destination = numpy.zeros(dst_shape, numpy.uint8) 42 | 43 | reproject( 44 | source, 45 | destination, 46 | src_transform=src_transform, 47 | src_crs=src_crs, 48 | dst_transform=dst_transform, 49 | dst_crs=dst_crs, 50 | resampling=RESAMPLING.nearest) 51 | 52 | # Assert that the destination is only partly filled. 53 | assert destination.any() 54 | assert not destination.all() 55 | 56 | 57 | See `examples/reproject.py `__ for code that writes the destination array to a GeoTIFF file. I've 58 | uploaded the resulting file to a Mapbox map to demonstrate that the reprojection is 59 | correct: https://a.tiles.mapbox.com/v3/sgillies.hfek2oko/page.html?secure=1#6/0.000/0.033. 60 | 61 | Reprojecting a GeoTIFF dataset 62 | ------------------------------ 63 | 64 | Here's a more real-world example of using ``reproject()`` to make an output dataset zoomed out by a factor of 2. 65 | Methods of the ``rasterio.Affine`` class help us generate the output dataset's transform matrix and, thereby, its 66 | spatial extent. 67 | 68 | .. code-block:: python 69 | 70 | import numpy 71 | import rasterio 72 | from rasterio import Affine as A 73 | from rasterio.warp import reproject, RESAMPLING 74 | 75 | with rasterio.open('rasterio/tests/data/RGB.byte.tif') as src: 76 | src_transform = src.affine 77 | 78 | # Zoom out by a factor of 2 from the center of the source 79 | # dataset. The destination transform is the product of the 80 | # source transform, a translation down and to the right, and 81 | # a scaling. 82 | dst_transform = src_transform*A.translation( 83 | -src.width/2.0, -src.height/2.0)*A.scale(2.0) 84 | 85 | data = src.read() 86 | 87 | kwargs = src.meta 88 | kwargs['transform'] = dst_transform 89 | 90 | with rasterio.open('/tmp/zoomed-out.tif', 'w', **kwargs) as dst: 91 | 92 | for i, band in enumerate(data, 1): 93 | dest = numpy.zeros_like(band) 94 | 95 | reproject( 96 | band, 97 | dest, 98 | src_transform=src_transform, 99 | src_crs=src.crs, 100 | dst_transform=dst_transform, 101 | dst_crs=src.crs, 102 | resampling=RESAMPLING.nearest) 103 | 104 | dst.write_band(i, dest) 105 | 106 | .. image:: https://farm8.staticflickr.com/7399/16390100651_54f01b8601_b_d.jpg) 107 | 108 | References 109 | ---------- 110 | 111 | .. [1] http://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.interpolation.geometric_transform.html#scipy.ndimage.interpolation.geometric_transform 112 | 113 | -------------------------------------------------------------------------------- /docs/tags.rst: -------------------------------------------------------------------------------- 1 | Tagging datasets and bands 2 | ========================== 3 | 4 | GDAL's `data model `__ includes 5 | collections of key, value pairs for major classes. In that model, these are 6 | "metadata", but since they don't have to be just for metadata, these key, value 7 | pairs are called "tags" in rasterio. 8 | 9 | Reading tags 10 | ------------ 11 | 12 | I'm going to use the rasterio interactive inspector in these examples below. 13 | 14 | .. code-block:: console 15 | 16 | $ rasterio.insp tests/data/RGB.byte.tif 17 | Rasterio 0.6 Interactive Inspector (Python 2.7.5) 18 | Type "src.name", "src.read_band(1)", or "help(src)" for more information. 19 | >>> 20 | 21 | Tags belong to namespaces. To get a copy of a dataset's tags from the default 22 | namespace, just call ``tags()`` with no arguments. 23 | 24 | .. code-block:: pycon 25 | 26 | >>>src.tags() 27 | {u'AREA_OR_POINT': u'Area'} 28 | 29 | A dataset's bands may have tags, too. Here are the tags from the default namespace 30 | for the first band, accessed using the positional band index argument of ``tags()``. 31 | 32 | .. code-block:: pycon 33 | 34 | >>> src.tags(1) 35 | {u'STATISTICS_MEAN': u'29.947726688477', u'STATISTICS_MINIMUM': u'0', u'STATISTICS_MAXIMUM': u'255', u'STATISTICS_STDDEV': u'52.340921626611'} 36 | 37 | These are the tags that came with the sample data I'm using to test rasterio. In 38 | practice, maintaining stats in the tags can be unreliable as there is no automatic 39 | update of the tags when the band's image data changes. 40 | 41 | The 3 standard, non-default GDAL tag namespaces are 'SUBDATASETS', 'IMAGE_STRUCTURE', 42 | and 'RPC'. You can get the tags from these namespaces using the `ns` keyword of 43 | ``tags()``. 44 | 45 | .. code-block:: pycon 46 | 47 | >>> src.tags(ns='IMAGE_STRUCTURE') 48 | {u'INTERLEAVE': u'PIXEL'} 49 | >>> src.tags(ns='SUBDATASETS') 50 | {} 51 | >>> src.tags(ns='RPC') 52 | {} 53 | 54 | Writing tags 55 | ------------ 56 | 57 | You can add new tags to a dataset or band, in the default or another namespace, 58 | using the ``update_tags()`` method. Unicode tag values, too, at least for TIFF 59 | files. 60 | 61 | .. code-block:: python 62 | 63 | import rasterio 64 | 65 | with rasterio.open( 66 | '/tmp/test.tif', 67 | 'w', 68 | driver='GTiff', 69 | count=1, 70 | dtype=rasterio.uint8, 71 | width=10, 72 | height=10) as dst: 73 | 74 | dst.update_tags(a='1', b='2') 75 | dst.update_tags(1, c=3) 76 | with pytest.raises(ValueError): 77 | dst.update_tags(4, d=4) 78 | 79 | # True 80 | assert dst.tags() == {'a': '1', 'b': '2'} 81 | # True 82 | assert dst.tags(1) == {'c': '3' } 83 | 84 | dst.update_tags(ns='rasterio_testing', rus=u'другая строка') 85 | # True 86 | assert dst.tags(ns='rasterio_testing') == {'rus': u'другая строка'} 87 | 88 | As with image data, tags aren't written to the file on disk until the dataset 89 | is closed. 90 | 91 | -------------------------------------------------------------------------------- /docs/windowed-rw.rst: -------------------------------------------------------------------------------- 1 | Windowed reading and writing 2 | ============================ 3 | 4 | Beginning in rasterio 0.3, you can read and write "windows" of raster files. 5 | This feature allows you to operate on rasters that are larger than your 6 | computers RAM or process chunks of very large rasters in parallel. 7 | 8 | Windows 9 | ------- 10 | 11 | A window is a view onto a rectangular subset of a raster dataset and is 12 | described in rasterio by a pair of range tuples. 13 | 14 | .. code-block:: python 15 | 16 | ((row_start, row_stop), (col_start, col_stop)) 17 | 18 | The first pair contains the indexes of the raster rows at which the window 19 | starts and stops. The second contains the indexes of the raster columns at 20 | which the window starts and stops. For example, 21 | 22 | .. code-block:: python 23 | 24 | ((0, 4), (0, 4)) 25 | 26 | Specifies a 4 x 4 window at the upper left corner of a raster dataset and 27 | 28 | .. code-block:: python 29 | 30 | ((10, 20), (10, 20)) 31 | 32 | specifies a 10 x 10 window with origin at row 10 and column 10. Use of `None` 33 | for a range value indicates either 0 (in the start position) or the full raster 34 | height or width (in the stop position). The window tuple 35 | 36 | .. code-block:: python 37 | 38 | ((None, 4), (None, 4)) 39 | 40 | also specifies a 4 x 4 window at the upper left corner of the raster and 41 | 42 | .. code-block:: python 43 | 44 | ((4, None), (4, None)) 45 | 46 | specifies a rectangular subset with upper left at row 4, column 4 and 47 | extending to the lower right corner of the raster dataset. 48 | 49 | Using window tuples should feel like using Python's range() and slice() 50 | functions. Range() selects a range of numbers from the sequence of all integers 51 | and slice() produces a object that can be used in slicing expressions. 52 | 53 | .. code-block:: pycon 54 | 55 | >>> range(10, 20) 56 | [10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 57 | >>> range(10, 20)[slice(4, None)] 58 | [14, 15, 16, 17, 18, 19] 59 | 60 | Reading 61 | ------- 62 | 63 | Here is an example of reading a 100 row x 100 column subset of the rasterio 64 | test file. 65 | 66 | .. code-block:: pycon 67 | 68 | >>> import rasterio 69 | >>> with rasterio.open('tests/data/RGB.byte.tif') as src: 70 | ... w = src.read_band(1, window=((0, 100), (0, 100))) 71 | ... 72 | >>> print(w.shape) 73 | (100, 100) 74 | 75 | Writing 76 | ------- 77 | 78 | Writing works similarly. The following creates a blank 500 column x 300 row 79 | GeoTIFF and plops 37500 pixels with value 127 into a window 30 pixels down from 80 | and 50 pixels to the right of the upper left corner of the GeoTIFF. 81 | 82 | .. code-block:: python 83 | 84 | image = numpy.ones((150, 250), dtype=rasterio.ubyte) * 127 85 | 86 | with rasterio.open( 87 | '/tmp/example.tif', 'w', 88 | driver='GTiff', width=500, height=300, count=1, 89 | dtype=image.dtype) as dst: 90 | dst.write_band(1, image, window=((30, 180), (50, 300))) 91 | 92 | The result: 93 | 94 | .. image:: http://farm6.staticflickr.com/5503/11378078386_cbe2fde02e_o_d.png 95 | :width: 500 96 | :height: 300 97 | 98 | Decimation 99 | ---------- 100 | 101 | If the write window is smaller than the data, the data will be decimated. 102 | Below, the window is scaled to one third of the source image. 103 | 104 | .. code-block:: python 105 | 106 | with rasterio.open('tests/data/RGB.byte.tif') as src: 107 | b, g, r = (src.read_band(k) for k in (1, 2, 3)) 108 | 109 | write_window = (30, 269), (50, 313) 110 | 111 | with rasterio.open( 112 | '/tmp/example.tif', 'w', 113 | driver='GTiff', width=500, height=300, count=3, 114 | dtype=r.dtype) as dst: 115 | for k, arr in [(1, b), (2, g), (3, r)]: 116 | dst.write_band(k, arr, window=write_window) 117 | 118 | And the result: 119 | 120 | .. image:: http://farm4.staticflickr.com/3804/11378361126_c034743079_o_d.png 121 | :width: 500 122 | :height: 300 123 | 124 | Advanced windows 125 | ---------------- 126 | 127 | Since windows are like slices, you can also use negative numbers in rasterio 128 | windows. 129 | 130 | .. code-block:: python 131 | 132 | ((-4, None), (-4, None)) 133 | 134 | specifies a 4 x 4 rectangular subset with upper left at 4 rows to the left of 135 | and 4 columns above the lower right corner of the dataset and extending to the 136 | lower right corner of the dataset. 137 | 138 | Below is an example of reading a raster subset and then writing it into a 139 | larger subset that is defined relative to the lower right corner of the 140 | destination dataset. 141 | 142 | .. code-block:: python 143 | 144 | read_window = (350, 410), (350, 450) 145 | 146 | with rasterio.open('tests/data/RGB.byte.tif') as src: 147 | b, g, r = (src.read_band(k, window=read_window) for k in (1, 2, 3)) 148 | 149 | write_window = (-240, None), (-400, None) 150 | 151 | with rasterio.open( 152 | '/tmp/example2.tif', 'w', 153 | driver='GTiff', width=500, height=300, count=3, 154 | dtype=r.dtype) as dst: 155 | for k, arr in [(1, b), (2, g), (3, r)]: 156 | dst.write_band(k, arr, window=write_window) 157 | 158 | This example also demonstrates decimation. 159 | 160 | .. image:: http://farm3.staticflickr.com/2827/11378772013_c8ab540f21_o_d.png 161 | :width: 500 162 | :height: 300 163 | 164 | Blocks 165 | ------ 166 | 167 | Raster datasets are generally composed of multiple blocks of data and 168 | windowed reads and writes are most efficient when the windows match the 169 | dataset's own block structure. When a file is opened to read, the shape 170 | of blocks for any band can be had from the block_shapes property. 171 | 172 | .. code-block:: pycon 173 | 174 | >>> with rasterio.open('tests/data/RGB.byte.tif') as src: 175 | ... for i, shape in enumerate(src.block_shapes, 1): 176 | ... print(i, shape) 177 | ... 178 | (1, (3, 791)) 179 | (2, (3, 791)) 180 | (3, (3, 791)) 181 | 182 | 183 | The block windows themselves can be had from the block_windows function. 184 | 185 | .. code-block:: pycon 186 | 187 | >>> with rasterio.open('tests/data/RGB.byte.tif') as src: 188 | ... for ji, window in src.block_windows(1): 189 | ... print(ji, window) 190 | ... 191 | ((0, 0), ((0, 3), (0, 791))) 192 | ((1, 0), ((3, 6), (0, 791))) 193 | ... 194 | 195 | This function returns an iterator that yields a pair of values. The second is 196 | a window tuple that can be used in calls to read_band or write_band. The first 197 | is the pair of row and column indexes of this block within all blocks of the 198 | dataset. 199 | 200 | You may read windows of data from a file block-by-block like this. 201 | 202 | .. code-block:: pycon 203 | 204 | >>> with rasterio.open('tests/data/RGB.byte.tif') as src: 205 | ... for ji, window in src.block_windows(1): 206 | ... r = src.read_band(1, window=window) 207 | ... print(r.shape) 208 | ... break 209 | ... 210 | (3, 791) 211 | 212 | Well-bred files have identically blocked bands, but GDAL allows otherwise and 213 | it's a good idea to test this assumption in your code. 214 | 215 | .. code-block:: pycon 216 | 217 | >>> with rasterio.open('tests/data/RGB.byte.tif') as src: 218 | ... assert len(set(src.block_shapes)) == 1 219 | ... for ji, window in src.block_windows(1): 220 | ... b, g, r = (src.read_band(k, window=window) for k in (1, 2, 3)) 221 | ... print(ji, r.shape, g.shape, b.shape) 222 | ... break 223 | ... 224 | ((0, 0), (3, 791), (3, 791), (3, 791)) 225 | 226 | The block_shapes property is a band-ordered list of block shapes and 227 | `set(src.block_shapes)` gives you the set of unique shapes. Asserting that 228 | there is only one item in the set is effectively the same as asserting that all 229 | bands have the same block structure. If they do, you can use the same windows 230 | for each. 231 | 232 | -------------------------------------------------------------------------------- /examples/async-rasterio.py: -------------------------------------------------------------------------------- 1 | """async-rasterio.py 2 | 3 | Operate on a raster dataset window-by-window using asyncio's event loop 4 | and thread executor. 5 | 6 | Simulates a CPU-bound thread situation where multiple threads can improve 7 | performance. 8 | """ 9 | 10 | import asyncio 11 | import time 12 | 13 | import numpy 14 | import rasterio 15 | 16 | from rasterio._example import compute 17 | 18 | def main(infile, outfile, with_threads=False): 19 | 20 | with rasterio.drivers(): 21 | 22 | # Open the source dataset. 23 | with rasterio.open(infile) as src: 24 | 25 | # Create a destination dataset based on source params. The 26 | # destination will be tiled, and we'll "process" the tiles 27 | # concurrently. 28 | 29 | meta = src.meta 30 | del meta['transform'] 31 | meta.update(affine=src.affine) 32 | meta.update(blockxsize=256, blockysize=256, tiled='yes') 33 | with rasterio.open(outfile, 'w', **meta) as dst: 34 | 35 | loop = asyncio.get_event_loop() 36 | 37 | # With the exception of the ``yield from`` statement, 38 | # process_window() looks like callback-free synchronous 39 | # code. With a coroutine, we can keep the read, compute, 40 | # and write statements close together for 41 | # maintainability. As in the concurrent-cpu-bound.py 42 | # example, all of the speedup is provided by 43 | # distributing raster computation across multiple 44 | # threads. The difference here is that we're submitting 45 | # jobs to the thread pool asynchronously. 46 | 47 | @asyncio.coroutine 48 | def process_window(window): 49 | 50 | # Read a window of data. 51 | data = src.read(window=window) 52 | 53 | # We run the raster computation in a separate thread 54 | # and pause until the computation finishes, letting 55 | # other coroutines advance. 56 | # 57 | # The _example.compute function modifies no Python 58 | # objects and releases the GIL. It can execute 59 | # concurrently. 60 | result = numpy.zeros(data.shape, dtype=data.dtype) 61 | if with_threads: 62 | yield from loop.run_in_executor( 63 | None, compute, data, result) 64 | else: 65 | compute(data, result) 66 | 67 | # Write the result. 68 | for i, arr in enumerate(result, 1): 69 | dst.write_band(i, arr, window=window) 70 | 71 | # Queue up the loop's tasks. 72 | tasks = [asyncio.Task(process_window(window)) 73 | for ij, window in dst.block_windows(1)] 74 | 75 | # Wait for all the tasks to finish, and close. 76 | loop.run_until_complete(asyncio.wait(tasks)) 77 | loop.close() 78 | 79 | if __name__ == '__main__': 80 | 81 | import argparse 82 | 83 | parser = argparse.ArgumentParser( 84 | description="Concurrent raster processing demo") 85 | parser.add_argument( 86 | 'input', 87 | metavar='INPUT', 88 | help="Input file name") 89 | parser.add_argument( 90 | 'output', 91 | metavar='OUTPUT', 92 | help="Output file name") 93 | parser.add_argument( 94 | '--with-workers', 95 | action='store_true', 96 | help="Run with a pool of worker threads") 97 | args = parser.parse_args() 98 | 99 | main(args.input, args.output, args.with_workers) 100 | 101 | -------------------------------------------------------------------------------- /examples/concurrent-cpu-bound.py: -------------------------------------------------------------------------------- 1 | """concurrent-cpu-bound.py 2 | 3 | Operate on a raster dataset window-by-window using a ThreadPoolExecutor. 4 | 5 | Simulates a CPU-bound thread situation where multiple threads can improve performance. 6 | 7 | With -j 4, the program returns in about 1/4 the time as with -j 1. 8 | """ 9 | 10 | import concurrent.futures 11 | import multiprocessing 12 | import time 13 | 14 | import numpy 15 | import rasterio 16 | from rasterio._example import compute 17 | 18 | def main(infile, outfile, num_workers=4): 19 | 20 | with rasterio.drivers(): 21 | 22 | # Open the source dataset. 23 | with rasterio.open(infile) as src: 24 | 25 | # Create a destination dataset based on source params. 26 | # The destination will be tiled, and we'll "process" the tiles 27 | # concurrently. 28 | meta = src.meta 29 | del meta['transform'] 30 | meta.update(affine=src.affine) 31 | meta.update(blockxsize=256, blockysize=256, tiled='yes') 32 | with rasterio.open(outfile, 'w', **meta) as dst: 33 | 34 | # Define a generator for data, window pairs. 35 | # We use the new read() method here to a 3D array with all 36 | # bands, but could also use read_band(). 37 | def jobs(): 38 | for ij, window in dst.block_windows(): 39 | data = src.read(window=window) 40 | result = numpy.zeros(data.shape, dtype=data.dtype) 41 | yield data, result, window 42 | 43 | # Submit the jobs to the thread pool executor. 44 | with concurrent.futures.ThreadPoolExecutor( 45 | max_workers=num_workers) as executor: 46 | 47 | # Map the futures returned from executor.submit() 48 | # to their destination windows. 49 | # 50 | # The _example.compute function modifies no Python 51 | # objects and releases the GIL. It can execute 52 | # concurrently. 53 | future_to_window = { 54 | executor.submit(compute, data, res): (res, window) 55 | for data, res, window in jobs()} 56 | 57 | # As the processing jobs are completed, get the 58 | # results and write the data to the appropriate 59 | # destination window. 60 | for future in concurrent.futures.as_completed( 61 | future_to_window): 62 | 63 | result, window = future_to_window[future] 64 | 65 | # Since there's no multiband write() method yet in 66 | # Rasterio, we use write_band for each part of the 67 | # 3D data array. 68 | for i, arr in enumerate(result, 1): 69 | dst.write_band(i, arr, window=window) 70 | 71 | 72 | if __name__ == '__main__': 73 | 74 | import argparse 75 | 76 | parser = argparse.ArgumentParser( 77 | description="Concurrent raster processing demo") 78 | parser.add_argument( 79 | 'input', 80 | metavar='INPUT', 81 | help="Input file name") 82 | parser.add_argument( 83 | 'output', 84 | metavar='OUTPUT', 85 | help="Output file name") 86 | parser.add_argument( 87 | '-j', 88 | metavar='NUM_JOBS', 89 | type=int, 90 | default=multiprocessing.cpu_count(), 91 | help="Number of concurrent jobs") 92 | args = parser.parse_args() 93 | 94 | main(args.input, args.output, args.j) 95 | 96 | -------------------------------------------------------------------------------- /examples/decimate.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import subprocess 3 | import tempfile 4 | 5 | import rasterio 6 | 7 | with rasterio.drivers(): 8 | 9 | with rasterio.open('tests/data/RGB.byte.tif') as src: 10 | b, g, r = (src.read_band(k) for k in (1, 2, 3)) 11 | meta = src.meta 12 | 13 | tmpfilename = os.path.join(tempfile.mkdtemp(), 'decimate.tif') 14 | 15 | meta.update( 16 | width=src.width/2, 17 | height=src.height/2) 18 | 19 | with rasterio.open( 20 | tmpfilename, 'w', 21 | **meta 22 | ) as dst: 23 | for k, a in [(1, b), (2, g), (3, r)]: 24 | dst.write_band(k, a) 25 | 26 | outfilename = os.path.join(tempfile.mkdtemp(), 'decimate.jpg') 27 | 28 | rasterio.copy(tmpfilename, outfilename, driver='JPEG', quality='30') 29 | 30 | info = subprocess.call(['open', outfilename]) 31 | 32 | -------------------------------------------------------------------------------- /examples/polygonize.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | 3 | import rasterio 4 | import rasterio._features as ftrz 5 | 6 | with rasterio.open('box.png') as src: 7 | image = src.read_band(1) 8 | 9 | pprint.pprint( 10 | list(ftrz.polygonize(image))) 11 | -------------------------------------------------------------------------------- /examples/rasterio_polygonize.py: -------------------------------------------------------------------------------- 1 | # Emulates GDAL's gdal_polygonize.py 2 | 3 | import argparse 4 | import logging 5 | import subprocess 6 | import sys 7 | 8 | import fiona 9 | import numpy as np 10 | import rasterio 11 | from rasterio.features import shapes 12 | 13 | 14 | logging.basicConfig(stream=sys.stderr, level=logging.INFO) 15 | logger = logging.getLogger('rasterio_polygonize') 16 | 17 | 18 | def main(raster_file, vector_file, driver, mask_value): 19 | 20 | with rasterio.drivers(): 21 | 22 | with rasterio.open(raster_file) as src: 23 | image = src.read_band(1) 24 | 25 | if mask_value is not None: 26 | mask = image == mask_value 27 | else: 28 | mask = None 29 | 30 | results = ( 31 | {'properties': {'raster_val': v}, 'geometry': s} 32 | for i, (s, v) 33 | in enumerate( 34 | shapes(image, mask=mask, transform=src.affine))) 35 | 36 | with fiona.open( 37 | vector_file, 'w', 38 | driver=driver, 39 | crs=src.crs, 40 | schema={'properties': [('raster_val', 'int')], 41 | 'geometry': 'Polygon'}) as dst: 42 | dst.writerecords(results) 43 | 44 | return dst.name 45 | 46 | if __name__ == '__main__': 47 | 48 | parser = argparse.ArgumentParser( 49 | description="Writes shapes of raster features to a vector file") 50 | parser.add_argument( 51 | 'input', 52 | metavar='INPUT', 53 | help="Input file name") 54 | parser.add_argument( 55 | 'output', 56 | metavar='OUTPUT', 57 | help="Output file name") 58 | parser.add_argument( 59 | '--output-driver', 60 | metavar='OUTPUT DRIVER', 61 | help="Output vector driver name") 62 | parser.add_argument( 63 | '--mask-value', 64 | default=None, 65 | type=int, 66 | metavar='MASK VALUE', 67 | help="Value to mask") 68 | args = parser.parse_args() 69 | 70 | name = main(args.input, args.output, args.output_driver, args.mask_value) 71 | 72 | print subprocess.check_output( 73 | ['ogrinfo', '-so', args.output, name]) 74 | 75 | -------------------------------------------------------------------------------- /examples/rasterize_geometry.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import numpy 3 | import sys 4 | import rasterio 5 | from rasterio.features import rasterize 6 | from rasterio.transform import IDENTITY 7 | 8 | logging.basicConfig(stream=sys.stderr, level=logging.INFO) 9 | logger = logging.getLogger('rasterize_geometry') 10 | 11 | 12 | rows = cols = 10 13 | geometry = {'type':'Polygon','coordinates':[[(2,2),(2,4.25),(4.25,4.25),(4.25,2),(2,2)]]} 14 | with rasterio.drivers(): 15 | result = rasterize([geometry], out_shape=(rows, cols)) 16 | with rasterio.open( 17 | "test.tif", 18 | 'w', 19 | driver='GTiff', 20 | width=cols, 21 | height=rows, 22 | count=1, 23 | dtype=numpy.uint8, 24 | nodata=0, 25 | transform=IDENTITY, 26 | crs={'init': "EPSG:4326"}) as out: 27 | out.write_band(1, result.astype(numpy.uint8)) 28 | -------------------------------------------------------------------------------- /examples/reproject.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import subprocess 4 | import tempfile 5 | 6 | import numpy 7 | import rasterio 8 | from rasterio import Affine as A 9 | from rasterio.warp import reproject, RESAMPLING 10 | 11 | tempdir = '/tmp' 12 | tiffname = os.path.join(tempdir, 'example.tif') 13 | 14 | with rasterio.drivers(): 15 | 16 | # Consider a 512 x 512 raster centered on 0 degrees E and 0 degrees N 17 | # with each pixel covering 15". 18 | rows, cols = src_shape = (512, 512) 19 | dpp = 1.0/240 # decimal degrees per pixel 20 | # The following is equivalent to 21 | # A(dpp, 0, -cols*dpp/2, 0, -dpp, rows*dpp/2). 22 | src_transform = A.translation(-cols*dpp/2, rows*dpp/2) * A.scale(dpp, -dpp) 23 | src_crs = {'init': 'EPSG:4326'} 24 | source = numpy.ones(src_shape, numpy.uint8)*255 25 | 26 | # Prepare to reproject this rasters to a 1024 x 1024 dataset in 27 | # Web Mercator (EPSG:3857) with origin at -8928592, 2999585. 28 | dst_shape = (1024, 1024) 29 | dst_transform = A.from_gdal(-237481.5, 425.0, 0.0, 237536.4, 0.0, -425.0) 30 | dst_transform = dst_transform.to_gdal() 31 | dst_crs = {'init': 'EPSG:3857'} 32 | destination = numpy.zeros(dst_shape, numpy.uint8) 33 | 34 | reproject( 35 | source, 36 | destination, 37 | src_transform=src_transform, 38 | src_crs=src_crs, 39 | dst_transform=dst_transform, 40 | dst_crs=dst_crs, 41 | resampling=RESAMPLING.nearest) 42 | 43 | # Assert that the destination is only partly filled. 44 | assert destination.any() 45 | assert not destination.all() 46 | 47 | # Write it out to a file. 48 | with rasterio.open( 49 | tiffname, 50 | 'w', 51 | driver='GTiff', 52 | width=dst_shape[1], 53 | height=dst_shape[0], 54 | count=1, 55 | dtype=numpy.uint8, 56 | nodata=0, 57 | transform=dst_transform, 58 | crs=dst_crs) as dst: 59 | dst.write_band(1, destination) 60 | 61 | info = subprocess.call(['open', tiffname]) 62 | 63 | -------------------------------------------------------------------------------- /examples/sieve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # sieve: demonstrate sieving and polygonizing of raster features. 4 | 5 | import subprocess 6 | 7 | import numpy 8 | import rasterio 9 | from rasterio.features import sieve, shapes 10 | 11 | 12 | # Register GDAL and OGR drivers. 13 | with rasterio.drivers(): 14 | 15 | # Read a raster to be sieved. 16 | with rasterio.open('tests/data/shade.tif') as src: 17 | shade = src.read_band(1) 18 | 19 | # Print the number of shapes in the source raster. 20 | print("Slope shapes: %d" % len(list(shapes(shade)))) 21 | 22 | # Sieve out features 13 pixels or smaller. 23 | sieved = sieve(shade, 13, out=numpy.zeros(src.shape, src.dtypes[0])) 24 | 25 | # Print the number of shapes in the sieved raster. 26 | print("Sieved (13) shapes: %d" % len(list(shapes(sieved)))) 27 | 28 | # Write out the sieved raster. 29 | kwargs = src.meta 30 | kwargs['transform'] = kwargs.pop('affine') 31 | with rasterio.open('example-sieved.tif', 'w', **kwargs) as dst: 32 | dst.write_band(1, sieved) 33 | 34 | # Dump out gdalinfo's report card and open (or "eog") the TIFF. 35 | print(subprocess.check_output( 36 | ['gdalinfo', '-stats', 'example-sieved.tif'])) 37 | subprocess.call(['open', 'example-sieved.tif']) 38 | 39 | -------------------------------------------------------------------------------- /examples/total.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import rasterio 3 | import subprocess 4 | 5 | with rasterio.drivers(CPL_DEBUG=True): 6 | 7 | # Read raster bands directly to Numpy arrays. 8 | with rasterio.open('tests/data/RGB.byte.tif') as src: 9 | r, g, b = src.read() 10 | 11 | # Combine arrays using the 'iadd' ufunc. Expecting that the sum will 12 | # exceed the 8-bit integer range, initialize it as 16-bit. Adding other 13 | # arrays to it in-place converts those arrays up and preserves the type 14 | # of the total array. 15 | total = numpy.zeros(r.shape, dtype=rasterio.uint16) 16 | for band in (r, g, b): 17 | total += band 18 | total /= 3 19 | 20 | # Write the product as a raster band to a new 8-bit file. For keyword 21 | # arguments, we start with the meta attributes of the source file, but 22 | # then change the band count to 1, set the dtype to uint8, and specify 23 | # LZW compression. 24 | kwargs = src.meta 25 | kwargs.update( 26 | dtype=rasterio.uint8, 27 | count=1, 28 | compress='lzw') 29 | 30 | with rasterio.open('example-total.tif', 'w', **kwargs) as dst: 31 | dst.write_band(1, total.astype(rasterio.uint8)) 32 | 33 | # Dump out gdalinfo's report card and open the image. 34 | info = subprocess.check_output( 35 | ['gdalinfo', '-stats', 'example-total.tif']) 36 | print(info) 37 | subprocess.call(['open', 'example-total.tif']) 38 | 39 | -------------------------------------------------------------------------------- /rasterio/__init__.py: -------------------------------------------------------------------------------- 1 | # rasterio 2 | 3 | from collections import namedtuple 4 | import logging 5 | import os 6 | import warnings 7 | 8 | from rasterio._base import eval_window, window_shape, window_index 9 | from rasterio._drivers import driver_count, GDALEnv 10 | import rasterio.dtypes 11 | from rasterio.dtypes import ( 12 | bool_, ubyte, uint8, uint16, int16, uint32, int32, float32, float64, 13 | complex_) 14 | from rasterio.five import string_types 15 | from rasterio.transform import Affine, guard_transform 16 | 17 | # Classes in rasterio._io are imported below just before we need them. 18 | 19 | __all__ = [ 20 | 'band', 'open', 'drivers', 'copy', 'pad'] 21 | __version__ = "0.17.1" 22 | 23 | log = logging.getLogger('rasterio') 24 | class NullHandler(logging.Handler): 25 | def emit(self, record): 26 | pass 27 | log.addHandler(NullHandler()) 28 | 29 | 30 | def open( 31 | path, mode='r', 32 | driver=None, 33 | width=None, height=None, 34 | count=None, 35 | crs=None, transform=None, 36 | dtype=None, 37 | nodata=None, 38 | **kwargs): 39 | """Open file at ``path`` in ``mode`` "r" (read), "r+" (read/write), 40 | or "w" (write) and return a ``Reader`` or ``Updater`` object. 41 | 42 | In write mode, a driver name such as "GTiff" or "JPEG" (see GDAL 43 | docs or ``gdal_translate --help`` on the command line), ``width`` 44 | (number of pixels per line) and ``height`` (number of lines), the 45 | ``count`` number of bands in the new file must be specified. 46 | Additionally, the data type for bands such as ``rasterio.ubyte`` for 47 | 8-bit bands or ``rasterio.uint16`` for 16-bit bands must be 48 | specified using the ``dtype`` argument. 49 | 50 | A coordinate reference system for raster datasets in write mode can 51 | be defined by the ``crs`` argument. It takes Proj4 style mappings 52 | like 53 | 54 | {'proj': 'longlat', 'ellps': 'WGS84', 'datum': 'WGS84', 55 | 'no_defs': True} 56 | 57 | An affine transformation that maps ``col,row`` pixel coordinates to 58 | ``x,y`` coordinates in the coordinate reference system can be 59 | specified using the ``transform`` argument. The value may be either 60 | an instance of ``affine.Affine`` or a 6-element sequence of the 61 | affine transformation matrix coefficients ``a, b, c, d, e, f``. 62 | These coefficients are shown in the figure below. 63 | 64 | | x | | a b c | | c | 65 | | y | = | d e f | | r | 66 | | 1 | | 0 0 1 | | 1 | 67 | 68 | a: rate of change of X with respect to increasing column, i.e. 69 | pixel width 70 | b: rotation, 0 if the raster is oriented "north up" 71 | c: X coordinate of the top left corner of the top left pixel 72 | f: Y coordinate of the top left corner of the top left pixel 73 | d: rotation, 0 if the raster is oriented "north up" 74 | e: rate of change of Y with respect to increasing row, usually 75 | a negative number i.e. -1 * pixel height 76 | f: Y coordinate of the top left corner of the top left pixel 77 | 78 | Finally, additional kwargs are passed to GDAL as driver-specific 79 | dataset creation parameters. 80 | """ 81 | if not isinstance(path, string_types): 82 | raise TypeError("invalid path: %r" % path) 83 | if mode and not isinstance(mode, string_types): 84 | raise TypeError("invalid mode: %r" % mode) 85 | if driver and not isinstance(driver, string_types): 86 | raise TypeError("invalid driver: %r" % driver) 87 | if mode in ('r', 'r+'): 88 | if not os.path.exists(path): 89 | raise IOError("no such file or directory: %r" % path) 90 | if transform: 91 | transform = guard_transform(transform) 92 | 93 | if mode == 'r': 94 | from rasterio._io import RasterReader 95 | s = RasterReader(path) 96 | elif mode == 'r+': 97 | from rasterio._io import writer 98 | s = writer(path, mode) 99 | elif mode == 'r-': 100 | from rasterio._base import DatasetReader 101 | s = DatasetReader(path) 102 | elif mode == 'w': 103 | from rasterio._io import writer 104 | s = writer(path, mode, driver=driver, 105 | width=width, height=height, count=count, 106 | crs=crs, transform=transform, dtype=dtype, 107 | nodata=nodata, 108 | **kwargs) 109 | else: 110 | raise ValueError( 111 | "mode string must be one of 'r', 'r+', or 'w', not %s" % mode) 112 | s.start() 113 | return s 114 | 115 | 116 | def copy(src, dst, **kw): 117 | """Copy a source dataset to a new destination with driver specific 118 | creation options. 119 | 120 | ``src`` must be an existing file and ``dst`` a valid output file. 121 | 122 | A ``driver`` keyword argument with value like 'GTiff' or 'JPEG' is 123 | used to control the output format. 124 | 125 | This is the one way to create write-once files like JPEGs. 126 | """ 127 | from rasterio._copy import RasterCopier 128 | with drivers(): 129 | return RasterCopier()(src, dst, **kw) 130 | 131 | 132 | def drivers(**kwargs): 133 | """Returns a gdal environment with registered drivers.""" 134 | if driver_count() == 0: 135 | log.debug("Creating a chief GDALEnv in drivers()") 136 | return GDALEnv(True, **kwargs) 137 | else: 138 | log.debug("Creating a not-responsible GDALEnv in drivers()") 139 | return GDALEnv(False, **kwargs) 140 | 141 | 142 | Band = namedtuple('Band', ['ds', 'bidx', 'dtype', 'shape']) 143 | 144 | def band(ds, bidx): 145 | """Wraps a dataset and a band index up as a 'Band'""" 146 | return Band( 147 | ds, 148 | bidx, 149 | set(ds.dtypes).pop(), 150 | ds.shape) 151 | 152 | 153 | def pad(array, transform, pad_width, mode=None, **kwargs): 154 | """Returns a padded array and shifted affine transform matrix. 155 | 156 | Array is padded using `numpy.pad()`.""" 157 | import numpy 158 | transform = guard_transform(transform) 159 | padded_array = numpy.pad(array, pad_width, mode, **kwargs) 160 | padded_trans = list(transform) 161 | padded_trans[2] -= pad_width*padded_trans[0] 162 | padded_trans[5] -= pad_width*padded_trans[4] 163 | return padded_array, Affine(*padded_trans[:6]) 164 | -------------------------------------------------------------------------------- /rasterio/_base.pxd: -------------------------------------------------------------------------------- 1 | # Base class. 2 | 3 | cdef class DatasetReader: 4 | # Read-only access to dataset metadata. No IO! 5 | 6 | cdef void *_hds 7 | 8 | cdef readonly object name 9 | cdef readonly object mode 10 | cdef readonly object width, height 11 | cdef readonly object shape 12 | cdef public object driver 13 | cdef public object _count 14 | cdef public object _dtypes 15 | cdef public object _closed 16 | cdef public object _crs 17 | cdef public object _crs_wkt 18 | cdef public object _transform 19 | cdef public object _block_shapes 20 | cdef public object _nodatavals 21 | cdef public object _read 22 | cdef object env 23 | 24 | cdef void *band(self, int bidx) 25 | 26 | -------------------------------------------------------------------------------- /rasterio/_copy.pyx: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import os.path 4 | 5 | from rasterio cimport _gdal 6 | 7 | 8 | log = logging.getLogger('rasterio') 9 | class NullHandler(logging.Handler): 10 | def emit(self, record): 11 | pass 12 | log.addHandler(NullHandler()) 13 | 14 | 15 | cdef class RasterCopier: 16 | 17 | def __call__(self, src, dst, **kw): 18 | cdef char **options = NULL 19 | src_b = src.encode('utf-8') 20 | cdef const char *src_c = src_b 21 | dst_b = dst.encode('utf-8') 22 | cdef const char *dst_c = dst_b 23 | cdef void *src_ds = _gdal.GDALOpen(src_c, 0) 24 | if src_ds == NULL: 25 | raise ValueError("NULL source dataset") 26 | driver = kw.pop('driver', 'GTiff') 27 | driver_b = driver.encode('utf-8') 28 | cdef const char *driver_c = driver_b 29 | cdef void *drv = _gdal.GDALGetDriverByName(driver_c) 30 | if drv == NULL: 31 | raise ValueError("NULL driver") 32 | strictness = 0 33 | if kw.pop('strict', None): 34 | strictness = 1 35 | 36 | # Creation options 37 | for k, v in kw.items(): 38 | k, v = k.upper(), v.upper() 39 | key_b = k.encode('utf-8') 40 | val_b = v.encode('utf-8') 41 | key_c = key_b 42 | val_c = val_b 43 | options = _gdal.CSLSetNameValue(options, key_c, val_c) 44 | log.debug("Option: %r\n", (k, v)) 45 | 46 | cdef void *dst_ds = _gdal.GDALCreateCopy( 47 | drv, dst_c, src_ds, strictness, NULL, NULL, NULL) 48 | if dst_ds == NULL: 49 | raise ValueError("NULL destination dataset") 50 | _gdal.GDALClose(src_ds) 51 | _gdal.GDALClose(dst_ds) 52 | 53 | if options: 54 | _gdal.CSLDestroy(options) 55 | 56 | -------------------------------------------------------------------------------- /rasterio/_drivers.pyx: -------------------------------------------------------------------------------- 1 | # The GDAL and OGR driver registry. 2 | # GDAL driver management. 3 | 4 | import os 5 | import os.path 6 | import logging 7 | import sys 8 | 9 | from rasterio.five import string_types 10 | 11 | 12 | cdef extern from "cpl_conv.h": 13 | void CPLFree (void *ptr) 14 | void CPLSetThreadLocalConfigOption (char *key, char *val) 15 | const char * CPLGetConfigOption ( const char *key, const char *default) 16 | 17 | 18 | cdef extern from "cpl_error.h": 19 | void CPLSetErrorHandler (void *handler) 20 | 21 | 22 | cdef extern from "gdal.h": 23 | void GDALAllRegister() 24 | void GDALDestroyDriverManager() 25 | int GDALGetDriverCount() 26 | void * GDALGetDriver(int i) 27 | const char * GDALGetDriverShortName(void *driver) 28 | const char * GDALGetDriverLongName(void *driver) 29 | 30 | 31 | cdef extern from "ogr_api.h": 32 | void OGRRegisterAll() 33 | void OGRCleanupAll() 34 | int OGRGetDriverCount() 35 | 36 | 37 | log = logging.getLogger('GDAL') 38 | class NullHandler(logging.Handler): 39 | def emit(self, record): 40 | pass 41 | log.addHandler(NullHandler()) 42 | 43 | 44 | level_map = { 45 | 0: 0, 46 | 1: logging.DEBUG, 47 | 2: logging.WARNING, 48 | 3: logging.ERROR, 49 | 4: logging.CRITICAL } 50 | 51 | code_map = { 52 | 0: 'CPLE_None', 53 | 1: 'CPLE_AppDefined', 54 | 2: 'CPLE_OutOfMemory', 55 | 3: 'CPLE_FileIO', 56 | 4: 'CPLE_OpenFailed', 57 | 5: 'CPLE_IllegalArg', 58 | 6: 'CPLE_NotSupported', 59 | 7: 'CPLE_AssertionFailed', 60 | 8: 'CPLE_NoWriteAccess', 61 | 9: 'CPLE_UserInterrupt', 62 | 10: 'CPLE_ObjectNull' 63 | } 64 | 65 | cdef void * errorHandler(int eErrClass, int err_no, char *msg): 66 | log.log(level_map[eErrClass], "%s in %s", code_map[err_no], msg) 67 | 68 | def driver_count(): 69 | return GDALGetDriverCount() + OGRGetDriverCount() 70 | 71 | 72 | cdef class GDALEnv(object): 73 | 74 | cdef object is_chef 75 | cdef public object options 76 | 77 | def __init__(self, is_chef=True, **options): 78 | self.is_chef = is_chef 79 | self.options = options.copy() 80 | 81 | def __enter__(self): 82 | self.start() 83 | return self 84 | 85 | def __exit__(self, exc_type=None, exc_val=None, exc_tb=None): 86 | self.stop() 87 | 88 | def start(self): 89 | cdef const char *key_c 90 | cdef const char *val_c 91 | GDALAllRegister() 92 | OGRRegisterAll() 93 | CPLSetErrorHandler(errorHandler) 94 | if driver_count() == 0: 95 | raise ValueError("Drivers not registered") 96 | 97 | if 'GDAL_DATA' not in os.environ: 98 | whl_datadir = os.path.abspath( 99 | os.path.join(os.path.dirname(__file__), "gdal_data")) 100 | share_datadir = os.path.join(sys.prefix, 'share/gdal') 101 | if os.path.exists(os.path.join(whl_datadir, 'pcs.csv')): 102 | os.environ['GDAL_DATA'] = whl_datadir 103 | elif os.path.exists(os.path.join(share_datadir, 'pcs.csv')): 104 | os.environ['GDAL_DATA'] = share_datadir 105 | if 'PROJ_LIB' not in os.environ: 106 | whl_datadir = os.path.abspath( 107 | os.path.join(os.path.dirname(__file__), "proj_data")) 108 | os.environ['PROJ_LIB'] = whl_datadir 109 | 110 | for key, val in self.options.items(): 111 | key_b = key.upper().encode('utf-8') 112 | key_c = key_b 113 | if isinstance(val, string_types): 114 | val_b = val.encode('utf-8') 115 | else: 116 | val_b = ('ON' if val else 'OFF').encode('utf-8') 117 | val_c = val_b 118 | CPLSetThreadLocalConfigOption(key_c, val_c) 119 | log.debug("Option %s=%s", key, CPLGetConfigOption(key_c, NULL)) 120 | return self 121 | 122 | def stop(self): 123 | cdef const char *key_c 124 | for key in self.options: 125 | key_b = key.upper().encode('utf-8') 126 | key_c = key_b 127 | CPLSetThreadLocalConfigOption(key_c, NULL) 128 | CPLSetErrorHandler(NULL) 129 | 130 | def drivers(self): 131 | cdef void *drv = NULL 132 | cdef const char *key = NULL 133 | cdef const char *val = NULL 134 | cdef int i 135 | result = {} 136 | for i in range(GDALGetDriverCount()): 137 | drv = GDALGetDriver(i) 138 | key = GDALGetDriverShortName(drv) 139 | key_b = key 140 | val = GDALGetDriverLongName(drv) 141 | val_b = val 142 | result[key_b.decode('utf-8')] = val_b.decode('utf-8') 143 | return result 144 | -------------------------------------------------------------------------------- /rasterio/_err.pyx: -------------------------------------------------------------------------------- 1 | """rasterio._err 2 | 3 | Transformation of GDAL C API errors to Python exceptions using Python's 4 | ``with`` statement and an error-handling context manager class. 5 | 6 | The ``cpl_errs`` error-handling context manager is intended for use in 7 | Rasterio's Cython code. When entering the body of a ``with`` statement, 8 | the context manager clears GDAL's error stack. On exit, the context 9 | manager pops the last error off the stack and raises an appropriate 10 | Python exception. It's otherwise pretty difficult to do this kind of 11 | thing. I couldn't make it work with a CPL error handler, Cython's 12 | C code swallows exceptions raised from C callbacks. 13 | 14 | When used to wrap a call to open a PNG in update mode 15 | 16 | with cpl_errs: 17 | cdef void *hds = GDALOpen('file.png', 1) 18 | if hds == NULL: 19 | raise ValueError("NULL dataset") 20 | 21 | the ValueError of last resort never gets raised because the context 22 | manager raises a more useful and informative error: 23 | 24 | Traceback (most recent call last): 25 | File "/Users/sean/code/rasterio/scripts/rio_insp", line 65, in 26 | with rasterio.open(args.src, args.mode) as src: 27 | File "/Users/sean/code/rasterio/rasterio/__init__.py", line 111, in open 28 | s.start() 29 | ValueError: The PNG driver does not support update access to existing datasets. 30 | """ 31 | 32 | # CPL function declarations. 33 | cdef extern from "cpl_error.h": 34 | int CPLGetLastErrorNo() 35 | const char* CPLGetLastErrorMsg() 36 | int CPLGetLastErrorType() 37 | void CPLErrorReset() 38 | 39 | # Map GDAL error numbers to Python exceptions. 40 | exception_map = { 41 | 1: RuntimeError, # CPLE_AppDefined 42 | 2: MemoryError, # CPLE_OutOfMemory 43 | 3: IOError, # CPLE_FileIO 44 | 4: IOError, # CPLE_OpenFailed 45 | 5: TypeError, # CPLE_IllegalArg 46 | 6: ValueError, # CPLE_NotSupported 47 | 7: AssertionError, # CPLE_AssertionFailed 48 | 8: IOError, # CPLE_NoWriteAccess 49 | 9: KeyboardInterrupt, # CPLE_UserInterrupt 50 | 10: ValueError # ObjectNull 51 | } 52 | 53 | 54 | cdef class GDALErrCtxManager: 55 | """A manager for GDAL error handling contexts.""" 56 | 57 | def __enter__(self): 58 | CPLErrorReset() 59 | return self 60 | 61 | def __exit__(self, exc_type=None, exc_val=None, exc_tb=None): 62 | cdef int err_type = CPLGetLastErrorType() 63 | cdef int err_no = CPLGetLastErrorNo() 64 | cdef const char *msg = CPLGetLastErrorMsg() 65 | # TODO: warn for err_type 2? 66 | if err_type >= 3: 67 | raise exception_map[err_no](msg) 68 | 69 | cpl_errs = GDALErrCtxManager() 70 | 71 | -------------------------------------------------------------------------------- /rasterio/_example.pyx: -------------------------------------------------------------------------------- 1 | # cython: boundscheck=False 2 | 3 | import numpy 4 | cimport numpy 5 | 6 | def compute( 7 | unsigned char[:, :, :] input, 8 | unsigned char[:, :, :] output): 9 | # Given input and output uint8 arrays, fakes an CPU-intensive 10 | # computation. 11 | cdef int I, J, K 12 | cdef int i, j, k, l 13 | cdef double val 14 | I = input.shape[0] 15 | J = input.shape[1] 16 | K = input.shape[2] 17 | with nogil: 18 | for i in range(I): 19 | for j in range(J): 20 | for k in range(K): 21 | val = input[i, j, k] 22 | for l in range(2000): 23 | val += 1.0 24 | val -= 2000.0 25 | output[~i, j, k] = val 26 | 27 | -------------------------------------------------------------------------------- /rasterio/_features.pxd: -------------------------------------------------------------------------------- 1 | 2 | cdef class GeomBuilder: 3 | cdef void *geom 4 | cdef object code 5 | cdef object geomtypename 6 | cdef object ndims 7 | cdef _buildCoords(self, void *geom) 8 | cpdef _buildPoint(self) 9 | cpdef _buildLineString(self) 10 | cpdef _buildLinearRing(self) 11 | cdef _buildParts(self, void *geom) 12 | cpdef _buildPolygon(self) 13 | cpdef _buildMultiPolygon(self) 14 | cdef build(self, void *geom) 15 | cpdef build_wkb(self, object wkb) 16 | 17 | 18 | cdef class OGRGeomBuilder: 19 | cdef void * _createOgrGeometry(self, int geom_type) 20 | cdef _addPointToGeometry(self, void *cogr_geometry, object coordinate) 21 | cdef void * _buildPoint(self, object coordinates) 22 | cdef void * _buildLineString(self, object coordinates) 23 | cdef void * _buildLinearRing(self, object coordinates) 24 | cdef void * _buildPolygon(self, object coordinates) 25 | cdef void * _buildMultiPoint(self, object coordinates) 26 | cdef void * _buildMultiLineString(self, object coordinates) 27 | cdef void * _buildMultiPolygon(self, object coordinates) 28 | cdef void * _buildGeometryCollection(self, object coordinates) 29 | cdef void * build(self, object geom) 30 | -------------------------------------------------------------------------------- /rasterio/_io.pxd: -------------------------------------------------------------------------------- 1 | cimport numpy as np 2 | 3 | from rasterio cimport _base 4 | 5 | 6 | cdef extern from "gdal.h": 7 | 8 | ctypedef enum GDALDataType: 9 | GDT_Unknown 10 | GDT_Byte 11 | GDT_UInt16 12 | GDT_Int16 13 | GDT_UInt32 14 | GDT_Int32 15 | GDT_Float32 16 | GDT_Float64 17 | GDT_CInt16 18 | GDT_CInt32 19 | GDT_CFloat32 20 | GDT_CFloat64 21 | GDT_TypeCount 22 | 23 | ctypedef enum GDALAccess: 24 | GA_ReadOnly 25 | GA_Update 26 | 27 | ctypedef enum GDALRWFlag: 28 | GF_Read 29 | GF_Write 30 | 31 | 32 | cdef class RasterReader(_base.DatasetReader): 33 | # Read-only access to raster data and metadata. 34 | pass 35 | 36 | 37 | cdef class RasterUpdater(RasterReader): 38 | # Read-write access to raster data and metadata. 39 | cdef readonly object _init_dtype 40 | cdef readonly object _init_nodata 41 | cdef readonly object _options 42 | 43 | 44 | cdef class IndirectRasterUpdater(RasterUpdater): 45 | pass 46 | 47 | 48 | cdef class InMemoryRaster: 49 | cdef void *dataset 50 | cdef void *band 51 | cdef double transform[6] 52 | cdef int band_ids[1] 53 | cdef np.ndarray _image 54 | 55 | 56 | ctypedef np.uint8_t DTYPE_UBYTE_t 57 | ctypedef np.uint16_t DTYPE_UINT16_t 58 | ctypedef np.int16_t DTYPE_INT16_t 59 | ctypedef np.uint32_t DTYPE_UINT32_t 60 | ctypedef np.int32_t DTYPE_INT32_t 61 | ctypedef np.float32_t DTYPE_FLOAT32_t 62 | ctypedef np.float64_t DTYPE_FLOAT64_t 63 | 64 | cdef int io_ubyte( 65 | void *hband, 66 | int mode, 67 | int xoff, 68 | int yoff, 69 | int width, 70 | int height, 71 | np.uint8_t[:, :] buffer) 72 | 73 | cdef int io_uint16( 74 | void *hband, 75 | int mode, 76 | int xoff, 77 | int yoff, 78 | int width, 79 | int height, 80 | np.uint16_t[:, :] buffer) 81 | 82 | cdef int io_int16( 83 | void *hband, 84 | int mode, 85 | int xoff, 86 | int yoff, 87 | int width, 88 | int height, 89 | np.int16_t[:, :] buffer) 90 | 91 | cdef int io_uint32( 92 | void *hband, 93 | int mode, 94 | int xoff, 95 | int yoff, 96 | int width, 97 | int height, 98 | np.uint32_t[:, :] buffer) 99 | 100 | cdef int io_int32( 101 | void *hband, 102 | int mode, 103 | int xoff, 104 | int yoff, 105 | int width, 106 | int height, 107 | np.int32_t[:, :] buffer) 108 | 109 | cdef int io_float32( 110 | void *hband, 111 | int mode, 112 | int xoff, 113 | int yoff, 114 | int width, 115 | int height, 116 | np.float32_t[:, :] buffer) 117 | 118 | cdef int io_float64( 119 | void *hband, 120 | int mode, 121 | int xoff, 122 | int yoff, 123 | int width, 124 | int height, 125 | np.float64_t[:, :] buffer) 126 | 127 | cdef int io_multi_ubyte( 128 | void *hds, 129 | int mode, 130 | int xoff, 131 | int yoff, 132 | int width, 133 | int height, 134 | np.uint8_t[:, :, :] buffer, 135 | long[:] indexes, 136 | int count) nogil 137 | 138 | cdef int io_multi_uint16( 139 | void *hds, 140 | int mode, 141 | int xoff, 142 | int yoff, 143 | int width, 144 | int height, 145 | np.uint16_t[:, :, :] buffer, 146 | long[:] indexes, 147 | int count) nogil 148 | 149 | cdef int io_multi_int16( 150 | void *hds, 151 | int mode, 152 | int xoff, 153 | int yoff, 154 | int width, 155 | int height, 156 | np.int16_t[:, :, :] buffer, 157 | long[:] indexes, 158 | int count) nogil 159 | 160 | cdef int io_multi_uint32( 161 | void *hds, 162 | int mode, 163 | int xoff, 164 | int yoff, 165 | int width, 166 | int height, 167 | np.uint32_t[:, :, :] buffer, 168 | long[:] indexes, 169 | int count) nogil 170 | 171 | cdef int io_multi_int32( 172 | void *hds, 173 | int mode, 174 | int xoff, 175 | int yoff, 176 | int width, 177 | int height, 178 | np.int32_t[:, :, :] buffer, 179 | long[:] indexes, 180 | int count) nogil 181 | 182 | cdef int io_multi_float32( 183 | void *hds, 184 | int mode, 185 | int xoff, 186 | int yoff, 187 | int width, 188 | int height, 189 | np.float32_t[:, :, :] buffer, 190 | long[:] indexes, 191 | int count) nogil 192 | 193 | cdef int io_multi_float64( 194 | void *hds, 195 | int mode, 196 | int xoff, 197 | int yoff, 198 | int width, 199 | int height, 200 | np.float64_t[:, :, :] buffer, 201 | long[:] indexes, 202 | int count) nogil 203 | 204 | cdef int io_multi_cint16( 205 | void *hds, 206 | int mode, 207 | int xoff, 208 | int yoff, 209 | int width, 210 | int height, 211 | np.complex_t[:, :, :] out, 212 | long[:] indexes, 213 | int count) 214 | 215 | cdef int io_multi_cint32( 216 | void *hds, 217 | int mode, 218 | int xoff, 219 | int yoff, 220 | int width, 221 | int height, 222 | np.complex_t[:, :, :] out, 223 | long[:] indexes, 224 | int count) 225 | 226 | cdef int io_multi_cfloat32( 227 | void *hds, 228 | int mode, 229 | int xoff, 230 | int yoff, 231 | int width, 232 | int height, 233 | np.complex64_t[:, :, :] out, 234 | long[:] indexes, 235 | int count) 236 | 237 | cdef int io_multi_cfloat64( 238 | void *hds, 239 | int mode, 240 | int xoff, 241 | int yoff, 242 | int width, 243 | int height, 244 | np.complex128_t[:, :, :] out, 245 | long[:] indexes, 246 | int count) 247 | 248 | cdef int io_auto(image, void *hband, bint write) 249 | -------------------------------------------------------------------------------- /rasterio/_ogr.pxd: -------------------------------------------------------------------------------- 1 | 2 | ctypedef int OGRErr 3 | ctypedef struct OGREnvelope: 4 | double MinX 5 | double MaxX 6 | double MinY 7 | double MaxY 8 | 9 | cdef extern from "ogr_core.h": 10 | char * OGRGeometryTypeToName(int) 11 | 12 | cdef extern from "ogr_api.h": 13 | char * OGR_Dr_GetName (void *driver) 14 | void * OGR_Dr_CreateDataSource (void *driver, char *path, char **options) 15 | int OGR_Dr_DeleteDataSource (void *driver, char *) 16 | int OGR_DS_DeleteLayer (void *datasource, int n) 17 | void * OGR_DS_CreateLayer (void *datasource, char *name, void *crs, int geomType, char **options) 18 | void * OGR_DS_ExecuteSQL (void *datasource, char *name, void *filter, char *dialext) 19 | void OGR_DS_Destroy (void *datasource) 20 | void * OGR_DS_GetDriver (void *layer_defn) 21 | void * OGR_DS_GetLayerByName (void *datasource, char *name) 22 | int OGR_DS_GetLayerCount (void *datasource) 23 | void * OGR_DS_GetLayer (void *datasource, int n) 24 | void OGR_DS_ReleaseResultSet (void *datasource, void *results) 25 | int OGR_DS_SyncToDisk (void *datasource) 26 | void * OGR_F_Create (void *featuredefn) 27 | void OGR_F_Destroy (void *feature) 28 | long OGR_F_GetFID (void *feature) 29 | int OGR_F_IsFieldSet (void *feature, int n) 30 | int OGR_F_GetFieldAsDateTime (void *feature, int n, int *y, int *m, int *d, int *h, int *m, int *s, int *z) 31 | double OGR_F_GetFieldAsDouble (void *feature, int n) 32 | int OGR_F_GetFieldAsInteger (void *feature, int n) 33 | char * OGR_F_GetFieldAsString (void *feature, int n) 34 | int OGR_F_GetFieldCount (void *feature) 35 | void * OGR_F_GetFieldDefnRef (void *feature, int n) 36 | int OGR_F_GetFieldIndex (void *feature, char *name) 37 | void * OGR_F_GetGeometryRef (void *feature) 38 | void OGR_F_SetFieldDateTime (void *feature, int n, int y, int m, int d, int hh, int mm, int ss, int tz) 39 | void OGR_F_SetFieldDouble (void *feature, int n, double value) 40 | void OGR_F_SetFieldInteger (void *feature, int n, int value) 41 | void OGR_F_SetFieldString (void *feature, int n, char *value) 42 | int OGR_F_SetGeometryDirectly (void *feature, void *geometry) 43 | void * OGR_FD_Create (char *name) 44 | int OGR_FD_GetFieldCount (void *featuredefn) 45 | void * OGR_FD_GetFieldDefn (void *featuredefn, int n) 46 | int OGR_FD_GetGeomType (void *featuredefn) 47 | char * OGR_FD_GetName (void *featuredefn) 48 | void * OGR_Fld_Create (char *name, int fieldtype) 49 | void OGR_Fld_Destroy (void *fielddefn) 50 | char * OGR_Fld_GetNameRef (void *fielddefn) 51 | int OGR_Fld_GetPrecision (void *fielddefn) 52 | int OGR_Fld_GetType (void *fielddefn) 53 | int OGR_Fld_GetWidth (void *fielddefn) 54 | void OGR_Fld_Set (void *fielddefn, char *name, int fieldtype, int width, int precision, int justification) 55 | void OGR_Fld_SetPrecision (void *fielddefn, int n) 56 | void OGR_Fld_SetWidth (void *fielddefn, int n) 57 | OGRErr OGR_G_AddGeometryDirectly (void *geometry, void *part) 58 | void OGR_G_AddPoint (void *geometry, double x, double y, double z) 59 | void OGR_G_AddPoint_2D (void *geometry, double x, double y) 60 | void OGR_G_CloseRings (void *geometry) 61 | void * OGR_G_CreateGeometry (int wkbtypecode) 62 | void * OGR_G_CreateGeometryFromJson(char *json) 63 | void OGR_G_DestroyGeometry (void *geometry) 64 | unsigned char * OGR_G_ExportToJson (void *geometry) 65 | void OGR_G_ExportToWkb (void *geometry, int endianness, char *buffer) 66 | int OGR_G_GetCoordinateDimension (void *geometry) 67 | int OGR_G_GetGeometryCount (void *geometry) 68 | unsigned char * OGR_G_GetGeometryName (void *geometry) 69 | int OGR_G_GetGeometryType (void *geometry) 70 | void * OGR_G_GetGeometryRef (void *geometry, int n) 71 | int OGR_G_GetPointCount (void *geometry) 72 | double OGR_G_GetX (void *geometry, int n) 73 | double OGR_G_GetY (void *geometry, int n) 74 | double OGR_G_GetZ (void *geometry, int n) 75 | void OGR_G_ImportFromWkb (void *geometry, unsigned char *bytes, int nbytes) 76 | int OGR_G_WkbSize (void *geometry) 77 | OGRErr OGR_L_CreateFeature (void *layer, void *feature) 78 | int OGR_L_CreateField (void *layer, void *fielddefn, int flexible) 79 | OGRErr OGR_L_GetExtent (void *layer, void *extent, int force) 80 | void * OGR_L_GetFeature (void *layer, int n) 81 | int OGR_L_GetFeatureCount (void *layer, int m) 82 | void * OGR_L_GetLayerDefn (void *layer) 83 | char * OGR_L_GetName (void *layer) 84 | void * OGR_L_GetNextFeature (void *layer) 85 | void * OGR_L_GetSpatialFilter (void *layer) 86 | void * OGR_L_GetSpatialRef (void *layer) 87 | void OGR_L_ResetReading (void *layer) 88 | void OGR_L_SetSpatialFilter (void *layer, void *geometry) 89 | void OGR_L_SetSpatialFilterRect ( 90 | void *layer, double minx, double miny, double maxx, double maxy 91 | ) 92 | int OGR_L_TestCapability (void *layer, char *name) 93 | void * OGRGetDriverByName (char *) 94 | void * OGROpen (char *path, int mode, void *x) 95 | void * OGROpenShared (char *path, int mode, void *x) 96 | int OGRReleaseDataSource (void *datasource) 97 | void OGRRegisterAll() 98 | void OGRCleanupAll() 99 | 100 | -------------------------------------------------------------------------------- /rasterio/coords.py: -------------------------------------------------------------------------------- 1 | 2 | from collections import namedtuple 3 | 4 | BoundingBox = namedtuple('BoundingBox', ('left', 'bottom', 'right', 'top')) 5 | 6 | -------------------------------------------------------------------------------- /rasterio/crs.py: -------------------------------------------------------------------------------- 1 | # Coordinate reference systems and functions. 2 | # 3 | # PROJ.4 is the law of this land: http://proj.osgeo.org/. But whereas PROJ.4 4 | # coordinate reference systems are described by strings of parameters such as 5 | # 6 | # +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs 7 | # 8 | # here we use mappings: 9 | # 10 | # {'proj': 'longlat', 'ellps': 'WGS84', 'datum': 'WGS84', 'no_defs': True} 11 | # 12 | 13 | from rasterio.five import string_types 14 | 15 | def to_string(crs): 16 | """Turn a parameter mapping into a more conventional PROJ.4 string. 17 | 18 | Mapping keys are tested against the ``all_proj_keys`` list. Values of 19 | ``True`` are omitted, leaving the key bare: {'no_defs': True} -> "+no_defs" 20 | and items where the value is otherwise not a str, int, or float are 21 | omitted. 22 | """ 23 | items = [] 24 | for k, v in sorted(filter( 25 | lambda x: x[0] in all_proj_keys and x[1] is not False and ( 26 | isinstance(x[1], (bool, int, float)) or 27 | isinstance(x[1], string_types)), 28 | crs.items() )): 29 | items.append( 30 | "+" + "=".join( 31 | map(str, filter( 32 | lambda y: (y or y == 0) and y is not True, (k, v)))) ) 33 | return " ".join(items) 34 | 35 | def from_string(prjs): 36 | """Turn a PROJ.4 string into a mapping of parameters. 37 | 38 | Bare parameters like "+no_defs" are given a value of ``True``. All keys 39 | are checked against the ``all_proj_keys`` list. 40 | """ 41 | parts = [o.lstrip('+') for o in prjs.strip().split()] 42 | def parse(v): 43 | if v in ('True', 'true'): 44 | return True 45 | elif v in ('False', 'false'): 46 | return False 47 | else: 48 | try: 49 | return int(v) 50 | except ValueError: 51 | pass 52 | try: 53 | return float(v) 54 | except ValueError: 55 | return v 56 | items = map( 57 | lambda kv: len(kv) == 2 and (kv[0], parse(kv[1])) or (kv[0], True), 58 | (p.split('=') for p in parts) ) 59 | return dict((k,v) for k, v in items if k in all_proj_keys) 60 | 61 | def from_epsg(code): 62 | """Given an integer code, returns an EPSG-like mapping. 63 | 64 | Note: the input code is not validated against an EPSG database. 65 | """ 66 | if int(code) <= 0: 67 | raise ValueError("EPSG codes are positive integers") 68 | return {'init': "epsg:%s" % code, 'no_defs': True} 69 | 70 | 71 | # Below is the big list of PROJ4 parameters from 72 | # http://trac.osgeo.org/proj/wiki/GenParms. 73 | # It is parsed into a list of paramter keys ``all_proj_keys``. 74 | 75 | _param_data = """ 76 | +a Semimajor radius of the ellipsoid axis 77 | +alpha ? Used with Oblique Mercator and possibly a few others 78 | +axis Axis orientation (new in 4.8.0) 79 | +b Semiminor radius of the ellipsoid axis 80 | +datum Datum name (see `proj -ld`) 81 | +ellps Ellipsoid name (see `proj -le`) 82 | +init Initialize from a named CRS 83 | +k Scaling factor (old name) 84 | +k_0 Scaling factor (new name) 85 | +lat_0 Latitude of origin 86 | +lat_1 Latitude of first standard parallel 87 | +lat_2 Latitude of second standard parallel 88 | +lat_ts Latitude of true scale 89 | +lon_0 Central meridian 90 | +lonc ? Longitude used with Oblique Mercator and possibly a few others 91 | +lon_wrap Center longitude to use for wrapping (see below) 92 | +nadgrids Filename of NTv2 grid file to use for datum transforms (see below) 93 | +no_defs Don't use the /usr/share/proj/proj_def.dat defaults file 94 | +over Allow longitude output outside -180 to 180 range, disables wrapping (see below) 95 | +pm Alternate prime meridian (typically a city name, see below) 96 | +proj Projection name (see `proj -l`) 97 | +south Denotes southern hemisphere UTM zone 98 | +to_meter Multiplier to convert map units to 1.0m 99 | +towgs84 3 or 7 term datum transform parameters (see below) 100 | +units meters, US survey feet, etc. 101 | +vto_meter vertical conversion to meters. 102 | +vunits vertical units. 103 | +x_0 False easting 104 | +y_0 False northing 105 | +zone UTM zone 106 | +a Semimajor radius of the ellipsoid axis 107 | +alpha ? Used with Oblique Mercator and possibly a few others 108 | +azi 109 | +b Semiminor radius of the ellipsoid axis 110 | +belgium 111 | +beta 112 | +czech 113 | +e Eccentricity of the ellipsoid = sqrt(1 - b^2/a^2) = sqrt( f*(2-f) ) 114 | +ellps Ellipsoid name (see `proj -le`) 115 | +es Eccentricity of the ellipsoid squared 116 | +f Flattening of the ellipsoid (often presented as an inverse, e.g. 1/298) 117 | +gamma 118 | +geoc 119 | +guam 120 | +h 121 | +k Scaling factor (old name) 122 | +K 123 | +k_0 Scaling factor (new name) 124 | +lat_0 Latitude of origin 125 | +lat_1 Latitude of first standard parallel 126 | +lat_2 Latitude of second standard parallel 127 | +lat_b 128 | +lat_t 129 | +lat_ts Latitude of true scale 130 | +lon_0 Central meridian 131 | +lon_1 132 | +lon_2 133 | +lonc ? Longitude used with Oblique Mercator and possibly a few others 134 | +lsat 135 | +m 136 | +M 137 | +n 138 | +no_cut 139 | +no_off 140 | +no_rot 141 | +ns 142 | +o_alpha 143 | +o_lat_1 144 | +o_lat_2 145 | +o_lat_c 146 | +o_lat_p 147 | +o_lon_1 148 | +o_lon_2 149 | +o_lon_c 150 | +o_lon_p 151 | +o_proj 152 | +over 153 | +p 154 | +path 155 | +proj Projection name (see `proj -l`) 156 | +q 157 | +R 158 | +R_a 159 | +R_A Compute radius such that the area of the sphere is the same as the area of the ellipsoid 160 | +rf Reciprocal of the ellipsoid flattening term (e.g. 298) 161 | +R_g 162 | +R_h 163 | +R_lat_a 164 | +R_lat_g 165 | +rot 166 | +R_V 167 | +s 168 | +south Denotes southern hemisphere UTM zone 169 | +sym 170 | +t 171 | +theta 172 | +tilt 173 | +to_meter Multiplier to convert map units to 1.0m 174 | +units meters, US survey feet, etc. 175 | +vopt 176 | +W 177 | +westo 178 | +x_0 False easting 179 | +y_0 False northing 180 | +zone UTM zone 181 | """ 182 | 183 | _lines = filter(lambda x: len(x) > 1, _param_data.split("\n")) 184 | all_proj_keys = list( 185 | set(line.split()[0].lstrip("+").strip() for line in _lines) 186 | ) + ['no_mayo'] 187 | -------------------------------------------------------------------------------- /rasterio/dtypes.py: -------------------------------------------------------------------------------- 1 | # Mapping of GDAL to Numpy data types. 2 | # 3 | # Since 0.13 we are not importing numpy here and data types are strings. 4 | # Happily strings can be used throughout Numpy and so existing code will 5 | # break. 6 | # 7 | # Within Rasterio, to test data types, we use Numpy's dtype() factory to 8 | # do something like this: 9 | # 10 | # if np.dtype(destination.dtype) == np.dtype(rasterio.uint8): ... 11 | # 12 | 13 | bool_ = 'bool' 14 | ubyte = uint8 = 'uint8' 15 | uint16 = 'uint16' 16 | int16 = 'int16' 17 | uint32 = 'uint32' 18 | int32 = 'int32' 19 | float32 = 'float32' 20 | float64 = 'float64' 21 | complex_ = 'complex' 22 | complex64 = 'complex64' 23 | complex128 = 'complex128' 24 | 25 | # Not supported: 26 | # GDT_CInt16 = 8, GDT_CInt32 = 9, GDT_CFloat32 = 10, GDT_CFloat64 = 11 27 | 28 | dtype_fwd = { 29 | 0: None, # GDT_Unknown 30 | 1: ubyte, # GDT_Byte 31 | 2: uint16, # GDT_UInt16 32 | 3: int16, # GDT_Int16 33 | 4: uint32, # GDT_UInt32 34 | 5: int32, # GDT_Int32 35 | 6: float32, # GDT_Float32 36 | 7: float64, # GDT_Float64 37 | 8: complex_, # GDT_CInt16 38 | 9: complex_, # GDT_CInt32 39 | 10: complex64, # GDT_CFloat32 40 | 11: complex128 } # GDT_CFloat64 41 | 42 | dtype_rev = dict((v, k) for k, v in dtype_fwd.items()) 43 | dtype_rev['uint8'] = 1 44 | 45 | typename_fwd = { 46 | 0: 'Unknown', 47 | 1: 'Byte', 48 | 2: 'UInt16', 49 | 3: 'Int16', 50 | 4: 'UInt32', 51 | 5: 'Int32', 52 | 6: 'Float32', 53 | 7: 'Float64', 54 | 8: 'CInt16', 55 | 9: 'CInt32', 56 | 10: 'CFloat32', 57 | 11: 'CFloat64' } 58 | 59 | typename_rev = dict((v, k) for k, v in typename_fwd.items()) 60 | 61 | def _gdal_typename(dt): 62 | try: 63 | return typename_fwd[dtype_rev[dt]] 64 | except KeyError: 65 | return typename_fwd[dtype_rev[dt().dtype.name]] 66 | 67 | def check_dtype(dt): 68 | if dt not in dtype_rev: 69 | try: 70 | return dt().dtype.name in dtype_rev 71 | except: 72 | return False 73 | return True 74 | 75 | 76 | def get_minimum_int_dtype(values): 77 | """ 78 | Uses range checking to determine the minimum integer data type required 79 | to represent values. 80 | 81 | :param values: numpy array 82 | :return: named data type that can be later used to create a numpy dtype 83 | """ 84 | 85 | min_value = values.min() 86 | max_value = values.max() 87 | 88 | if min_value >= 0: 89 | if max_value <= 255: 90 | return uint8 91 | elif max_value <= 65535: 92 | return uint16 93 | elif max_value <= 4294967295: 94 | return uint32 95 | elif min_value >= -32768 and max_value <= 32767: 96 | return int16 97 | elif min_value >= -2147483648 and max_value <= 2147483647: 98 | return int32 99 | -------------------------------------------------------------------------------- /rasterio/enums.py: -------------------------------------------------------------------------------- 1 | 2 | from enum import IntEnum 3 | 4 | class ColorInterp(IntEnum): 5 | undefined=0 6 | grey=1 7 | gray=1 8 | palette=2 9 | red=3 10 | green=4 11 | blue=5 12 | alpha=6 13 | hue=7 14 | saturation=8 15 | lightness=9 16 | cyan=10 17 | magenta=11 18 | yellow=12 19 | black=13 -------------------------------------------------------------------------------- /rasterio/five.py: -------------------------------------------------------------------------------- 1 | # Python 2-3 compatibility 2 | 3 | import itertools 4 | import sys 5 | 6 | if sys.version_info[0] >= 3: 7 | string_types = str, 8 | text_type = str 9 | integer_types = int, 10 | zip_longest = itertools.zip_longest 11 | else: 12 | string_types = basestring, 13 | text_type = unicode 14 | integer_types = int, long 15 | zip_longest = itertools.izip_longest 16 | -------------------------------------------------------------------------------- /rasterio/rio/__init__.py: -------------------------------------------------------------------------------- 1 | # module of CLI commands. 2 | -------------------------------------------------------------------------------- /rasterio/rio/bands.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os.path 3 | import sys 4 | 5 | import click 6 | from cligj import files_inout_arg, format_opt 7 | 8 | import rasterio 9 | 10 | from rasterio.five import zip_longest 11 | from rasterio.rio.cli import cli 12 | 13 | 14 | PHOTOMETRIC_CHOICES = [val.lower() for val in [ 15 | 'MINISBLACK', 16 | 'MINISWHITE', 17 | 'RGB', 18 | 'CMYK', 19 | 'YCBCR', 20 | 'CIELAB', 21 | 'ICCLAB', 22 | 'ITULAB']] 23 | 24 | 25 | # Stack command. 26 | @cli.command(short_help="Stack a number of bands into a multiband dataset.") 27 | @files_inout_arg 28 | @format_opt 29 | @click.option('--bidx', multiple=True, 30 | help="Indexes of input file bands.") 31 | @click.option('--photometric', default=None, 32 | type=click.Choice(PHOTOMETRIC_CHOICES), 33 | help="Photometric interpretation") 34 | @click.pass_context 35 | def stack(ctx, files, driver, bidx, photometric): 36 | """Stack a number of bands from one or more input files into a 37 | multiband dataset. 38 | 39 | Input datasets must be of a kind: same data type, dimensions, etc. The 40 | output is cloned from the first input. 41 | 42 | By default, rio-stack will take all bands from each input and write them 43 | in same order to the output. Optionally, bands for each input may be 44 | specified using a simple syntax: 45 | 46 | --bidx N takes the Nth band from the input (first band is 1). 47 | 48 | --bidx M,N,0 takes bands M, N, and O. 49 | 50 | --bidx M..O takes bands M-O, inclusive. 51 | 52 | --bidx ..N takes all bands up to and including N. 53 | 54 | --bidx N.. takes all bands from N to the end. 55 | 56 | Examples, using the Rasterio testing dataset, which produce a copy. 57 | 58 | rio stack RGB.byte.tif -o stacked.tif 59 | 60 | rio stack RGB.byte.tif --bidx 1,2,3 -o stacked.tif 61 | 62 | rio stack RGB.byte.tif --bidx 1..3 -o stacked.tif 63 | 64 | rio stack RGB.byte.tif --bidx ..2 RGB.byte.tif --bidx 3.. -o stacked.tif 65 | 66 | """ 67 | import numpy as np 68 | 69 | verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 2 70 | logger = logging.getLogger('rio') 71 | try: 72 | with rasterio.drivers(CPL_DEBUG=verbosity>2): 73 | output = files[-1] 74 | files = files[:-1] 75 | output_count = 0 76 | indexes = [] 77 | for path, item in zip_longest(files, bidx, fillvalue=None): 78 | with rasterio.open(path) as src: 79 | src_indexes = src.indexes 80 | if item is None: 81 | indexes.append(src_indexes) 82 | output_count += len(src_indexes) 83 | elif '..' in item: 84 | start, stop = map( 85 | lambda x: int(x) if x else None, item.split('..')) 86 | if start is None: 87 | start = 1 88 | indexes.append(src_indexes[slice(start-1, stop)]) 89 | output_count += len(src_indexes[slice(start-1, stop)]) 90 | else: 91 | parts = list(map(int, item.split(','))) 92 | if len(parts) == 1: 93 | indexes.append(parts[0]) 94 | output_count += 1 95 | else: 96 | parts = list(parts) 97 | indexes.append(parts) 98 | output_count += len(parts) 99 | 100 | with rasterio.open(files[0]) as first: 101 | kwargs = first.meta 102 | kwargs['transform'] = kwargs.pop('affine') 103 | 104 | kwargs.update( 105 | driver=driver, 106 | count=output_count) 107 | 108 | if photometric: 109 | kwargs['photometric'] = photometric 110 | 111 | with rasterio.open(output, 'w', **kwargs) as dst: 112 | dst_idx = 1 113 | for path, index in zip(files, indexes): 114 | with rasterio.open(path) as src: 115 | if isinstance(index, int): 116 | data = src.read(index) 117 | dst.write(data, dst_idx) 118 | dst_idx += 1 119 | elif isinstance(index, list): 120 | data = src.read(index) 121 | dst.write(data, range(dst_idx, dst_idx+len(index))) 122 | dst_idx += len(index) 123 | 124 | sys.exit(0) 125 | except Exception: 126 | logger.exception("Failed. Exception caught") 127 | sys.exit(1) 128 | -------------------------------------------------------------------------------- /rasterio/rio/cli.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import sys 4 | 5 | import click 6 | from cligj import verbose_opt, quiet_opt 7 | 8 | import rasterio 9 | 10 | 11 | def configure_logging(verbosity): 12 | log_level = max(10, 30 - 10*verbosity) 13 | logging.basicConfig(stream=sys.stderr, level=log_level) 14 | 15 | 16 | # The CLI command group. 17 | @click.group(help="Rasterio command line interface.") 18 | @verbose_opt 19 | @quiet_opt 20 | @click.version_option(version=rasterio.__version__, message='%(version)s') 21 | @click.pass_context 22 | def cli(ctx, verbose, quiet): 23 | verbosity = verbose - quiet 24 | configure_logging(verbosity) 25 | ctx.obj = {} 26 | ctx.obj['verbosity'] = verbosity 27 | 28 | 29 | def coords(obj): 30 | """Yield all coordinate coordinate tuples from a geometry or feature. 31 | From python-geojson package.""" 32 | if isinstance(obj, (tuple, list)): 33 | coordinates = obj 34 | elif 'geometry' in obj: 35 | coordinates = obj['geometry']['coordinates'] 36 | else: 37 | coordinates = obj.get('coordinates', obj) 38 | for e in coordinates: 39 | if isinstance(e, (float, int)): 40 | yield tuple(coordinates) 41 | break 42 | else: 43 | for f in coords(e): 44 | yield f 45 | 46 | 47 | def write_features( 48 | fobj, collection, sequence=False, geojson_type='feature', use_rs=False, 49 | **dump_kwds): 50 | """Read an iterator of (feat, bbox) pairs and write to file using 51 | the selected modes.""" 52 | # Sequence of features expressed as bbox, feature, or collection. 53 | if sequence: 54 | for feat in collection(): 55 | xs, ys = zip(*coords(feat)) 56 | bbox = (min(xs), min(ys), max(xs), max(ys)) 57 | if use_rs: 58 | fobj.write(u'\u001e') 59 | if geojson_type == 'feature': 60 | fobj.write(json.dumps(feat, **dump_kwds)) 61 | elif geojson_type == 'bbox': 62 | fobj.write(json.dumps(bbox, **dump_kwds)) 63 | else: 64 | fobj.write( 65 | json.dumps({ 66 | 'type': 'FeatureCollection', 67 | 'bbox': bbox, 68 | 'features': [feat]}, **dump_kwds)) 69 | fobj.write('\n') 70 | # Aggregate all features into a single object expressed as 71 | # bbox or collection. 72 | else: 73 | features = list(collection()) 74 | if geojson_type == 'bbox': 75 | fobj.write(json.dumps(collection.bbox, **dump_kwds)) 76 | elif geojson_type == 'feature': 77 | fobj.write(json.dumps(features[0], **dump_kwds)) 78 | else: 79 | fobj.write(json.dumps({ 80 | 'bbox': collection.bbox, 81 | 'type': 'FeatureCollection', 82 | 'features': features}, 83 | **dump_kwds)) 84 | fobj.write('\n') 85 | -------------------------------------------------------------------------------- /rasterio/rio/info.py: -------------------------------------------------------------------------------- 1 | # Info command. 2 | 3 | import json 4 | import logging 5 | import os.path 6 | import pprint 7 | import sys 8 | 9 | import click 10 | 11 | import rasterio 12 | import rasterio.crs 13 | from rasterio.rio.cli import cli 14 | 15 | 16 | @cli.command(short_help="Print information about the rio environment.") 17 | @click.option('--formats', 'key', flag_value='formats', default=True, 18 | help="Enumerate the available formats.") 19 | @click.pass_context 20 | def env(ctx, key): 21 | """Print information about the Rasterio environment: available 22 | formats, etc. 23 | """ 24 | verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 25 | logger = logging.getLogger('rio') 26 | stdout = click.get_text_stream('stdout') 27 | with rasterio.drivers(CPL_DEBUG=(verbosity > 2)) as env: 28 | if key == 'formats': 29 | for k, v in sorted(env.drivers().items()): 30 | stdout.write("%s: %s\n" % (k, v)) 31 | stdout.write('\n') 32 | 33 | 34 | @cli.command(short_help="Print information about a data file.") 35 | @click.argument('input', type=click.Path(exists=True)) 36 | @click.option('--meta', 'aspect', flag_value='meta', default=True, 37 | help="Show data file structure (default).") 38 | @click.option('--tags', 'aspect', flag_value='tags', 39 | help="Show data file tags.") 40 | @click.option('--namespace', help="Select a tag namespace.") 41 | @click.option('--indent', default=None, type=int, 42 | help="Indentation level for pretty printed output") 43 | # Options to pick out a single metadata item and print it as 44 | # a string. 45 | @click.option('--count', 'meta_member', flag_value='count', 46 | help="Print the count of bands.") 47 | @click.option('--dtype', 'meta_member', flag_value='dtype', 48 | help="Print the dtype name.") 49 | @click.option('--nodata', 'meta_member', flag_value='nodata', 50 | help="Print the nodata value.") 51 | @click.option('-f', '--format', '--driver', 'meta_member', flag_value='driver', 52 | help="Print the format driver.") 53 | @click.option('--shape', 'meta_member', flag_value='shape', 54 | help="Print the (height, width) shape.") 55 | @click.option('--height', 'meta_member', flag_value='height', 56 | help="Print the height (number of rows).") 57 | @click.option('--width', 'meta_member', flag_value='width', 58 | help="Print the width (number of columns).") 59 | @click.option('--crs', 'meta_member', flag_value='crs', 60 | help="Print the CRS as a PROJ.4 string.") 61 | @click.option('--bounds', 'meta_member', flag_value='bounds', 62 | help="Print the boundary coordinates " 63 | "(left, bottom, right, top).") 64 | @click.pass_context 65 | def info(ctx, input, aspect, indent, namespace, meta_member): 66 | """Print metadata about the dataset as JSON. 67 | 68 | Optionally print a single metadata item as a string. 69 | """ 70 | verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 71 | logger = logging.getLogger('rio') 72 | stdout = click.get_text_stream('stdout') 73 | try: 74 | with rasterio.drivers(CPL_DEBUG=(verbosity > 2)): 75 | with rasterio.open(input, 'r-') as src: 76 | info = src.meta 77 | del info['affine'] 78 | del info['transform'] 79 | info['shape'] = info['height'], info['width'] 80 | info['bounds'] = src.bounds 81 | proj4 = rasterio.crs.to_string(src.crs) 82 | if proj4.startswith('+init=epsg'): 83 | proj4 = proj4.split('=')[1].upper() 84 | info['crs'] = proj4 85 | if aspect == 'meta': 86 | if meta_member: 87 | if isinstance(info[meta_member], (list, tuple)): 88 | print(" ".join(map(str, info[meta_member]))) 89 | else: 90 | print(info[meta_member]) 91 | else: 92 | stdout.write(json.dumps(info, indent=indent)) 93 | stdout.write("\n") 94 | elif aspect == 'tags': 95 | stdout.write(json.dumps(src.tags(ns=namespace), 96 | indent=indent)) 97 | stdout.write("\n") 98 | sys.exit(0) 99 | except Exception: 100 | logger.exception("Failed. Exception caught") 101 | sys.exit(1) 102 | -------------------------------------------------------------------------------- /rasterio/rio/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from rasterio.rio.cli import cli 4 | from rasterio.rio.bands import stack 5 | from rasterio.rio.features import shapes, rasterize 6 | from rasterio.rio.info import env, info 7 | from rasterio.rio.merge import merge 8 | from rasterio.rio.rio import bounds, insp, transform 9 | -------------------------------------------------------------------------------- /rasterio/rio/merge.py: -------------------------------------------------------------------------------- 1 | # Merge command. 2 | 3 | import logging 4 | import math 5 | import os.path 6 | import sys 7 | import warnings 8 | 9 | import click 10 | from cligj import files_inout_arg, format_opt 11 | 12 | import rasterio 13 | from rasterio.rio.cli import cli 14 | from rasterio.transform import Affine 15 | 16 | 17 | @cli.command(short_help="Merge a stack of raster datasets.") 18 | @files_inout_arg 19 | @format_opt 20 | @click.option('--bounds', nargs=4, type=float, default=None, 21 | help="Output bounds: left, bottom, right, top.") 22 | @click.option('--res', nargs=2, type=float, default=None, 23 | help="Output dataset resolution: pixel width, pixel height") 24 | @click.option('--nodata', '-n', type=float, default=None, 25 | help="Override nodata values defined in input datasets") 26 | @click.pass_context 27 | def merge(ctx, files, driver, bounds, res, nodata): 28 | """Copy valid pixels from input files to an output file. 29 | 30 | All files must have the same number of bands, data type, and 31 | coordinate reference system. 32 | 33 | Input files are merged in their listed order using the reverse 34 | painter's algorithm. If the output file exists, its values will be 35 | overwritten by input values. 36 | 37 | Geospatial bounds and resolution of a new output file in the 38 | units of the input file coordinate reference system may be provided 39 | and are otherwise taken from the first input file. 40 | """ 41 | import numpy as np 42 | 43 | verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 44 | logger = logging.getLogger('rio') 45 | 46 | try: 47 | with rasterio.drivers(CPL_DEBUG=verbosity>2): 48 | output = files[-1] 49 | files = files[:-1] 50 | 51 | with rasterio.open(files[0]) as first: 52 | first_res = first.res 53 | kwargs = first.meta 54 | kwargs.pop('affine') 55 | nodataval = first.nodatavals[0] 56 | dtype = first.dtypes[0] 57 | 58 | if os.path.exists(output): 59 | # TODO: prompt user to update existing file (-i option) like: 60 | # overwrite b.tif? (y/n [n]) n 61 | # not overwritten 62 | dst = rasterio.open(output, 'r+') 63 | nodataval = dst.nodatavals[0] 64 | dtype = dst.dtypes[0] 65 | dest = np.zeros((dst.count,) + dst.shape, dtype=dtype) 66 | else: 67 | # Create new output file. 68 | # Extent from option or extent of all inputs. 69 | if not bounds: 70 | # scan input files. 71 | xs = [] 72 | ys = [] 73 | for f in files: 74 | with rasterio.open(f) as src: 75 | left, bottom, right, top = src.bounds 76 | xs.extend([left, right]) 77 | ys.extend([bottom, top]) 78 | bounds = min(xs), min(ys), max(xs), max(ys) 79 | output_transform = Affine.translation(bounds[0], bounds[3]) 80 | 81 | # Resolution/pixel size. 82 | if not res: 83 | res = first_res 84 | output_transform *= Affine.scale(res[0], -res[1]) 85 | 86 | # Dataset shape. 87 | output_width = int(math.ceil((bounds[2]-bounds[0])/res[0])) 88 | output_height = int(math.ceil((bounds[3]-bounds[1])/res[1])) 89 | 90 | kwargs['driver'] == driver 91 | kwargs['transform'] = output_transform 92 | kwargs['width'] = output_width 93 | kwargs['height'] = output_height 94 | 95 | dst = rasterio.open(output, 'w', **kwargs) 96 | dest = np.zeros((first.count, output_height, output_width), 97 | dtype=dtype) 98 | 99 | if nodata is not None: 100 | nodataval = nodata 101 | 102 | if nodataval is not None: 103 | # Only fill if the nodataval is within dtype's range. 104 | inrange = False 105 | if np.dtype(dtype).kind in ('i', 'u'): 106 | info = np.iinfo(dtype) 107 | inrange = (info.min <= nodataval <= info.max) 108 | elif np.dtype(dtype).kind == 'f': 109 | info = np.finfo(dtype) 110 | inrange = (info.min <= nodataval <= info.max) 111 | if inrange: 112 | dest.fill(nodataval) 113 | else: 114 | warnings.warn( 115 | "Input file's nodata value, %s, is beyond the valid " 116 | "range of its data type, %s. Consider overriding it " 117 | "using the --nodata option for better results." % ( 118 | nodataval, dtype)) 119 | else: 120 | nodataval = 0 121 | 122 | for fname in reversed(files): 123 | with rasterio.open(fname) as src: 124 | # Real World (tm) use of boundless reads. 125 | # This approach uses the maximum amount of memory to solve 126 | # the problem. Making it more efficient is a TODO. 127 | window = src.window(*dst.bounds) 128 | data = np.zeros_like(dest) 129 | data = src.read( 130 | out=data, 131 | window=window, 132 | boundless=True, 133 | masked=True) 134 | np.copyto(dest, data, 135 | where=np.logical_and( 136 | dest==nodataval, data.mask==False)) 137 | 138 | if dst.mode == 'r+': 139 | data = dst.read(masked=True) 140 | np.copyto(dest, data, 141 | where=np.logical_and( 142 | dest==nodataval, data.mask==False)) 143 | 144 | dst.write(dest) 145 | dst.close() 146 | 147 | sys.exit(0) 148 | except Exception: 149 | logger.exception("Failed. Exception caught") 150 | sys.exit(1) 151 | -------------------------------------------------------------------------------- /rasterio/rio/rio.py: -------------------------------------------------------------------------------- 1 | """Rasterio command line interface""" 2 | 3 | import functools 4 | import json 5 | import logging 6 | import os.path 7 | import pprint 8 | import sys 9 | import warnings 10 | 11 | import click 12 | from cligj import ( 13 | precision_opt, indent_opt, compact_opt, projection_geographic_opt, 14 | projection_projected_opt, projection_mercator_opt, 15 | sequence_opt, use_rs_opt, geojson_type_collection_opt, 16 | geojson_type_feature_opt, geojson_type_bbox_opt) 17 | 18 | import rasterio 19 | from rasterio.rio.cli import cli, write_features 20 | 21 | 22 | warnings.simplefilter('default') 23 | 24 | 25 | # Commands are below. 26 | # 27 | # Command bodies less than ~20 lines, e.g. info() below, can go in this 28 | # module. Longer ones, e.g. insp() shall call functions imported from 29 | # rasterio.tool. 30 | 31 | # Insp command. 32 | @cli.command(short_help="Open a data file and start an interpreter.") 33 | @click.argument('input', type=click.Path(exists=True)) 34 | @click.option( 35 | '--mode', 36 | type=click.Choice(['r', 'r+']), 37 | default='r', 38 | help="File mode (default 'r').") 39 | @click.pass_context 40 | def insp(ctx, input, mode): 41 | import rasterio.tool 42 | verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 43 | logger = logging.getLogger('rio') 44 | try: 45 | with rasterio.drivers(CPL_DEBUG=verbosity>2): 46 | with rasterio.open(input, mode) as src: 47 | rasterio.tool.main( 48 | "Rasterio %s Interactive Inspector (Python %s)\n" 49 | 'Type "src.meta", "src.read_band(1)", or "help(src)" ' 50 | 'for more information.' % ( 51 | rasterio.__version__, 52 | '.'.join(map(str, sys.version_info[:3]))), 53 | src) 54 | sys.exit(0) 55 | except Exception: 56 | logger.exception("Failed. Exception caught") 57 | sys.exit(1) 58 | 59 | 60 | # Bounds command. 61 | @cli.command(short_help="Write bounding boxes to stdout as GeoJSON.") 62 | # One or more files, the bounds of each are a feature in the collection 63 | # object or feature sequence. 64 | @click.argument('input', nargs=-1, type=click.Path(exists=True)) 65 | @precision_opt 66 | @indent_opt 67 | @compact_opt 68 | @projection_geographic_opt 69 | @projection_projected_opt 70 | @projection_mercator_opt 71 | @sequence_opt 72 | @use_rs_opt 73 | @geojson_type_collection_opt(True) 74 | @geojson_type_feature_opt(False) 75 | @geojson_type_bbox_opt(False) 76 | @click.pass_context 77 | def bounds(ctx, input, precision, indent, compact, projection, sequence, 78 | use_rs, geojson_type): 79 | """Write bounding boxes to stdout as GeoJSON for use with, e.g., 80 | geojsonio 81 | 82 | $ rio bounds *.tif | geojsonio 83 | 84 | """ 85 | import rasterio.warp 86 | verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 87 | logger = logging.getLogger('rio') 88 | dump_kwds = {'sort_keys': True} 89 | if indent: 90 | dump_kwds['indent'] = indent 91 | if compact: 92 | dump_kwds['separators'] = (',', ':') 93 | stdout = click.get_text_stream('stdout') 94 | 95 | # This is the generator for (feature, bbox) pairs. 96 | class Collection(object): 97 | 98 | def __init__(self): 99 | self._xs = [] 100 | self._ys = [] 101 | 102 | @property 103 | def bbox(self): 104 | return min(self._xs), min(self._ys), max(self._xs), max(self._ys) 105 | 106 | def __call__(self): 107 | for i, path in enumerate(input): 108 | with rasterio.open(path) as src: 109 | bounds = src.bounds 110 | xs = [bounds[0], bounds[2]] 111 | ys = [bounds[1], bounds[3]] 112 | if projection == 'geographic': 113 | xs, ys = rasterio.warp.transform( 114 | src.crs, {'init': 'epsg:4326'}, xs, ys) 115 | if projection == 'mercator': 116 | xs, ys = rasterio.warp.transform( 117 | src.crs, {'init': 'epsg:3857'}, xs, ys) 118 | if precision >= 0: 119 | xs = [round(v, precision) for v in xs] 120 | ys = [round(v, precision) for v in ys] 121 | bbox = [min(xs), min(ys), max(xs), max(ys)] 122 | 123 | yield { 124 | 'type': 'Feature', 125 | 'bbox': bbox, 126 | 'geometry': { 127 | 'type': 'Polygon', 128 | 'coordinates': [[ 129 | [xs[0], ys[0]], 130 | [xs[1], ys[0]], 131 | [xs[1], ys[1]], 132 | [xs[0], ys[1]], 133 | [xs[0], ys[0]] ]]}, 134 | 'properties': { 135 | 'id': str(i), 'title': path} } 136 | 137 | self._xs.extend(bbox[::2]) 138 | self._ys.extend(bbox[1::2]) 139 | 140 | col = Collection() 141 | # Use the generator defined above as input to the generic output 142 | # writing function. 143 | try: 144 | with rasterio.drivers(CPL_DEBUG=verbosity>2): 145 | write_features( 146 | stdout, col, sequence=sequence, 147 | geojson_type=geojson_type, use_rs=use_rs, 148 | **dump_kwds) 149 | sys.exit(0) 150 | except Exception: 151 | logger.exception("Failed. Exception caught") 152 | sys.exit(1) 153 | 154 | 155 | # Transform command. 156 | @cli.command(short_help="Transform coordinates.") 157 | @click.argument('input', default='-', required=False) 158 | @click.option('--src_crs', default='EPSG:4326', help="Source CRS.") 159 | @click.option('--dst_crs', default='EPSG:4326', help="Destination CRS.") 160 | @precision_opt 161 | @click.pass_context 162 | def transform(ctx, input, src_crs, dst_crs, precision): 163 | import rasterio.warp 164 | 165 | verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 166 | logger = logging.getLogger('rio') 167 | 168 | # Handle the case of file, stream, or string input. 169 | try: 170 | src = click.open_file(input).readlines() 171 | except IOError: 172 | src = [input] 173 | 174 | try: 175 | with rasterio.drivers(CPL_DEBUG=verbosity>2): 176 | if src_crs.startswith('EPSG'): 177 | src_crs = {'init': src_crs} 178 | elif os.path.exists(src_crs): 179 | with rasterio.open(src_crs) as f: 180 | src_crs = f.crs 181 | if dst_crs.startswith('EPSG'): 182 | dst_crs = {'init': dst_crs} 183 | elif os.path.exists(dst_crs): 184 | with rasterio.open(dst_crs) as f: 185 | dst_crs = f.crs 186 | for line in src: 187 | coords = json.loads(line) 188 | xs = coords[::2] 189 | ys = coords[1::2] 190 | xs, ys = rasterio.warp.transform(src_crs, dst_crs, xs, ys) 191 | if precision >= 0: 192 | xs = [round(v, precision) for v in xs] 193 | ys = [round(v, precision) for v in ys] 194 | result = [0]*len(coords) 195 | result[::2] = xs 196 | result[1::2] = ys 197 | print(json.dumps(result)) 198 | 199 | sys.exit(0) 200 | except Exception: 201 | logger.exception("Failed. Exception caught") 202 | sys.exit(1) 203 | -------------------------------------------------------------------------------- /rasterio/tool.py: -------------------------------------------------------------------------------- 1 | 2 | import code 3 | import collections 4 | import logging 5 | import sys 6 | 7 | try: 8 | import matplotlib.pyplot as plt 9 | except ImportError: 10 | plt = None 11 | 12 | import numpy 13 | 14 | import rasterio 15 | 16 | 17 | logger = logging.getLogger('rasterio') 18 | 19 | Stats = collections.namedtuple('Stats', ['min', 'max', 'mean']) 20 | 21 | # Collect dictionary of functions for use in the interpreter in main() 22 | funcs = locals() 23 | 24 | 25 | def show(source, cmap='gray'): 26 | """Show a raster using matplotlib. 27 | 28 | The raster may be either an ndarray or a (dataset, bidx) 29 | tuple. 30 | """ 31 | if isinstance(source, tuple): 32 | arr = source[0].read_band(source[1]) 33 | else: 34 | arr = source 35 | if plt is not None: 36 | plt.imshow(arr, cmap=cmap) 37 | plt.show() 38 | else: 39 | raise ImportError("matplotlib could not be imported") 40 | 41 | 42 | def stats(source): 43 | """Return a tuple with raster min, max, and mean. 44 | """ 45 | if isinstance(source, tuple): 46 | arr = source[0].read_band(source[1]) 47 | else: 48 | arr = source 49 | return Stats(numpy.min(arr), numpy.max(arr), numpy.mean(arr)) 50 | 51 | 52 | def main(banner, dataset): 53 | """ Main entry point for use with interpreter """ 54 | code.interact( 55 | banner, 56 | local=dict(funcs, src=dataset, np=numpy, rio=rasterio, plt=plt)) 57 | 58 | return 0 59 | -------------------------------------------------------------------------------- /rasterio/transform.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | from affine import Affine 4 | 5 | IDENTITY = Affine.identity() 6 | 7 | 8 | def tastes_like_gdal(seq): 9 | """Return True if `seq` matches the GDAL geotransform pattern.""" 10 | return seq[2] == seq[4] == 0.0 and seq[1] > 0 and seq[5] < 0 11 | 12 | 13 | def guard_transform(transform): 14 | """Return an Affine transformation instance""" 15 | if not isinstance(transform, Affine): 16 | if tastes_like_gdal(transform): 17 | warnings.warn( 18 | "GDAL-style transforms are deprecated and will not " 19 | "be supported in Rasterio 1.0.", 20 | FutureWarning, 21 | stacklevel=2) 22 | transform = Affine.from_gdal(*transform) 23 | else: 24 | transform = Affine(*transform) 25 | return transform 26 | -------------------------------------------------------------------------------- /rasterio/warp.py: -------------------------------------------------------------------------------- 1 | """Raster warping and reprojection""" 2 | 3 | from rasterio._warp import _reproject, _transform, _transform_geom, RESAMPLING 4 | from rasterio.transform import guard_transform 5 | 6 | 7 | def transform(src_crs, dst_crs, xs, ys, zs=None): 8 | """ 9 | Transform vectors of x, y and optionally z from source 10 | coordinate reference system into target. 11 | 12 | Parameters 13 | ------------ 14 | src_crs: dict 15 | Source coordinate reference system, in rasterio dict format. 16 | Example: {'init': 'EPSG:4326'} 17 | dst_crs: dict 18 | Target coordinate reference system. 19 | xs: array_like 20 | Contains x values. Will be cast to double floating point values. 21 | ys: array_like 22 | Contains y values. 23 | zs: array_like, optional 24 | Contains z values. Assumed to be all 0 if absent. 25 | 26 | Returns 27 | --------- 28 | out: tuple of array_like, (xs, ys, [zs]) 29 | Tuple of x, y, and optionally z vectors, transformed into the target 30 | coordinate reference system. 31 | """ 32 | return _transform(src_crs, dst_crs, xs, ys, zs) 33 | 34 | 35 | def transform_geom( 36 | src_crs, 37 | dst_crs, 38 | geom, 39 | antimeridian_cutting=False, 40 | antimeridian_offset=10.0, 41 | precision=-1): 42 | """ 43 | Transform geometry from source coordinate reference system into target. 44 | 45 | Parameters 46 | ------------ 47 | src_crs: dict 48 | Source coordinate reference system, in rasterio dict format. 49 | Example: {'init': 'EPSG:4326'} 50 | dst_crs: dict 51 | Target coordinate reference system. 52 | geom: GeoJSON like dict object 53 | antimeridian_cutting: bool, optional 54 | If True, cut geometries at the antimeridian, otherwise geometries will 55 | not be cut (default). 56 | antimeridian_offset: float 57 | Offset from the antimeridian in degrees (default: 10) within which 58 | any geometries will be split. 59 | precision: float 60 | If >= 0, geometry coordinates will be rounded to this number of decimal 61 | places after the transform operation, otherwise original coordinate 62 | values will be preserved (default). 63 | 64 | Returns 65 | --------- 66 | out: GeoJSON like dict object 67 | Transformed geometry in GeoJSON dict format 68 | """ 69 | 70 | return _transform_geom( 71 | src_crs, 72 | dst_crs, 73 | geom, 74 | antimeridian_cutting, 75 | antimeridian_offset, 76 | precision) 77 | 78 | 79 | def reproject( 80 | source, 81 | destination, 82 | src_transform=None, 83 | src_crs=None, 84 | dst_transform=None, 85 | dst_crs=None, 86 | resampling=RESAMPLING.nearest, 87 | **kwargs): 88 | """ 89 | Reproject a source raster to a destination raster. 90 | 91 | Parameters 92 | ------------ 93 | source: ndarray or rasterio Band 94 | Source raster. 95 | destination: ndarray or rasterio Band 96 | Target raster. 97 | src_transform: affine transform object, optional 98 | Source affine transformation. Required if source and destination 99 | are ndarrays. Will be derived from source if it is a rasterio Band. 100 | src_crs: dict, optional 101 | Source coordinate reference system, in rasterio dict format. 102 | Required if source and destination are ndarrays. 103 | Will be derived from source if it is a rasterio Band. 104 | Example: {'init': 'EPSG:4326'} 105 | dst_transform: affine transform object, optional 106 | Target affine transformation. Required if source and destination 107 | are ndarrays. Will be derived from target if it is a rasterio Band. 108 | dst_crs: dict, optional 109 | Target coordinate reference system. Required if source and destination 110 | are ndarrays. Will be derived from target if it is a rasterio Band. 111 | resampling: int 112 | Resampling method to use. One of the following: 113 | RESAMPLING.nearest, 114 | RESAMPLING.bilinear, 115 | RESAMPLING.cubic, 116 | RESAMPLING.cubic_spline, 117 | RESAMPLING.lanczos, 118 | RESAMPLING.average, 119 | RESAMPLING.mode 120 | kwargs: dict, optional 121 | Additional arguments passed to transformation function. 122 | 123 | Returns 124 | --------- 125 | out: None 126 | Output is written to destination. 127 | """ 128 | 129 | if src_transform: 130 | src_transform = guard_transform(src_transform).to_gdal() 131 | if dst_transform: 132 | dst_transform = guard_transform(dst_transform).to_gdal() 133 | 134 | _reproject( 135 | source, 136 | destination, 137 | src_transform, 138 | src_crs, 139 | dst_transform, 140 | dst_crs, 141 | resampling, 142 | **kwargs) 143 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | affine 2 | cligj 3 | coveralls>=0.4 4 | cython>=0.20 5 | delocate 6 | enum34 7 | numpy>=1.8.0 8 | pytest 9 | setuptools>=0.9.8 10 | wheel 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | affine 2 | cligj 3 | enum34 4 | numpy>=1.8.0 5 | setuptools 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | tests=rasterio/tests 3 | nocapture=True 4 | verbosity=3 5 | logging-filter=rasterio 6 | logging-level=DEBUG 7 | with-coverage=1 8 | cover-package=rasterio 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Two environmental variables influence this script. 4 | # 5 | # GDAL_CONFIG: the path to a gdal-config program that points to GDAL headers, 6 | # libraries, and data files. 7 | # 8 | # PACKAGE_DATA: if defined, GDAL and PROJ4 data files will be copied into the 9 | # source or binary distribution. This is essential when creating self-contained 10 | # binary wheels. 11 | 12 | import logging 13 | import os 14 | import pprint 15 | import shutil 16 | import subprocess 17 | import sys 18 | 19 | from setuptools import setup 20 | from setuptools.extension import Extension 21 | 22 | logging.basicConfig() 23 | log = logging.getLogger() 24 | 25 | # python -W all setup.py ... 26 | if 'all' in sys.warnoptions: 27 | log.level = logging.DEBUG 28 | 29 | # Parse the version from the fiona module. 30 | with open('rasterio/__init__.py') as f: 31 | for line in f: 32 | if line.find("__version__") >= 0: 33 | version = line.split("=")[1].strip() 34 | version = version.strip('"') 35 | version = version.strip("'") 36 | continue 37 | 38 | with open('VERSION.txt', 'w') as f: 39 | f.write(version) 40 | 41 | # Use Cython if available. 42 | try: 43 | from Cython.Build import cythonize 44 | except ImportError: 45 | cythonize = None 46 | 47 | # By default we'll try to get options via gdal-config. On systems without, 48 | # options will need to be set in setup.cfg or on the setup command line. 49 | include_dirs = [] 50 | library_dirs = [] 51 | libraries = [] 52 | extra_link_args = [] 53 | 54 | try: 55 | import numpy 56 | include_dirs.append(numpy.get_include()) 57 | except ImportError: 58 | log.critical("Numpy and its headers are required to run setup(). Exiting.") 59 | sys.exit(1) 60 | 61 | try: 62 | gdal_config = os.environ.get('GDAL_CONFIG', 'gdal-config') 63 | with open("gdal-config.txt", "w") as gcfg: 64 | subprocess.call([gdal_config, "--cflags"], stdout=gcfg) 65 | subprocess.call([gdal_config, "--libs"], stdout=gcfg) 66 | subprocess.call([gdal_config, "--datadir"], stdout=gcfg) 67 | with open("gdal-config.txt", "r") as gcfg: 68 | cflags = gcfg.readline().strip() 69 | libs = gcfg.readline().strip() 70 | datadir = gcfg.readline().strip() 71 | for item in cflags.split(): 72 | if item.startswith("-I"): 73 | include_dirs.extend(item[2:].split(":")) 74 | for item in libs.split(): 75 | if item.startswith("-L"): 76 | library_dirs.extend(item[2:].split(":")) 77 | elif item.startswith("-l"): 78 | libraries.append(item[2:]) 79 | else: 80 | # e.g. -framework GDAL 81 | extra_link_args.append(item) 82 | 83 | # Conditionally copy the GDAL data. To be used in conjunction with 84 | # the bdist_wheel command to make self-contained binary wheels. 85 | if os.environ.get('PACKAGE_DATA'): 86 | try: 87 | shutil.rmtree('rasterio/gdal_data') 88 | except OSError: 89 | pass 90 | shutil.copytree(datadir, 'rasterio/gdal_data') 91 | 92 | except Exception as e: 93 | log.warning("Failed to get options via gdal-config: %s", str(e)) 94 | 95 | # Conditionally copy PROJ.4 data. 96 | if os.environ.get('PACKAGE_DATA'): 97 | projdatadir = os.environ.get('PROJ_LIB', '/usr/local/share/proj') 98 | if os.path.exists(projdatadir): 99 | try: 100 | shutil.rmtree('rasterio/proj_data') 101 | except OSError: 102 | pass 103 | shutil.copytree(projdatadir, 'rasterio/proj_data') 104 | 105 | ext_options = dict( 106 | include_dirs=include_dirs, 107 | library_dirs=library_dirs, 108 | libraries=libraries, 109 | extra_link_args=extra_link_args) 110 | 111 | log.debug('ext_options:\n%s', pprint.pformat(ext_options)) 112 | 113 | # When building from a repo, Cython is required. 114 | if os.path.exists("MANIFEST.in") and "clean" not in sys.argv: 115 | log.info("MANIFEST.in found, presume a repo, cythonizing...") 116 | if not cythonize: 117 | log.critical( 118 | "Cython.Build.cythonize not found. " 119 | "Cython is required to build from a repo.") 120 | sys.exit(1) 121 | ext_modules = cythonize([ 122 | Extension( 123 | 'rasterio._base', ['rasterio/_base.pyx'], **ext_options), 124 | Extension( 125 | 'rasterio._io', ['rasterio/_io.pyx'], **ext_options), 126 | Extension( 127 | 'rasterio._copy', ['rasterio/_copy.pyx'], **ext_options), 128 | Extension( 129 | 'rasterio._features', ['rasterio/_features.pyx'], **ext_options), 130 | Extension( 131 | 'rasterio._drivers', ['rasterio/_drivers.pyx'], **ext_options), 132 | Extension( 133 | 'rasterio._warp', ['rasterio/_warp.pyx'], **ext_options), 134 | Extension( 135 | 'rasterio._err', ['rasterio/_err.pyx'], **ext_options), 136 | Extension( 137 | 'rasterio._example', ['rasterio/_example.pyx'], **ext_options), 138 | ], quiet=True) 139 | 140 | # If there's no manifest template, as in an sdist, we just specify .c files. 141 | else: 142 | ext_modules = [ 143 | Extension( 144 | 'rasterio._base', ['rasterio/_base.c'], **ext_options), 145 | Extension( 146 | 'rasterio._io', ['rasterio/_io.c'], **ext_options), 147 | Extension( 148 | 'rasterio._copy', ['rasterio/_copy.c'], **ext_options), 149 | Extension( 150 | 'rasterio._features', ['rasterio/_features.c'], **ext_options), 151 | Extension( 152 | 'rasterio._drivers', ['rasterio/_drivers.c'], **ext_options), 153 | Extension( 154 | 'rasterio._warp', ['rasterio/_warp.cpp'], **ext_options), 155 | Extension( 156 | 'rasterio._err', ['rasterio/_err.c'], **ext_options), 157 | Extension( 158 | 'rasterio._example', ['rasterio/_example.c'], **ext_options), 159 | ] 160 | 161 | with open('README.rst') as f: 162 | readme = f.read() 163 | 164 | # Runtime requirements. 165 | inst_reqs = [ 166 | 'affine>=1.0', 167 | 'cligj', 168 | 'Numpy>=1.7' ] 169 | 170 | if sys.version_info < (3, 4): 171 | inst_reqs.append('enum34') 172 | 173 | setup_args = dict( 174 | name='rasterio', 175 | version=version, 176 | description="Fast and direct raster I/O for use with Numpy and SciPy", 177 | long_description=readme, 178 | classifiers=[ 179 | 'Development Status :: 4 - Beta', 180 | 'Intended Audience :: Developers', 181 | 'Intended Audience :: Information Technology', 182 | 'Intended Audience :: Science/Research', 183 | 'License :: OSI Approved :: BSD License', 184 | 'Programming Language :: C', 185 | 'Programming Language :: Python :: 2.6', 186 | 'Programming Language :: Python :: 2.7', 187 | 'Programming Language :: Python :: 3.3', 188 | 'Programming Language :: Python :: 3.4', 189 | 'Topic :: Multimedia :: Graphics :: Graphics Conversion', 190 | 'Topic :: Scientific/Engineering :: GIS'], 191 | keywords='raster gdal', 192 | author='Sean Gillies', 193 | author_email='sean@mapbox.com', 194 | url='https://github.com/mapbox/rasterio', 195 | license='BSD', 196 | package_dir={'': '.'}, 197 | packages=['rasterio', 'rasterio.rio'], 198 | entry_points=''' 199 | [console_scripts] 200 | rio=rasterio.rio.main:cli 201 | ''', 202 | include_package_data=True, 203 | ext_modules=ext_modules, 204 | zip_safe=False, 205 | install_requires=inst_reqs) 206 | 207 | if os.environ.get('PACKAGE_DATA'): 208 | setup_args['package_data'] = {'rasterio': ['gdal_data/*', 'proj_data/*']} 209 | 210 | setup(**setup_args) 211 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import operator 3 | import os 4 | import sys 5 | 6 | import pytest 7 | from click.testing import CliRunner 8 | 9 | 10 | if sys.version_info > (3,): 11 | reduce = functools.reduce 12 | 13 | test_files = [os.path.join(os.path.dirname(__file__), p) for p in [ 14 | 'data/RGB.byte.tif', 'data/float.tif', 'data/float_nan.tif', 'data/shade.tif']] 15 | 16 | 17 | def pytest_cmdline_main(config): 18 | # Bail if the test raster data is not present. Test data is not 19 | # distributed with sdists since 0.12. 20 | if reduce(operator.and_, map(os.path.exists, test_files)): 21 | print("Test data present.") 22 | else: 23 | print("Test data not present. See download directions in tests/README.txt") 24 | sys.exit(1) 25 | 26 | 27 | @pytest.fixture(scope='function') 28 | def runner(): 29 | return CliRunner() 30 | -------------------------------------------------------------------------------- /tests/data/README.rst: -------------------------------------------------------------------------------- 1 | Testing 2 | ======= 3 | 4 | Rasterio's tests require several raster data files. Grab them from 5 | 6 | https://github.com/mapbox/rasterio/tree/master/tests/data 7 | 8 | and copy them to this directory. 9 | 10 | The RGB.byte.tif file is derived from USGS Landsat 7 ETM imagery. The shade.tif 11 | file is derived from USGS SRTM 90 data. The float.tif and float_nan.tif files 12 | are original works of the Rasterio authors. All test images are licensed under 13 | the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication: 14 | http://creativecommons.org/publicdomain/zero/1.0/. 15 | -------------------------------------------------------------------------------- /tests/data/RGB.byte.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgillies/rasterio/fcc13eb63c62256611b399e2eb8fc65e50133a2b/tests/data/RGB.byte.tif -------------------------------------------------------------------------------- /tests/data/float.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgillies/rasterio/fcc13eb63c62256611b399e2eb8fc65e50133a2b/tests/data/float.tif -------------------------------------------------------------------------------- /tests/data/float_nan.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgillies/rasterio/fcc13eb63c62256611b399e2eb8fc65e50133a2b/tests/data/float_nan.tif -------------------------------------------------------------------------------- /tests/data/shade.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgillies/rasterio/fcc13eb63c62256611b399e2eb8fc65e50133a2b/tests/data/shade.tif -------------------------------------------------------------------------------- /tests/test_band.py: -------------------------------------------------------------------------------- 1 | import rasterio 2 | 3 | def test_band(): 4 | with rasterio.open('tests/data/RGB.byte.tif') as src: 5 | b = rasterio.band(src, 1) 6 | assert b.ds == src 7 | assert b.bidx == 1 8 | assert b.dtype in src.dtypes 9 | assert b.shape == src.shape 10 | 11 | -------------------------------------------------------------------------------- /tests/test_blocks.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os.path 3 | import unittest 4 | import shutil 5 | import subprocess 6 | import sys 7 | import tempfile 8 | 9 | import numpy 10 | 11 | import rasterio 12 | 13 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 14 | 15 | class WindowTest(unittest.TestCase): 16 | def test_window_shape_errors(self): 17 | # Positive height and width are needed when stop is None. 18 | self.assertRaises( 19 | ValueError, 20 | rasterio.window_shape, 21 | (((10, 20),(10, None)),) ) 22 | self.assertRaises( 23 | ValueError, 24 | rasterio.window_shape, 25 | (((None, 10),(10, 20)),) ) 26 | def test_window_shape_None_start(self): 27 | self.assertEqual( 28 | rasterio.window_shape(((None,4),(None,102))), 29 | (4, 102)) 30 | def test_window_shape_None_stop(self): 31 | self.assertEqual( 32 | rasterio.window_shape(((10, None),(10, None)), 100, 90), 33 | (90, 80)) 34 | def test_window_shape_positive(self): 35 | self.assertEqual( 36 | rasterio.window_shape(((0,4),(1,102))), 37 | (4, 101)) 38 | def test_window_shape_negative(self): 39 | self.assertEqual( 40 | rasterio.window_shape(((-10, None),(-10, None)), 100, 90), 41 | (10, 10)) 42 | self.assertEqual( 43 | rasterio.window_shape(((~0, None),(~0, None)), 100, 90), 44 | (1, 1)) 45 | self.assertEqual( 46 | rasterio.window_shape(((None, ~0),(None, ~0)), 100, 90), 47 | (99, 89)) 48 | def test_eval(self): 49 | self.assertEqual( 50 | rasterio.eval_window(((-10, None), (-10, None)), 100, 90), 51 | ((90, 100), (80, 90))) 52 | self.assertEqual( 53 | rasterio.eval_window(((None, -10), (None, -10)), 100, 90), 54 | ((0, 90), (0, 80))) 55 | 56 | def test_window_index(): 57 | idx = rasterio.window_index(((0,4),(1,12))) 58 | assert len(idx) == 2 59 | r, c = idx 60 | assert r.start == 0 61 | assert r.stop == 4 62 | assert c.start == 1 63 | assert c.stop == 12 64 | arr = numpy.ones((20,20)) 65 | assert arr[idx].shape == (4, 11) 66 | 67 | class RasterBlocksTest(unittest.TestCase): 68 | def test_blocks(self): 69 | with rasterio.open('tests/data/RGB.byte.tif') as s: 70 | self.assertEqual(len(s.block_shapes), 3) 71 | self.assertEqual(s.block_shapes, [(3, 791), (3, 791), (3, 791)]) 72 | windows = s.block_windows(1) 73 | (j,i), first = next(windows) 74 | self.assertEqual((j,i), (0, 0)) 75 | self.assertEqual(first, ((0, 3), (0, 791))) 76 | windows = s.block_windows() 77 | (j,i), first = next(windows) 78 | self.assertEqual((j,i), (0, 0)) 79 | self.assertEqual(first, ((0, 3), (0, 791))) 80 | (j, i), second = next(windows) 81 | self.assertEqual((j,i), (1, 0)) 82 | self.assertEqual(second, ((3, 6), (0, 791))) 83 | (j, i), last = list(windows)[~0] 84 | self.assertEqual((j,i), (239, 0)) 85 | self.assertEqual(last, ((717, 718), (0, 791))) 86 | def test_block_coverage(self): 87 | with rasterio.open('tests/data/RGB.byte.tif') as s: 88 | self.assertEqual( 89 | s.width*s.height, 90 | sum((w[0][1]-w[0][0])*(w[1][1]-w[1][0]) 91 | for ji, w in s.block_windows(1))) 92 | 93 | class WindowReadTest(unittest.TestCase): 94 | def test_read_window(self): 95 | with rasterio.open('tests/data/RGB.byte.tif') as s: 96 | windows = s.block_windows(1) 97 | ji, first_window = next(windows) 98 | first_block = s.read_band(1, window=first_window) 99 | self.assertEqual(first_block.dtype, rasterio.ubyte) 100 | self.assertEqual( 101 | first_block.shape, 102 | rasterio.window_shape(first_window)) 103 | 104 | class WindowWriteTest(unittest.TestCase): 105 | def setUp(self): 106 | self.tempdir = tempfile.mkdtemp() 107 | def tearDown(self): 108 | shutil.rmtree(self.tempdir) 109 | def test_write_window(self): 110 | name = os.path.join(self.tempdir, "test_write_window.tif") 111 | a = numpy.ones((50, 50), dtype=rasterio.ubyte) * 127 112 | with rasterio.open( 113 | name, 'w', 114 | driver='GTiff', width=100, height=100, count=1, 115 | dtype=a.dtype) as s: 116 | s.write_band(1, a, window=((30, 80), (10, 60))) 117 | # subprocess.call(["open", name]) 118 | info = subprocess.check_output(["gdalinfo", "-stats", name]) 119 | self.assert_( 120 | "Minimum=0.000, Maximum=127.000, " 121 | "Mean=31.750, StdDev=54.993" in info.decode('utf-8'), 122 | info) 123 | 124 | -------------------------------------------------------------------------------- /tests/test_colorinterp.py: -------------------------------------------------------------------------------- 1 | 2 | import rasterio 3 | from rasterio.enums import ColorInterp 4 | 5 | 6 | def test_colorinterp(tmpdir): 7 | 8 | with rasterio.drivers(): 9 | 10 | with rasterio.open('tests/data/RGB.byte.tif') as src: 11 | assert src.colorinterp(1) == ColorInterp.red 12 | assert src.colorinterp(2) == ColorInterp.green 13 | assert src.colorinterp(3) == ColorInterp.blue 14 | 15 | tiffname = str(tmpdir.join('foo.tif')) 16 | 17 | meta = src.meta 18 | meta['photometric'] = 'CMYK' 19 | meta['count'] = 4 20 | with rasterio.open(tiffname, 'w', **meta) as dst: 21 | assert dst.colorinterp(1) == ColorInterp.cyan 22 | assert dst.colorinterp(2) == ColorInterp.magenta 23 | assert dst.colorinterp(3) == ColorInterp.yellow 24 | assert dst.colorinterp(4) == ColorInterp.black 25 | 26 | -------------------------------------------------------------------------------- /tests/test_colormap.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pytest 3 | import subprocess 4 | import sys 5 | 6 | import rasterio 7 | 8 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 9 | 10 | def test_write_colormap(tmpdir): 11 | 12 | with rasterio.drivers(): 13 | 14 | with rasterio.open('tests/data/shade.tif') as src: 15 | shade = src.read_band(1) 16 | meta = src.meta 17 | 18 | tiffname = str(tmpdir.join('foo.tif')) 19 | 20 | with rasterio.open(tiffname, 'w', **meta) as dst: 21 | dst.write_band(1, shade) 22 | dst.write_colormap(1, {0: (255, 0, 0, 255), 255: (0, 0, 255, 255)}) 23 | cmap = dst.colormap(1) 24 | assert cmap[0] == (255, 0, 0, 255) 25 | assert cmap[255] == (0, 0, 255, 255) 26 | 27 | with rasterio.open(tiffname) as src: 28 | cmap = src.colormap(1) 29 | assert cmap[0] == (255, 0, 0, 255) 30 | assert cmap[255] == (0, 0, 255, 255) 31 | 32 | # subprocess.call(['open', tiffname]) 33 | 34 | -------------------------------------------------------------------------------- /tests/test_coords.py: -------------------------------------------------------------------------------- 1 | 2 | import rasterio 3 | 4 | def test_bounds(): 5 | with rasterio.open('tests/data/RGB.byte.tif') as src: 6 | assert src.bounds == (101985.0, 2611485.0, 339315.0, 2826915.0) 7 | 8 | def test_ul(): 9 | with rasterio.open('tests/data/RGB.byte.tif') as src: 10 | assert src.ul(0, 0) == (101985.0, 2826915.0) 11 | assert src.ul(1, 0) == (101985.0, 2826614.95821727) 12 | assert src.ul(src.height, src.width) == (339315.0, 2611485.0) 13 | assert tuple( 14 | round(v, 6) for v in src.ul(~0, ~0) 15 | ) == (339014.962073, 2611785.041783) 16 | 17 | def test_res(): 18 | with rasterio.open('tests/data/RGB.byte.tif') as src: 19 | assert tuple(round(v, 6) for v in src.res) == (300.037927, 300.041783) 20 | 21 | def test_index(): 22 | with rasterio.open('tests/data/RGB.byte.tif') as src: 23 | assert src.index(101985.0, 2826915.0) == (0, 0) 24 | assert src.index(101985.0+400.0, 2826915.0) == (0, 1) 25 | assert src.index(101985.0+400.0, 2826915.0-700.0) == (2, 1) 26 | 27 | def test_window(): 28 | with rasterio.open('tests/data/RGB.byte.tif') as src: 29 | left, bottom, right, top = src.bounds 30 | assert src.window(left, bottom, right, top) == ((0, src.height), 31 | (0, src.width)) 32 | assert src.window(left, top-400, left+400, top) == ((0, 1), (0, 1)) 33 | assert src.window(left, top-500, left+500, top) == ((0, 2), (0, 2)) 34 | 35 | -------------------------------------------------------------------------------- /tests/test_copy.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os.path 3 | import unittest 4 | import shutil 5 | import subprocess 6 | import sys 7 | import tempfile 8 | 9 | import numpy 10 | 11 | import rasterio 12 | 13 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 14 | 15 | class CopyTest(unittest.TestCase): 16 | def setUp(self): 17 | self.tempdir = tempfile.mkdtemp() 18 | def tearDown(self): 19 | shutil.rmtree(self.tempdir) 20 | def test_copy(self): 21 | name = os.path.join(self.tempdir, 'test_copy.tif') 22 | rasterio.copy( 23 | 'tests/data/RGB.byte.tif', 24 | name) 25 | info = subprocess.check_output(["gdalinfo", name]) 26 | self.assert_("GTiff" in info.decode('utf-8')) 27 | -------------------------------------------------------------------------------- /tests/test_crs.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pytest 3 | import subprocess 4 | import sys 5 | 6 | import rasterio 7 | from rasterio import crs 8 | 9 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 10 | 11 | # When possible, Rasterio gives you the CRS in the form of an EPSG code. 12 | def test_read_epsg(tmpdir): 13 | with rasterio.drivers(): 14 | with rasterio.open('tests/data/RGB.byte.tif') as src: 15 | assert src.crs == {'init': 'epsg:32618'} 16 | 17 | def test_read_epsg3857(tmpdir): 18 | tiffname = str(tmpdir.join('lol.tif')) 19 | subprocess.call([ 20 | 'gdalwarp', '-t_srs', 'EPSG:3857', 21 | 'tests/data/RGB.byte.tif', tiffname]) 22 | with rasterio.drivers(): 23 | with rasterio.open(tiffname) as src: 24 | assert src.crs == {'init': 'epsg:3857'} 25 | 26 | # Ensure that CRS sticks when we write a file. 27 | def test_write_3857(tmpdir): 28 | src_path = str(tmpdir.join('lol.tif')) 29 | subprocess.call([ 30 | 'gdalwarp', '-t_srs', 'EPSG:3857', 31 | 'tests/data/RGB.byte.tif', src_path]) 32 | dst_path = str(tmpdir.join('wut.tif')) 33 | with rasterio.drivers(): 34 | with rasterio.open(src_path) as src: 35 | with rasterio.open(dst_path, 'w', **src.meta) as dst: 36 | assert dst.crs == {'init': 'epsg:3857'} 37 | info = subprocess.check_output([ 38 | 'gdalinfo', dst_path]) 39 | assert """PROJCS["WGS 84 / Pseudo-Mercator", 40 | GEOGCS["WGS 84", 41 | DATUM["WGS_1984", 42 | SPHEROID["WGS 84",6378137,298.257223563, 43 | AUTHORITY["EPSG","7030"]], 44 | AUTHORITY["EPSG","6326"]], 45 | PRIMEM["Greenwich",0], 46 | UNIT["degree",0.0174532925199433], 47 | AUTHORITY["EPSG","4326"]], 48 | PROJECTION["Mercator_1SP"], 49 | PARAMETER["central_meridian",0], 50 | PARAMETER["scale_factor",1], 51 | PARAMETER["false_easting",0], 52 | PARAMETER["false_northing",0], 53 | UNIT["metre",1, 54 | AUTHORITY["EPSG","9001"]], 55 | EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"], 56 | AUTHORITY["EPSG","3857"]]""" in info.decode('utf-8') 57 | 58 | 59 | def test_bare_parameters(): 60 | """ Make sure that bare parameters (e.g., no_defs) are handled properly, 61 | even if they come in with key=True. This covers interaction with pyproj, 62 | which makes presents bare parameters as key=.""" 63 | 64 | # Example produced by pyproj 65 | crs_dict = crs.from_string('+lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True +proj=lcc +x_0=0 +units=m +lat_2=77 +lat_1=49 +lat_0=0') 66 | assert crs_dict.get('no_defs', False) is True 67 | -------------------------------------------------------------------------------- /tests/test_driver_management.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | 4 | import rasterio 5 | from rasterio._drivers import driver_count, GDALEnv 6 | 7 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 8 | 9 | def test_drivers(): 10 | with rasterio.drivers() as m: 11 | assert driver_count() > 0 12 | assert type(m) == GDALEnv 13 | 14 | n = rasterio.drivers() 15 | assert driver_count() > 0 16 | assert type(n) == GDALEnv 17 | 18 | def test_options(tmpdir): 19 | """Test that setting CPL_DEBUG=True results in GDAL debug messages. 20 | """ 21 | logger = logging.getLogger('GDAL') 22 | logger.setLevel(logging.DEBUG) 23 | logfile1 = str(tmpdir.join('test_options1.log')) 24 | fh = logging.FileHandler(logfile1) 25 | logger.addHandler(fh) 26 | 27 | # With CPL_DEBUG=True, expect debug messages from GDAL in 28 | # logfile1 29 | with rasterio.drivers(CPL_DEBUG=True): 30 | with rasterio.open("tests/data/RGB.byte.tif") as src: 31 | pass 32 | 33 | log = open(logfile1).read() 34 | assert "GDAL: GDALOpen(tests/data/RGB.byte.tif" in log 35 | 36 | # The GDAL env above having exited, CPL_DEBUG should be OFF. 37 | logfile2 = str(tmpdir.join('test_options2.log')) 38 | fh = logging.FileHandler(logfile2) 39 | logger.addHandler(fh) 40 | 41 | with rasterio.open("tests/data/RGB.byte.tif") as src: 42 | pass 43 | 44 | # Expect no debug messages from GDAL. 45 | log = open(logfile2).read() 46 | assert "GDAL: GDALOpen(tests/data/RGB.byte.tif" not in log 47 | 48 | -------------------------------------------------------------------------------- /tests/test_dtypes.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import rasterio.dtypes 4 | 5 | def test_np_dt_uint8(): 6 | assert rasterio.dtypes.check_dtype(np.uint8) 7 | 8 | def test_dt_ubyte(): 9 | assert rasterio.dtypes.check_dtype(rasterio.ubyte) 10 | 11 | def test_gdal_name(): 12 | assert rasterio.dtypes._gdal_typename(rasterio.ubyte) == 'Byte' 13 | assert rasterio.dtypes._gdal_typename(np.uint8) == 'Byte' 14 | assert rasterio.dtypes._gdal_typename(np.uint16) == 'UInt16' 15 | -------------------------------------------------------------------------------- /tests/test_features_bounds.py: -------------------------------------------------------------------------------- 1 | from rasterio.features import bounds 2 | 3 | 4 | # Tests copied from Fiona 1.4.1 5 | 6 | def test_bounds_point(): 7 | g = {'type': 'Point', 'coordinates': [10, 10]} 8 | assert bounds(g) == (10, 10, 10, 10) 9 | 10 | 11 | def test_bounds_line(): 12 | g = {'type': 'LineString', 'coordinates': [[0, 0], [10, 10]]} 13 | assert bounds(g) == (0, 0, 10, 10) 14 | 15 | 16 | def test_bounds_polygon(): 17 | g = {'type': 'Polygon', 'coordinates': [[[0, 0], [10, 10], [10, 0]]]} 18 | assert bounds(g) == (0, 0, 10, 10) 19 | 20 | 21 | def test_bounds_z(): 22 | g = {'type': 'Point', 'coordinates': [10, 10, 10]} 23 | assert bounds(g) == (10, 10, 10, 10) 24 | 25 | 26 | # TODO: add these to Fiona with update to bounds 27 | def test_bounds_existing_bbox(): 28 | """ Test with existing bbox in geojson, similar to that produced by 29 | rasterio. Values specifically modified here for testing, bboxes are not 30 | valid as written. 31 | """ 32 | 33 | fc = { 34 | 'bbox': [-107, 40, -105, 41], 35 | 'features': [{ 36 | 'bbox': [-107, 40, -104, 42], 37 | 'geometry': { 38 | 'coordinates': [ 39 | [[-107, 40], [-106, 40], [-106, 41], [-107, 41], [-107, 40]] 40 | ], 41 | 'type': 'Polygon' 42 | }, 43 | 'type': 'Feature' 44 | }], 45 | 'type': 'FeatureCollection' 46 | } 47 | assert bounds(fc['features'][0]) == (-107, 40, -104, 42) 48 | assert bounds(fc) == (-107, 40, -105, 41) 49 | 50 | 51 | def test_feature_collection(): 52 | fc = { 53 | 'features': [{ 54 | 'geometry': { 55 | 'coordinates': [ 56 | [[-107, 40], [-106, 40], [-106, 41], [-107, 41], [-107, 40]] 57 | ], 58 | 'type': 'Polygon' 59 | }, 60 | 'type': 'Feature' 61 | }], 62 | 'type': 'FeatureCollection' 63 | } 64 | assert bounds(fc['features'][0]) == (-107, 40, -106, 41) 65 | assert bounds(fc) == (-107, 40, -106, 41) 66 | -------------------------------------------------------------------------------- /tests/test_features_rasterize.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | import numpy 4 | import pytest 5 | 6 | import rasterio 7 | from rasterio.features import shapes, rasterize 8 | 9 | 10 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 11 | 12 | 13 | def test_rasterize_geometries(): 14 | """ 15 | Make sure that geometries are correctly rasterized according to parameters 16 | """ 17 | 18 | rows = cols = 10 19 | transform = (1.0, 0.0, 0.0, 0.0, 1.0, 0.0) 20 | geometry = { 21 | 'type': 'Polygon', 22 | 'coordinates': [[(2, 2), (2, 4.25), (4.25, 4.25), (4.25, 2), (2, 2)]] 23 | } 24 | 25 | with rasterio.drivers(): 26 | # we expect a subset of the pixels using default mode 27 | result = rasterize([geometry], out_shape=(rows, cols)) 28 | truth = numpy.zeros((rows, cols)) 29 | truth[2:4, 2:4] = 1 30 | assert numpy.array_equal(result, truth) 31 | 32 | out = numpy.zeros((rows, cols)) 33 | result = rasterize([geometry], out=out, default_value=1) 34 | assert numpy.array_equal(out, truth) 35 | 36 | # we expect all touched pixels 37 | result = rasterize( 38 | [geometry], out_shape=(rows, cols), all_touched=True 39 | ) 40 | truth = numpy.zeros((rows, cols)) 41 | truth[2:5, 2:5] = 1 42 | assert numpy.array_equal(result, truth) 43 | 44 | # we expect the pixel value to match the one we pass in 45 | value = 5 46 | result = rasterize([(geometry, value)], out_shape=(rows, cols)) 47 | truth = numpy.zeros((rows, cols)) 48 | truth[2:4, 2:4] = value 49 | assert numpy.array_equal(result, truth) 50 | 51 | # Check the fill and default transform. 52 | # we expect the pixel value to match the one we pass in 53 | value = 5 54 | result = rasterize( 55 | [(geometry, value)], 56 | out_shape=(rows, cols), 57 | fill=1 58 | ) 59 | truth = numpy.ones((rows, cols)) 60 | truth[2:4, 2:4] = value 61 | assert numpy.array_equal(result, truth) 62 | 63 | 64 | def test_rasterize_dtype(): 65 | """Make sure that data types are handled correctly""" 66 | 67 | rows = cols = 10 68 | transform = (1.0, 0.0, 0.0, 0.0, 1.0, 0.0) 69 | geometry = { 70 | 'type': 'Polygon', 71 | 'coordinates': [[(2, 2), (2, 4.25), (4.25, 4.25), (4.25, 2), (2, 2)]] 72 | } 73 | 74 | with rasterio.drivers(): 75 | # Supported types should all work properly 76 | supported_types = ( 77 | ('int16', -32768), 78 | ('int32', -2147483648), 79 | ('uint8', 255), 80 | ('uint16', 65535), 81 | ('uint32', 4294967295), 82 | ('float32', 1.434532), 83 | ('float64', -98332.133422114) 84 | ) 85 | 86 | for dtype, default_value in supported_types: 87 | truth = numpy.zeros((rows, cols), dtype=dtype) 88 | truth[2:4, 2:4] = default_value 89 | 90 | result = rasterize( 91 | [geometry], 92 | out_shape=(rows, cols), 93 | default_value=default_value, 94 | dtype=dtype 95 | ) 96 | assert numpy.array_equal(result, truth) 97 | assert numpy.dtype(result.dtype) == numpy.dtype(truth.dtype) 98 | 99 | result = rasterize( 100 | [(geometry, default_value)], 101 | out_shape=(rows, cols) 102 | ) 103 | if numpy.dtype(dtype).kind == 'f': 104 | assert numpy.allclose(result, truth) 105 | else: 106 | assert numpy.array_equal(result, truth) 107 | # Since dtype is auto-detected, it may not match due to upcasting 108 | 109 | # Unsupported types should all raise exceptions 110 | unsupported_types = ( 111 | ('int8', -127), 112 | ('int64', 20439845334323), 113 | ('float16', -9343.232) 114 | ) 115 | 116 | for dtype, default_value in unsupported_types: 117 | with pytest.raises(ValueError): 118 | rasterize( 119 | [geometry], 120 | out_shape=(rows, cols), 121 | default_value=default_value, 122 | dtype=dtype 123 | ) 124 | 125 | with pytest.raises(ValueError): 126 | rasterize( 127 | [(geometry, default_value)], 128 | out_shape=(rows, cols), 129 | dtype=dtype 130 | ) 131 | 132 | # Mismatched values and dtypes should raise exceptions 133 | mismatched_types = (('uint8', 3.2423), ('uint8', -2147483648)) 134 | for dtype, default_value in mismatched_types: 135 | with pytest.raises(ValueError): 136 | rasterize( 137 | [geometry], 138 | out_shape=(rows, cols), 139 | default_value=default_value, 140 | dtype=dtype 141 | ) 142 | 143 | with pytest.raises(ValueError): 144 | rasterize( 145 | [(geometry, default_value)], 146 | out_shape=(rows, cols), 147 | dtype=dtype 148 | ) 149 | 150 | 151 | def test_rasterize_geometries_symmetric(): 152 | """Make sure that rasterize is symmetric with shapes""" 153 | 154 | rows = cols = 10 155 | transform = (1.0, 0.0, 0.0, 0.0, -1.0, 0.0) 156 | truth = numpy.zeros((rows, cols), dtype=rasterio.ubyte) 157 | truth[2:5, 2:5] = 1 158 | with rasterio.drivers(): 159 | s = shapes(truth, transform=transform) 160 | result = rasterize(s, out_shape=(rows, cols), transform=transform) 161 | assert numpy.array_equal(result, truth) 162 | -------------------------------------------------------------------------------- /tests/test_features_shapes.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | import numpy 4 | import pytest 5 | 6 | import rasterio 7 | import rasterio.features as ftrz 8 | 9 | 10 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 11 | 12 | 13 | def test_shapes(): 14 | """Test creation of shapes from pixel values""" 15 | 16 | image = numpy.zeros((20, 20), dtype=rasterio.ubyte) 17 | image[5:15, 5:15] = 127 18 | with rasterio.drivers(): 19 | shapes = ftrz.shapes(image) 20 | shape, val = next(shapes) 21 | assert shape['type'] == 'Polygon' 22 | assert len(shape['coordinates']) == 2 # exterior and hole 23 | assert val == 0 24 | shape, val = next(shapes) 25 | assert shape['type'] == 'Polygon' 26 | assert len(shape['coordinates']) == 1 # no hole 27 | assert val == 127 28 | try: 29 | shape, val = next(shapes) 30 | except StopIteration: 31 | assert True 32 | else: 33 | assert False 34 | 35 | 36 | def test_shapes_band_shortcut(): 37 | """Test rasterio bands as input to shapes""" 38 | 39 | with rasterio.drivers(): 40 | with rasterio.open('tests/data/shade.tif') as src: 41 | shapes = ftrz.shapes(rasterio.band(src, 1)) 42 | shape, val = next(shapes) 43 | assert shape['type'] == 'Polygon' 44 | assert len(shape['coordinates']) == 1 45 | assert val == 255 46 | 47 | 48 | def test_shapes_internal_driver_manager(): 49 | """Make sure this works if driver is managed outside this test""" 50 | 51 | image = numpy.zeros((20, 20), dtype=rasterio.ubyte) 52 | image[5:15, 5:15] = 127 53 | shapes = ftrz.shapes(image) 54 | shape, val = next(shapes) 55 | assert shape['type'] == 'Polygon' 56 | 57 | 58 | def test_shapes_connectivity(): 59 | """Test connectivity options""" 60 | 61 | image = numpy.zeros((20, 20), dtype=rasterio.ubyte) 62 | image[5:11, 5:11] = 1 63 | image[11, 11] = 1 64 | 65 | shapes = ftrz.shapes(image, connectivity=8) 66 | shape, val = next(shapes) 67 | assert len(shape['coordinates'][0]) == 9 68 | # Note: geometry is not technically valid at this point, it has a self 69 | # intersection at 11,11 70 | 71 | 72 | def test_shapes_dtype(): 73 | """Test image data type handling""" 74 | 75 | rows = cols = 10 76 | with rasterio.drivers(): 77 | supported_types = ( 78 | ('int16', -32768), 79 | ('int32', -2147483648), 80 | ('uint8', 255), 81 | ('uint16', 65535), 82 | ('float32', 1.434532) 83 | ) 84 | 85 | for dtype, test_value in supported_types: 86 | image = numpy.zeros((rows, cols), dtype=dtype) 87 | image[2:5, 2:5] = test_value 88 | 89 | shapes = ftrz.shapes(image) 90 | shape, value = next(shapes) 91 | if dtype == 'float32': 92 | assert round(value, 6) == round(test_value, 6) 93 | else: 94 | assert value == test_value 95 | 96 | # Unsupported types should all raise exceptions 97 | unsupported_types = ( 98 | ('int8', -127), 99 | ('uint32', 4294967295), 100 | ('int64', 20439845334323), 101 | ('float16', -9343.232), 102 | ('float64', -98332.133422114) 103 | ) 104 | 105 | for dtype, test_value in unsupported_types: 106 | with pytest.raises(ValueError): 107 | image = numpy.zeros((rows, cols), dtype=dtype) 108 | image[2:5, 2:5] = test_value 109 | shapes = ftrz.shapes(image) 110 | shape, value = next(shapes) 111 | -------------------------------------------------------------------------------- /tests/test_features_sieve.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | import numpy 4 | import pytest 5 | 6 | import rasterio 7 | import rasterio.features as ftrz 8 | 9 | 10 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 11 | 12 | 13 | def test_sieve(): 14 | """Test sieving a 10x10 feature from an ndarray.""" 15 | 16 | image = numpy.zeros((20, 20), dtype=rasterio.ubyte) 17 | image[5:15, 5:15] = 1 18 | 19 | # An attempt to sieve out features smaller than 100 should not change the 20 | # image. 21 | with rasterio.drivers(): 22 | sieved_image = ftrz.sieve(image, 100) 23 | assert numpy.array_equal(sieved_image, image) 24 | 25 | # Setting the size to 100 should leave us an empty, False image. 26 | with rasterio.drivers(): 27 | sieved_image = ftrz.sieve(image, 101) 28 | assert not sieved_image.any() 29 | 30 | 31 | def test_sieve_connectivity(): 32 | """Test proper behavior of connectivity""" 33 | 34 | image = numpy.zeros((20, 20), dtype=rasterio.ubyte) 35 | image[5:15:2, 5:15] = 1 36 | image[6, 4] = 1 37 | image[8, 15] = 1 38 | image[10, 4] = 1 39 | image[12, 15] = 1 40 | 41 | # Diagonals not connected, all become small features that will be removed 42 | sieved_image = ftrz.sieve(image, 54, connectivity=4) 43 | assert not sieved_image.any() 44 | 45 | # Diagonals connected, everything is retained 46 | sieved_image = ftrz.sieve(image, 54, connectivity=8) 47 | assert numpy.array_equal(sieved_image, image) 48 | 49 | 50 | def test_sieve_output(): 51 | """Test proper behavior of output image, if passed into sieve""" 52 | 53 | with rasterio.drivers(): 54 | shape = (20, 20) 55 | image = numpy.zeros(shape, dtype=rasterio.ubyte) 56 | image[5:15, 5:15] = 1 57 | 58 | # Output should match returned array 59 | output = numpy.zeros_like(image) 60 | output[1:3, 1:3] = 5 61 | sieved_image = ftrz.sieve(image, 100, output=output) 62 | assert numpy.array_equal(output, sieved_image) 63 | 64 | # Output of different dtype should fail 65 | output = numpy.zeros(shape, dtype=rasterio.int32) 66 | with pytest.raises(ValueError): 67 | ftrz.sieve(image, 100, output) 68 | 69 | 70 | def test_sieve_mask(): 71 | """Test proper behavior of mask image, if passed int sieve""" 72 | 73 | with rasterio.drivers(): 74 | shape = (20, 20) 75 | image = numpy.zeros(shape, dtype=rasterio.ubyte) 76 | image[5:15, 5:15] = 1 77 | image[1:3, 1:3] = 2 78 | 79 | # Blank mask has no effect, only areas smaller than size will be 80 | # removed 81 | mask = numpy.ones(shape, dtype=rasterio.bool_) 82 | sieved_image = ftrz.sieve(image, 100, mask=mask) 83 | truth = numpy.zeros_like(image) 84 | truth[5:15, 5:15] = 1 85 | assert numpy.array_equal(sieved_image, truth) 86 | 87 | # Only areas within the overlap of the mask and values will be kept 88 | mask = numpy.ones(shape, dtype=rasterio.bool_) 89 | mask[7:10, 7:10] = False 90 | sieved_image = ftrz.sieve(image, 100, mask=mask) 91 | truth = numpy.zeros_like(image) 92 | truth[7:10, 7:10] = 1 93 | assert numpy.array_equal(sieved_image, truth) 94 | 95 | # mask of other type than rasterio.bool_ should fail 96 | mask = numpy.zeros(shape, dtype=rasterio.uint8) 97 | with pytest.raises(ValueError): 98 | ftrz.sieve(image, 100, mask=mask) 99 | 100 | 101 | def test_dtypes(): 102 | """Test data type support for sieve""" 103 | 104 | rows = cols = 10 105 | with rasterio.drivers(): 106 | supported_types = ( 107 | ('int16', -32768), 108 | ('int32', -2147483648), 109 | ('uint8', 255), 110 | ('uint16', 65535) 111 | ) 112 | 113 | for dtype, test_value in supported_types: 114 | image = numpy.zeros((rows, cols), dtype=dtype) 115 | image[2:5, 2:5] = test_value 116 | 117 | # Sieve should return the original image 118 | sieved_image = ftrz.sieve(image, 2) 119 | assert numpy.array_equal(image, sieved_image) 120 | assert numpy.dtype(sieved_image.dtype).name == dtype 121 | 122 | # Sieve should return a blank image 123 | sieved_image = ftrz.sieve(image, 10) 124 | assert numpy.array_equal(numpy.zeros_like(image), sieved_image) 125 | assert numpy.dtype(sieved_image.dtype).name == dtype 126 | 127 | # Unsupported types should all raise exceptions 128 | unsupported_types = ( 129 | ('int8', -127), 130 | ('uint32', 4294967295), 131 | ('int64', 20439845334323), 132 | ('float16', -9343.232), 133 | ('float32', 1.434532), 134 | ('float64', -98332.133422114) 135 | ) 136 | 137 | for dtype, test_value in unsupported_types: 138 | with pytest.raises(ValueError): 139 | image = numpy.zeros((rows, cols), dtype=dtype) 140 | image[2:5, 2:5] = test_value 141 | sieved_image = ftrz.sieve(image, 2) 142 | -------------------------------------------------------------------------------- /tests/test_indexing.py: -------------------------------------------------------------------------------- 1 | 2 | import rasterio 3 | 4 | def test_index(): 5 | with rasterio.open('tests/data/RGB.byte.tif') as src: 6 | left, bottom, right, top = src.bounds 7 | assert src.index(left, top) == (0, 0) 8 | assert src.index(right, top) == (0, src.width) 9 | assert src.index(right, bottom) == (src.height, src.width) 10 | assert src.index(left, bottom) == (src.height, 0) 11 | 12 | def test_full_window(): 13 | with rasterio.open('tests/data/RGB.byte.tif') as src: 14 | assert src.window(*src.bounds) == tuple(zip((0, 0), src.shape)) 15 | 16 | def test_window_no_exception(): 17 | with rasterio.open('tests/data/RGB.byte.tif') as src: 18 | left, bottom, right, top = src.bounds 19 | left -= 1000.0 20 | assert src.window(left, bottom, right, top) == ( 21 | (0, src.height), (-3, src.width)) 22 | -------------------------------------------------------------------------------- /tests/test_no_georef.py: -------------------------------------------------------------------------------- 1 | # You should be able to write rasters with no georeferencing, e.g., plain old 2 | # PNGs and JPEGs. 3 | 4 | import rasterio 5 | 6 | 7 | def test_write(tmpdir): 8 | name = str(tmpdir.join("test.png")) 9 | with rasterio.open('tests/data/RGB.byte.tif') as src: 10 | kwargs = src.meta.copy() 11 | del kwargs['affine'] 12 | del kwargs['transform'] 13 | del kwargs['crs'] 14 | kwargs['driver'] = 'PNG' 15 | with rasterio.open(name, 'w', **kwargs) as dst: 16 | dst.write(src.read()) 17 | 18 | 19 | def test_read_write(tmpdir): 20 | tif1 = str(tmpdir.join("test.tif")) 21 | tif2 = str(tmpdir.join("test2.tif")) 22 | with rasterio.open('tests/data/RGB.byte.tif') as src: 23 | kwargs = src.meta.copy() 24 | del kwargs['affine'] 25 | del kwargs['transform'] 26 | del kwargs['crs'] 27 | with rasterio.open(tif1, 'w', **kwargs) as dst: 28 | dst.write(src.read()) 29 | with rasterio.open(tif1) as src, rasterio.open(tif2, 'w', **src.meta) as dst: 30 | dst.write(src.read()) 31 | -------------------------------------------------------------------------------- /tests/test_nodata.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pytest 3 | import re 4 | import subprocess 5 | import sys 6 | 7 | import rasterio 8 | 9 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 10 | 11 | def test_nodata(tmpdir): 12 | dst_path = str(tmpdir.join('lol.tif')) 13 | with rasterio.drivers(): 14 | with rasterio.open('tests/data/RGB.byte.tif') as src: 15 | with rasterio.open(dst_path, 'w', **src.meta) as dst: 16 | assert dst.meta['nodata'] == 0.0 17 | assert dst.nodatavals == [0.0, 0.0, 0.0] 18 | info = subprocess.check_output([ 19 | 'gdalinfo', dst_path]) 20 | pattern = b'Band 1.*?NoData Value=0' 21 | assert re.search(pattern, info, re.DOTALL) is not None 22 | pattern = b'Band 2.*?NoData Value=0' 23 | assert re.search(pattern, info, re.DOTALL) is not None 24 | pattern = b'Band 2.*?NoData Value=0' 25 | assert re.search(pattern, info, re.DOTALL) is not None 26 | 27 | def test_set_nodata(tmpdir): 28 | dst_path = str(tmpdir.join('lol.tif')) 29 | with rasterio.drivers(): 30 | with rasterio.open('tests/data/RGB.byte.tif') as src: 31 | meta = src.meta 32 | meta['nodata'] = 42 33 | with rasterio.open(dst_path, 'w', **meta) as dst: 34 | assert dst.meta['nodata'] == 42 35 | assert dst.nodatavals == [42, 42, 42] 36 | info = subprocess.check_output([ 37 | 'gdalinfo', dst_path]) 38 | pattern = b'Band 1.*?NoData Value=42' 39 | assert re.search(pattern, info, re.DOTALL) is not None 40 | pattern = b'Band 2.*?NoData Value=42' 41 | assert re.search(pattern, info, re.DOTALL) is not None 42 | pattern = b'Band 2.*?NoData Value=42' 43 | assert re.search(pattern, info, re.DOTALL) is not None 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /tests/test_pad.py: -------------------------------------------------------------------------------- 1 | 2 | import affine 3 | import numpy 4 | 5 | import rasterio 6 | 7 | 8 | def test_pad(): 9 | arr = numpy.ones((10, 10)) 10 | trans = affine.Affine(1.0, 0.0, 0.0, 0.0, -1.0, 10.0) 11 | arr2, trans2 = rasterio.pad(arr, trans, 2, 'edge') 12 | assert arr2.shape == (14, 14) 13 | assert trans2.xoff == -2.0 14 | assert trans2.yoff == 12.0 15 | 16 | -------------------------------------------------------------------------------- /tests/test_png.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import subprocess 3 | import sys 4 | import re 5 | import numpy 6 | import rasterio 7 | 8 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 9 | 10 | 11 | def test_write_ubyte(tmpdir): 12 | name = str(tmpdir.mkdir("sub").join("test_write_ubyte.png")) 13 | a = numpy.ones((100, 100), dtype=rasterio.ubyte) * 127 14 | with rasterio.open( 15 | name, 'w', 16 | driver='PNG', width=100, height=100, count=1, 17 | dtype=a.dtype) as s: 18 | s.write_band(1, a) 19 | info = subprocess.check_output(["gdalinfo", "-stats", name]).decode('utf-8') 20 | assert "Minimum=127.000, Maximum=127.000, Mean=127.000, StdDev=0.000" in info 21 | -------------------------------------------------------------------------------- /tests/test_read_boundless.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | 4 | import numpy 5 | 6 | import rasterio 7 | 8 | 9 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 10 | 11 | 12 | def test_read_boundless_natural_extent(): 13 | with rasterio.open('tests/data/RGB.byte.tif') as src: 14 | data = src.read(boundless=True) 15 | assert data.shape == (3, src.height, src.width) 16 | assert abs(data[0].mean() - src.read(1).mean()) < 0.0001 17 | assert data.any() 18 | 19 | def test_read_boundless_beyond(): 20 | with rasterio.open('tests/data/RGB.byte.tif') as src: 21 | data = src.read(window=((-200, -100), (-200, -100)), boundless=True) 22 | assert data.shape == (3, 100, 100) 23 | assert not data.any() 24 | 25 | 26 | def test_read_boundless_beyond2(): 27 | with rasterio.open('tests/data/RGB.byte.tif') as src: 28 | data = src.read(window=((1000, 1100), (1000, 1100)), boundless=True) 29 | assert data.shape == (3, 100, 100) 30 | assert not data.any() 31 | 32 | 33 | def test_read_boundless_overlap(): 34 | with rasterio.open('tests/data/RGB.byte.tif') as src: 35 | data = src.read(window=((-200, 200), (-200, 200)), boundless=True) 36 | assert data.shape == (3, 400, 400) 37 | assert data.any() 38 | assert data[0,399,399] == 13 39 | 40 | 41 | def test_read_boundless_resample(): 42 | with rasterio.open('tests/data/RGB.byte.tif') as src: 43 | out = numpy.zeros((3, 800, 800), dtype=numpy.uint8) 44 | data = src.read( 45 | out=out, 46 | window=((-200, 200), (-200, 200)), 47 | masked=True, 48 | boundless=True) 49 | assert data.shape == (3, 800, 800) 50 | assert data.any() 51 | assert data[0,798,798] == 13 52 | -------------------------------------------------------------------------------- /tests/test_read_resample.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | import rasterio 4 | 5 | 6 | # Rasterio exposes GDAL's resampling/decimation on I/O. These are the tests 7 | # that it does this correctly. 8 | # 9 | # Rasterio's test dataset is 718 rows by 791 columns. 10 | 11 | def test_read_out_shape_resample_down(): 12 | with rasterio.open('tests/data/RGB.byte.tif') as s: 13 | out = numpy.zeros((7, 8), dtype=rasterio.ubyte) 14 | data = s.read(1, out=out) 15 | expected = numpy.array([ 16 | [ 0, 8, 5, 7, 0, 0, 0, 0], 17 | [ 0, 6, 61, 15, 27, 15, 24, 128], 18 | [ 0, 20, 152, 23, 15, 19, 28, 0], 19 | [ 0, 17, 255, 25, 255, 22, 32, 0], 20 | [ 9, 7, 14, 16, 19, 18, 36, 0], 21 | [ 6, 27, 43, 207, 38, 31, 73, 0], 22 | [ 0, 0, 0, 0, 74, 23, 0, 0]], dtype=numpy.uint8) 23 | assert (data == expected).all() # all True. 24 | 25 | 26 | def test_read_out_shape_resample_up(): 27 | # Instead of testing array items, test statistics. Upsampling by an even 28 | # constant factor shouldn't change the mean. 29 | with rasterio.open('tests/data/RGB.byte.tif') as s: 30 | out = numpy.zeros((7180, 7910), dtype=rasterio.ubyte) 31 | data = s.read(1, out=out, masked=True) 32 | assert data.shape == (7180, 7910) 33 | assert data.mean() == s.read(1).mean() 34 | -------------------------------------------------------------------------------- /tests/test_revolvingdoor.py: -------------------------------------------------------------------------------- 1 | # Test of opening and closing and opening 2 | 3 | import logging 4 | import os.path 5 | import shutil 6 | import subprocess 7 | import sys 8 | import tempfile 9 | import unittest 10 | 11 | import rasterio 12 | 13 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 14 | log = logging.getLogger('rasterio.tests') 15 | 16 | class RevolvingDoorTest(unittest.TestCase): 17 | 18 | def setUp(self): 19 | self.tempdir = tempfile.mkdtemp() 20 | 21 | def tearDown(self): 22 | shutil.rmtree(self.tempdir) 23 | 24 | def test_write_colormap_revolving_door(self): 25 | 26 | with rasterio.open('tests/data/shade.tif') as src: 27 | shade = src.read_band(1) 28 | meta = src.meta 29 | 30 | tiffname = os.path.join(self.tempdir, 'foo.tif') 31 | 32 | with rasterio.open(tiffname, 'w', **meta) as dst: 33 | dst.write_band(1, shade) 34 | 35 | with rasterio.open(tiffname) as src: 36 | pass 37 | 38 | -------------------------------------------------------------------------------- /tests/test_rio_bands.py: -------------------------------------------------------------------------------- 1 | import click 2 | from click.testing import CliRunner 3 | 4 | import rasterio 5 | from rasterio.rio import bands 6 | 7 | 8 | def test_photometic_choices(): 9 | assert len(bands.PHOTOMETRIC_CHOICES) == 8 10 | 11 | 12 | def test_stack(tmpdir): 13 | outputname = str(tmpdir.join('stacked.tif')) 14 | runner = CliRunner() 15 | result = runner.invoke( 16 | bands.stack, 17 | ['tests/data/RGB.byte.tif', outputname], 18 | catch_exceptions=False) 19 | assert result.exit_code == 0 20 | with rasterio.open(outputname) as out: 21 | assert out.count == 3 22 | 23 | 24 | def test_stack_list(tmpdir): 25 | outputname = str(tmpdir.join('stacked.tif')) 26 | runner = CliRunner() 27 | result = runner.invoke( 28 | bands.stack, 29 | ['tests/data/RGB.byte.tif', '--bidx', '1,2,3', outputname]) 30 | assert result.exit_code == 0 31 | with rasterio.open(outputname) as out: 32 | assert out.count == 3 33 | 34 | 35 | def test_stack_slice(tmpdir): 36 | outputname = str(tmpdir.join('stacked.tif')) 37 | runner = CliRunner() 38 | result = runner.invoke( 39 | bands.stack, 40 | [ 41 | 'tests/data/RGB.byte.tif', '--bidx', '..2', 42 | 'tests/data/RGB.byte.tif', '--bidx', '3..', 43 | outputname]) 44 | assert result.exit_code == 0 45 | with rasterio.open(outputname) as out: 46 | assert out.count == 3 47 | 48 | 49 | def test_stack_single_slice(tmpdir): 50 | outputname = str(tmpdir.join('stacked.tif')) 51 | runner = CliRunner() 52 | result = runner.invoke( 53 | bands.stack, 54 | [ 55 | 'tests/data/RGB.byte.tif', '--bidx', '1', 56 | 'tests/data/RGB.byte.tif', '--bidx', '2..', 57 | '--photometric', 'rgb', 58 | outputname]) 59 | assert result.exit_code == 0 60 | with rasterio.open(outputname) as out: 61 | assert out.count == 3 62 | 63 | 64 | def test_format_jpeg(tmpdir): 65 | outputname = str(tmpdir.join('stacked.jpg')) 66 | runner = CliRunner() 67 | result = runner.invoke( 68 | bands.stack, 69 | ['tests/data/RGB.byte.tif', outputname, '--format', 'JPEG']) 70 | assert result.exit_code == 0 71 | 72 | 73 | def test_error(tmpdir): 74 | outputname = str(tmpdir.join('stacked.tif')) 75 | runner = CliRunner() 76 | result = runner.invoke( 77 | bands.stack, 78 | ['tests/data/RGB.byte.tif', outputname, '--driver', 'BOGUS']) 79 | assert result.exit_code == 1 80 | -------------------------------------------------------------------------------- /tests/test_rio_info.py: -------------------------------------------------------------------------------- 1 | import click 2 | from click.testing import CliRunner 3 | 4 | 5 | import rasterio 6 | from rasterio.rio import cli, info 7 | 8 | 9 | def test_env(): 10 | runner = CliRunner() 11 | result = runner.invoke(info.env, ['--formats']) 12 | assert result.exit_code == 0 13 | assert 'GTiff' in result.output 14 | 15 | 16 | def test_info_err(): 17 | runner = CliRunner() 18 | result = runner.invoke( 19 | info.info, 20 | ['tests']) 21 | assert result.exit_code == 1 22 | 23 | 24 | def test_info(): 25 | runner = CliRunner() 26 | result = runner.invoke( 27 | info.info, 28 | ['tests/data/RGB.byte.tif']) 29 | assert result.exit_code == 0 30 | assert '"count": 3' in result.output 31 | 32 | 33 | def test_info_verbose(): 34 | runner = CliRunner() 35 | result = runner.invoke( 36 | cli.cli, 37 | ['-v', 'info', 'tests/data/RGB.byte.tif']) 38 | assert result.exit_code == 0 39 | 40 | 41 | def test_info_quiet(): 42 | runner = CliRunner() 43 | result = runner.invoke( 44 | cli.cli, 45 | ['-q', 'info', 'tests/data/RGB.byte.tif']) 46 | assert result.exit_code == 0 47 | 48 | 49 | def test_info_count(): 50 | runner = CliRunner() 51 | result = runner.invoke( 52 | info.info, 53 | ['tests/data/RGB.byte.tif', '--count']) 54 | assert result.exit_code == 0 55 | assert result.output == '3\n' 56 | 57 | 58 | def test_info_nodatavals(): 59 | runner = CliRunner() 60 | result = runner.invoke( 61 | info.info, 62 | ['tests/data/RGB.byte.tif', '--bounds']) 63 | assert result.exit_code == 0 64 | assert result.output == '101985.0 2611485.0 339315.0 2826915.0\n' 65 | 66 | 67 | def test_info_tags(): 68 | runner = CliRunner() 69 | result = runner.invoke( 70 | info.info, 71 | ['tests/data/RGB.byte.tif', '--tags']) 72 | assert result.exit_code == 0 73 | assert result.output == '{"AREA_OR_POINT": "Area"}\n' 74 | -------------------------------------------------------------------------------- /tests/test_rio_rio.py: -------------------------------------------------------------------------------- 1 | import click 2 | from click.testing import CliRunner 3 | 4 | 5 | import rasterio 6 | from rasterio.rio import cli, rio 7 | 8 | 9 | def test_version(): 10 | runner = CliRunner() 11 | result = runner.invoke(cli.cli, ['--version']) 12 | assert result.exit_code == 0 13 | assert rasterio.__version__ in result.output 14 | 15 | 16 | def test_insp(): 17 | runner = CliRunner() 18 | result = runner.invoke( 19 | rio.insp, 20 | ['tests/data/RGB.byte.tif']) 21 | assert result.exit_code == 0 22 | 23 | 24 | def test_insp_err(): 25 | runner = CliRunner() 26 | result = runner.invoke( 27 | rio.insp, 28 | ['tests']) 29 | assert result.exit_code == 1 30 | 31 | 32 | def test_bounds_defaults(): 33 | runner = CliRunner() 34 | result = runner.invoke( 35 | rio.bounds, 36 | ['tests/data/RGB.byte.tif']) 37 | assert result.exit_code == 0 38 | assert 'FeatureCollection' in result.output 39 | 40 | 41 | def test_bounds_err(): 42 | runner = CliRunner() 43 | result = runner.invoke( 44 | rio.bounds, 45 | ['tests']) 46 | assert result.exit_code == 1 47 | 48 | 49 | def test_bounds_feature(): 50 | runner = CliRunner() 51 | result = runner.invoke( 52 | rio.bounds, 53 | ['tests/data/RGB.byte.tif', '--feature']) 54 | assert result.exit_code == 0 55 | assert result.output.count('Polygon') == 1 56 | 57 | 58 | def test_bounds_obj_bbox(): 59 | runner = CliRunner() 60 | result = runner.invoke( 61 | rio.bounds, 62 | ['tests/data/RGB.byte.tif', '--bbox', '--precision', '2']) 63 | assert result.exit_code == 0 64 | assert result.output.strip() == '[-78.9, 23.56, -76.6, 25.55]' 65 | 66 | 67 | def test_bounds_compact(): 68 | runner = CliRunner() 69 | result = runner.invoke( 70 | rio.bounds, 71 | ['tests/data/RGB.byte.tif', '--bbox', '--precision', '2', '--compact']) 72 | assert result.exit_code == 0 73 | assert result.output.strip() == '[-78.9,23.56,-76.6,25.55]' 74 | 75 | 76 | def test_bounds_indent(): 77 | runner = CliRunner() 78 | result = runner.invoke( 79 | rio.bounds, 80 | ['tests/data/RGB.byte.tif', '--bbox', '--indent', '2', '--precision', '2']) 81 | assert result.exit_code == 0 82 | assert len(result.output.split('\n')) == 7 83 | 84 | 85 | def test_bounds_obj_bbox_mercator(): 86 | runner = CliRunner() 87 | result = runner.invoke( 88 | rio.bounds, 89 | ['tests/data/RGB.byte.tif', '--bbox', '--mercator', '--precision', '3']) 90 | assert result.exit_code == 0 91 | assert result.output.strip() == '[-8782900.033, 2700489.278, -8527010.472, 2943560.235]' 92 | 93 | 94 | def test_bounds_obj_bbox_projected(): 95 | runner = CliRunner() 96 | result = runner.invoke( 97 | rio.bounds, 98 | ['tests/data/RGB.byte.tif', '--bbox', '--projected', '--precision', '3']) 99 | assert result.exit_code == 0 100 | assert result.output.strip() == '[101985.0, 2611485.0, 339315.0, 2826915.0]' 101 | 102 | 103 | def test_bounds_seq(): 104 | runner = CliRunner() 105 | result = runner.invoke( 106 | rio.bounds, 107 | ['tests/data/RGB.byte.tif', 'tests/data/RGB.byte.tif', '--sequence']) 108 | assert result.exit_code == 0 109 | assert result.output.count('Polygon') == 2 110 | 111 | result = runner.invoke( 112 | rio.bounds, 113 | ['tests/data/RGB.byte.tif', 'tests/data/RGB.byte.tif', '--sequence', '--bbox', '--precision', '2']) 114 | assert result.exit_code == 0 115 | assert result.output == '[-78.9, 23.56, -76.6, 25.55]\n[-78.9, 23.56, -76.6, 25.55]\n' 116 | assert '\x1e' not in result.output 117 | 118 | 119 | def test_bounds_seq_rs(): 120 | runner = CliRunner() 121 | result = runner.invoke( 122 | rio.bounds, 123 | ['tests/data/RGB.byte.tif', 'tests/data/RGB.byte.tif', '--sequence', '--rs', '--bbox', '--precision', '2']) 124 | assert result.exit_code == 0 125 | assert result.output == '\x1e[-78.9, 23.56, -76.6, 25.55]\n\x1e[-78.9, 23.56, -76.6, 25.55]\n' 126 | 127 | 128 | def test_transform_err(): 129 | runner = CliRunner() 130 | result = runner.invoke( 131 | rio.transform, 132 | [], "[-78.0]") 133 | assert result.exit_code == 1 134 | 135 | 136 | def test_transform_point(): 137 | runner = CliRunner() 138 | result = runner.invoke( 139 | rio.transform, 140 | ['--dst_crs', 'EPSG:32618', '--precision', '2'], 141 | "[-78.0, 23.0]", catch_exceptions=False) 142 | assert result.exit_code == 0 143 | assert result.output.strip() == '[192457.13, 2546667.68]' 144 | 145 | 146 | def test_transform_point_dst_file(): 147 | runner = CliRunner() 148 | result = runner.invoke( 149 | rio.transform, 150 | ['--dst_crs', 'tests/data/RGB.byte.tif', '--precision', '2'], 151 | "[-78.0, 23.0]") 152 | assert result.exit_code == 0 153 | assert result.output.strip() == '[192457.13, 2546667.68]' 154 | 155 | 156 | def test_transform_point_src_file(): 157 | runner = CliRunner() 158 | result = runner.invoke( 159 | rio.transform, 160 | ['--src_crs', 'tests/data/RGB.byte.tif', '--precision', '2'], 161 | "[192457.13, 2546667.68]") 162 | assert result.exit_code == 0 163 | assert result.output.strip() == '[-78.0, 23.0]' 164 | 165 | 166 | def test_transform_point_2(): 167 | runner = CliRunner() 168 | result = runner.invoke( 169 | rio.transform, 170 | ['[-78.0, 23.0]', '--dst_crs', 'EPSG:32618', '--precision', '2']) 171 | assert result.exit_code == 0 172 | assert result.output.strip() == '[192457.13, 2546667.68]' 173 | 174 | 175 | def test_transform_point_multi(): 176 | runner = CliRunner() 177 | result = runner.invoke( 178 | rio.transform, 179 | ['--dst_crs', 'EPSG:32618', '--precision', '2'], 180 | "[-78.0, 23.0]\n[-78.0, 23.0]", catch_exceptions=False) 181 | assert result.exit_code == 0 182 | assert result.output.strip() == '[192457.13, 2546667.68]\n[192457.13, 2546667.68]' 183 | -------------------------------------------------------------------------------- /tests/test_tags.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | import logging 3 | import sys 4 | 5 | import pytest 6 | import rasterio 7 | 8 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) 9 | 10 | def test_tags_read(): 11 | with rasterio.open('tests/data/RGB.byte.tif') as src: 12 | assert src.tags() == {'AREA_OR_POINT': 'Area'} 13 | assert src.tags(ns='IMAGE_STRUCTURE') == {'INTERLEAVE': 'PIXEL'} 14 | assert src.tags(ns='bogus') == {} 15 | assert 'STATISTICS_MAXIMUM' in src.tags(1) 16 | with pytest.raises(ValueError): 17 | tags = src.tags(4) 18 | 19 | def test_tags_update(tmpdir): 20 | tiffname = str(tmpdir.join('foo.tif')) 21 | with rasterio.open( 22 | tiffname, 23 | 'w', 24 | driver='GTiff', 25 | count=1, 26 | dtype=rasterio.uint8, 27 | width=10, 28 | height=10) as dst: 29 | 30 | dst.update_tags(a='1', b='2') 31 | dst.update_tags(1, c=3) 32 | with pytest.raises(ValueError): 33 | dst.update_tags(4, d=4) 34 | 35 | assert dst.tags() == {'a': '1', 'b': '2'} 36 | assert dst.tags(1) == {'c': '3' } 37 | 38 | # Assert that unicode tags work. 39 | # Russian text appropriated from pytest issue #319 40 | # https://bitbucket.org/hpk42/pytest/issue/319/utf-8-output-in-assertion-error-converted 41 | dst.update_tags(ns='rasterio_testing', rus=u'другая строка') 42 | assert dst.tags(ns='rasterio_testing') == {'rus': u'другая строка'} 43 | 44 | with rasterio.open(tiffname) as src: 45 | assert src.tags() == {'a': '1', 'b': '2'} 46 | assert src.tags(1) == {'c': '3'} 47 | assert src.tags(ns='rasterio_testing') == {'rus': u'другая строка'} 48 | 49 | def test_tags_update_twice(): 50 | with rasterio.open( 51 | 'test.tif', 'w', 52 | 'GTiff', 3, 4, 1, dtype=rasterio.ubyte) as dst: 53 | dst.update_tags(a=1, b=2) 54 | assert dst.tags() == {'a': '1', 'b': '2'} 55 | dst.update_tags(c=3) 56 | assert dst.tags() == {'a': '1', 'b': '2', 'c': '3'} 57 | 58 | 59 | def test_tags_eq(): 60 | with rasterio.open( 61 | 'test.tif', 'w', 62 | 'GTiff', 3, 4, 1, dtype=rasterio.ubyte) as dst: 63 | dst.update_tags(a="foo=bar") 64 | assert dst.tags() == {'a': "foo=bar"} 65 | -------------------------------------------------------------------------------- /tests/test_tool.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | try: 4 | import matplotlib.pyplot as plt 5 | except ImportError: 6 | plt = None 7 | 8 | import rasterio 9 | from rasterio.tool import show, stats 10 | 11 | 12 | def test_stats(): 13 | with rasterio.drivers(): 14 | with rasterio.open('tests/data/RGB.byte.tif') as src: 15 | results = stats((src, 1)) 16 | assert results[0] == 1 17 | assert results[1] == 255 18 | assert np.isclose(results[2], 44.4344) 19 | 20 | results2 = stats(src.read_band(1)) 21 | assert np.allclose(np.array(results), np.array(results2)) 22 | 23 | 24 | def test_show(): 25 | """ 26 | This test only verifies that code up to the point of plotting with 27 | matplotlib works correctly. Tests do not exercise matplotlib. 28 | """ 29 | if plt: 30 | # Return because plotting causes the tests to block until the plot 31 | # window is closed. 32 | return 33 | 34 | with rasterio.drivers(): 35 | with rasterio.open('tests/data/RGB.byte.tif') as src: 36 | try: 37 | show((src, 1)) 38 | except ImportError: 39 | pass 40 | 41 | try: 42 | show(src.read_band(1)) 43 | except ImportError: 44 | pass -------------------------------------------------------------------------------- /tests/test_transform.py: -------------------------------------------------------------------------------- 1 | 2 | import rasterio 3 | 4 | def test_window(): 5 | with rasterio.open('tests/data/RGB.byte.tif') as src: 6 | left, bottom, right, top = src.bounds 7 | assert src.window(left, bottom, right, top) == ((0, src.height), 8 | (0, src.width)) 9 | assert src.window(left, top-src.res[1], left+src.res[0], top) == ( 10 | (0, 1), (0, 1)) 11 | 12 | -------------------------------------------------------------------------------- /tests/test_update.py: -------------------------------------------------------------------------------- 1 | 2 | import shutil 3 | import subprocess 4 | import re 5 | 6 | import affine 7 | import numpy 8 | import pytest 9 | 10 | import rasterio 11 | 12 | def test_update_tags(tmpdir): 13 | tiffname = str(tmpdir.join('foo.tif')) 14 | shutil.copy('tests/data/RGB.byte.tif', tiffname) 15 | with rasterio.open(tiffname, 'r+') as f: 16 | f.update_tags(a='1', b='2') 17 | f.update_tags(1, c=3) 18 | with pytest.raises(ValueError): 19 | f.update_tags(4, d=4) 20 | assert f.tags() == {'AREA_OR_POINT': 'Area', 'a': '1', 'b': '2'} 21 | assert ('c', '3') in f.tags(1).items() 22 | info = subprocess.check_output(["gdalinfo", tiffname]).decode('utf-8') 23 | assert re.search("Metadata:\W+a=1\W+AREA_OR_POINT=Area\W+b=2", info) 24 | 25 | def test_update_band(tmpdir): 26 | tiffname = str(tmpdir.join('foo.tif')) 27 | shutil.copy('tests/data/RGB.byte.tif', tiffname) 28 | with rasterio.open(tiffname, 'r+') as f: 29 | f.write_band(1, numpy.zeros(f.shape, dtype=f.dtypes[0])) 30 | with rasterio.open(tiffname) as f: 31 | assert not f.read_band(1).any() 32 | 33 | def test_update_spatial(tmpdir): 34 | tiffname = str(tmpdir.join('foo.tif')) 35 | shutil.copy('tests/data/RGB.byte.tif', tiffname) 36 | with rasterio.open(tiffname, 'r+') as f: 37 | f.transform = affine.Affine.from_gdal(1.0, 1.0, 0.0, 0.0, 0.0, -1.0) 38 | f.crs = {'init': 'epsg:4326'} 39 | with rasterio.open(tiffname) as f: 40 | assert list(f.transform) == [1.0, 1.0, 0.0, 0.0, 0.0, -1.0] 41 | assert list(f.affine.to_gdal()) == [1.0, 1.0, 0.0, 0.0, 0.0, -1.0] 42 | assert f.crs == {'init': 'epsg:4326'} 43 | 44 | def test_update_spatial_epsg(tmpdir): 45 | tiffname = str(tmpdir.join('foo.tif')) 46 | shutil.copy('tests/data/RGB.byte.tif', tiffname) 47 | with rasterio.open(tiffname, 'r+') as f: 48 | f.transform = affine.Affine.from_gdal(1.0, 1.0, 0.0, 0.0, 0.0, -1.0) 49 | f.crs = 'EPSG:4326' 50 | with rasterio.open(tiffname) as f: 51 | assert list(f.transform) == [1.0, 1.0, 0.0, 0.0, 0.0, -1.0] 52 | assert list(f.affine.to_gdal()) == [1.0, 1.0, 0.0, 0.0, 0.0, -1.0] 53 | assert f.crs == {'init': 'epsg:4326'} 54 | 55 | def test_update_nodatavals(tmpdir): 56 | tiffname = str(tmpdir.join('foo.tif')) 57 | shutil.copy('tests/data/RGB.byte.tif', tiffname) 58 | with rasterio.open(tiffname, 'r+') as f: 59 | f.nodatavals = [-1, -1, -1] 60 | with rasterio.open(tiffname) as f: 61 | assert f.nodatavals == [-1, -1, -1] 62 | --------------------------------------------------------------------------------