Skip to content

Commits on Source 4

......@@ -2,10 +2,6 @@ dist: trusty
language: python
python:
- "2.7"
- "3.6"
env:
global:
- PIP_WHEEL_DIR=$HOME/.cache/pip/wheels
......@@ -14,17 +10,33 @@ env:
- GDALBUILD=$HOME/gdalbuild
- PROJINST=$HOME/gdalinstall
- PROJBUILD=$HOME/projbuild
matrix:
- GDALVERSION="1.11.5" PROJVERSION="4.8.0"
- GDALVERSION="2.0.3" PROJVERSION="4.9.3"
- GDALVERSION="2.1.4" PROJVERSION="4.9.3"
- GDALVERSION="2.2.4" PROJVERSION="4.9.3"
- GDALVERSION="2.3.3" PROJVERSION="4.9.3"
- GDALVERSION="2.4.2" PROJVERSION="4.9.3"
- GDALVERSION="3.0.1" PROJVERSION="6.1.1"
- GDALVERSION="master" PROJVERSION="6.1.1"
matrix:
jobs:
include:
- python: "2.7"
env: GDALVERSION="1.11.5" PROJVERSION="4.8.0"
- python: "2.7"
env: GDALVERSION="2.2.4" PROJVERSION="4.9.3"
- python: "3.6"
env: GDALVERSION="2.2.4" PROJVERSION="4.9.3"
- python: "3.6"
env: GDALVERSION="2.3.3" PROJVERSION="4.9.3"
- python: "2.7"
env: GDALVERSION="2.4.3" PROJVERSION="4.9.3"
- python: "3.6"
env: GDALVERSION="2.4.3" PROJVERSION="4.9.3"
- python: "3.7"
env: GDALVERSION="2.4.3" PROJVERSION="4.9.3"
- python: "3.8"
env: GDALVERSION="2.4.3" PROJVERSION="4.9.3"
- python: "3.6"
env: GDALVERSION="3.0.1" PROJVERSION="6.1.1"
- python: "3.7"
env: GDALVERSION="3.0.1" PROJVERSION="6.1.1"
- python: "3.8"
env: GDALVERSION="3.0.1" PROJVERSION="6.1.1"
- python: "3.8"
env: GDALVERSION="master" PROJVERSION="6.1.1"
allow_failures:
- env: GDALVERSION="master" PROJVERSION="6.1.1"
......
Changes
=======
1.1.1 (2019-11-13)
------------------
Bug fixes:
- Calling a dataset's sample method with coordinates outside the extent of a
dataset with a nodata value of None has raised a TypeError (#1822). Now, it
gives the values we would get from a boundless read of the dataset's values.
- Use new set_proj_search_path() function to set the PROJ data search path. For
GDAL versions before 3.0 this sets the PROJ_LIB environment variable. For
GDAL version 3.0 this calls OSRSetPROJSearchPaths(), which overrides
PROJ_LIB (#1823).
- Check for header.dxf file instead of pcs.csv when looking for installed GDAL
data. The latter is gone with GDAL 3.0 but the former remains (#1823).
- RGB rasters are now properly displayed by rasterio.plot.show (#1650).
Notes:
- The wheels on PyPI include GDAL 2.4.3 with a patch that addresses the
multithreading issue reported in #1828.
1.1.0 (2019-10-07)
------------------
......
rasterio (1.1.1-1) unstable; urgency=medium
* Team upload.
* New upstream release.
-- Bas Couwenberg <sebastic@debian.org> Thu, 14 Nov 2019 05:29:17 +0100
rasterio (1.1.0-1) unstable; urgency=medium
* Team upload.
......
......@@ -42,7 +42,7 @@ import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
__version__ = "1.1.0"
__version__ = "1.1.1"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
......
......@@ -20,6 +20,7 @@ import threading
from rasterio._base cimport _safe_osr_release
from rasterio._err import CPLE_BaseError
from rasterio._err cimport exc_wrap_ogrerr, exc_wrap_int
from rasterio._shim cimport set_proj_search_path
from libc.stdio cimport stderr
......@@ -243,18 +244,18 @@ class GDALDataFinder(object):
if prefix is None:
prefix = __file__
datadir = os.path.abspath(os.path.join(os.path.dirname(prefix), "gdal_data"))
return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
return datadir if os.path.exists(os.path.join(datadir, 'header.dxf')) else None
def search_prefix(self, prefix=sys.prefix):
"""Check sys.prefix location"""
datadir = os.path.join(prefix, 'share', 'gdal')
return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
return datadir if os.path.exists(os.path.join(datadir, 'header.dxf')) else None
def search_debian(self, prefix=sys.prefix):
"""Check Debian locations"""
gdal_release_name = GDALVersionInfo("RELEASE_NAME")
datadir = os.path.join(prefix, 'share', 'gdal', '{}.{}'.format(*gdal_release_name.split('.')[:2]))
return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
return datadir if os.path.exists(os.path.join(datadir, 'header.dxf')) else None
@contextmanager
......@@ -359,6 +360,8 @@ cdef class GDALEnv(ConfigEnv):
if 'PROJ_LIB' in os.environ:
log.debug("PROJ_LIB found in environment: %r.", os.environ['PROJ_LIB'])
path = os.environ["PROJ_LIB"]
set_proj_data_search_path(path)
elif PROJDataFinder().has_data():
log.debug("PROJ data files are available at built-in paths")
......@@ -367,8 +370,8 @@ cdef class GDALEnv(ConfigEnv):
path = PROJDataFinder().search()
if path:
os.environ['PROJ_LIB'] = path
log.debug("PROJ data not found in environment, set to %r.", path)
log.debug("PROJ data not found in environment, setting to %r.", path)
set_proj_data_search_path(path)
if driver_count() == 0:
CPLPopErrorHandler()
......@@ -404,3 +407,8 @@ cdef class GDALEnv(ConfigEnv):
def _dump_open_datasets(self):
GDALDumpOpenDatasets(stderr)
def set_proj_data_search_path(path):
"""Set PROJ data search path"""
set_proj_search_path(path)
......@@ -762,30 +762,35 @@ cdef class DatasetReaderBase(DatasetBase):
mask = mask | self.read_masks(i, **kwargs)
return mask
def sample(self, xy, indexes=None):
def sample(self, xy, indexes=None, masked=False):
"""Get the values of a dataset at certain positions
Values are from the nearest pixel. They are not interpolated.
Parameters
----------
xy : iterable, pairs of floats
A sequence or generator of (x, y) pairs.
indexes : list of ints or a single int, optional
If `indexes` is a list, the result is a 3D array, but is
a 2D array if it is a band index number.
xy : iterable
Pairs of x, y coordinates (floats) in the dataset's
reference system.
indexes : int or list of int
Indexes of dataset bands to sample.
masked : bool, default: False
Whether to mask samples that fall outside the extent of the
dataset.
Returns
-------
Iterable, yielding dataset values for the specified `indexes`
as an ndarray.
------
iterable
Arrays of length equal to the number of specified indexes
containing the dataset values for the bands corresponding to
those indexes.
"""
# In https://github.com/mapbox/rasterio/issues/378 a user has
# found what looks to be a Cython generator bug. Until that can
# be confirmed and fixed, the workaround is a pure Python
# generator implemented in sample.py.
return sample_gen(self, xy, indexes)
return sample_gen(self, xy, indexes=indexes, masked=masked)
@contextmanager
......
......@@ -7,3 +7,4 @@ cdef int io_multi_band(GDALDatasetH hds, int mode, float xoff, float yoff, float
cdef int io_multi_mask(GDALDatasetH hds, int mode, float xoff, float yoff, float width, float height, object data, Py_ssize_t[:] indexes, int resampling=*) except -1
cdef const char* osr_get_name(OGRSpatialReferenceH hSrs)
cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs)
cdef void set_proj_search_path(object path)
......@@ -5,6 +5,8 @@ include "directives.pxi"
# The baseline GDAL API.
include "gdal.pxi"
import os
# Implementation specific to GDAL<2.0
from rasterio import dtypes
from rasterio.enums import Resampling
......@@ -185,3 +187,7 @@ cdef const char* osr_get_name(OGRSpatialReferenceH hSrs):
cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs):
pass
cdef void set_proj_search_path(object path):
os.environ["PROJ_LIB"] = path
......@@ -8,6 +8,7 @@ include "gdal.pxi"
# Shim API for GDAL >= 2.0
include "shim_rasterioex.pxi"
import os
# Declarations and implementations specific for GDAL = 2.0
cdef extern from "gdal.h" nogil:
......@@ -77,3 +78,7 @@ cdef const char* osr_get_name(OGRSpatialReferenceH hSrs):
cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs):
pass
cdef void set_proj_search_path(object path):
os.environ["PROJ_LIB"] = path
......@@ -10,6 +10,7 @@ include "gdal.pxi"
# Shim API for GDAL >= 2.0
include "shim_rasterioex.pxi"
import os
# Declarations and implementations specific for GDAL >= 2.1
cdef extern from "gdal.h" nogil:
......@@ -78,3 +79,7 @@ cdef const char* osr_get_name(OGRSpatialReferenceH hSrs):
cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs):
pass
cdef void set_proj_search_path(object path):
os.environ["PROJ_LIB"] = path
......@@ -25,6 +25,7 @@ cdef extern from "ogr_srs_api.h" nogil:
const char* OSRGetName(OGRSpatialReferenceH hSRS)
void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS, OSRAxisMappingStrategy)
void OSRSetPROJSearchPaths(const char *const *papszPaths)
from rasterio._err cimport exc_wrap_pointer
......@@ -90,3 +91,12 @@ cdef const char* osr_get_name(OGRSpatialReferenceH hSrs):
cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs):
OSRSetAxisMappingStrategy(hSrs, OAMS_TRADITIONAL_GIS_ORDER)
cdef void set_proj_search_path(object path):
cdef char **paths = NULL
cdef const char *path_c = NULL
path_b = path.encode("utf-8")
path_c = path_b
paths = CSLAddString(paths, path_c)
OSRSetPROJSearchPaths(paths)
......@@ -10,7 +10,7 @@ import warnings
from rasterio._env import (
GDALEnv, get_gdal_config, set_gdal_config,
GDALDataFinder, PROJDataFinder)
GDALDataFinder, PROJDataFinder, set_proj_data_search_path)
from rasterio.compat import string_types, getargspec
from rasterio.errors import (
EnvError, GDALVersionError, RasterioDeprecationWarning)
......@@ -645,13 +645,9 @@ if 'GDAL_DATA' not in os.environ:
os.environ['GDAL_DATA'] = path
log.debug("GDAL_DATA not found in environment, set to %r.", path)
if 'PROJ_LIB' not in os.environ:
path = PROJDataFinder().search_wheel()
if path:
os.environ['PROJ_LIB'] = path
log.debug("PROJ data found in package, PROJ_LIB set to %r.", path)
if "PROJ_LIB" in os.environ:
path = os.environ["PROJ_LIB"]
set_proj_data_search_path(path)
# See https://github.com/mapbox/rasterio/issues/1631.
elif PROJDataFinder().has_data():
......@@ -661,5 +657,5 @@ if 'PROJ_LIB' not in os.environ:
path = PROJDataFinder().search()
if path:
os.environ['PROJ_LIB'] = path
log.debug("PROJ data not found in environment, set to %r.", path)
log.debug("PROJ data not found in environment, setting to %r.", path)
set_proj_data_search_path(path)
......@@ -93,10 +93,19 @@ def show(source, with_bounds=True, contour=False, contour_label_kws=None,
arr = source.read(1, masked=True)
else:
try:
source_colorinterp = OrderedDict(zip(source.indexes, source.colorinterp))
# Lookup table for the color space in the source file. This will allow us to re-order it
# to RGB if needed
source_colorinterp = OrderedDict(zip(source.colorinterp, source.indexes))
colorinterp = rasterio.enums.ColorInterp
# Gather the indexes of the RGB channels in that order
rgb_indexes = [source_colorinterp[ci] for ci in
(colorinterp.red, colorinterp.green, colorinterp.blue)]
# Read the image in the proper order so the numpy array will have the colors in the
# order expected by matplotlib (RGB)
arr = source.read(rgb_indexes, masked=True)
arr = reshape_as_image(arr)
......
......@@ -5,8 +5,29 @@ import numpy
from rasterio.windows import Window
def sample_gen(dataset, xy, indexes=None):
"""Generator for sampled pixels"""
def sample_gen(dataset, xy, indexes=None, masked=False):
"""Sample pixels from a dataset
Parameters
----------
dataset : rasterio Dataset
Opened in "r" mode.
xy : iterable
Pairs of x, y coordinates in the dataset's reference system.
indexes : int or list of int
Indexes of dataset bands to sample.
masked : bool, default: False
Whether to mask samples that fall outside the extent of the
dataset.
Yields
------
array
A array of length equal to the number of specified indexes
containing the dataset values for the bands corresponding to
those indexes.
"""
index = dataset.index
read = dataset.read
......@@ -15,9 +36,9 @@ def sample_gen(dataset, xy, indexes=None):
for x, y in xy:
row_off, col_off = index(x, y)
if row_off < 0 or col_off < 0:
yield numpy.ones((dataset.count,), dtype=dataset.dtypes[0]) * dataset.nodata
else:
# if row_off < 0 or col_off < 0:
# yield numpy.ones((dataset.count,), dtype=dataset.dtypes[0]) * dataset.nodata
# else:
window = Window(col_off, row_off, 1, 1)
data = read(indexes, window=window, masked=False)
data = read(indexes, window=window, masked=masked, boundless=True)
yield data[:, 0, 0]
......@@ -13,7 +13,7 @@ def mock_wheel(tmpdir):
moduledir = tmpdir.mkdir("rasterio")
moduledir.ensure("__init__.py")
moduledir.ensure("_env.py")
moduledir.ensure("gdal_data/pcs.csv")
moduledir.ensure("gdal_data/header.dxf")
moduledir.ensure("proj_data/epsg")
return moduledir
......@@ -21,7 +21,7 @@ def mock_wheel(tmpdir):
@pytest.fixture
def mock_fhs(tmpdir):
"""A fake FHS system"""
tmpdir.ensure("share/gdal/pcs.csv")
tmpdir.ensure("share/gdal/header.dxf")
tmpdir.ensure("share/proj/epsg")
return tmpdir
......@@ -29,13 +29,13 @@ def mock_fhs(tmpdir):
@pytest.fixture
def mock_debian(tmpdir):
"""A fake Debian multi-install system"""
tmpdir.ensure("share/gdal/1.11/pcs.csv")
tmpdir.ensure("share/gdal/2.0/pcs.csv")
tmpdir.ensure("share/gdal/2.1/pcs.csv")
tmpdir.ensure("share/gdal/2.2/pcs.csv")
tmpdir.ensure("share/gdal/2.3/pcs.csv")
tmpdir.ensure("share/gdal/2.4/pcs.csv")
tmpdir.ensure("share/gdal/3.0/pcs.csv")
tmpdir.ensure("share/gdal/1.11/header.dxf")
tmpdir.ensure("share/gdal/2.0/header.dxf")
tmpdir.ensure("share/gdal/2.1/header.dxf")
tmpdir.ensure("share/gdal/2.2/header.dxf")
tmpdir.ensure("share/gdal/2.3/header.dxf")
tmpdir.ensure("share/gdal/2.4/header.dxf")
tmpdir.ensure("share/gdal/3.0/header.dxf")
tmpdir.ensure("share/proj/epsg")
return tmpdir
......
......@@ -138,7 +138,7 @@ def test_ensure_env_decorator_sets_gdal_data_prefix(find_file, gdalenv, monkeypa
find_file.return_value = None
tmpdir.ensure("share/gdal/pcs.csv")
tmpdir.ensure("share/gdal/header.dxf")
monkeypatch.delenv('GDAL_DATA', raising=False)
monkeypatch.setattr(sys, 'prefix', str(tmpdir))
......@@ -154,7 +154,7 @@ def test_ensure_env_decorator_sets_gdal_data_wheel(find_file, gdalenv, monkeypat
find_file.return_value = None
tmpdir.ensure("gdal_data/pcs.csv")
tmpdir.ensure("gdal_data/header.dxf")
monkeypatch.delenv('GDAL_DATA', raising=False)
monkeypatch.setattr(_env, '__file__', str(tmpdir.join(os.path.basename(_env.__file__))))
......
import numpy
import rasterio
......@@ -13,6 +15,24 @@ def test_sampling_beyond_bounds():
assert list(data) == [0, 0, 0]
def test_sampling_beyond_bounds_no_nodata():
with rasterio.open('tests/data/RGB2.byte.tif') as src:
data = next(src.sample([(-10, 2719200.0)]))
assert list(data) == [0, 0, 0]
def test_sampling_beyond_bounds_masked():
with rasterio.open('tests/data/RGBA.byte.tif') as src:
data = next(src.sample([(-10, 2719200.0)], masked=True))
assert list(data.mask) == [True, True, True, False]
def test_sampling_beyond_bounds_nan():
with rasterio.open('tests/data/float_nan.tif') as src:
data = next(src.sample([(-10, 0.0)]))
assert numpy.isnan(data)
def test_sampling_indexes():
with rasterio.open('tests/data/RGB.byte.tif') as src:
data = next(src.sample([(220650.0, 2719200.0)], indexes=[2]))
......