sarlo.io¶
Common file read/write operations for working with radar data
GeoXML
¶
Stores spatial reference information from topophase XML data.
Attributes:
Name | Type | Description |
---|---|---|
xmin |
int |
longitude minimum |
ymin |
int |
latitude minimum |
xmax |
int |
longitude maximum |
ymax |
int |
latitude maximum |
xres |
range resolution |
|
yres |
azimuth resolution |
|
width |
int |
x pixel numbers |
height |
int |
y pixel numbers |
bounds |
list |
[ymax, ymin, xmin, xmax] |
Source code in sarlo/io.py
class GeoXML(object):
"""Stores spatial reference information from topophase XML data.
Attributes:
xmin: longitude minimum
ymin: latitude minimum
xmax: longitude maximum
ymax: latitude maximum
xres: range resolution
yres: azimuth resolution
width: x pixel numbers
height: y pixel numbers
bounds: [ymax, ymin, xmin, xmax]
"""
def __init__(self, xmlfile: str):
"""Stores geospatial information from isce raster xml files
Args:
xmlfile: ISCE .xml file for a given raster
Returns:
self: the Sentinel1 object.
"""
self.ymax: int = None
self.xmax: int = None
self.ymin: int = None
self.xmin: int = None
self.ysize: int = None
self.xsize: int = None
self.height: int = None
self.width: int = None
self.bounds: list = []
self.xmlfile: str = xmlfile
def parser(self):
"""Returns spatial reference data from Sentinel-1 phase correction XML
Args:
xmlfile: Str path to an xml file
Returns:
self: object with the spatial reference data as attributes
"""
tree = ET.parse(self.xmlfile)
root = tree.getroot()
# parse the tree to extract georeferencing info
delta_array = np.array([])
start_array = np.array([])
size_array = np.array([], dtype=np.int32)
for size in root.iter("property"):
if size.items()[0][1] == "size":
size_array = np.append(size_array, int(size.find("value").text))
for delta_val in root.iter("property"):
if delta_val.items()[0][1] == "delta":
delta_array = np.append(
delta_array, float(delta_val.find("value").text)
)
for start_val in root.iter("property"):
if start_val.items()[0][1] == "startingvalue":
start_array = np.append(
start_array, float(start_val.find("value").text)
)
end_array = start_array + size_array * delta_array
# use standard geospatial terminology to reference file sizes and spatial bounds
self.ymax = max(start_array[1], end_array[1])
self.ymin = min(start_array[1], end_array[1])
self.xmax = max(start_array[0], end_array[0])
self.xmin = min(start_array[0], end_array[0])
self.bounds = [self.ymax, self.ymin, self.xmin, self.xmax]
self.width = size_array[0]
self.height = size_array[1]
self.ysize = delta_array[1]
self.xsize = delta_array[0]
__init__(self, xmlfile)
special
¶
Stores geospatial information from isce raster xml files
Parameters:
Name | Type | Description | Default |
---|---|---|---|
xmlfile |
str |
ISCE .xml file for a given raster |
required |
Returns:
Type | Description |
---|---|
self |
the Sentinel1 object. |
Source code in sarlo/io.py
def __init__(self, xmlfile: str):
"""Stores geospatial information from isce raster xml files
Args:
xmlfile: ISCE .xml file for a given raster
Returns:
self: the Sentinel1 object.
"""
self.ymax: int = None
self.xmax: int = None
self.ymin: int = None
self.xmin: int = None
self.ysize: int = None
self.xsize: int = None
self.height: int = None
self.width: int = None
self.bounds: list = []
self.xmlfile: str = xmlfile
parser(self)
¶
Returns spatial reference data from Sentinel-1 phase correction XML
Parameters:
Name | Type | Description | Default |
---|---|---|---|
xmlfile |
Str path to an xml file |
required |
Returns:
Type | Description |
---|---|
self |
object with the spatial reference data as attributes |
Source code in sarlo/io.py
def parser(self):
"""Returns spatial reference data from Sentinel-1 phase correction XML
Args:
xmlfile: Str path to an xml file
Returns:
self: object with the spatial reference data as attributes
"""
tree = ET.parse(self.xmlfile)
root = tree.getroot()
# parse the tree to extract georeferencing info
delta_array = np.array([])
start_array = np.array([])
size_array = np.array([], dtype=np.int32)
for size in root.iter("property"):
if size.items()[0][1] == "size":
size_array = np.append(size_array, int(size.find("value").text))
for delta_val in root.iter("property"):
if delta_val.items()[0][1] == "delta":
delta_array = np.append(
delta_array, float(delta_val.find("value").text)
)
for start_val in root.iter("property"):
if start_val.items()[0][1] == "startingvalue":
start_array = np.append(
start_array, float(start_val.find("value").text)
)
end_array = start_array + size_array * delta_array
# use standard geospatial terminology to reference file sizes and spatial bounds
self.ymax = max(start_array[1], end_array[1])
self.ymin = min(start_array[1], end_array[1])
self.xmax = max(start_array[0], end_array[0])
self.xmin = min(start_array[0], end_array[0])
self.bounds = [self.ymax, self.ymin, self.xmin, self.xmax]
self.width = size_array[0]
self.height = size_array[1]
self.ysize = delta_array[1]
self.xsize = delta_array[0]
Sentinel1
¶
Class description.
Attributes:
Name | Type | Description |
---|---|---|
xml |
str |
topsProc.xml file from ISCE processing |
res |
int |
range pixel size |
lambda |
wavelength |
|
baseline |
int |
distance between observation points (Satellite to Satellite) |
center_range |
int |
range between the antenna's phase center and the point on the ground |
incid_angle |
int |
The incidence angle is the angle defined by the incident radar beam and the vertical (normal) to the intercepting surface. |
Source code in sarlo/io.py
class Sentinel1(object):
"""Class description.
Attributes:
xml: topsProc.xml file from ISCE processing
res: range pixel size
lambda: wavelength
baseline: distance between observation points (Satellite to Satellite)
center_range: range between the antenna's phase center and the point on the ground
incid_angle: The incidence angle is the angle defined by the incident radar beam and the vertical (normal) to the intercepting surface.
"""
def __init__(self, xml: str):
"""Function description.
Args:
xml: some kind of xml file
Returns:
self: the Sentinel1 object.
"""
self.xml: str = xml
self.res: int = None
self.llambda: int = None
self.baseline: int = None
self.center_range: int = None
self.incid_angle: int = None
def parse_xml(self):
"""Read Stripmap and TOPS xml files.
Args:
None.
Returns:
None. Updates the object attributes
"""
tree = ET.parse(self.xml)
root = tree.getroot()
self.res = float(
root.findall("./reference/instrument/range_pixel_size")[0].text
) # TOPS has
self.llambda = float(
root.findall("./reference/instrument/radar_wavelength")[0].text
) # TOPS has
try:
first_range = float(
root.findall("./runTopo/inputs/range_first_sample")[0].text
)
except:
first_range = float(
root.findall("./runTopo/inputs/RANGE_FIRST_SAMPLE")[0].text
) # StartingRange is slant range to first pixel
try:
num_range_bin = int(root.findall("./runTopo/inputs/width")[0].text)
except:
num_range_bin = int(root.findall("./runTopo/inputs/WIDTH")[0].text)
try:
num_range_looks = int(
root.findall("./runTopo/inputs/number_range_looks")[0].text
)
except:
num_range_looks = int(
root.findall("./runTopo/inputs/NUMBER_RANGE_LOOKS")[0].text
)
# Strange as the formula calls for the slant range not the center range. And this isn't how I would calculate center range either
self.center_range = (
first_range + (num_range_bin / 2 - 1) * self.res * num_range_looks
)
self.incid_angle = float(
root.findall("./reference/instrument/incidence_angle")[0].text
)
baseline_top = float(root.findall("./baseline/perp_baseline_top")[0].text)
baseline_bottom = float(root.findall("./baseline/perp_baseline_bottom")[0].text)
self.baseline = (baseline_bottom + baseline_top) / 2
__init__(self, xml)
special
¶
Function description.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
xml |
str |
some kind of xml file |
required |
Returns:
Type | Description |
---|---|
self |
the Sentinel1 object. |
Source code in sarlo/io.py
def __init__(self, xml: str):
"""Function description.
Args:
xml: some kind of xml file
Returns:
self: the Sentinel1 object.
"""
self.xml: str = xml
self.res: int = None
self.llambda: int = None
self.baseline: int = None
self.center_range: int = None
self.incid_angle: int = None
parse_xml(self)
¶
Read Stripmap and TOPS xml files.
Returns:
Type | Description |
---|---|
None. Updates the object attributes |
Source code in sarlo/io.py
def parse_xml(self):
"""Read Stripmap and TOPS xml files.
Args:
None.
Returns:
None. Updates the object attributes
"""
tree = ET.parse(self.xml)
root = tree.getroot()
self.res = float(
root.findall("./reference/instrument/range_pixel_size")[0].text
) # TOPS has
self.llambda = float(
root.findall("./reference/instrument/radar_wavelength")[0].text
) # TOPS has
try:
first_range = float(
root.findall("./runTopo/inputs/range_first_sample")[0].text
)
except:
first_range = float(
root.findall("./runTopo/inputs/RANGE_FIRST_SAMPLE")[0].text
) # StartingRange is slant range to first pixel
try:
num_range_bin = int(root.findall("./runTopo/inputs/width")[0].text)
except:
num_range_bin = int(root.findall("./runTopo/inputs/WIDTH")[0].text)
try:
num_range_looks = int(
root.findall("./runTopo/inputs/number_range_looks")[0].text
)
except:
num_range_looks = int(
root.findall("./runTopo/inputs/NUMBER_RANGE_LOOKS")[0].text
)
# Strange as the formula calls for the slant range not the center range. And this isn't how I would calculate center range either
self.center_range = (
first_range + (num_range_bin / 2 - 1) * self.res * num_range_looks
)
self.incid_angle = float(
root.findall("./reference/instrument/incidence_angle")[0].text
)
baseline_top = float(root.findall("./baseline/perp_baseline_top")[0].text)
baseline_bottom = float(root.findall("./baseline/perp_baseline_bottom")[0].text)
self.baseline = (baseline_bottom + baseline_top) / 2
geo_data
¶
Class description.
Attributes:
Name | Type | Description |
---|---|---|
file |
str |
raster file to pull geo_data from |
xmin |
int |
longitude minimum |
ymin |
int |
latitute minimum |
xmax |
int |
longitude maximum |
ymax |
int |
longitude minimum |
bounds |
list |
[xmin, ymin, xmax, ymax] |
xsize |
int |
range resolution |
ysize |
int |
azimuth resolution |
width |
int |
number of columns |
height |
int |
number of rows |
crs |
coordinate resolution system |
Source code in sarlo/io.py
class geo_data(object):
"""Class description.
Attributes:
file: raster file to pull geo_data from
xmin: longitude minimum
ymin: latitute minimum
xmax: longitude maximum
ymax: longitude minimum
bounds: [xmin, ymin, xmax, ymax]
xsize: range resolution
ysize: azimuth resolution
width: number of columns
height: number of rows
crs: coordinate resolution system
"""
def __init__(self, file):
"""Create the geo_data reader object.
Args:
file: path to a raster file to open.
Returns:
self: the geo_data object.
"""
self.file: str = file
self.xmin: int = None
self.ymin: int = None
self.xmax: int = None
self.ymax: int = None
self.bounds: list = []
self.xsize: int = None
self.ysize: int = None
self.width: int = None
self.height: int = None
self.crs = None
def read_geotiff(self):
"""Reads raster spatial reference and projection data.
Args:
None.
Returns:
None. Updates the object's spatial attributes.
"""
image = gdal.Open(self.file)
self.width = image.RasterXSize
self.height = image.RasterYSize
xmin, xsize, xoff, ymax, yoff, ysize = image.GetGeoTransform()
self.xmin = xmin
self.xmax = xmin + xoff + (self.width * xsize)
self.ymin = ymax + yoff + (self.height * ysize)
self.ymax = ymax
self.xsize = xsize
self.ysize = ysize
self.bounds = [self.xmin, self.ymin, self.xmax, self.ymax]
proj = image.GetProjection()
self.crs = proj.strip()
# clear the file pointer to avoid memory issues
image = None
__init__(self, file)
special
¶
Create the geo_data reader object.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
file |
path to a raster file to open. |
required |
Returns:
Type | Description |
---|---|
self |
the geo_data object. |
Source code in sarlo/io.py
def __init__(self, file):
"""Create the geo_data reader object.
Args:
file: path to a raster file to open.
Returns:
self: the geo_data object.
"""
self.file: str = file
self.xmin: int = None
self.ymin: int = None
self.xmax: int = None
self.ymax: int = None
self.bounds: list = []
self.xsize: int = None
self.ysize: int = None
self.width: int = None
self.height: int = None
self.crs = None
read_geotiff(self)
¶
Reads raster spatial reference and projection data.
Returns:
Type | Description |
---|---|
None. Updates the object's spatial attributes. |
Source code in sarlo/io.py
def read_geotiff(self):
"""Reads raster spatial reference and projection data.
Args:
None.
Returns:
None. Updates the object's spatial attributes.
"""
image = gdal.Open(self.file)
self.width = image.RasterXSize
self.height = image.RasterYSize
xmin, xsize, xoff, ymax, yoff, ysize = image.GetGeoTransform()
self.xmin = xmin
self.xmax = xmin + xoff + (self.width * xsize)
self.ymin = ymax + yoff + (self.height * ysize)
self.ymax = ymax
self.xsize = xsize
self.ysize = ysize
self.bounds = [self.xmin, self.ymin, self.xmax, self.ymax]
proj = image.GetProjection()
self.crs = proj.strip()
# clear the file pointer to avoid memory issues
image = None
write_geotiff(directory, outfilename, geo, data)
¶
write geotiff file
Parameters:
Name | Type | Description | Default |
---|---|---|---|
directory |
str |
directory outfile is to be written to |
required |
outfilename |
str |
file name to be written |
required |
geo |
object |
geospatial object with crs and size attributes |
required |
data |
2D array of spatial data |
required |
Return None. Writes geotiff
Source code in sarlo/io.py
def write_geotiff(directory: str, outfilename: str, geo: object, data):
"""write geotiff file
Args:
directory: directory outfile is to be written to
outfilename: file name to be written
geo: geospatial object with crs and size attributes
data: 2D array of spatial data
Return
None. Writes geotiff
"""
driver = gdal.GetDriverByName("GTiff")
outRaster = driver.Create(directory + outfilename + ".tif", geo.height, geo.width)
outRaster.SetGeoTransform([geo.xmin, geo.xsize, 0, geo.ymax, 0, geo.ysize])
outband = outRaster.GetRasterBand(1)
outband.WriteArray(data)
outRasterSRS = osr.SpatialReference()
outRasterSRS.ImportFromEPSG(4326)
outRaster.SetProjection(outRasterSRS.ExportToWkt())
outband.FlushCache()
write_gif(data, directory, outfilename)
¶
Write gif image file
Parameters:
Name | Type | Description | Default |
---|---|---|---|
data |
2D array to be written as a gif |
required | |
directory |
str |
directory in which file will be created |
required |
outfilename |
str |
name of file to be created |
required |
Returns:
Type | Description |
---|---|
None. |
Source code in sarlo/io.py
def write_gif(data, directory: str, outfilename: str):
"""Write gif image file
Args:
data: 2D array to be written as a gif
directory: directory in which file will be created
outfilename: name of file to be created
Return:
None.
"""
(row, col) = data.shape
data = data.flatten()
# Get the nonzerp indices and min/max
nz_IND = np.nonzero(data)
nz_min = data[nz_IND[0]].min()
nz_max = data[nz_IND[0]].max()
# Set the scaled values
data255 = data.copy()
data255[nz_IND[0]] = (data[nz_IND[0]] - nz_min) * 255 / (nz_max - nz_min) + 1
# Reshape the array of scaled values
data255 = np.reshape(data255, (row, col))
gif_img = Image.open(os.path.join(directory, outfilename + ".tif"))
gif_img.save(os.path.join(directory, outfilename + ".gif"), "GIF", transparency=0)
write_kml(directory, outfilename, geo, gif)
¶
Writes kml overlay from gif file
Parameters:
Name | Type | Description | Default |
---|---|---|---|
directory |
str |
directory outfile is to be written to |
required |
outfilename |
str |
file name to be written |
required |
geo |
object |
geospatial object with crs and size attributes |
required |
gif |
str |
gif file to be written to kml |
required |
Return None. Writes geotiff
Source code in sarlo/io.py
def write_kml(directory: str, outfilename: str, geo: object, gif: str):
"""Writes kml overlay from gif file
Args:
directory: directory outfile is to be written to
outfilename: file name to be written
geo: geospatial object with crs and size attributes
gif: gif file to be written to kml
Return
None. Writes geotiff
"""
kml = simplekml.Kml()
arraykml = kml.newgroundoverlay(name=outfilename)
arraykml.icon.href = os.path.join(directory, gif)
arraykml.latlonbox.north = geo.ymax
arraykml.latlonbox.south = geo.ymin
arraykml.latlonbox.east = geo.xmax
arraykml.latlonbox.west = geo.xmin
kml.save(os.path.join(directory, outfilename + ".kml"))