microproduct/dem-sentiral/ISCEApp/site-packages/osgeo_utils/auxiliary/rectangle.py

289 lines
8.5 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ******************************************************************************
#
# Project: GDAL
# Purpose: rectangle class (x, y, width, height)
# Author: Idan Miara, <idan@miara.com>
#
# ******************************************************************************
# Copyright (c) 2020, Idan Miara <idan@miara.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
# ******************************************************************************
class GeoRectangle:
__slots__ = ['x', 'y', 'w', 'h']
def __init__(self, x, y, w, h, allow_negative_size=False):
if w <= 0:
if allow_negative_size:
x = x + w
w = -w
else:
w = 0
if h <= 0:
if allow_negative_size:
y = y + h
h = -h
else:
h = 0
self.x = x
self.y = y
self.w = w
self.h = h
def __eq__(self, other):
if not isinstance(other, GeoRectangle):
# don't attempt to compare against unrelated types
return False
return self.xywh == other.xywh
def __round__(self, *args, **kwargs):
return self.from_lrdu(*(round(i, *args, **kwargs) for i in self.lrdu))
def is_empty(self):
return self.w <= 0 or self.h <= 0
def intersect(self, other: "GeoRectangle"):
return GeoRectangle.from_min_max(
max(self.min_x, other.min_x),
min(self.max_x, other.max_x),
max(self.min_y, other.min_y),
min(self.max_y, other.max_y),
)
def union(self, other: "GeoRectangle"):
return GeoRectangle.from_min_max(
min(self.min_x, other.min_x),
max(self.max_x, other.max_x),
min(self.min_y, other.min_y),
max(self.max_y, other.max_y),
)
def round(self, digits):
self.x = round(self.x, digits)
self.y = round(self.y, digits)
self.w = round(self.w, digits)
self.h = round(self.h, digits)
def align(self, geo_transform):
# compute the pixel-aligned bounding box (larger than the feature's bbox)
left = self.min_x - (self.min_x - geo_transform[0]) % geo_transform[1]
right = self.max_x + (geo_transform[1] - ((self.max_x - geo_transform[0]) % geo_transform[1]))
bottom = self.min_y + (geo_transform[5] - ((self.min_y - geo_transform[3]) % geo_transform[5]))
top = self.max_y - (self.max_y - geo_transform[3]) % geo_transform[5]
return self.from_lrud(left, right, top, bottom)
def get_partition(self, part: "GeoRectangle"):
# part: x,y - part indexes; w,h - part counts
part_width = self.w / part.w
part_hight = self.h / part.h
return GeoRectangle(
self.x + part.x * part_width,
self.y + part.y * part_hight,
part_width,
part_hight,
)
@classmethod
def empty(cls):
return cls(0, 0, 0, 0)
@classmethod
def from_lrud(cls, l, r, u, d):
ret = cls(l, d, r - l, u - d)
return ret
@classmethod
# same as min_max
def from_lrdu(cls, l, r, d, u):
ret = cls(l, d, r - l, u - d)
return ret
@classmethod
def from_lurd(cls, l, u, r, d):
""" from projwin (minx maxy maxx miny) == (ulx uly lrx lry) == (l u r d) """
ret = cls(l, d, r - l, u - d)
return ret
@classmethod
# same as min_max
def from_xwyh(cls, x, w, y, h, allow_negative_size=False):
ret = cls(x, y, w, h, allow_negative_size)
return ret
@classmethod
# # same as cls
def from_xywh(cls, x, y, w, h, allow_negative_size=False):
ret = cls(x, y, w, h, allow_negative_size)
return ret
@classmethod
# # same as cls
def from_xywhps(cls, x, y, w, h, px, py):
ret = cls(x, y, w*px, h*py, True)
return ret
@classmethod
# same as lrdu
def from_min_max(cls, min_x, max_x, min_y, max_y):
ret = cls(min_x, min_y, max_x - min_x, max_y - min_y)
return ret
@classmethod
def from_center_and_radius(cls, cent_x, cent_y, rad_x, rad_y=None):
if rad_y is None:
rad_y = rad_x
x = cent_x - rad_x
y = cent_y - rad_y
w = rad_x * 2
h = rad_y * 2
ret = cls(x, y, w, h)
return ret
@classmethod
def from_points(cls, points):
return cls.from_min_max(
min(p[0] for p in points),
max(p[0] for p in points),
min(p[1] for p in points),
max(p[1] for p in points),
)
@classmethod
def from_geotransform_and_size(cls, gt, size):
if gt[2] or gt[4]:
return cls.from_points(get_points_extent(gt, *size))
else:
# faster method
origin = (gt[0], gt[3])
pixel_size = (gt[1], gt[5])
extent = cls.from_xywhps(*origin, *size, *pixel_size)
# extent_b = cls.from_points(get_points_extent(gt, *size))
# assert extent == extent_b
return extent
def to_pixels(self, pixel_size):
return self.from_xwyh(self.x / pixel_size[0], self.w / pixel_size[0],
self.y / pixel_size[1], self.h / pixel_size[1], True)
@classmethod
def from_geotransform_and_size_to_pix(cls, gt, size):
origin = (gt[0], gt[3])
pixel_size = (gt[1], gt[5])
pix_origin = list(origin[i] / pixel_size[i] for i in (0, 1))
# pix_bounds = list(origin[i] / pixel_size[i] + size[i] for i in (0, 1))
return cls.from_xwyh(pix_origin[0], size[0], pix_origin[1], size[1])
@property
def area(self):
return self.w * self.h
@property
def size(self):
return self.w, self.h
@property
def left(self):
return self.x
@property
def right(self):
return self.x + self.w
@property
def down(self):
return self.y
@property
def up(self):
return self.y + self.h
@property
def min_x(self):
return self.x
@property
def max_x(self):
return self.x + self.w
@property
def min_y(self):
return self.y
@property
def max_y(self):
return self.y + self.h
@property
def lurd(self):
return self.left, self.up, self.right, self.down
@property
def lrud(self):
return self.left, self.right, self.up, self.down
@property
def ldru(self):
return self.left, self.down, self.right, self.up
@property
def lrdu(self):
return self.left, self.right, self.down, self.up
@property
def xywh(self):
return self.x, self.y, self.w, self.h
@property
def xwyh(self):
return self.x, self.w, self.y, self.h
@property
def min_max(self):
return self.min_x, self.max_x, self.min_y, self.max_y
def __str__(self):
return f"Rectangle(x[{self.min_x},{self.max_x}], y[{self.min_y},{self.max_y}] wh[{self.w},{self.h}])"
def __repr__(self):
return f"Rectangle(x:{self.x}, y:{self.y}, w:{self.w}, h:{self.h})"
def __hash__(self):
return hash(self.xywh)
def get_points_extent(gt, cols, rows):
"""Return list of corner coordinates from a geotransform"""
def transform_point(px, py):
x = gt[0] + (px * gt[1]) + (py * gt[2])
y = gt[3] + (px * gt[4]) + (py * gt[5])
return x, y
return [
transform_point(0, 0),
transform_point(0, rows),
transform_point(cols, rows),
transform_point(cols, 0),
]