import re
import gws
import gws.gis.proj
import gws.gis.extent
import gws.common.metadata
import gws.types as t
#:export
[docs]class SourceStyle(t.Data):
is_default = False
legend: t.Url = ''
meta: t.MetaData = None
name: str = ''
#:export
[docs]class SourceLayer(t.Data):
data_source = {}
supported_crs: t.List[t.Crs] = []
supported_bounds: t.List[t.Bounds] = []
is_expanded = False
is_group = False
is_image = False
is_queryable = False
is_visible = False
layers: t.List['SourceLayer'] = []
meta: t.MetaData = None
name = ''
title = ''
opacity = 1
scale_range: t.List[float] = []
styles: t.List[SourceStyle] = []
legend = ''
resource_urls = {}
a_path = ''
a_uid = ''
a_level = 0
[docs]class LayerFilter(t.Config):
"""Layer filter"""
level: int = 0 #: use layers at this level
names: t.Optional[t.List[str]] #: use these layer names (top-to-bottom order)
pattern: t.Regex = '' #: use layers whose full path matches a pattern
[docs]def layer_matches(sl: t.SourceLayer, slf: LayerFilter) -> bool:
"""Check if a source layer matches the filter"""
if not slf:
return True
s = gws.get(slf, 'level')
if s and sl.a_level != s:
return False
s = gws.get(slf, 'names')
if s is not None and sl.name not in s:
return False
s = gws.get(slf, 'pattern')
if s and not re.search(s, sl.a_path):
return False
return True
[docs]def filter_layers(layers: t.List[t.SourceLayer], slf: LayerFilter, image_only=False, queryable_only=False) -> t.List[t.SourceLayer]:
"""Filter source layers by the given layer filter."""
if slf:
s = gws.get(slf, 'level')
if s:
layers = [sl for sl in layers if sl.a_level == s]
s = gws.get(slf, 'names')
if s:
# NB: if 'names' is given, maintain the given order, which is expected to be top-to-bottom
# see note in ext/layers/wms
layers2 = []
for name in s:
for sl in layers:
if sl.name == name:
layers2.append(sl)
break
layers = layers2
s = gws.get(slf, 'pattern')
if s:
layers = [sl for sl in layers if re.search(s, sl.a_path)]
if image_only:
layers = [sl for sl in layers if sl.is_image]
if queryable_only:
layers = [sl for sl in layers if sl.is_queryable]
return layers
[docs]def image_layers(sl: t.SourceLayer) -> t.List[t.SourceLayer]:
if sl.is_image:
return [sl]
if sl.layers:
return [s for sub in sl.layers for s in image_layers(sub)]
return []
[docs]def bounds_from_layers(source_layers: t.List[t.SourceLayer], target_crs) -> t.Bounds:
"""Return merged bounds from a list of source layers in the target_crs."""
exts = []
for sl in source_layers:
if not sl.supported_bounds:
continue
bb = _best_bounds(sl.supported_bounds, target_crs)
if bb:
e = gws.gis.extent.transform(bb.extent, bb.crs, target_crs)
exts.append(e)
if exts:
# gws.p('BOUNDS', [sl.name for sl in source_layers], target_crs, exts, gws.gis.extent.merge(exts))
return t.Bounds(
crs=target_crs,
extent=gws.gis.extent.merge(exts))
[docs]def crs_from_layers(source_layers: t.List[t.SourceLayer]) -> t.List[t.Crs]:
"""Return an intersection of crs supported by each source layer."""
cs = set()
for sl in source_layers:
if not sl.supported_crs:
continue
if not cs:
cs.update(sl.supported_crs)
else:
cs = cs.intersection(sl.supported_crs)
return sorted(cs)
def _best_bounds(bs: t.List[t.Bounds], target_crs):
for b in bs:
if gws.gis.proj.equal(b.crs, target_crs):
return b
for b in bs:
if gws.gis.proj.equal(b.crs, gws.EPSG_3857):
return b
for b in bs:
return b