import math
import re
import fiona.transform
import gws.gis.proj
import gws.gis.shape
import gws.types as t
[docs]def from_string(s: str) -> t.Optional[t.Extent]:
"""Create an extent from a comma-separated string "1000,2000,20000 40000" """
try:
ext = [float(n) for n in s.split(',')]
except:
return None
return valid(ext)
[docs]def from_center(xy: t.Point, size: t.Size) -> t.Extent:
return (
xy[0] - size[0] / 2,
xy[1] - size[1] / 2,
xy[0] + size[0] / 2,
xy[1] + size[1] / 2,
)
[docs]def from_box(box: str) -> t.Optional[t.Extent]:
"""Create an extent from a Postgis BOX(1000 2000,20000 40000)"""
if not box:
return None
m = re.match(r'^BOX\((.+?)\)$', str(box).upper())
if not m:
return None
try:
a, b = m.group(1).split(',')
a, b = a.split(), b.split()
ext = (
float(a[0]),
float(a[1]),
float(b[0]),
float(b[1]),
)
except:
return None
return valid(ext)
[docs]def valid(e) -> t.Optional[t.Extent]:
try:
ok = len(e) == 4 and all(math.isfinite(p) for p in e)
except:
ok = False
return _sort(e) if ok else None
[docs]def list_valid(exts: t.List[t.Extent]) -> t.List[t.Extent]:
ls = []
for e in exts:
e = valid(e)
if e:
ls.append(e)
return ls
[docs]def merge(exts: t.List[t.Extent]) -> t.Extent:
c = False
res = (1e20, 1e20, -1e20, -1e20)
for e in exts:
e = _sort(e)
res = (
min(res[0], e[0]),
min(res[1], e[1]),
max(res[2], e[2]),
max(res[3], e[3])
)
c = True
return res if c else None
[docs]def constrain(a: t.Extent, b: t.Extent) -> t.Extent:
a = _sort(a)
b = _sort(b)
return (
max(a[0], b[0]),
max(a[1], b[1]),
min(a[2], b[2]),
min(a[3], b[3]),
)
[docs]def center(e: t.Extent) -> t.Point:
return (
e[0] + (e[2] - e[0]) / 2,
e[1] + (e[3] - e[1]) / 2,
)
[docs]def size(e: t.Extent) -> t.Size:
return (
e[2] - e[0],
e[3] - e[1],
)
[docs]def diagonal(e: t.Extent) -> float:
return math.sqrt((e[2] - e[0]) ** 2 + (e[3] - e[1]) ** 2)
[docs]def circumsquare(e: t.Extent) -> t.Extent:
"""A circumscribed square of the extent."""
d = diagonal(e)
return from_center(center(e), (d, d))
[docs]def buffer(e: t.Extent, buf: int) -> t.Extent:
e = _sort(e)
return (
e[0] - buf,
e[1] - buf,
e[2] + buf,
e[3] + buf,
)
[docs]def intersect(a: t.Extent, b: t.Extent) -> bool:
a = _sort(a)
b = _sort(b)
return a[0] <= b[2] and a[2] >= b[0] and a[1] <= b[3] and a[3] >= b[1]
[docs]def swap_xy(e: t.Extent) -> t.Extent:
return e[1], e[0], e[3], e[2]
def _sort(e):
# our extents are always [minx, miny, maxx, maxy]
return (
min(e[0], e[2]),
min(e[1], e[3]),
max(e[0], e[2]),
max(e[1], e[3]),
)