import gws
import gws.types as t
import gws.gis.shape
import gws.common.style
import gws.tools.svg
import gws.tools.xml2
_COMBINED_UID_DELIMITER = '___'
[docs]def from_geojson(js, crs, key_column='id'):
atts = {}
uid = ''
for k, v in js.get('properties', {}).items():
if k == key_column:
uid = v
else:
atts[k] = v
return Feature(
uid=uid,
attributes=atts,
shape=gws.gis.shape.from_geometry(js['geometry'], crs),
)
[docs]def from_props(p: t.FeatureProps):
return Feature(
uid=p.get('uid'),
attributes=p.get('attributes'),
elements=p.get('elements'),
shape=p.get('shape'),
style=p.get('style'),
)
#:export
[docs]class FeatureProps(t.Data):
uid: t.Optional[str]
attributes: t.Optional[t.List[t.Attribute]]
elements: t.Optional[dict]
layerUid: t.Optional[str]
shape: t.Optional[t.ShapeProps]
style: t.Optional[t.StyleProps]
#:export IFeature
[docs]class Feature(t.IFeature):
def __init__(self, uid=None, attributes=None, category=None, elements=None, shape=None, style=None):
self.attributes: t.List[t.Attribute] = []
self.category: str = category
self.templates: t.Optional[t.List[t.ITemplate]] = None
self.data_model: t.Optional[t.IModel] = None
self.elements = {}
self.layer: t.Optional[t.ILayer] = None
self.shape: t.Optional[t.IShape] = None
self.style: t.Optional[t.IStyle] = None
self.uid: str = ''
self._init(uid, attributes, elements, shape, style)
@property
def props(self) -> t.FeatureProps:
return t.FeatureProps(
uid=self.full_uid,
attributes=self.attributes,
shape=self.shape.props if self.shape else None,
style=self.style,
elements=self.elements,
layerUid=self.layer.uid if self.layer else None,
)
@property
def props_for_render(self) -> t.FeatureProps:
return t.FeatureProps(
uid=self.full_uid,
attributes=[],
shape=self.shape.props if self.shape else None,
layerUid=self.layer.uid if self.layer else None,
)
@property
def full_uid(self) -> str:
uid = self.uid or ''
if self.layer:
uid = f'{self.layer.uid}{_COMBINED_UID_DELIMITER}{uid}'
return uid
@property
def template_context(self) -> dict:
d = {a.name: a.value for a in self.attributes}
d['category'] = self.category
d['feature'] = self
d['layer'] = self.layer
d['uid'] = self.uid
return d
@property
def attr_dict(self) -> dict:
return {a.name: a.value for a in self.attributes}
[docs] def attr(self, name: str):
for a in self.attributes:
if a.name == name:
return a.value
[docs] def to_svg(self, rv: t.MapRenderView, style: t.IStyle = None) -> str:
return gws.tools.svg.as_xml(self.to_svg_tags(rv, style))
[docs] def to_geojson(self) -> dict:
props = {a.name: a.value for a in self.attributes}
props['id'] = self.uid
return {
'type': 'Feature',
'properties': props,
'geometry': self.shape.props.geometry if self.shape else None
}
[docs] def apply_data_model(self, model: t.IModel = None) -> t.IFeature:
model = model or self.data_model
if model:
self.attributes = model.apply(self.attributes)
return self
[docs] def apply_templates(self, templates: t.List[t.ITemplate] = None, extra_context: dict = None, keys: t.List[str] = None) -> t.IFeature:
used = set()
templates = templates or self.templates
ctx = gws.merge(self.template_context, extra_context)
for tpl in templates:
if tpl.category == 'feature' and (tpl.key not in used) and (not keys or tpl.key in keys):
self.elements[tpl.key] = tpl.render(context=ctx).content
used.add(tpl.key)
return self
def _init(self, uid, attributes, elements, shape, style):
if isinstance(uid, str) and _COMBINED_UID_DELIMITER in uid:
uid = uid.split(_COMBINED_UID_DELIMITER)[-1]
self.uid = uid
self.elements = elements or {}
self.attributes = []
if attributes:
if isinstance(attributes, dict):
attributes = [t.Attribute({'name': k, 'value': v}) for k, v in attributes.items()]
self.attributes = attributes
if shape:
if isinstance(shape, gws.gis.shape.Shape):
self.shape = shape
else:
self.shape = gws.gis.shape.from_props(shape)
if style:
if isinstance(style, dict):
style = t.StyleProps(style)
self.style = gws.common.style.from_props(style)