/
generic_image.py
120 lines (98 loc) · 3.78 KB
/
generic_image.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2017-2019 Satpy developers
#
# This file is part of satpy.
#
# satpy is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# satpy is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# satpy. If not, see <http://www.gnu.org/licenses/>.
"""
Reader for generic image (e.g. gif, png, jpg, tif, geotiff, ...).
Returns a dataset without calibration. Includes coordinates if
available in the file (eg. geotiff).
"""
import logging
import rasterio
import xarray as xr
import dask.array as da
import numpy as np
from satpy.readers.file_handlers import BaseFileHandler
from satpy import CHUNK_SIZE
from pyresample import utils
BANDS = {1: ['L'],
2: ['L', 'A'],
3: ['R', 'G', 'B'],
4: ['R', 'G', 'B', 'A']}
logger = logging.getLogger(__name__)
class GenericImageFileHandler(BaseFileHandler):
"""Handle reading of generic image files."""
def __init__(self, filename, filename_info, filetype_info):
"""Initialize filehandler."""
super(GenericImageFileHandler, self).__init__(
filename, filename_info, filetype_info)
self.finfo = filename_info
try:
self.finfo['end_time'] = self.finfo['start_time']
except KeyError:
pass
self.finfo['filename'] = self.filename
self.file_content = {}
self.area = None
self.read()
def read(self):
"""Read the image."""
dataset = rasterio.open(self.finfo['filename'])
# Create area definition
if hasattr(dataset, 'crs') and dataset.crs is not None:
self.area = utils.get_area_def_from_raster(dataset)
data = xr.open_rasterio(dataset, chunks=(1, CHUNK_SIZE, CHUNK_SIZE))
attrs = data.attrs.copy()
# Rename to Satpy convention
data = data.rename({'band': 'bands'})
# Rename bands to [R, G, B, A], or a subset of those
data['bands'] = BANDS[data.bands.size]
# Mask data if alpha channel is present
try:
data = mask_image_data(data)
except ValueError as err:
logger.warning(err)
data.attrs = attrs
self.file_content['image'] = data
def get_area_def(self, dsid):
"""Get area definition of the image."""
if self.area is None:
raise NotImplementedError("No CRS information available from image")
return self.area
@property
def start_time(self):
"""Return start time."""
return self.finfo['start_time']
@property
def end_time(self):
"""Return end time."""
return self.finfo['end_time']
def get_dataset(self, key, info):
"""Get a dataset from the file."""
logger.debug("Reading %s.", key)
return self.file_content[key.name]
def mask_image_data(data):
"""Mask image data if alpha channel is present."""
if data.bands.size in (2, 4):
if not np.issubdtype(data.dtype, np.integer):
raise ValueError("Only integer datatypes can be used as a mask.")
mask = data.data[-1, :, :] == np.iinfo(data.dtype).min
data = data.astype(np.float64)
masked_data = da.stack([da.where(mask, np.nan, data.data[i, :, :])
for i in range(data.shape[0])])
data.data = masked_data
data = data.sel(bands=BANDS[data.bands.size - 1])
return data