import re
import gws
import gws.common.auth
import gws.common.metadata
import gws.common.model
import gws.common.ows.provider
import gws.common.search
import gws.common.style
import gws.common.template
import gws.config.parser
import gws.gis.extent
import gws.gis.feature
import gws.gis.ows
import gws.gis.proj
import gws.gis.shape
import gws.gis.source
import gws.gis.zoom
import gws.tools.net
import gws.tools.units
import gws.tools.svg
import gws.types as t
from gws import cached_property
from . import types
_DEFAULT_STYLE_VALUES = {
'fill': 'rgba(0,0,0,1)',
'stroke': 'rgba(0,0,0,1)',
'stoke_width': 1,
}
_DEFAULT_LEGEND_HTML = """<div class="legend"><img src="{path}"/></div>"""
[docs]class Config(t.WithTypeAndAccess):
"""Layer configuration"""
clientOptions: types.ClientOptions = {} #: options for the layer display in the client
dataModel: t.Optional[gws.common.model.Config] #: layer data model
display: types.DisplayMode = 'box' #: layer display mode
edit: t.Optional[types.EditConfig] #: editing permissions
extent: t.Optional[t.Extent] #: layer extent
extentBuffer: t.Optional[int] #: extent buffer
legend: types.LegendConfig = {} #: legend configuration
meta: t.Optional[gws.common.metadata.Config] #: layer meta data
opacity: float = 1 #: layer opacity
ows: types.OwsConfig = {} #: OWS services options
search: gws.common.search.Config = {} #: layer search configuration
templates: t.Optional[t.List[t.ext.template.Config]] #: client templates
title: str = '' #: layer title
zoom: t.Optional[gws.gis.zoom.Config] #: layer resolutions and scales
[docs]class CustomConfig(t.WithAccess):
"""Custom layer configuration"""
applyTo: t.Optional[gws.gis.source.LayerFilter] #: source layers this configuration applies to
clientOptions: t.Optional[types.ClientOptions] #: options for the layer display in the client
dataModel: t.Optional[gws.common.model.Config] #: layer data model
display: t.Optional[types.DisplayMode] #: layer display mode
edit: t.Optional[types.EditConfig] #: editing permissions
extent: t.Optional[t.Extent] #: layer extent
extentBuffer: t.Optional[int] #: extent buffer
legend: t.Optional[types.LegendConfig] #: legend configuration
meta: t.Optional[gws.common.metadata.Config] #: layer meta data
opacity: t.Optional[float] #: layer opacity
ows: t.Optional[types.OwsConfig] #: OWS services options
search: t.Optional[gws.common.search.Config] #: layer search configuration
templates: t.Optional[t.List[t.ext.template.Config]] #:client templates
title: t.Optional[str] #: layer title
zoom: t.Optional[gws.gis.zoom.Config] #: layer resolutions and scales
#:export
[docs]class LayerLegend(t.Data):
enabled: bool
path: str
url: str
template: t.ITemplate
#:export ILayer
[docs]class Layer(gws.Object, t.ILayer):
@property
def props(self):
return types.LayerProps(
extent=self.extent if self.extent != self.map.extent else None,
meta=gws.common.metadata.props(self.meta),
opacity=self.opacity,
options=self.client_options,
resolutions=self.resolutions,
title=self.title,
uid=self.uid,
)
@property
def description(self) -> str:
context = {'layer': self}
return self.description_template.render(context).content
@cached_property
def has_search(self) -> bool:
return len(self.get_children('gws.ext.search.provider')) > 0
@property
def has_legend(self) -> bool:
return self.legend.enabled
@cached_property
def own_bounds(self) -> t.Optional[t.Bounds]:
return
@property
def default_search_provider(self) -> t.Optional[t.ISearchProvider]:
return
@property
def legend_url(self):
return gws.SERVER_ENDPOINT + f'/cmd/mapHttpGetLegend/layerUid/{self.uid}'
[docs] def post_configure(self):
super().post_configure()
try:
self.configure_search()
except Exception as e:
gws.log.exception()
legend = self.configure_legend()
if legend:
self.legend = legend
self.legend.options = self.var('legend.options', default={})
[docs] def edit_access(self, user):
# @TODO granular edit access
if self.is_editable and self.edit_options and user.can_use(self.edit_options, parent=self):
return ['all']
[docs] def edit_operation(self, operation: str, feature_props: t.List[t.FeatureProps]) -> t.List[t.IFeature]:
pass
[docs] def props_for(self, user):
p = super().props_for(user)
if p:
p['editAccess'] = self.edit_access(user)
return p
[docs] def mapproxy_config(self, mc):
pass
[docs] def render_box(self, rv: t.MapRenderView, extra_params=None):
return None
[docs] def render_xyz(self, x, y, z):
return None
[docs] def render_svg(self, rv: t.MapRenderView, style: t.IStyle = None) -> str:
return gws.tools.svg.as_xml(self.render_svg_tags(rv, style))
[docs] def render_legend(self, context=None) -> t.Optional[str]:
"""Render a legend and return the path to the legend image."""
if self.legend.path:
return self.legend.path
cache_path = gws.LEGEND_CACHE_DIR + '/' + self.uid + '.png'
if self.legend.url:
try:
r = gws.gis.ows.request.raw_get(self.legend.url)
except gws.gis.ows.error.Error as e:
gws.log.error(f'layer {self.uid!r}: legend download failed: {e!r}')
self.legend.enabled = False
return
gws.write_file_b(cache_path, r.content)
self.legend.path = cache_path
return self.legend.path
if self.legend.template:
self.legend.template.render(context, out_path=cache_path, format='png')
self.legend.path = cache_path
return self.legend.path
img = self.render_legend_image(context)
if img:
gws.write_file_b(cache_path, img)
self.legend.path = cache_path
return self.legend.path
self.legend.enabled = False
[docs] def render_legend_image(self, context=None) -> bytes:
pass
[docs] def render_html_legend(self, context=None) -> str:
"""Render a legend in the html format."""
if self.legend.template:
return self.legend.template.render(context).content
path = self.render_legend(context)
if path:
return _DEFAULT_LEGEND_HTML.replace('{path}', path)
return ''
[docs] def get_features(self, bounds: t.Bounds, limit: int = 0) -> t.List[t.IFeature]:
return []
[docs] def ows_enabled(self, service: t.IOwsService) -> bool:
if not self._ows_enabled:
return False
if self._ows_enabled_services_uids:
return service.uid in self._ows_enabled_services_uids
if self._ows_enabled_services_pattern:
return re.search(self._ows_enabled_services_pattern, service.uid) is not None
if service.type == 'wms' and self.supports_wms:
return True
if service.type == 'wfs' and self.supports_wfs:
return True
if self.layers:
return any(la.ows_enabled(service) for la in self.layers)
return False