Coverage for gws-app/gws/base/map/core.py: 95%
56 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
1"""Default map object."""
3from typing import Optional
5import gws
6import gws.base.layer
7import gws.lib.crs
8import gws.lib.bounds
9import gws.lib.extent
10import gws.gis.zoom
13@gws.ext.config.map('default')
14class Config(gws.Config):
15 """Map with its layers, extent, CRS and zoom levels."""
17 center: Optional[gws.Point]
18 """Initial map center."""
19 coordinatePrecision: Optional[int]
20 """Decimal places for coordinates."""
21 crs: Optional[gws.CrsName] = 'EPSG:3857'
22 """CRS of the map and its layers."""
23 extent: Optional[gws.Extent]
24 """Map extent in map CRS coordinates."""
25 extentBuffer: Optional[int]
26 """Buffer added around the configured extent, in map units."""
27 layers: list[gws.ext.config.layer]
28 """Map layers."""
29 title: str = ''
30 """Map title, used as the root layer title."""
31 wrapX: bool = False
32 """Repeat the world horizontally in the client."""
33 zoom: Optional[gws.gis.zoom.Config]
34 """Allowed scales or zoom levels and the initial zoom."""
37@gws.ext.props.map('default')
38class Props(gws.Data):
39 crs: str
40 crsDef: Optional[str]
41 coordinatePrecision: int
42 extent: gws.Extent
43 center: gws.Point
44 initResolution: float
45 rootLayer: gws.base.layer.Props
46 resolutions: list[float]
47 title: str = ''
48 wrapX: bool
51class _RootLayer(gws.base.layer.group.Object):
52 """Root layer of a map."""
54 parent: 'Object'
55 """The map."""
58@gws.ext.object.map('default')
59class Object(gws.Map):
60 """Default map."""
62 wrapX: bool
63 """Repeat the world horizontally in the client."""
65 def configure(self):
66 self.title = self.cfg('title') or self.cfg('_defaultTitle') or ''
68 p = self.cfg('crs')
69 crs = gws.lib.crs.require(p) if p else gws.lib.crs.WEBMERCATOR
71 p = self.cfg('extent')
72 if p:
73 ext = gws.lib.extent.from_list(p)
74 if not ext or not gws.lib.extent.is_valid(ext):
75 raise gws.ConfigurationError(f'invalid extent {p!r}')
76 buf = self.cfg('extentBuffer') or 0
77 self.bounds = gws.Bounds(crs=crs, extent=gws.lib.extent.buffer(ext, buf))
78 self.wgsExtent = gws.lib.extent.transform_to_wgs(self.bounds.extent, crs)
79 else:
80 self.wgsExtent = crs.wgsMaxExtent
81 self.bounds = gws.Bounds(crs=crs, extent=gws.lib.extent.transform_from_wgs(self.wgsExtent, crs))
83 self.center = self.cfg('center') or gws.lib.extent.center(self.bounds.extent)
84 self.wrapX = self.cfg('wrapX', default=False)
86 p = self.cfg('zoom')
87 gws.gis.zoom.warn_deprecated_options(p, self.root)
88 self.resolutions = gws.gis.zoom.resolutions_from_config(p, crs=self.bounds.crs)
89 self.initResolution = gws.gis.zoom.init_resolution(p, self.resolutions, crs=self.bounds.crs)
91 p = self.cfg('coordinatePrecision')
92 self.coordinatePrecision = p or self.bounds.crs.coordinatePrecision
94 self.rootLayer = self.create_child(
95 gws.ext.object.layer,
96 type='group',
97 title=self.title,
98 layers=self.cfg('layers'),
99 _parentWgsExtent=self.wgsExtent,
100 _mapCrs=self.bounds.crs,
101 _parentResolutions=self.resolutions,
102 )
104 if not self.rootLayer:
105 raise gws.Error(f'missing or invalid root layer in {self!r}')
107 def props(self, user):
108 return gws.Data(
109 crs=self.bounds.crs.epsg,
110 crsDef=self.bounds.crs.proj4text,
111 coordinatePrecision=self.coordinatePrecision,
112 extent=self.bounds.extent,
113 center=self.center,
114 initResolution=self.initResolution,
115 rootLayer=self.rootLayer,
116 resolutions=sorted(self.resolutions, reverse=True),
117 title=self.title,
118 wrapX=self.wrapX,
119 )