From c65ce528eb90bbb878ec23172104ea9db1faf8c3 Mon Sep 17 00:00:00 2001 From: "Brandon N. Benton" Date: Fri, 20 Dec 2024 10:19:44 -0800 Subject: [PATCH 001/122] dual sampler, queue, and batch handler with obs. modifying Sup3rDataset to work with three data members. --- sup3r/preprocessing/__init__.py | 8 +- sup3r/preprocessing/base.py | 55 +++++--- .../preprocessing/batch_handlers/__init__.py | 1 + sup3r/preprocessing/batch_handlers/factory.py | 7 + sup3r/preprocessing/batch_queues/__init__.py | 1 + sup3r/preprocessing/batch_queues/with_obs.py | 50 +++++++ sup3r/preprocessing/samplers/__init__.py | 1 + sup3r/preprocessing/samplers/with_obs.py | 69 ++++++++++ tests/training/test_train_dual_with_obs.py | 125 ++++++++++++++++++ 9 files changed, 297 insertions(+), 20 deletions(-) create mode 100644 sup3r/preprocessing/batch_queues/with_obs.py create mode 100644 sup3r/preprocessing/samplers/with_obs.py create mode 100644 tests/training/test_train_dual_with_obs.py diff --git a/sup3r/preprocessing/__init__.py b/sup3r/preprocessing/__init__.py index 770f09159b..0832d8d3a9 100644 --- a/sup3r/preprocessing/__init__.py +++ b/sup3r/preprocessing/__init__.py @@ -33,8 +33,14 @@ BatchHandlerMom2SepSF, BatchHandlerMom2SF, DualBatchHandler, + DualBatchHandlerWithObs, +) +from .batch_queues import ( + BatchQueueDC, + DualBatchQueue, + DualBatchQueueWithObs, + SingleBatchQueue, ) -from .batch_queues import BatchQueueDC, DualBatchQueue, SingleBatchQueue from .cachers import Cacher from .collections import Collection, StatsCollection from .data_handlers import ( diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index 5ddd9dea0f..bc1897e17f 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -183,17 +183,18 @@ def rewrap(self, data): """Rewrap data as ``Sup3rDataset`` after calling parent method.""" if isinstance(data, type(self)): return data - return ( - type(self)(low_res=data[0], high_res=data[1]) - if len(data) > 1 - else type(self)(high_res=data[0]) - ) + if len(data) == 2: + return type(self)(low_res=data[0], high_res=data[1]) + if len(data) == 3: + return type(self)(low_res=data[0], high_res=data[1], obs=data[2]) + return type(self)(high_res=data[0]) def sample(self, idx): """Get samples from ``self._ds`` members. idx should be either a tuple of slices for the dimensions (south_north, west_east, time) and a list - of feature names or a 2-tuple of the same, for dual datasets.""" - if len(self._ds) == 2: + of feature names or a tuple of the same, for multi-member datasets + (dual datasets and dual with observations datasets).""" + if len(self._ds) > 1: return tuple(d.sample(idx[i]) for i, d in enumerate(self)) return self._ds[-1].sample(idx) @@ -228,10 +229,12 @@ def shape(self): def features(self): """The features are determined by the set of features from all data members.""" + if len(self._ds) == 1: + return self._ds[0].features feats = [ - f for f in self._ds[0].features if f not in self._ds[-1].features + f for f in self._ds[0].features if f not in self._ds[1].features ] - feats += self._ds[-1].features + feats += self._ds[1].features return feats @property @@ -258,13 +261,13 @@ def mean(self, **kwargs): """Use the high_res members to compute the means. These are used for normalization during training.""" kwargs['skipna'] = kwargs.get('skipna', True) - return self._ds[-1].mean(**kwargs) + return self._ds[1 if len(self._ds) > 1 else 0].mean(**kwargs) def std(self, **kwargs): """Use the high_res members to compute the stds. These are used for normalization during training.""" kwargs['skipna'] = kwargs.get('skipna', True) - return self._ds[-1].std(**kwargs) + return self._ds[1 if len(self._ds) > 1 else 0].std(**kwargs) def normalize(self, means, stds): """Normalize dataset using the given mean and stds. These are provided @@ -309,10 +312,12 @@ def __init__( such. This is a tuple when the `.data` attribute belongs to a :class:`~.collections.base.Collection` object like :class:`~.batch_handlers.factory.BatchHandler`. Otherwise this is - :class:`~.Sup3rDataset` object, which is either a wrapped 2-tuple - or 1-tuple (e.g. ``len(data) == 2`` or ``len(data) == 1)``. This is - a 2-tuple when ``.data`` belongs to a dual container object like - :class:`~.samplers.DualSampler` and a 1-tuple otherwise. + :class:`~.Sup3rDataset` object, which is either a wrapped 3-tuple, + 2-tuple, or 1-tuple (e.g. ``len(data) == 3``, ``len(data) == 2`` or + ``len(data) == 1)``. This is a 3-tuple when ``.data`` belongs to a + container object like :class:`~.samplers.DualSamplerWithObs`, a + 2-tuple when ``.data`` belongs to a dual container object like + :class:`~.samplers.DualSampler`, and a 1-tuple otherwise. """ self.data = data @@ -345,10 +350,12 @@ def wrap(self, data): tuple when the `.data` attribute belongs to a :class:`~.collections.base.Collection` object like :class:`~.batch_handlers.factory.BatchHandler`. Otherwise this is - :class:`~.Sup3rDataset` object, which is either a wrapped 2-tuple or - 1-tuple (e.g. ``len(data) == 2`` or ``len(data) == 1)``. This is a - 2-tuple when ``.data`` belongs to a dual container object like - :class:`~.samplers.DualSampler` and a 1-tuple otherwise. + :class:`~.Sup3rDataset` object, which is either a wrapped 3-tuple, + 2-tuple, or 1-tuple (e.g. ``len(data) == 3``, ``len(data) == 2`` or + ``len(data) == 1)``. This is a 3-tuple when ``.data`` belongs to a + container object like :class:`~.samplers.DualSamplerWithObs`, a 2-tuple + when ``.data`` belongs to a dual container object like + :class:`~.samplers.DualSampler`, and a 1-tuple otherwise. """ if data is None: return data @@ -365,6 +372,16 @@ def wrap(self, data): logger.warning(msg) warn(msg) data = Sup3rDataset(low_res=data[0], high_res=data[1]) + elif isinstance(data, tuple) and len(data) == 3: + msg = ( + f'{self.__class__.__name__}.data is being set with a ' + '3-tuple without explicit dataset names. We will assume ' + 'first tuple member is low-res, second is high-res, and third ' + 'is obs' + ) + logger.warning(msg) + warn(msg) + data = Sup3rDataset(low_res=data[0], high_res=data[1], obs=data[2]) elif not isinstance(data, Sup3rDataset): name = getattr(data, 'name', None) or 'high_res' data = Sup3rDataset(**{name: data}) diff --git a/sup3r/preprocessing/batch_handlers/__init__.py b/sup3r/preprocessing/batch_handlers/__init__.py index 08bba8d6b8..d66b10126e 100644 --- a/sup3r/preprocessing/batch_handlers/__init__.py +++ b/sup3r/preprocessing/batch_handlers/__init__.py @@ -11,4 +11,5 @@ BatchHandlerMom2SepSF, BatchHandlerMom2SF, DualBatchHandler, + DualBatchHandlerWithObs, ) diff --git a/sup3r/preprocessing/batch_handlers/factory.py b/sup3r/preprocessing/batch_handlers/factory.py index 7e63c34b65..4b6ddf3aa6 100644 --- a/sup3r/preprocessing/batch_handlers/factory.py +++ b/sup3r/preprocessing/batch_handlers/factory.py @@ -14,10 +14,12 @@ QueueMom2SF, ) from sup3r.preprocessing.batch_queues.dual import DualBatchQueue +from sup3r.preprocessing.batch_queues.with_obs import DualBatchQueueWithObs from sup3r.preprocessing.collections.stats import StatsCollection from sup3r.preprocessing.samplers.base import Sampler from sup3r.preprocessing.samplers.cc import DualSamplerCC from sup3r.preprocessing.samplers.dual import DualSampler +from sup3r.preprocessing.samplers.with_obs import DualSamplerWithObs from sup3r.preprocessing.utilities import ( check_signatures, get_class_kwargs, @@ -315,6 +317,11 @@ def stop(self): DualBatchHandler = BatchHandlerFactory( DualBatchQueue, DualSampler, name='DualBatchHandler' ) + +DualBatchHandlerWithObs = BatchHandlerFactory( + DualBatchQueueWithObs, DualSamplerWithObs, name='DualBatchHandlerWithObs' +) + BatchHandlerCC = BatchHandlerFactory( DualBatchQueue, DualSamplerCC, name='BatchHandlerCC' ) diff --git a/sup3r/preprocessing/batch_queues/__init__.py b/sup3r/preprocessing/batch_queues/__init__.py index 63053f1235..067d210702 100644 --- a/sup3r/preprocessing/batch_queues/__init__.py +++ b/sup3r/preprocessing/batch_queues/__init__.py @@ -12,3 +12,4 @@ ) from .dc import BatchQueueDC, ValBatchQueueDC from .dual import DualBatchQueue +from .with_obs import DualBatchQueueWithObs diff --git a/sup3r/preprocessing/batch_queues/with_obs.py b/sup3r/preprocessing/batch_queues/with_obs.py new file mode 100644 index 0000000000..4c2a16571c --- /dev/null +++ b/sup3r/preprocessing/batch_queues/with_obs.py @@ -0,0 +1,50 @@ +"""DualBatchQueue with additional observation data on the same grid as the +high-res data. The observation data is sampled with the same index as the +high-res data during training.""" + +import logging + +from scipy.ndimage import gaussian_filter + +from .dual import DualBatchQueue + +logger = logging.getLogger(__name__) + + +class DualBatchQueueWithObs(DualBatchQueue): + """Base BatchQueue for use with + :class:`~sup3r.preprocessing.samplers.DualSamplerWithObs` objects.""" + + _signature_objs = (DualBatchQueue,) + + @property + def queue_shape(self): + """Shape of objects stored in the queue.""" + return [ + (self.batch_size, *self.lr_shape), + (self.batch_size, *self.hr_shape), + (self.batch_size, *self.hr_shape), + ] + + def transform(self, samples, smoothing=None, smoothing_ignore=None): + """Perform smoothing if requested. + + Note + ---- + This does not include temporal or spatial coarsening like + :class:`SingleBatchQueue` + """ + low_res, high_res, obs = samples + + if smoothing is not None: + feat_iter = [ + j + for j in range(low_res.shape[-1]) + if self.features[j] not in smoothing_ignore + ] + for i in range(low_res.shape[0]): + for j in feat_iter: + low_res[i, ..., j] = gaussian_filter( + low_res[i, ..., j], smoothing, mode='nearest' + ) + return low_res, high_res, obs diff --git a/sup3r/preprocessing/samplers/__init__.py b/sup3r/preprocessing/samplers/__init__.py index e281616d56..990e23861f 100644 --- a/sup3r/preprocessing/samplers/__init__.py +++ b/sup3r/preprocessing/samplers/__init__.py @@ -9,3 +9,4 @@ from .cc import DualSamplerCC from .dc import SamplerDC from .dual import DualSampler +from .with_obs import DualSamplerWithObs diff --git a/sup3r/preprocessing/samplers/with_obs.py b/sup3r/preprocessing/samplers/with_obs.py new file mode 100644 index 0000000000..267794b9b2 --- /dev/null +++ b/sup3r/preprocessing/samplers/with_obs.py @@ -0,0 +1,69 @@ +"""Extended Sampler for sampling observation data in addition to standard +gridded training data.""" + +from typing import Dict, Optional + +from sup3r.preprocessing.base import Sup3rDataset +from sup3r.preprocessing.samplers.dual import DualSampler + + +class DualSamplerWithObs(DualSampler): + """Dual Sampler which also samples from extra observation data. The + observation data is on the same grid as the high-resolution data but + includes NaNs at points where observation data doesn't exist. This will + be used in an additional content loss term.""" + + def __init__( + self, + data: Sup3rDataset, + sample_shape: Optional[tuple] = None, + batch_size: int = 16, + s_enhance: int = 1, + t_enhance: int = 24, + feature_sets: Optional[Dict] = None, + ): + """ + Parameters + ---------- + data : Sup3rDataset + A :class:`~sup3r.preprocessing.base.Sup3rDataset` instance with + low-res, high-res, and obs data members. The observation data is on + the same grid as the high-res data. + sample_shape : tuple + Size of arrays to sample from the high-res data. The sample shape + for the low-res sampler will be determined from the enhancement + factors. + s_enhance : int + Spatial enhancement factor + t_enhance : int + Temporal enhancement factor + feature_sets : Optional[dict] + Optional dictionary describing how the full set of features is + split between `lr_only_features` and `hr_exo_features`. + + lr_only_features : list | tuple + List of feature names or patt*erns that should only be + included in the low-res training set and not the high-res + observations. + hr_exo_features : list | tuple + List of feature names or patt*erns that should be included + in the high-resolution observation but not expected to be + output from the generative model. An example is high-res + topography that is to be injected mid-network. + """ + super().__init__( + data, + sample_shape=sample_shape, + batch_size=batch_size, + s_enhance=s_enhance, + t_enhance=t_enhance, + feature_sets=feature_sets, + ) + + def get_sample_index(self, n_obs=None): + """Get paired sample index, consisting of index for the low res sample + and the index for the high res sample with the same spatiotemporal + extent, with an additional index (same as the index for the high-res + data) for the observation data""" + lr_index, hr_index = super().get_sample_index(n_obs=n_obs) + return (lr_index, hr_index, hr_index) diff --git a/tests/training/test_train_dual_with_obs.py b/tests/training/test_train_dual_with_obs.py new file mode 100644 index 0000000000..b66b3c85dc --- /dev/null +++ b/tests/training/test_train_dual_with_obs.py @@ -0,0 +1,125 @@ +"""Test the training of GANs with dual data handler""" + +import os +import tempfile + +import numpy as np +import pytest + +from sup3r.models import Sup3rGan +from sup3r.preprocessing import ( + DataHandler, + DualBatchHandlerWithObs, + DualRasterizer, + Sup3rDataset, +) +from sup3r.preprocessing.samplers import DualSamplerWithObs +from sup3r.utilities.pytest.helpers import BatchHandlerTesterFactory + +TARGET_COORD = (39.01, -105.15) +FEATURES = ['u_100m', 'v_100m'] + + +DualBatchHandlerWithObsTester = BatchHandlerTesterFactory( + DualBatchHandlerWithObs, DualSamplerWithObs +) + + +@pytest.mark.parametrize( + [ + 'fp_gen', + 'fp_disc', + 's_enhance', + 't_enhance', + 'sample_shape', + 'mode', + ], + [ + (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16), 'lazy'), + (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16), 'eager'), + (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (20, 20, 1), 'lazy'), + (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (20, 20, 1), 'eager'), + ], +) +def test_train_h5_nc( + fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, mode, n_epoch=2 +): + """Test model training with a dual data handler / batch handler with h5 and + era as hr / lr datasets with additional observation data used in extra + content loss. Tests both spatiotemporal and spatial models.""" + + lr = 1e-5 + kwargs = { + 'features': FEATURES, + 'target': TARGET_COORD, + 'shape': (20, 20), + } + hr_handler = DataHandler( + pytest.FP_WTK, + **kwargs, + time_slice=slice(None, None, 1), + ) + lr_handler = DataHandler( + pytest.FP_ERA, + features=FEATURES, + time_slice=slice(None, None, 30), + ) + + # time indices conflict with t_enhance + with pytest.raises(AssertionError): + dual_rasterizer = DualRasterizer( + data=(lr_handler.data, hr_handler.data), + s_enhance=s_enhance, + t_enhance=t_enhance, + ) + + lr_handler = DataHandler( + pytest.FP_ERA, + features=FEATURES, + time_slice=slice(None, None, t_enhance), + ) + + dual_rasterizer = DualRasterizer( + data=(lr_handler.data, hr_handler.data), + s_enhance=s_enhance, + t_enhance=t_enhance, + ) + obs_data = dual_rasterizer.high_res.copy() + + dual_with_obs = Sup3rDataset( + low_res=dual_rasterizer.low_res, + high_res=dual_rasterizer.high_res, + obs=obs_data, + ) + + batch_handler = DualBatchHandlerWithObsTester( + train_containers=[dual_with_obs], + val_containers=[], + sample_shape=sample_shape, + batch_size=3, + s_enhance=s_enhance, + t_enhance=t_enhance, + n_batches=3, + mode=mode, + ) + + Sup3rGan.seed() + model = Sup3rGan( + fp_gen, fp_disc, learning_rate=lr, loss='MeanAbsoluteError' + ) + + with tempfile.TemporaryDirectory() as td: + model_kwargs = { + 'input_resolution': {'spatial': '30km', 'temporal': '60min'}, + 'n_epoch': n_epoch, + 'weight_gen_advers': 0.0, + 'train_gen': True, + 'train_disc': False, + 'checkpoint_int': 1, + 'out_dir': os.path.join(td, 'test_{epoch}'), + } + + model.train(batch_handler, **model_kwargs) + + tlossg = model.history['train_loss_gen'].values + assert np.sum(np.diff(tlossg)) < 0 From 16ca65cc061a832292ffbdad46efc1fdc57c03a8 Mon Sep 17 00:00:00 2001 From: "Brandon N. Benton" Date: Fri, 20 Dec 2024 15:43:13 -0800 Subject: [PATCH 002/122] training with obs test --- sup3r/models/abstract.py | 15 ++++++- sup3r/models/base.py | 19 +++------ sup3r/preprocessing/batch_queues/with_obs.py | 17 ++++++++ tests/training/test_train_dual_with_obs.py | 43 ++++++++++---------- 4 files changed, 58 insertions(+), 36 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 47561f4ed2..53e43939f0 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -1364,6 +1364,7 @@ def run_gradient_descent( low_res, hi_res_true, training_weights, + obs_data=None, optimizer=None, multi_gpu=False, **calc_loss_kwargs, @@ -1385,6 +1386,8 @@ def run_gradient_descent( training_weights : list A list of layer weights that are to-be-trained based on the current loss weight values. + obs_data : tf.Tensor | None + Optional observation data to use in additional content loss term. optimizer : tf.keras.optimizers.Optimizer Optimizer class to use to update weights. This can be different if you're training just the generator or one of the discriminator @@ -1413,6 +1416,7 @@ def run_gradient_descent( low_res, hi_res_true, training_weights, + obs_data=obs_data, device_name=self.default_device, **calc_loss_kwargs, ) @@ -1426,6 +1430,11 @@ def run_gradient_descent( futures = [] lr_chunks = np.array_split(low_res, len(self.gpu_list)) hr_true_chunks = np.array_split(hi_res_true, len(self.gpu_list)) + obs_data_chunks = ( + [None] * len(hr_true_chunks) + if obs_data is None + else np.array_split(obs_data, len(self.gpu_list)) + ) split_mask = False mask_chunks = None if 'mask' in calc_loss_kwargs: @@ -1444,6 +1453,7 @@ def run_gradient_descent( lr_chunks[i], hr_true_chunks[i], training_weights, + obs_data=obs_data_chunks[i], device_name=f'/gpu:{i}', **calc_loss_kwargs, ) @@ -1666,6 +1676,7 @@ def get_single_grad( low_res, hi_res_true, training_weights, + obs_data=None, device_name=None, **calc_loss_kwargs, ): @@ -1685,6 +1696,8 @@ def get_single_grad( training_weights : list A list of layer weights that are to-be-trained based on the current loss weight values. + obs_data : tf.Tensor | None + Optional observation data to use in additional content loss term. device_name : None | str Optional tensorflow device name for GPU placement. Note that if a GPU is available, variables will be placed on that GPU even if @@ -1708,7 +1721,7 @@ def get_single_grad( hi_res_exo = self.get_hr_exo_input(hi_res_true) hi_res_gen = self._tf_generate(low_res, hi_res_exo) loss_out = self.calc_loss( - hi_res_true, hi_res_gen, **calc_loss_kwargs + hi_res_true, hi_res_gen, obs_data=obs_data, **calc_loss_kwargs ) loss, loss_details = loss_out grad = tape.gradient(loss, training_weights) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 90f372444a..7ed7b0f947 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -554,7 +554,7 @@ def update_adversarial_weights( Parameters ---------- - history : dict + history : dicts Dictionary with information on how often discriminators were trained during current and previous epochs. adaptive_update_fraction : float @@ -1017,7 +1017,7 @@ def _train_batch( return loss_details def _post_batch( - self, ib, b_loss_details, loss_mean_window, n_batches, previous_means + self, ib, b_loss_details, n_batches, previous_means ): """Update loss details after the current batch and write to log. @@ -1027,8 +1027,6 @@ def _post_batch( Index of the current batch b_loss_details : dict Dictionary of loss details for the current batch - loss_mean_window : int - Number of batches to use in the running loss means n_batches : int Number of batches in an epoch previous_means : dict @@ -1057,10 +1055,8 @@ def _post_batch( trained_gen = bool(self._train_record['gen_train_frac'].values[-1]) trained_disc = bool(self._train_record['disc_train_frac'].values[-1]) - disc_loss = self._train_record['train_loss_disc'].values - disc_loss = disc_loss[-loss_mean_window:].mean() - gen_loss = self._train_record['train_loss_gen'].values - gen_loss = gen_loss[-loss_mean_window:].mean() + disc_loss = self._train_record['train_loss_disc'].values.mean() + gen_loss = self._train_record['train_loss_gen'].values.mean() logger.debug( 'Batch {} out of {} has (gen / disc) loss of: ' @@ -1083,7 +1079,7 @@ def _post_batch( ) logger.warning(msg) warn(msg) - loss_means = self._train_record.iloc[-loss_mean_window:].mean(axis=0) + loss_means = self._train_record.mean(axis=0) return loss_means.to_dict() def _train_epoch( @@ -1113,10 +1109,6 @@ def _train_epoch( Lower and upper bounds for the discriminator loss outside of which the discriminators will not train unless train_disc=True or and train_gen=False. - loss_mean_window : int - Number of batches to use to compute generator and discriminator - loss means, which are used to decide whether to train each network - for a given batch. Defaults to the number of batches in an epoch multi_gpu : bool Flag to break up the batch for parallel gradient descent calculations on multiple gpus. If True and multiple GPUs are @@ -1174,7 +1166,6 @@ def _train_epoch( loss_means = self._post_batch( ib, b_loss_details, - loss_mean_window, len(batch_handler), loss_means, ) diff --git a/sup3r/preprocessing/batch_queues/with_obs.py b/sup3r/preprocessing/batch_queues/with_obs.py index 4c2a16571c..0a0a0890e6 100644 --- a/sup3r/preprocessing/batch_queues/with_obs.py +++ b/sup3r/preprocessing/batch_queues/with_obs.py @@ -3,6 +3,7 @@ high-res data during training.""" import logging +from collections import namedtuple from scipy.ndimage import gaussian_filter @@ -15,6 +16,8 @@ class DualBatchQueueWithObs(DualBatchQueue): """Base BatchQueue for use with :class:`~sup3r.preprocessing.samplers.DualSamplerWithObs` objects.""" + Batch = namedtuple('Batch', ['low_res', 'high_res', 'obs']) + _signature_objs = (DualBatchQueue,) @property @@ -48,3 +51,17 @@ def transform(self, samples, smoothing=None, smoothing_ignore=None): low_res[i, ..., j], smoothing, mode='nearest' ) return low_res, high_res, obs + + def post_proc(self, samples) -> Batch: + """Performs some post proc on dequeued samples before sending out for + training. Post processing can include coarsening on high-res data (if + :class:`Collection` consists of :class:`Sampler` objects and not + :class:`DualSampler` objects), smoothing, etc + + Returns + ------- + Batch : namedtuple + namedtuple with `low_res`, `high_res`, and `obs` attributes + """ + lr, hr, obs = self.transform(samples, **self.transform_kwargs) + return self.Batch(low_res=lr, high_res=hr, obs=obs) diff --git a/tests/training/test_train_dual_with_obs.py b/tests/training/test_train_dual_with_obs.py index b66b3c85dc..ae5a883571 100644 --- a/tests/training/test_train_dual_with_obs.py +++ b/tests/training/test_train_dual_with_obs.py @@ -1,5 +1,6 @@ """Test the training of GANs with dual data handler""" +import itertools import os import tempfile @@ -8,6 +9,7 @@ from sup3r.models import Sup3rGan from sup3r.preprocessing import ( + Container, DataHandler, DualBatchHandlerWithObs, DualRasterizer, @@ -41,7 +43,7 @@ (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (20, 20, 1), 'eager'), ], ) -def test_train_h5_nc( +def test_train_coarse_h5( fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, mode, n_epoch=2 ): """Test model training with a dual data handler / batch handler with h5 and @@ -59,23 +61,11 @@ def test_train_h5_nc( **kwargs, time_slice=slice(None, None, 1), ) - lr_handler = DataHandler( - pytest.FP_ERA, - features=FEATURES, - time_slice=slice(None, None, 30), - ) - - # time indices conflict with t_enhance - with pytest.raises(AssertionError): - dual_rasterizer = DualRasterizer( - data=(lr_handler.data, hr_handler.data), - s_enhance=s_enhance, - t_enhance=t_enhance, - ) lr_handler = DataHandler( - pytest.FP_ERA, - features=FEATURES, + pytest.FP_WTK, + **kwargs, + hr_spatial_coarsen=s_enhance, time_slice=slice(None, None, t_enhance), ) @@ -85,11 +75,20 @@ def test_train_h5_nc( t_enhance=t_enhance, ) obs_data = dual_rasterizer.high_res.copy() - - dual_with_obs = Sup3rDataset( - low_res=dual_rasterizer.low_res, - high_res=dual_rasterizer.high_res, - obs=obs_data, + for feat in FEATURES: + tmp = np.full(obs_data[feat].shape, np.nan) + lat_ids = list(range(0, 20, 4)) + lon_ids = list(range(0, 20, 4)) + for ilat, ilon in itertools.product(lat_ids, lon_ids): + tmp[ilat, ilon, :] = obs_data[feat][ilat, ilon] + obs_data[feat] = (obs_data[feat].dims, tmp) + + dual_with_obs = Container( + data=Sup3rDataset( + low_res=dual_rasterizer.low_res, + high_res=dual_rasterizer.high_res, + obs=obs_data, + ) ) batch_handler = DualBatchHandlerWithObsTester( @@ -122,4 +121,6 @@ def test_train_h5_nc( model.train(batch_handler, **model_kwargs) tlossg = model.history['train_loss_gen'].values + tlosso = model.history['train_loss_obs'].values assert np.sum(np.diff(tlossg)) < 0 + assert np.sum(np.diff(tlosso)) < 0 From 8630629831f7073d2f103df04d379c465e6d4fab Mon Sep 17 00:00:00 2001 From: "Brandon N. Benton" Date: Sat, 21 Dec 2024 08:18:27 -0800 Subject: [PATCH 003/122] split up interface and abstact model --- sup3r/models/abstract.py | 626 ++------------------- sup3r/models/base.py | 3 +- sup3r/models/conditional.py | 3 +- sup3r/models/interface.py | 527 +++++++++++++++++ sup3r/models/tensorboard.py | 85 +++ sup3r/preprocessing/base.py | 39 +- tests/training/test_train_dual_with_obs.py | 17 +- 7 files changed, 686 insertions(+), 614 deletions(-) create mode 100644 sup3r/models/interface.py create mode 100644 sup3r/models/tensorboard.py diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 53e43939f0..7c0de5c95c 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -2,11 +2,9 @@ import copy import json -import locale import logging import os import pprint -import re import time from abc import ABC, abstractmethod from concurrent.futures import ThreadPoolExecutor @@ -25,593 +23,18 @@ from sup3r.preprocessing.data_handlers import ExoData from sup3r.preprocessing.utilities import numpy_if_tensor from sup3r.utilities import VERSION_RECORD -from sup3r.utilities.utilities import Timer, camel_to_underscore, safe_cast +from sup3r.utilities.utilities import safe_cast -logger = logging.getLogger(__name__) - - -class TensorboardMixIn: - """MixIn class for tensorboard logging and profiling. - - Note: To monitor model training with tensorboard run ``ssh -N -f -L - localhost:6006:localhost:6006 @`` on your local machine and - then ``tensorboard --logdir `` on the remote machine - """ - - def __init__(self): - self._tb_writer = None - self._tb_log_dir = None - self._write_tb_profile = False - self._total_batches = None - self._history = None - self.timer = Timer() - - @property - def total_batches(self): - """Record of total number of batches for logging.""" - if self._total_batches is None and self._history is None: - self._total_batches = 0 - elif self._history is None and 'total_batches' in self._history: - self._total_batches = self._history['total_batches'].values[-1] - elif self._total_batches is None and self._history is not None: - self._total_batches = 0 - return self._total_batches - - @total_batches.setter - def total_batches(self, value): - """Set total number of batches.""" - self._total_batches = value - - def dict_to_tensorboard(self, entry): - """Write data to tensorboard log file. This is usually a loss_details - dictionary. - - Parameters - ---------- - entry: dict - Dictionary of values to write to tensorboard log file - """ - if self._tb_writer is not None: - with self._tb_writer.as_default(): - for name, value in entry.items(): - if isinstance(value, str): - tf.summary.text(name, value, self.total_batches) - else: - tf.summary.scalar(name, value, self.total_batches) - - def profile_to_tensorboard(self, name): - """Write profile data to tensorboard log file. - - Parameters - ---------- - name : str - Tag name to use for profile info - """ - if self._tb_writer is not None and self._write_tb_profile: - with self._tb_writer.as_default(): - tf.summary.trace_export( - name=name, - step=self.total_batches, - profiler_outdir=self._tb_log_dir, - ) - - def _init_tensorboard_writer(self, out_dir): - """Initialize the ``tf.summary.SummaryWriter`` to use for writing - tensorboard compatible log files. - - Parameters - ---------- - out_dir : str - Standard out_dir where model epochs are saved. e.g. './gan_{epoch}' - """ - tb_log_pardir = os.path.abspath(os.path.join(out_dir, os.pardir)) - self._tb_log_dir = os.path.join(tb_log_pardir, 'logs') - os.makedirs(self._tb_log_dir, exist_ok=True) - self._tb_writer = tf.summary.create_file_writer(self._tb_log_dir) - - -class AbstractInterface(ABC): - """ - Abstract class to define the required interface for Sup3r model subclasses - - Note that this only sets the required interfaces for a GAN that can be - loaded from disk and used to predict synthetic outputs. The interface for - models that can be trained will be set in another class. - """ - - @classmethod - @abstractmethod - def load(cls, model_dir, verbose=True): - """Load the GAN with its sub-networks from a previously saved-to output - directory. - - Parameters - ---------- - model_dir - Directory to load GAN model files from. - verbose : bool - Flag to log information about the loaded model. - - Returns - ------- - out : BaseModel - Returns a pretrained gan model that was previously saved to - model_dir - """ - - @abstractmethod - def generate( - self, low_res, norm_in=True, un_norm_out=True, exogenous_data=None - ): - """Use the generator model to generate high res data from low res - input. This is the public generate function.""" - - @staticmethod - def seed(s=0): - """ - Set the random seed for reproducible results. - - Parameters - ---------- - s : int - Random seed - """ - CustomNetwork.seed(s=s) - - @property - def input_dims(self): - """Get dimension of model generator input. This is usually 4D for - spatial models and 5D for spatiotemporal models. This gives the input - to the first step if the model is multi-step. Returns 5 for linear - models. - - Returns - ------- - int - """ - # pylint: disable=E1101 - if hasattr(self, '_gen'): - return self._gen.layers[0].rank - if hasattr(self, 'models'): - return self.models[0].input_dims - return 5 - - @property - def is_5d(self): - """Check if model expects spatiotemporal input""" - return self.input_dims == 5 - - @property - def is_4d(self): - """Check if model expects spatial only input""" - return self.input_dims == 4 - - # pylint: disable=E1101 - def get_s_enhance_from_layers(self): - """Compute factor by which model will enhance spatial resolution from - layer attributes. Used in model training during high res coarsening""" - s_enhance = None - if hasattr(self, '_gen'): - s_enhancements = [ - getattr(layer, '_spatial_mult', 1) - for layer in self._gen.layers - ] - s_enhance = int(np.prod(s_enhancements)) - return s_enhance - - # pylint: disable=E1101 - def get_t_enhance_from_layers(self): - """Compute factor by which model will enhance temporal resolution from - layer attributes. Used in model training during high res coarsening""" - t_enhance = None - if hasattr(self, '_gen'): - t_enhancements = [ - getattr(layer, '_temporal_mult', 1) - for layer in self._gen.layers - ] - t_enhance = int(np.prod(t_enhancements)) - return t_enhance - - @property - def s_enhance(self): - """Factor by which model will enhance spatial resolution. Used in - model training during high res coarsening and also in forward pass - routine to determine shape of needed exogenous data""" - models = getattr(self, 'models', [self]) - s_enhances = [m.meta.get('s_enhance', None) for m in models] - s_enhance = ( - self.get_s_enhance_from_layers() - if any(s is None for s in s_enhances) - else int(np.prod(s_enhances)) - ) - if len(models) == 1: - self.meta['s_enhance'] = s_enhance - return s_enhance - - @property - def t_enhance(self): - """Factor by which model will enhance temporal resolution. Used in - model training during high res coarsening and also in forward pass - routine to determine shape of needed exogenous data""" - models = getattr(self, 'models', [self]) - t_enhances = [m.meta.get('t_enhance', None) for m in models] - t_enhance = ( - self.get_t_enhance_from_layers() - if any(t is None for t in t_enhances) - else int(np.prod(t_enhances)) - ) - if len(models) == 1: - self.meta['t_enhance'] = t_enhance - return t_enhance - - @property - def s_enhancements(self): - """List of spatial enhancement factors. In the case of a single step - model this is just ``[self.s_enhance]``. This is used to determine - shapes of needed exogenous data in forward pass routine""" - if hasattr(self, 'models'): - return [model.s_enhance for model in self.models] - return [self.s_enhance] - - @property - def t_enhancements(self): - """List of temporal enhancement factors. In the case of a single step - model this is just ``[self.t_enhance]``. This is used to determine - shapes of needed exogenous data in forward pass routine""" - if hasattr(self, 'models'): - return [model.t_enhance for model in self.models] - return [self.t_enhance] - - @property - def input_resolution(self): - """Resolution of input data. Given as a dictionary - ``{'spatial': '...km', 'temporal': '...min'}``. The numbers are - required to be integers in the units specified. The units are not - strict as long as the resolution of the exogenous data, when extracting - exogenous data, is specified in the same units.""" - input_resolution = self.meta.get('input_resolution', None) - msg = 'model.input_resolution is None. This needs to be set.' - assert input_resolution is not None, msg - return input_resolution - - def _get_numerical_resolutions(self): - """Get the input and output resolutions without units. e.g. for - ``{"spatial": "30km", "temporal": "60min"}`` this returns - ``{"spatial": 30, "temporal": 60}``""" - ires_num = { - k: int(re.search(r'\d+', v).group(0)) - for k, v in self.input_resolution.items() - } - enhancements = {'spatial': self.s_enhance, 'temporal': self.t_enhance} - ores_num = {k: v // enhancements[k] for k, v in ires_num.items()} - return ires_num, ores_num - - def _ensure_valid_input_resolution(self): - """Ensure ehancement factors evenly divide input_resolution""" - - if self.input_resolution is None: - return - - ires_num, ores_num = self._get_numerical_resolutions() - s_enhance = self.meta['s_enhance'] - t_enhance = self.meta['t_enhance'] - check = ( - ires_num['temporal'] / ores_num['temporal'] == t_enhance - and ires_num['spatial'] / ores_num['spatial'] == s_enhance - ) - msg = ( - f'Enhancement factors (s_enhance={s_enhance}, ' - f't_enhance={t_enhance}) do not evenly divide ' - f'input resolution ({self.input_resolution})' - ) - if not check: - logger.error(msg) - raise RuntimeError(msg) - - def _ensure_valid_enhancement_factors(self): - """Ensure user provided enhancement factors are the same as those - computed from layer attributes""" - t_enhance = self.meta.get('t_enhance', None) - s_enhance = self.meta.get('s_enhance', None) - if s_enhance is None or t_enhance is None: - return - - layer_se = self.get_s_enhance_from_layers() - layer_te = self.get_t_enhance_from_layers() - layer_se = layer_se if layer_se is not None else self.meta['s_enhance'] - layer_te = layer_te if layer_te is not None else self.meta['t_enhance'] - msg = ( - f'Enhancement factors computed from layer attributes ' - f'(s_enhance={layer_se}, t_enhance={layer_te}) ' - f'conflict with user provided values (s_enhance={s_enhance}, ' - f't_enhance={t_enhance})' - ) - check = layer_se == s_enhance or layer_te == t_enhance - if not check: - logger.error(msg) - raise RuntimeError(msg) - - @property - def output_resolution(self): - """Resolution of output data. Given as a dictionary - {'spatial': '...km', 'temporal': '...min'}. This is computed from the - input resolution and the enhancement factors.""" - output_res = self.meta.get('output_resolution', None) - if self.input_resolution is not None and output_res is None: - ires_num, ores_num = self._get_numerical_resolutions() - output_res = { - k: v.replace(str(ires_num[k]), str(ores_num[k])) - for k, v in self.input_resolution.items() - } - self.meta['output_resolution'] = output_res - return output_res - - def _combine_fwp_input(self, low_res, exogenous_data=None): - """Combine exogenous_data at input resolution with low_res data prior - to forward pass through generator - - Parameters - ---------- - low_res : np.ndarray - Low-resolution input data, usually a 4D or 5D array of shape: - (n_obs, spatial_1, spatial_2, n_features) - (n_obs, spatial_1, spatial_2, n_temporal, n_features) - exogenous_data : dict | ExoData | None - Special dictionary (class:`ExoData`) of exogenous feature data with - entries describing whether features should be combined at input, a - mid network layer, or with output. This doesn't have to include - the 'model' key since this data is for a single step model. - - Returns - ------- - low_res : np.ndarray - Low-resolution input data combined with exogenous_data, usually a - 4D or 5D array of shape: - (n_obs, spatial_1, spatial_2, n_features) - (n_obs, spatial_1, spatial_2, n_temporal, n_features) - """ - if exogenous_data is None: - return low_res - - if ( - not isinstance(exogenous_data, ExoData) - and exogenous_data is not None - ): - exogenous_data = ExoData(exogenous_data) - - fnum_diff = len(self.lr_features) - low_res.shape[-1] - exo_feats = [] if fnum_diff <= 0 else self.lr_features[-fnum_diff:] - msg = ( - f'Provided exogenous_data: {exogenous_data} is missing some ' - f'required features ({exo_feats})' - ) - assert all(feature in exogenous_data for feature in exo_feats), msg - if exogenous_data is not None and fnum_diff > 0: - for feature in exo_feats: - exo_input = exogenous_data.get_combine_type_data( - feature, 'input' - ) - if exo_input is not None: - low_res = np.concatenate((low_res, exo_input), axis=-1) +from .tensorboard import TensorboardMixIn - return low_res - - def _combine_fwp_output(self, hi_res, exogenous_data=None): - """Combine exogenous_data at output resolution with generated hi_res - data following forward pass output. - - Parameters - ---------- - hi_res : np.ndarray - High-resolution output data, usually a 4D or 5D array of shape: - (n_obs, spatial_1, spatial_2, n_features) - (n_obs, spatial_1, spatial_2, n_temporal, n_features) - exogenous_data : dict | ExoData | None - Special dictionary (class:`ExoData`) of exogenous feature data with - entries describing whether features should be combined at input, a - mid network layer, or with output. This doesn't have to include - the 'model' key since this data is for a single step model. - - Returns - ------- - hi_res : np.ndarray - High-resolution output data combined with exogenous_data, usually a - 4D or 5D array of shape: - (n_obs, spatial_1, spatial_2, n_features) - (n_obs, spatial_1, spatial_2, n_temporal, n_features) - """ - if exogenous_data is None: - return hi_res - - if ( - not isinstance(exogenous_data, ExoData) - and exogenous_data is not None - ): - exogenous_data = ExoData(exogenous_data) - - fnum_diff = len(self.hr_out_features) - hi_res.shape[-1] - exo_feats = [] if fnum_diff <= 0 else self.hr_out_features[-fnum_diff:] - msg = ( - 'Provided exogenous_data is missing some required features ' - f'({exo_feats})' - ) - assert all(feature in exogenous_data for feature in exo_feats), msg - if exogenous_data is not None and fnum_diff > 0: - for feature in exo_feats: - exo_output = exogenous_data.get_combine_type_data( - feature, 'output' - ) - if exo_output is not None: - hi_res = np.concatenate((hi_res, exo_output), axis=-1) - return hi_res - - @tf.function - def _combine_loss_input(self, high_res_true, high_res_gen): - """Combine exogenous feature data from high_res_true with high_res_gen - for loss calculation - - Parameters - ---------- - high_res_true : tf.Tensor - Ground truth high resolution spatiotemporal data. - high_res_gen : tf.Tensor - Superresolved high resolution spatiotemporal data generated by the - generative model. - - Returns - ------- - high_res_gen : tf.Tensor - Same as input with exogenous data combined with high_res input - """ - if high_res_true.shape[-1] > high_res_gen.shape[-1]: - for feature in self.hr_exo_features: - f_idx = self.hr_exo_features.index(feature) - f_idx += len(self.hr_out_features) - exo_data = high_res_true[..., f_idx : f_idx + 1] - high_res_gen = tf.concat((high_res_gen, exo_data), axis=-1) - return high_res_gen - - @property - @abstractmethod - def meta(self): - """Get meta data dictionary that defines how the model was created""" - - @property - def lr_features(self): - """Get a list of low-resolution features input to the generative model. - This includes low-resolution features that might be supplied - exogenously at inference time but that were in the low-res batches - during training""" - return self.meta.get('lr_features', []) - - @property - def hr_out_features(self): - """Get the list of high-resolution output feature names that the - generative model outputs.""" - return self.meta.get('hr_out_features', []) - - @property - def hr_exo_features(self): - """Get list of high-resolution exogenous filter names the model uses. - If the model has N concat or add layers this list will be the last N - features in the training features list. The ordering is assumed to be - the same as the order of concat or add layers. If training features is - [..., topo, sza], and the model has 2 concat or add layers, exo - features will be [topo, sza]. Topo will then be used in the first - concat layer and sza will be used in the second""" - # pylint: disable=E1101 - features = [] - if hasattr(self, '_gen'): - features = [ - layer.name - for layer in self._gen.layers - if isinstance(layer, (Sup3rAdder, Sup3rConcat)) - ] - return features - - @property - def smoothing(self): - """Value of smoothing parameter used in gaussian filtering of coarsened - high res data.""" - return self.meta.get('smoothing', None) - - @property - def smoothed_features(self): - """Get the list of smoothed input feature names that the generative - model was trained on.""" - return self.meta.get('smoothed_features', []) - - @property - def model_params(self): - """ - Model parameters, used to save model to disc - - Returns - ------- - dict - """ - return {'meta': self.meta} - - @property - def version_record(self): - """A record of important versions that this model was built with. - - Returns - ------- - dict - """ - return VERSION_RECORD - - def set_model_params(self, **kwargs): - """Set parameters used for training the model - - Parameters - ---------- - kwargs : dict - Keyword arguments including 'input_resolution', - 'lr_features', 'hr_exo_features', 'hr_out_features', - 'smoothed_features', 's_enhance', 't_enhance', 'smoothing' - """ - - keys = ( - 'input_resolution', - 'lr_features', - 'hr_exo_features', - 'hr_out_features', - 'smoothed_features', - 's_enhance', - 't_enhance', - 'smoothing', - ) - keys = [k for k in keys if k in kwargs] - - hr_exo_feat = kwargs.get('hr_exo_features', []) - msg = ( - f'Expected high-res exo features {self.hr_exo_features} ' - f'based on model architecture but received "hr_exo_features" ' - f'from data handler: {hr_exo_feat}' - ) - assert list(self.hr_exo_features) == list(hr_exo_feat), msg - - for var in keys: - val = self.meta.get(var, None) - if val is None: - self.meta[var] = kwargs[var] - elif val != kwargs[var]: - msg = ( - 'Model was previously trained with {var}={} but ' - 'received new {var}={}'.format(val, kwargs[var], var=var) - ) - logger.warning(msg) - warn(msg) - - self._ensure_valid_enhancement_factors() - self._ensure_valid_input_resolution() - - def save_params(self, out_dir): - """ - Parameters - ---------- - out_dir : str - Directory to save linear model params. This directory will be - created if it does not already exist. - """ - if not os.path.exists(out_dir): - os.makedirs(out_dir, exist_ok=True) - - fp_params = os.path.join(out_dir, 'model_params.json') - with open( - fp_params, 'w', encoding=locale.getpreferredencoding(False) - ) as f: - params = self.model_params - json.dump(params, f, sort_keys=True, indent=2, default=safe_cast) +logger = logging.getLogger(__name__) # pylint: disable=E1101,W0201,E0203 class AbstractSingleModel(ABC, TensorboardMixIn): """ - Abstract class to define the required training interface - for Sup3r model subclasses + Abstract class to define the required training interface for Sup3r model + subclasses """ def __init__(self): @@ -1726,3 +1149,42 @@ def get_single_grad( loss, loss_details = loss_out grad = tape.gradient(loss, training_weights) return grad, loss_details + + @abstractmethod + def calc_loss( + self, + hi_res_true, + hi_res_gen, + obs_data=None, + weight_gen_advers=0.001, + train_gen=True, + train_disc=False, + ): + """Calculate the GAN loss function using generated and true high + resolution data. + + Parameters + ---------- + hi_res_true : tf.Tensor + Ground truth high resolution spatiotemporal data. + hi_res_gen : tf.Tensor + Superresolved high resolution spatiotemporal data generated by the + generative model. + obs_data : tf.Tensor | None + Optional observation data to use in additional content loss term. + weight_gen_advers : float + Weight factor for the adversarial loss component of the generator + vs. the discriminator. + train_gen : bool + True if generator is being trained, then loss=loss_gen + train_disc : bool + True if disc is being trained, then loss=loss_disc + + Returns + ------- + loss : tf.Tensor + 0D tensor representing the loss value for the network being trained + (either generator or one of the discriminators) + loss_details : dict + Namespace of the breakdown of loss components + """ diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 7ed7b0f947..0cb78b8de4 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -14,7 +14,8 @@ from sup3r.preprocessing.utilities import get_class_kwargs from sup3r.utilities import VERSION_RECORD -from .abstract import AbstractInterface, AbstractSingleModel +from .abstract import AbstractSingleModel +from .interface import AbstractInterface from .utilities import get_optimizer_class logger = logging.getLogger(__name__) diff --git a/sup3r/models/conditional.py b/sup3r/models/conditional.py index f9a40d2e33..18d6b5129c 100644 --- a/sup3r/models/conditional.py +++ b/sup3r/models/conditional.py @@ -12,7 +12,8 @@ from sup3r.utilities import VERSION_RECORD -from .abstract import AbstractInterface, AbstractSingleModel +from .abstract import AbstractSingleModel +from .interface import AbstractInterface logger = logging.getLogger(__name__) diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py new file mode 100644 index 0000000000..6d18ef7d1e --- /dev/null +++ b/sup3r/models/interface.py @@ -0,0 +1,527 @@ +"""Abstract class defining the required interface for Sup3r model subclasses""" + +import json +import locale +import logging +import os +import pprint +import re +import time +from abc import ABC, abstractmethod +from concurrent.futures import ThreadPoolExecutor +from inspect import signature +from warnings import warn + +import numpy as np +import tensorflow as tf +from phygnn import CustomNetwork +from phygnn.layers.custom_layers import Sup3rAdder, Sup3rConcat +from rex.utilities.utilities import safe_json_load +from tensorflow.keras import optimizers + +import sup3r.utilities.loss_metrics +from sup3r.preprocessing.data_handlers import ExoData +from sup3r.preprocessing.utilities import numpy_if_tensor +from sup3r.utilities import VERSION_RECORD +from sup3r.utilities.utilities import safe_cast + +from .tensorboard import TensorboardMixIn + +logger = logging.getLogger(__name__) + + +class AbstractInterface(ABC): + """ + Abstract class to define the required interface for Sup3r model subclasses + + Note that this only sets the required interfaces for a GAN that can be + loaded from disk and used to predict synthetic outputs. The interface for + models that can be trained will be set in another class. + """ + + @classmethod + @abstractmethod + def load(cls, model_dir, verbose=True): + """Load the GAN with its sub-networks from a previously saved-to output + directory. + + Parameters + ---------- + model_dir + Directory to load GAN model files from. + verbose : bool + Flag to log information about the loaded model. + + Returns + ------- + out : BaseModel + Returns a pretrained gan model that was previously saved to + model_dir + """ + + @abstractmethod + def generate( + self, low_res, norm_in=True, un_norm_out=True, exogenous_data=None + ): + """Use the generator model to generate high res data from low res + input. This is the public generate function.""" + + @staticmethod + def seed(s=0): + """ + Set the random seed for reproducible results. + + Parameters + ---------- + s : int + Random seed + """ + CustomNetwork.seed(s=s) + + @property + def input_dims(self): + """Get dimension of model generator input. This is usually 4D for + spatial models and 5D for spatiotemporal models. This gives the input + to the first step if the model is multi-step. Returns 5 for linear + models. + + Returns + ------- + int + """ + # pylint: disable=E1101 + if hasattr(self, '_gen'): + return self._gen.layers[0].rank + if hasattr(self, 'models'): + return self.models[0].input_dims + return 5 + + @property + def is_5d(self): + """Check if model expects spatiotemporal input""" + return self.input_dims == 5 + + @property + def is_4d(self): + """Check if model expects spatial only input""" + return self.input_dims == 4 + + # pylint: disable=E1101 + def get_s_enhance_from_layers(self): + """Compute factor by which model will enhance spatial resolution from + layer attributes. Used in model training during high res coarsening""" + s_enhance = None + if hasattr(self, '_gen'): + s_enhancements = [ + getattr(layer, '_spatial_mult', 1) + for layer in self._gen.layers + ] + s_enhance = int(np.prod(s_enhancements)) + return s_enhance + + # pylint: disable=E1101 + def get_t_enhance_from_layers(self): + """Compute factor by which model will enhance temporal resolution from + layer attributes. Used in model training during high res coarsening""" + t_enhance = None + if hasattr(self, '_gen'): + t_enhancements = [ + getattr(layer, '_temporal_mult', 1) + for layer in self._gen.layers + ] + t_enhance = int(np.prod(t_enhancements)) + return t_enhance + + @property + def s_enhance(self): + """Factor by which model will enhance spatial resolution. Used in + model training during high res coarsening and also in forward pass + routine to determine shape of needed exogenous data""" + models = getattr(self, 'models', [self]) + s_enhances = [m.meta.get('s_enhance', None) for m in models] + s_enhance = ( + self.get_s_enhance_from_layers() + if any(s is None for s in s_enhances) + else int(np.prod(s_enhances)) + ) + if len(models) == 1: + self.meta['s_enhance'] = s_enhance + return s_enhance + + @property + def t_enhance(self): + """Factor by which model will enhance temporal resolution. Used in + model training during high res coarsening and also in forward pass + routine to determine shape of needed exogenous data""" + models = getattr(self, 'models', [self]) + t_enhances = [m.meta.get('t_enhance', None) for m in models] + t_enhance = ( + self.get_t_enhance_from_layers() + if any(t is None for t in t_enhances) + else int(np.prod(t_enhances)) + ) + if len(models) == 1: + self.meta['t_enhance'] = t_enhance + return t_enhance + + @property + def s_enhancements(self): + """List of spatial enhancement factors. In the case of a single step + model this is just ``[self.s_enhance]``. This is used to determine + shapes of needed exogenous data in forward pass routine""" + if hasattr(self, 'models'): + return [model.s_enhance for model in self.models] + return [self.s_enhance] + + @property + def t_enhancements(self): + """List of temporal enhancement factors. In the case of a single step + model this is just ``[self.t_enhance]``. This is used to determine + shapes of needed exogenous data in forward pass routine""" + if hasattr(self, 'models'): + return [model.t_enhance for model in self.models] + return [self.t_enhance] + + @property + def input_resolution(self): + """Resolution of input data. Given as a dictionary + ``{'spatial': '...km', 'temporal': '...min'}``. The numbers are + required to be integers in the units specified. The units are not + strict as long as the resolution of the exogenous data, when extracting + exogenous data, is specified in the same units.""" + input_resolution = self.meta.get('input_resolution', None) + msg = 'model.input_resolution is None. This needs to be set.' + assert input_resolution is not None, msg + return input_resolution + + def _get_numerical_resolutions(self): + """Get the input and output resolutions without units. e.g. for + ``{"spatial": "30km", "temporal": "60min"}`` this returns + ``{"spatial": 30, "temporal": 60}``""" + ires_num = { + k: int(re.search(r'\d+', v).group(0)) + for k, v in self.input_resolution.items() + } + enhancements = {'spatial': self.s_enhance, 'temporal': self.t_enhance} + ores_num = {k: v // enhancements[k] for k, v in ires_num.items()} + return ires_num, ores_num + + def _ensure_valid_input_resolution(self): + """Ensure ehancement factors evenly divide input_resolution""" + + if self.input_resolution is None: + return + + ires_num, ores_num = self._get_numerical_resolutions() + s_enhance = self.meta['s_enhance'] + t_enhance = self.meta['t_enhance'] + check = ( + ires_num['temporal'] / ores_num['temporal'] == t_enhance + and ires_num['spatial'] / ores_num['spatial'] == s_enhance + ) + msg = ( + f'Enhancement factors (s_enhance={s_enhance}, ' + f't_enhance={t_enhance}) do not evenly divide ' + f'input resolution ({self.input_resolution})' + ) + if not check: + logger.error(msg) + raise RuntimeError(msg) + + def _ensure_valid_enhancement_factors(self): + """Ensure user provided enhancement factors are the same as those + computed from layer attributes""" + t_enhance = self.meta.get('t_enhance', None) + s_enhance = self.meta.get('s_enhance', None) + if s_enhance is None or t_enhance is None: + return + + layer_se = self.get_s_enhance_from_layers() + layer_te = self.get_t_enhance_from_layers() + layer_se = layer_se if layer_se is not None else self.meta['s_enhance'] + layer_te = layer_te if layer_te is not None else self.meta['t_enhance'] + msg = ( + f'Enhancement factors computed from layer attributes ' + f'(s_enhance={layer_se}, t_enhance={layer_te}) ' + f'conflict with user provided values (s_enhance={s_enhance}, ' + f't_enhance={t_enhance})' + ) + check = layer_se == s_enhance or layer_te == t_enhance + if not check: + logger.error(msg) + raise RuntimeError(msg) + + @property + def output_resolution(self): + """Resolution of output data. Given as a dictionary + {'spatial': '...km', 'temporal': '...min'}. This is computed from the + input resolution and the enhancement factors.""" + output_res = self.meta.get('output_resolution', None) + if self.input_resolution is not None and output_res is None: + ires_num, ores_num = self._get_numerical_resolutions() + output_res = { + k: v.replace(str(ires_num[k]), str(ores_num[k])) + for k, v in self.input_resolution.items() + } + self.meta['output_resolution'] = output_res + return output_res + + def _combine_fwp_input(self, low_res, exogenous_data=None): + """Combine exogenous_data at input resolution with low_res data prior + to forward pass through generator + + Parameters + ---------- + low_res : np.ndarray + Low-resolution input data, usually a 4D or 5D array of shape: + (n_obs, spatial_1, spatial_2, n_features) + (n_obs, spatial_1, spatial_2, n_temporal, n_features) + exogenous_data : dict | ExoData | None + Special dictionary (class:`ExoData`) of exogenous feature data with + entries describing whether features should be combined at input, a + mid network layer, or with output. This doesn't have to include + the 'model' key since this data is for a single step model. + + Returns + ------- + low_res : np.ndarray + Low-resolution input data combined with exogenous_data, usually a + 4D or 5D array of shape: + (n_obs, spatial_1, spatial_2, n_features) + (n_obs, spatial_1, spatial_2, n_temporal, n_features) + """ + if exogenous_data is None: + return low_res + + if ( + not isinstance(exogenous_data, ExoData) + and exogenous_data is not None + ): + exogenous_data = ExoData(exogenous_data) + + fnum_diff = len(self.lr_features) - low_res.shape[-1] + exo_feats = [] if fnum_diff <= 0 else self.lr_features[-fnum_diff:] + msg = ( + f'Provided exogenous_data: {exogenous_data} is missing some ' + f'required features ({exo_feats})' + ) + assert all(feature in exogenous_data for feature in exo_feats), msg + if exogenous_data is not None and fnum_diff > 0: + for feature in exo_feats: + exo_input = exogenous_data.get_combine_type_data( + feature, 'input' + ) + if exo_input is not None: + low_res = np.concatenate((low_res, exo_input), axis=-1) + + return low_res + + def _combine_fwp_output(self, hi_res, exogenous_data=None): + """Combine exogenous_data at output resolution with generated hi_res + data following forward pass output. + + Parameters + ---------- + hi_res : np.ndarray + High-resolution output data, usually a 4D or 5D array of shape: + (n_obs, spatial_1, spatial_2, n_features) + (n_obs, spatial_1, spatial_2, n_temporal, n_features) + exogenous_data : dict | ExoData | None + Special dictionary (class:`ExoData`) of exogenous feature data with + entries describing whether features should be combined at input, a + mid network layer, or with output. This doesn't have to include + the 'model' key since this data is for a single step model. + + Returns + ------- + hi_res : np.ndarray + High-resolution output data combined with exogenous_data, usually a + 4D or 5D array of shape: + (n_obs, spatial_1, spatial_2, n_features) + (n_obs, spatial_1, spatial_2, n_temporal, n_features) + """ + if exogenous_data is None: + return hi_res + + if ( + not isinstance(exogenous_data, ExoData) + and exogenous_data is not None + ): + exogenous_data = ExoData(exogenous_data) + + fnum_diff = len(self.hr_out_features) - hi_res.shape[-1] + exo_feats = [] if fnum_diff <= 0 else self.hr_out_features[-fnum_diff:] + msg = ( + 'Provided exogenous_data is missing some required features ' + f'({exo_feats})' + ) + assert all(feature in exogenous_data for feature in exo_feats), msg + if exogenous_data is not None and fnum_diff > 0: + for feature in exo_feats: + exo_output = exogenous_data.get_combine_type_data( + feature, 'output' + ) + if exo_output is not None: + hi_res = np.concatenate((hi_res, exo_output), axis=-1) + return hi_res + + @tf.function + def _combine_loss_input(self, high_res_true, high_res_gen): + """Combine exogenous feature data from high_res_true with high_res_gen + for loss calculation + + Parameters + ---------- + high_res_true : tf.Tensor + Ground truth high resolution spatiotemporal data. + high_res_gen : tf.Tensor + Superresolved high resolution spatiotemporal data generated by the + generative model. + + Returns + ------- + high_res_gen : tf.Tensor + Same as input with exogenous data combined with high_res input + """ + if high_res_true.shape[-1] > high_res_gen.shape[-1]: + for feature in self.hr_exo_features: + f_idx = self.hr_exo_features.index(feature) + f_idx += len(self.hr_out_features) + exo_data = high_res_true[..., f_idx : f_idx + 1] + high_res_gen = tf.concat((high_res_gen, exo_data), axis=-1) + return high_res_gen + + @property + @abstractmethod + def meta(self): + """Get meta data dictionary that defines how the model was created""" + + @property + def lr_features(self): + """Get a list of low-resolution features input to the generative model. + This includes low-resolution features that might be supplied + exogenously at inference time but that were in the low-res batches + during training""" + return self.meta.get('lr_features', []) + + @property + def hr_out_features(self): + """Get the list of high-resolution output feature names that the + generative model outputs.""" + return self.meta.get('hr_out_features', []) + + @property + def hr_exo_features(self): + """Get list of high-resolution exogenous filter names the model uses. + If the model has N concat or add layers this list will be the last N + features in the training features list. The ordering is assumed to be + the same as the order of concat or add layers. If training features is + [..., topo, sza], and the model has 2 concat or add layers, exo + features will be [topo, sza]. Topo will then be used in the first + concat layer and sza will be used in the second""" + # pylint: disable=E1101 + features = [] + if hasattr(self, '_gen'): + features = [ + layer.name + for layer in self._gen.layers + if isinstance(layer, (Sup3rAdder, Sup3rConcat)) + ] + return features + + @property + def smoothing(self): + """Value of smoothing parameter used in gaussian filtering of coarsened + high res data.""" + return self.meta.get('smoothing', None) + + @property + def smoothed_features(self): + """Get the list of smoothed input feature names that the generative + model was trained on.""" + return self.meta.get('smoothed_features', []) + + @property + def model_params(self): + """ + Model parameters, used to save model to disc + + Returns + ------- + dict + """ + return {'meta': self.meta} + + @property + def version_record(self): + """A record of important versions that this model was built with. + + Returns + ------- + dict + """ + return VERSION_RECORD + + def set_model_params(self, **kwargs): + """Set parameters used for training the model + + Parameters + ---------- + kwargs : dict + Keyword arguments including 'input_resolution', + 'lr_features', 'hr_exo_features', 'hr_out_features', + 'smoothed_features', 's_enhance', 't_enhance', 'smoothing' + """ + + keys = ( + 'input_resolution', + 'lr_features', + 'hr_exo_features', + 'hr_out_features', + 'smoothed_features', + 's_enhance', + 't_enhance', + 'smoothing', + ) + keys = [k for k in keys if k in kwargs] + + hr_exo_feat = kwargs.get('hr_exo_features', []) + msg = ( + f'Expected high-res exo features {self.hr_exo_features} ' + f'based on model architecture but received "hr_exo_features" ' + f'from data handler: {hr_exo_feat}' + ) + assert list(self.hr_exo_features) == list(hr_exo_feat), msg + + for var in keys: + val = self.meta.get(var, None) + if val is None: + self.meta[var] = kwargs[var] + elif val != kwargs[var]: + msg = ( + 'Model was previously trained with {var}={} but ' + 'received new {var}={}'.format(val, kwargs[var], var=var) + ) + logger.warning(msg) + warn(msg) + + self._ensure_valid_enhancement_factors() + self._ensure_valid_input_resolution() + + def save_params(self, out_dir): + """ + Parameters + ---------- + out_dir : str + Directory to save linear model params. This directory will be + created if it does not already exist. + """ + if not os.path.exists(out_dir): + os.makedirs(out_dir, exist_ok=True) + + fp_params = os.path.join(out_dir, 'model_params.json') + with open( + fp_params, 'w', encoding=locale.getpreferredencoding(False) + ) as f: + params = self.model_params + json.dump(params, f, sort_keys=True, indent=2, default=safe_cast) diff --git a/sup3r/models/tensorboard.py b/sup3r/models/tensorboard.py new file mode 100644 index 0000000000..00c5764622 --- /dev/null +++ b/sup3r/models/tensorboard.py @@ -0,0 +1,85 @@ +"""Abstract class defining the required interface for Sup3r model subclasses""" + +import logging +import os + +import tensorflow as tf + +from sup3r.utilities.utilities import Timer + +logger = logging.getLogger(__name__) + + +class TensorboardMixIn: + """MixIn class for tensorboard logging and profiling.""" + + def __init__(self): + self._tb_writer = None + self._tb_log_dir = None + self._write_tb_profile = False + self._total_batches = None + self._history = None + self.timer = Timer() + + @property + def total_batches(self): + """Record of total number of batches for logging.""" + if self._total_batches is None and self._history is None: + self._total_batches = 0 + elif self._history is None and 'total_batches' in self._history: + self._total_batches = self._history['total_batches'].values[-1] + elif self._total_batches is None and self._history is not None: + self._total_batches = 0 + return self._total_batches + + @total_batches.setter + def total_batches(self, value): + """Set total number of batches.""" + self._total_batches = value + + def dict_to_tensorboard(self, entry): + """Write data to tensorboard log file. This is usually a loss_details + dictionary. + + Parameters + ---------- + entry: dict + Dictionary of values to write to tensorboard log file + """ + if self._tb_writer is not None: + with self._tb_writer.as_default(): + for name, value in entry.items(): + if isinstance(value, str): + tf.summary.text(name, value, self.total_batches) + else: + tf.summary.scalar(name, value, self.total_batches) + + def profile_to_tensorboard(self, name): + """Write profile data to tensorboard log file. + + Parameters + ---------- + name : str + Tag name to use for profile info + """ + if self._tb_writer is not None and self._write_tb_profile: + with self._tb_writer.as_default(): + tf.summary.trace_export( + name=name, + step=self.total_batches, + profiler_outdir=self._tb_log_dir, + ) + + def _init_tensorboard_writer(self, out_dir): + """Initialize the ``tf.summary.SummaryWriter`` to use for writing + tensorboard compatible log files. + + Parameters + ---------- + out_dir : str + Standard out_dir where model epochs are saved. e.g. './gan_{epoch}' + """ + tb_log_pardir = os.path.abspath(os.path.join(out_dir, os.pardir)) + self._tb_log_dir = os.path.join(tb_log_pardir, 'logs') + os.makedirs(self._tb_log_dir, exist_ok=True) + self._tb_writer = tf.summary.create_file_writer(self._tb_log_dir) \ No newline at end of file diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index bc1897e17f..1cfa0c7eaa 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -12,7 +12,7 @@ import pprint from abc import ABCMeta from collections import namedtuple -from typing import Mapping, Tuple, Union +from typing import Dict, Mapping, Tuple, Union from warnings import warn import numpy as np @@ -183,11 +183,9 @@ def rewrap(self, data): """Rewrap data as ``Sup3rDataset`` after calling parent method.""" if isinstance(data, type(self)): return data - if len(data) == 2: - return type(self)(low_res=data[0], high_res=data[1]) - if len(data) == 3: - return type(self)(low_res=data[0], high_res=data[1], obs=data[2]) - return type(self)(high_res=data[0]) + if len(data) == 1: + return type(self)(high_res=data[0]) + return type(self)(**dict(zip(['low_res', 'high_res', 'obs'], data))) def sample(self, idx): """Get samples from ``self._ds`` members. idx should be either a tuple @@ -295,7 +293,12 @@ class Container(metaclass=Sup3rMeta): def __init__( self, data: Union[ - Sup3rX, Sup3rDataset, Tuple[Sup3rX, ...], Tuple[Sup3rDataset, ...] + Sup3rX, + Sup3rDataset, + Tuple[Sup3rX, ...], + Tuple[Sup3rDataset, ...], + Dict[str, Sup3rX], + Dict[str, Sup3rDataset], ] = None, ): """ @@ -363,25 +366,19 @@ def wrap(self, data): if is_type_of(data, Sup3rDataset): return data - if isinstance(data, tuple) and len(data) == 2: - msg = ( - f'{self.__class__.__name__}.data is being set with a ' - '2-tuple without explicit dataset names. We will assume ' - 'first tuple member is low-res and second is high-res.' - ) - logger.warning(msg) - warn(msg) - data = Sup3rDataset(low_res=data[0], high_res=data[1]) - elif isinstance(data, tuple) and len(data) == 3: + if isinstance(data, dict): + data = Sup3rDataset(**data) + + default_names = ['low_res', 'high_res', 'obs'] + if isinstance(data, tuple) and len(data) > 1: msg = ( f'{self.__class__.__name__}.data is being set with a ' - '3-tuple without explicit dataset names. We will assume ' - 'first tuple member is low-res, second is high-res, and third ' - 'is obs' + f'{len(data)}-tuple without explicit dataset names. We will ' + f'assume name ordering: {default_names[:len(data)]}' ) logger.warning(msg) warn(msg) - data = Sup3rDataset(low_res=data[0], high_res=data[1], obs=data[2]) + data = Sup3rDataset(**dict(zip(default_names, data))) elif not isinstance(data, Sup3rDataset): name = getattr(data, 'name', None) or 'high_res' data = Sup3rDataset(**{name: data}) diff --git a/tests/training/test_train_dual_with_obs.py b/tests/training/test_train_dual_with_obs.py index ae5a883571..01ab41e50b 100644 --- a/tests/training/test_train_dual_with_obs.py +++ b/tests/training/test_train_dual_with_obs.py @@ -13,7 +13,6 @@ DataHandler, DualBatchHandlerWithObs, DualRasterizer, - Sup3rDataset, ) from sup3r.preprocessing.samplers import DualSamplerWithObs from sup3r.utilities.pytest.helpers import BatchHandlerTesterFactory @@ -46,9 +45,9 @@ def test_train_coarse_h5( fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, mode, n_epoch=2 ): - """Test model training with a dual data handler / batch handler with h5 and - era as hr / lr datasets with additional observation data used in extra - content loss. Tests both spatiotemporal and spatial models.""" + """Test model training with a dual data handler / batch handler with + additional sparse observation data used in extra content loss term. Tests + both spatiotemporal and spatial models.""" lr = 1e-5 kwargs = { @@ -84,11 +83,11 @@ def test_train_coarse_h5( obs_data[feat] = (obs_data[feat].dims, tmp) dual_with_obs = Container( - data=Sup3rDataset( - low_res=dual_rasterizer.low_res, - high_res=dual_rasterizer.high_res, - obs=obs_data, - ) + data={ + 'low_res': dual_rasterizer.low_res, + 'high_res': dual_rasterizer.high_res, + 'obs': obs_data, + } ) batch_handler = DualBatchHandlerWithObsTester( From 9e35ce0b962d76f9753f19f466a8e38a24485613 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 22 Dec 2024 07:52:24 -0800 Subject: [PATCH 004/122] made dual batch queue flexible enough to account for additional obs member in data --- README.rst | 2 +- pyproject.toml | 15 ++++- sup3r/models/interface.py | 10 --- sup3r/models/linear.py | 2 +- sup3r/models/multi_step.py | 2 +- sup3r/models/tensorboard.py | 2 +- sup3r/preprocessing/__init__.py | 8 ++- sup3r/preprocessing/batch_handlers/factory.py | 6 +- sup3r/preprocessing/batch_queues/__init__.py | 3 +- sup3r/preprocessing/batch_queues/abstract.py | 18 +++-- sup3r/preprocessing/batch_queues/dual.py | 20 ++++-- sup3r/preprocessing/batch_queues/with_obs.py | 67 ------------------- sup3r/preprocessing/samplers/dual.py | 4 +- sup3r/preprocessing/samplers/with_obs.py | 27 ++++++-- 14 files changed, 81 insertions(+), 105 deletions(-) delete mode 100644 sup3r/preprocessing/batch_queues/with_obs.py diff --git a/README.rst b/README.rst index d3b0aea48b..3d321accce 100644 --- a/README.rst +++ b/README.rst @@ -78,4 +78,4 @@ Brandon Benton, Grant Buster, Guilherme Pimenta Castelao, Malik Hassanaly, Pavlo Acknowledgments =============== -This work was authored by the National Renewable Energy Laboratory, operated by Alliance for Sustainable Energy, LLC, for the U.S. Department of Energy (DOE) under Contract No. DE-AC36-08GO28308. This research was supported by the Grid Modernization Initiative of the U.S. Department of Energy (DOE) as part of its Grid Modernization Laboratory Consortium, a strategic partnership between DOE and the national laboratories to bring together leading experts, technologies, and resources to collaborate on the goal of modernizing the nation’s grid. Funding provided by the the DOE Office of Energy Efficiency and Renewable Energy (EERE), the DOE Office of Electricity (OE), DOE Grid Deployment Office (GDO), the DOE Office of Fossil Energy and Carbon Management (FECM), and the DOE Office of Cybersecurity, Energy Security, and Emergency Response (CESER), the DOE Advanced Scientific Computing Research (ASCR) program, the DOE Solar Energy Technologies Office (SETO), the DOE Wind Energy Technologies Office (WETO), the United States Agency for International Development (USAID), and the Laboratory Directed Research and Development (LDRD) program at the National Renewable Energy Laboratory. The research was performed using computational resources sponsored by the Department of Energy's Office of Energy Efficiency and Renewable Energy and located at the National Renewable Energy Laboratory. The views expressed in the article do not necessarily represent the views of the DOE or the U.S. Government. The U.S. Government retains and the publisher, by accepting the article for publication, acknowledges that the U.S. Government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this work, or allow others to do so, for U.S. Government purposes. +This work was authored by the National Renewable Energy Laboratory, operated by Alliance for Sustainable Energy, LLC, for the U.S. Department of Energy (DOE) under Contract No. DE-AC36-08GO28308. This research was supported by the Grid Modernization Initiative of the U.S. Department of Energy (DOE) as part of its Grid Modernization Laboratory Consortium, a strategic partnership between DOE and the national laboratories to bring together leading experts, technologies, and resources to collaborate on the goal of modernizing the nation’s grid. Funding provided by the the DOE Office of Energy Efficiency and Renewable Energy (EERE), the DOE Office of Electricity (OE), DOE Grid Deployment Office (GDO), the DOE Office of Fossil Energy and Carbon Management (FECM), and the DOE Office of Cybersecurity, Energy Security, and Emergency Response (CESER), the DOE Advanced Scientific Computing Research (ASCR) program, the DOE Solar Energy Technologies Office (SETO), the DOE Wind Energy Technologies Office (WETO), the United States Agency for International Development (USAID), and the Laboratory Directed Research and Development (LDRD) program at the National Renewable Energy Laboratory. The research was performed using computational resources sponsored by the Department of Energy's Office of Energy Efficiency and Renewable Energy and located at the National Renewable Energy Laboratory. The views expressed in the article do not necessarily represent the views of the DOE or the U.S. Government. The U.S. Government retains and the publisher, by accepting the article for publication, acknowledges that the U.S. Government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this work, or allow others to do so, for U.S. Government purposes. diff --git a/pyproject.toml b/pyproject.toml index b745b6dfe6..6db7863b28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,10 +42,21 @@ dependencies = [ "pytest>=5.2", "scipy>=1.0.0", "sphinx>=7.0", - "tensorflow>2.4,<2.16", "xarray>=2023.0" ] +# If used, cause glibc conflict +# [tool.pixi.target.linux-64.dependencies] +# cuda = ">=11.8" +# cudnn = {version = ">=8.6.0", channel = "conda-forge"} +# # 8.9.7 + +[tool.pixi.target.linux-64.pypi-dependencies] +tensorflow = {version = "~=2.15.1", extras = ["and-cuda"] } + +[tool.pixi.target.osx-arm64.dependencies] +tensorflow = {version = "~=2.15.0", channel = "conda-forge"} + [project.optional-dependencies] dev = [ "build>=0.5", @@ -272,7 +283,6 @@ matplotlib = ">=3.1" numpy = "~=1.7" pandas = ">=2.0" scipy = ">=1.0.0" -tensorflow = ">2.4,<2.16" xarray = ">=2023.0" [tool.pixi.pypi-dependencies] @@ -284,6 +294,7 @@ NREL-farms = { version = ">=1.0.4" } [tool.pixi.environments] default = { solve-group = "default" } +kestrel = { features = ["kestrel"], solve-group = "default" } dev = { features = ["dev", "doc", "test"], solve-group = "default" } doc = { features = ["doc"], solve-group = "default" } test = { features = ["test"], solve-group = "default" } diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index 6d18ef7d1e..c81065f86f 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -4,29 +4,19 @@ import locale import logging import os -import pprint import re -import time from abc import ABC, abstractmethod -from concurrent.futures import ThreadPoolExecutor -from inspect import signature from warnings import warn import numpy as np import tensorflow as tf from phygnn import CustomNetwork from phygnn.layers.custom_layers import Sup3rAdder, Sup3rConcat -from rex.utilities.utilities import safe_json_load -from tensorflow.keras import optimizers -import sup3r.utilities.loss_metrics from sup3r.preprocessing.data_handlers import ExoData -from sup3r.preprocessing.utilities import numpy_if_tensor from sup3r.utilities import VERSION_RECORD from sup3r.utilities.utilities import safe_cast -from .tensorboard import TensorboardMixIn - logger = logging.getLogger(__name__) diff --git a/sup3r/models/linear.py b/sup3r/models/linear.py index 2c00a02b55..8ca9f0a0a5 100644 --- a/sup3r/models/linear.py +++ b/sup3r/models/linear.py @@ -6,7 +6,7 @@ import numpy as np -from .abstract import AbstractInterface +from .interface import AbstractInterface from .utilities import st_interp logger = logging.getLogger(__name__) diff --git a/sup3r/models/multi_step.py b/sup3r/models/multi_step.py index d447633cfb..e0d33cca02 100644 --- a/sup3r/models/multi_step.py +++ b/sup3r/models/multi_step.py @@ -14,8 +14,8 @@ import sup3r.models from sup3r.preprocessing.data_handlers import ExoData -from .abstract import AbstractInterface from .base import Sup3rGan +from .interface import AbstractInterface logger = logging.getLogger(__name__) diff --git a/sup3r/models/tensorboard.py b/sup3r/models/tensorboard.py index 00c5764622..4712c95220 100644 --- a/sup3r/models/tensorboard.py +++ b/sup3r/models/tensorboard.py @@ -82,4 +82,4 @@ def _init_tensorboard_writer(self, out_dir): tb_log_pardir = os.path.abspath(os.path.join(out_dir, os.pardir)) self._tb_log_dir = os.path.join(tb_log_pardir, 'logs') os.makedirs(self._tb_log_dir, exist_ok=True) - self._tb_writer = tf.summary.create_file_writer(self._tb_log_dir) \ No newline at end of file + self._tb_writer = tf.summary.create_file_writer(self._tb_log_dir) diff --git a/sup3r/preprocessing/__init__.py b/sup3r/preprocessing/__init__.py index 0832d8d3a9..4420efba6e 100644 --- a/sup3r/preprocessing/__init__.py +++ b/sup3r/preprocessing/__init__.py @@ -64,4 +64,10 @@ Rasterizer, SzaRasterizer, ) -from .samplers import DualSampler, DualSamplerCC, Sampler, SamplerDC +from .samplers import ( + DualSampler, + DualSamplerCC, + DualSamplerWithObs, + Sampler, + SamplerDC, +) diff --git a/sup3r/preprocessing/batch_handlers/factory.py b/sup3r/preprocessing/batch_handlers/factory.py index 4b6ddf3aa6..a2b0be5264 100644 --- a/sup3r/preprocessing/batch_handlers/factory.py +++ b/sup3r/preprocessing/batch_handlers/factory.py @@ -13,8 +13,10 @@ QueueMom2SepSF, QueueMom2SF, ) -from sup3r.preprocessing.batch_queues.dual import DualBatchQueue -from sup3r.preprocessing.batch_queues.with_obs import DualBatchQueueWithObs +from sup3r.preprocessing.batch_queues.dual import ( + DualBatchQueue, + DualBatchQueueWithObs, +) from sup3r.preprocessing.collections.stats import StatsCollection from sup3r.preprocessing.samplers.base import Sampler from sup3r.preprocessing.samplers.cc import DualSamplerCC diff --git a/sup3r/preprocessing/batch_queues/__init__.py b/sup3r/preprocessing/batch_queues/__init__.py index 067d210702..1e91caa1b8 100644 --- a/sup3r/preprocessing/batch_queues/__init__.py +++ b/sup3r/preprocessing/batch_queues/__init__.py @@ -11,5 +11,4 @@ QueueMom2SF, ) from .dc import BatchQueueDC, ValBatchQueueDC -from .dual import DualBatchQueue -from .with_obs import DualBatchQueueWithObs +from .dual import DualBatchQueue, DualBatchQueueWithObs diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index ff15f55f69..a0733e6026 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -22,7 +22,11 @@ from sup3r.utilities.utilities import RANDOM_GENERATOR, Timer if TYPE_CHECKING: - from sup3r.preprocessing.samplers import DualSampler, Sampler + from sup3r.preprocessing.samplers import ( + DualSampler, + DualSamplerWithObs, + Sampler, + ) logger = logging.getLogger(__name__) @@ -36,7 +40,9 @@ class AbstractBatchQueue(Collection, ABC): def __init__( self, - samplers: Union[List['Sampler'], List['DualSampler']], + samplers: Union[ + List['Sampler'], List['DualSampler'], List['DualSamplerWithObs'] + ], batch_size: int = 16, n_batches: int = 64, s_enhance: int = 1, @@ -188,10 +194,12 @@ def post_proc(self, samples) -> Batch: Returns ------- Batch : namedtuple - namedtuple with `low_res` and `high_res` attributes + namedtuple with `low_res` and `high_res` attributes. Could also + include additional members for subclass queues + (i.e. ``DualBatchQueueWithObs``) """ - lr, hr = self.transform(samples, **self.transform_kwargs) - return self.Batch(low_res=lr, high_res=hr) + tsamps = self.transform(samples, **self.transform_kwargs) + return self.Batch(**dict(zip(self.Batch._fields, tsamps))) def start(self) -> None: """Start thread to keep sample queue full for batches.""" diff --git a/sup3r/preprocessing/batch_queues/dual.py b/sup3r/preprocessing/batch_queues/dual.py index 691350cf7d..549145f421 100644 --- a/sup3r/preprocessing/batch_queues/dual.py +++ b/sup3r/preprocessing/batch_queues/dual.py @@ -2,6 +2,7 @@ interface with models.""" import logging +from collections import namedtuple from scipy.ndimage import gaussian_filter @@ -28,10 +29,10 @@ def __init__(self, samplers, **kwargs): @property def queue_shape(self): """Shape of objects stored in the queue.""" - return [ - (self.batch_size, *self.lr_shape), - (self.batch_size, *self.hr_shape), - ] + queue_shapes = [(self.batch_size, *self.lr_shape)] + hr_mems = len(self.Batch._fields) - 1 + queue_shapes += [(self.batch_size, *self.hr_shape)] * hr_mems + return queue_shapes def check_enhancement_factors(self): """Make sure each DualSampler has the same enhancment factors and they @@ -58,7 +59,7 @@ def transform(self, samples, smoothing=None, smoothing_ignore=None): This does not include temporal or spatial coarsening like :class:`SingleBatchQueue` """ - low_res, high_res = samples + low_res = samples[0] if smoothing is not None: feat_iter = [ @@ -71,4 +72,11 @@ def transform(self, samples, smoothing=None, smoothing_ignore=None): low_res[i, ..., j] = gaussian_filter( low_res[i, ..., j], smoothing, mode='nearest' ) - return low_res, high_res + return low_res, *samples[1:] + + +class DualBatchQueueWithObs(DualBatchQueue): + """BatchQueue for use with + :class:`~sup3r.preprocessing.samplers.DualSamplerWithObs` objects.""" + + Batch = namedtuple('Batch', ['low_res', 'high_res', 'obs']) diff --git a/sup3r/preprocessing/batch_queues/with_obs.py b/sup3r/preprocessing/batch_queues/with_obs.py deleted file mode 100644 index 0a0a0890e6..0000000000 --- a/sup3r/preprocessing/batch_queues/with_obs.py +++ /dev/null @@ -1,67 +0,0 @@ -"""DualBatchQueue with additional observation data on the same grid as the -high-res data. The observation data is sampled with the same index as the -high-res data during training.""" - -import logging -from collections import namedtuple - -from scipy.ndimage import gaussian_filter - -from .dual import DualBatchQueue - -logger = logging.getLogger(__name__) - - -class DualBatchQueueWithObs(DualBatchQueue): - """Base BatchQueue for use with - :class:`~sup3r.preprocessing.samplers.DualSamplerWithObs` objects.""" - - Batch = namedtuple('Batch', ['low_res', 'high_res', 'obs']) - - _signature_objs = (DualBatchQueue,) - - @property - def queue_shape(self): - """Shape of objects stored in the queue.""" - return [ - (self.batch_size, *self.lr_shape), - (self.batch_size, *self.hr_shape), - (self.batch_size, *self.hr_shape), - ] - - def transform(self, samples, smoothing=None, smoothing_ignore=None): - """Perform smoothing if requested. - - Note - ---- - This does not include temporal or spatial coarsening like - :class:`SingleBatchQueue` - """ - low_res, high_res, obs = samples - - if smoothing is not None: - feat_iter = [ - j - for j in range(low_res.shape[-1]) - if self.features[j] not in smoothing_ignore - ] - for i in range(low_res.shape[0]): - for j in feat_iter: - low_res[i, ..., j] = gaussian_filter( - low_res[i, ..., j], smoothing, mode='nearest' - ) - return low_res, high_res, obs - - def post_proc(self, samples) -> Batch: - """Performs some post proc on dequeued samples before sending out for - training. Post processing can include coarsening on high-res data (if - :class:`Collection` consists of :class:`Sampler` objects and not - :class:`DualSampler` objects), smoothing, etc - - Returns - ------- - Batch : namedtuple - namedtuple with `low_res`, `high_res`, and `obs` attributes - """ - lr, hr, obs = self.transform(samples, **self.transform_kwargs) - return self.Batch(low_res=lr, high_res=hr, obs=obs) diff --git a/sup3r/preprocessing/samplers/dual.py b/sup3r/preprocessing/samplers/dual.py index b021265d93..368650439c 100644 --- a/sup3r/preprocessing/samplers/dual.py +++ b/sup3r/preprocessing/samplers/dual.py @@ -108,11 +108,11 @@ def check_for_consistent_shapes(self): self.lr_data.shape[2] * self.t_enhance, ) msg = ( - f'hr_data.shape {self.hr_data.shape} and enhanced ' + f'hr_data.shape {self.hr_data.shape[:-1]} and enhanced ' f'lr_data.shape {enhanced_shape} are not compatible with ' 'the given enhancement factors' ) - assert self.hr_data.shape[:3] == enhanced_shape, msg + assert self.hr_data.shape[:-1] == enhanced_shape, msg def get_sample_index(self, n_obs=None): """Get paired sample index, consisting of index for the low res sample diff --git a/sup3r/preprocessing/samplers/with_obs.py b/sup3r/preprocessing/samplers/with_obs.py index 267794b9b2..1f74f8d80f 100644 --- a/sup3r/preprocessing/samplers/with_obs.py +++ b/sup3r/preprocessing/samplers/with_obs.py @@ -1,10 +1,14 @@ """Extended Sampler for sampling observation data in addition to standard gridded training data.""" +import logging from typing import Dict, Optional from sup3r.preprocessing.base import Sup3rDataset -from sup3r.preprocessing.samplers.dual import DualSampler + +from .dual import DualSampler + +logger = logging.getLogger(__name__) class DualSamplerWithObs(DualSampler): @@ -19,7 +23,7 @@ def __init__( sample_shape: Optional[tuple] = None, batch_size: int = 16, s_enhance: int = 1, - t_enhance: int = 24, + t_enhance: int = 1, feature_sets: Optional[Dict] = None, ): """ @@ -27,8 +31,7 @@ def __init__( ---------- data : Sup3rDataset A :class:`~sup3r.preprocessing.base.Sup3rDataset` instance with - low-res, high-res, and obs data members. The observation data is on - the same grid as the high-res data. + low-res and high-res data members sample_shape : tuple Size of arrays to sample from the high-res data. The sample shape for the low-res sampler will be determined from the enhancement @@ -51,6 +54,22 @@ def __init__( output from the generative model. An example is high-res topography that is to be injected mid-network. """ + + msg = ( + f'{self.__class__.__name__} requires a Sup3rDataset object ' + 'with `.low_res`, `.high_res`, and `.obs` data members, in that ' + 'order' + ) + assert ( + hasattr(data, 'low_res') + and hasattr(data, 'high_res') + and hasattr(data, 'obs') + ), msg + assert ( + data.low_res == data[0] + and data.high_res == data[1] + and data.obs == data[2] + ), msg super().__init__( data, sample_shape=sample_shape, From 7c52bf5ef936fc11854821c7c11df4a8678985cc Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 22 Dec 2024 12:26:42 -0800 Subject: [PATCH 005/122] tensorboard mixin moved to model utilities. dual queue completely absorbed WithObs variant. --- sup3r/models/abstract.py | 2 +- sup3r/models/tensorboard.py | 85 ------------------ sup3r/models/utilities.py | 79 +++++++++++++++++ sup3r/preprocessing/__init__.py | 7 +- sup3r/preprocessing/batch_handlers/factory.py | 10 +-- sup3r/preprocessing/batch_queues/__init__.py | 2 +- sup3r/preprocessing/batch_queues/abstract.py | 4 +- sup3r/preprocessing/batch_queues/dual.py | 8 +- sup3r/preprocessing/samplers/__init__.py | 3 +- sup3r/preprocessing/samplers/dual.py | 77 ++++++++++++++++ sup3r/preprocessing/samplers/with_obs.py | 88 ------------------- 11 files changed, 166 insertions(+), 199 deletions(-) delete mode 100644 sup3r/models/tensorboard.py delete mode 100644 sup3r/preprocessing/samplers/with_obs.py diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 7c0de5c95c..ea47ec3c39 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -25,7 +25,7 @@ from sup3r.utilities import VERSION_RECORD from sup3r.utilities.utilities import safe_cast -from .tensorboard import TensorboardMixIn +from .utilities import TensorboardMixIn logger = logging.getLogger(__name__) diff --git a/sup3r/models/tensorboard.py b/sup3r/models/tensorboard.py deleted file mode 100644 index 4712c95220..0000000000 --- a/sup3r/models/tensorboard.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Abstract class defining the required interface for Sup3r model subclasses""" - -import logging -import os - -import tensorflow as tf - -from sup3r.utilities.utilities import Timer - -logger = logging.getLogger(__name__) - - -class TensorboardMixIn: - """MixIn class for tensorboard logging and profiling.""" - - def __init__(self): - self._tb_writer = None - self._tb_log_dir = None - self._write_tb_profile = False - self._total_batches = None - self._history = None - self.timer = Timer() - - @property - def total_batches(self): - """Record of total number of batches for logging.""" - if self._total_batches is None and self._history is None: - self._total_batches = 0 - elif self._history is None and 'total_batches' in self._history: - self._total_batches = self._history['total_batches'].values[-1] - elif self._total_batches is None and self._history is not None: - self._total_batches = 0 - return self._total_batches - - @total_batches.setter - def total_batches(self, value): - """Set total number of batches.""" - self._total_batches = value - - def dict_to_tensorboard(self, entry): - """Write data to tensorboard log file. This is usually a loss_details - dictionary. - - Parameters - ---------- - entry: dict - Dictionary of values to write to tensorboard log file - """ - if self._tb_writer is not None: - with self._tb_writer.as_default(): - for name, value in entry.items(): - if isinstance(value, str): - tf.summary.text(name, value, self.total_batches) - else: - tf.summary.scalar(name, value, self.total_batches) - - def profile_to_tensorboard(self, name): - """Write profile data to tensorboard log file. - - Parameters - ---------- - name : str - Tag name to use for profile info - """ - if self._tb_writer is not None and self._write_tb_profile: - with self._tb_writer.as_default(): - tf.summary.trace_export( - name=name, - step=self.total_batches, - profiler_outdir=self._tb_log_dir, - ) - - def _init_tensorboard_writer(self, out_dir): - """Initialize the ``tf.summary.SummaryWriter`` to use for writing - tensorboard compatible log files. - - Parameters - ---------- - out_dir : str - Standard out_dir where model epochs are saved. e.g. './gan_{epoch}' - """ - tb_log_pardir = os.path.abspath(os.path.join(out_dir, os.pardir)) - self._tb_log_dir = os.path.join(tb_log_pardir, 'logs') - os.makedirs(self._tb_log_dir, exist_ok=True) - self._tb_writer = tf.summary.create_file_writer(self._tb_log_dir) diff --git a/sup3r/models/utilities.py b/sup3r/models/utilities.py index 8e825f1246..68e0f01021 100644 --- a/sup3r/models/utilities.py +++ b/sup3r/models/utilities.py @@ -1,13 +1,17 @@ """Utilities shared across the `sup3r.models` module""" import logging +import os import sys import threading import numpy as np +import tensorflow as tf from scipy.interpolate import RegularGridInterpolator from tensorflow.keras import optimizers +from sup3r.utilities.utilities import Timer + logger = logging.getLogger(__name__) @@ -58,6 +62,81 @@ def run(self): model_thread.join() +class TensorboardMixIn: + """MixIn class for tensorboard logging and profiling.""" + + def __init__(self): + self._tb_writer = None + self._tb_log_dir = None + self._write_tb_profile = False + self._total_batches = None + self._history = None + self.timer = Timer() + + @property + def total_batches(self): + """Record of total number of batches for logging.""" + if self._total_batches is None and self._history is None: + self._total_batches = 0 + elif self._history is None and 'total_batches' in self._history: + self._total_batches = self._history['total_batches'].values[-1] + elif self._total_batches is None and self._history is not None: + self._total_batches = 0 + return self._total_batches + + @total_batches.setter + def total_batches(self, value): + """Set total number of batches.""" + self._total_batches = value + + def dict_to_tensorboard(self, entry): + """Write data to tensorboard log file. This is usually a loss_details + dictionary. + + Parameters + ---------- + entry: dict + Dictionary of values to write to tensorboard log file + """ + if self._tb_writer is not None: + with self._tb_writer.as_default(): + for name, value in entry.items(): + if isinstance(value, str): + tf.summary.text(name, value, self.total_batches) + else: + tf.summary.scalar(name, value, self.total_batches) + + def profile_to_tensorboard(self, name): + """Write profile data to tensorboard log file. + + Parameters + ---------- + name : str + Tag name to use for profile info + """ + if self._tb_writer is not None and self._write_tb_profile: + with self._tb_writer.as_default(): + tf.summary.trace_export( + name=name, + step=self.total_batches, + profiler_outdir=self._tb_log_dir, + ) + + def _init_tensorboard_writer(self, out_dir): + """Initialize the ``tf.summary.SummaryWriter`` to use for writing + tensorboard compatible log files. + + Parameters + ---------- + out_dir : str + Standard out_dir where model epochs are saved. e.g. './gan_{epoch}' + """ + tb_log_pardir = os.path.abspath(os.path.join(out_dir, os.pardir)) + self._tb_log_dir = os.path.join(tb_log_pardir, 'logs') + os.makedirs(self._tb_log_dir, exist_ok=True) + self._tb_writer = tf.summary.create_file_writer(self._tb_log_dir) + + def get_optimizer_class(conf): """Get optimizer class from keras""" if hasattr(optimizers, conf['name']): diff --git a/sup3r/preprocessing/__init__.py b/sup3r/preprocessing/__init__.py index 4420efba6e..0070fad12b 100644 --- a/sup3r/preprocessing/__init__.py +++ b/sup3r/preprocessing/__init__.py @@ -35,12 +35,7 @@ DualBatchHandler, DualBatchHandlerWithObs, ) -from .batch_queues import ( - BatchQueueDC, - DualBatchQueue, - DualBatchQueueWithObs, - SingleBatchQueue, -) +from .batch_queues import BatchQueueDC, DualBatchQueue, SingleBatchQueue from .cachers import Cacher from .collections import Collection, StatsCollection from .data_handlers import ( diff --git a/sup3r/preprocessing/batch_handlers/factory.py b/sup3r/preprocessing/batch_handlers/factory.py index a2b0be5264..c43e9198a0 100644 --- a/sup3r/preprocessing/batch_handlers/factory.py +++ b/sup3r/preprocessing/batch_handlers/factory.py @@ -13,15 +13,11 @@ QueueMom2SepSF, QueueMom2SF, ) -from sup3r.preprocessing.batch_queues.dual import ( - DualBatchQueue, - DualBatchQueueWithObs, -) +from sup3r.preprocessing.batch_queues.dual import DualBatchQueue from sup3r.preprocessing.collections.stats import StatsCollection from sup3r.preprocessing.samplers.base import Sampler from sup3r.preprocessing.samplers.cc import DualSamplerCC -from sup3r.preprocessing.samplers.dual import DualSampler -from sup3r.preprocessing.samplers.with_obs import DualSamplerWithObs +from sup3r.preprocessing.samplers.dual import DualSampler, DualSamplerWithObs from sup3r.preprocessing.utilities import ( check_signatures, get_class_kwargs, @@ -321,7 +317,7 @@ def stop(self): ) DualBatchHandlerWithObs = BatchHandlerFactory( - DualBatchQueueWithObs, DualSamplerWithObs, name='DualBatchHandlerWithObs' + DualBatchQueue, DualSamplerWithObs, name='DualBatchHandlerWithObs' ) BatchHandlerCC = BatchHandlerFactory( diff --git a/sup3r/preprocessing/batch_queues/__init__.py b/sup3r/preprocessing/batch_queues/__init__.py index 1e91caa1b8..63053f1235 100644 --- a/sup3r/preprocessing/batch_queues/__init__.py +++ b/sup3r/preprocessing/batch_queues/__init__.py @@ -11,4 +11,4 @@ QueueMom2SF, ) from .dc import BatchQueueDC, ValBatchQueueDC -from .dual import DualBatchQueue, DualBatchQueueWithObs +from .dual import DualBatchQueue diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index a0733e6026..d5a6cfd958 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -195,8 +195,8 @@ def post_proc(self, samples) -> Batch: ------- Batch : namedtuple namedtuple with `low_res` and `high_res` attributes. Could also - include additional members for subclass queues - (i.e. ``DualBatchQueueWithObs``) + include additional members for integration with + ``DualSamplerWithObs`` """ tsamps = self.transform(samples, **self.transform_kwargs) return self.Batch(**dict(zip(self.Batch._fields, tsamps))) diff --git a/sup3r/preprocessing/batch_queues/dual.py b/sup3r/preprocessing/batch_queues/dual.py index 549145f421..4b64c593d9 100644 --- a/sup3r/preprocessing/batch_queues/dual.py +++ b/sup3r/preprocessing/batch_queues/dual.py @@ -21,6 +21,7 @@ def __init__(self, samplers, **kwargs): -------- :class:`~sup3r.preprocessing.batch_queues.abstract.AbstractBatchQueue` """ + self.Batch = namedtuple('Batch', samplers[0]._fields) super().__init__(samplers, **kwargs) self.check_enhancement_factors() @@ -73,10 +74,3 @@ def transform(self, samples, smoothing=None, smoothing_ignore=None): low_res[i, ..., j], smoothing, mode='nearest' ) return low_res, *samples[1:] - - -class DualBatchQueueWithObs(DualBatchQueue): - """BatchQueue for use with - :class:`~sup3r.preprocessing.samplers.DualSamplerWithObs` objects.""" - - Batch = namedtuple('Batch', ['low_res', 'high_res', 'obs']) diff --git a/sup3r/preprocessing/samplers/__init__.py b/sup3r/preprocessing/samplers/__init__.py index 990e23861f..545da5a3c3 100644 --- a/sup3r/preprocessing/samplers/__init__.py +++ b/sup3r/preprocessing/samplers/__init__.py @@ -8,5 +8,4 @@ from .base import Sampler from .cc import DualSamplerCC from .dc import SamplerDC -from .dual import DualSampler -from .with_obs import DualSamplerWithObs +from .dual import DualSampler, DualSamplerWithObs diff --git a/sup3r/preprocessing/samplers/dual.py b/sup3r/preprocessing/samplers/dual.py index 368650439c..f8a70be096 100644 --- a/sup3r/preprocessing/samplers/dual.py +++ b/sup3r/preprocessing/samplers/dual.py @@ -136,3 +136,80 @@ def get_sample_index(self, n_obs=None): ] hr_index = (*hr_index, self.hr_features) return (lr_index, hr_index) + + +class DualSamplerWithObs(DualSampler): + """Dual Sampler which also samples from extra observation data. The + observation data is on the same grid as the high-resolution data but + includes NaNs at points where observation data doesn't exist. This will + be used in an additional content loss term.""" + + def __init__( + self, + data: Sup3rDataset, + sample_shape: Optional[tuple] = None, + batch_size: int = 16, + s_enhance: int = 1, + t_enhance: int = 1, + feature_sets: Optional[Dict] = None, + ): + """ + Parameters + ---------- + data : Sup3rDataset + A :class:`~sup3r.preprocessing.base.Sup3rDataset` instance with + low-res and high-res data members + sample_shape : tuple + Size of arrays to sample from the high-res data. The sample shape + for the low-res sampler will be determined from the enhancement + factors. + s_enhance : int + Spatial enhancement factor + t_enhance : int + Temporal enhancement factor + feature_sets : Optional[dict] + Optional dictionary describing how the full set of features is + split between `lr_only_features` and `hr_exo_features`. + + lr_only_features : list | tuple + List of feature names or patt*erns that should only be + included in the low-res training set and not the high-res + observations. + hr_exo_features : list | tuple + List of feature names or patt*erns that should be included + in the high-resolution observation but not expected to be + output from the generative model. An example is high-res + topography that is to be injected mid-network. + """ + + msg = ( + f'{self.__class__.__name__} requires a Sup3rDataset object ' + 'with `.low_res`, `.high_res`, and `.obs` data members, in that ' + 'order' + ) + assert ( + hasattr(data, 'low_res') + and hasattr(data, 'high_res') + and hasattr(data, 'obs') + ), msg + assert ( + data.low_res == data[0] + and data.high_res == data[1] + and data.obs == data[2] + ), msg + super().__init__( + data, + sample_shape=sample_shape, + batch_size=batch_size, + s_enhance=s_enhance, + t_enhance=t_enhance, + feature_sets=feature_sets, + ) + + def get_sample_index(self, n_obs=None): + """Get paired sample index, consisting of index for the low res sample + and the index for the high res sample with the same spatiotemporal + extent, with an additional index (same as the index for the high-res + data) for the observation data""" + lr_index, hr_index = super().get_sample_index(n_obs=n_obs) + return (lr_index, hr_index, hr_index) diff --git a/sup3r/preprocessing/samplers/with_obs.py b/sup3r/preprocessing/samplers/with_obs.py deleted file mode 100644 index 1f74f8d80f..0000000000 --- a/sup3r/preprocessing/samplers/with_obs.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Extended Sampler for sampling observation data in addition to standard -gridded training data.""" - -import logging -from typing import Dict, Optional - -from sup3r.preprocessing.base import Sup3rDataset - -from .dual import DualSampler - -logger = logging.getLogger(__name__) - - -class DualSamplerWithObs(DualSampler): - """Dual Sampler which also samples from extra observation data. The - observation data is on the same grid as the high-resolution data but - includes NaNs at points where observation data doesn't exist. This will - be used in an additional content loss term.""" - - def __init__( - self, - data: Sup3rDataset, - sample_shape: Optional[tuple] = None, - batch_size: int = 16, - s_enhance: int = 1, - t_enhance: int = 1, - feature_sets: Optional[Dict] = None, - ): - """ - Parameters - ---------- - data : Sup3rDataset - A :class:`~sup3r.preprocessing.base.Sup3rDataset` instance with - low-res and high-res data members - sample_shape : tuple - Size of arrays to sample from the high-res data. The sample shape - for the low-res sampler will be determined from the enhancement - factors. - s_enhance : int - Spatial enhancement factor - t_enhance : int - Temporal enhancement factor - feature_sets : Optional[dict] - Optional dictionary describing how the full set of features is - split between `lr_only_features` and `hr_exo_features`. - - lr_only_features : list | tuple - List of feature names or patt*erns that should only be - included in the low-res training set and not the high-res - observations. - hr_exo_features : list | tuple - List of feature names or patt*erns that should be included - in the high-resolution observation but not expected to be - output from the generative model. An example is high-res - topography that is to be injected mid-network. - """ - - msg = ( - f'{self.__class__.__name__} requires a Sup3rDataset object ' - 'with `.low_res`, `.high_res`, and `.obs` data members, in that ' - 'order' - ) - assert ( - hasattr(data, 'low_res') - and hasattr(data, 'high_res') - and hasattr(data, 'obs') - ), msg - assert ( - data.low_res == data[0] - and data.high_res == data[1] - and data.obs == data[2] - ), msg - super().__init__( - data, - sample_shape=sample_shape, - batch_size=batch_size, - s_enhance=s_enhance, - t_enhance=t_enhance, - feature_sets=feature_sets, - ) - - def get_sample_index(self, n_obs=None): - """Get paired sample index, consisting of index for the low res sample - and the index for the high res sample with the same spatiotemporal - extent, with an additional index (same as the index for the high-res - data) for the observation data""" - lr_index, hr_index = super().get_sample_index(n_obs=n_obs) - return (lr_index, hr_index, hr_index) From 709792f551911d4654c031b4e3452d3f3a706e84 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 22 Dec 2024 19:13:50 -0800 Subject: [PATCH 006/122] integrated dual sampler with obs into base dual sampler. --- sup3r/preprocessing/__init__.py | 2 - .../preprocessing/batch_handlers/__init__.py | 1 - sup3r/preprocessing/batch_handlers/factory.py | 6 +- sup3r/preprocessing/rasterizers/dual.py | 4 +- sup3r/preprocessing/samplers/__init__.py | 2 +- sup3r/preprocessing/samplers/dual.py | 102 +++------------- tests/training/test_train_dual_with_obs.py | 113 +++++++++++++++++- 7 files changed, 134 insertions(+), 96 deletions(-) diff --git a/sup3r/preprocessing/__init__.py b/sup3r/preprocessing/__init__.py index 0070fad12b..315e9b79aa 100644 --- a/sup3r/preprocessing/__init__.py +++ b/sup3r/preprocessing/__init__.py @@ -33,7 +33,6 @@ BatchHandlerMom2SepSF, BatchHandlerMom2SF, DualBatchHandler, - DualBatchHandlerWithObs, ) from .batch_queues import BatchQueueDC, DualBatchQueue, SingleBatchQueue from .cachers import Cacher @@ -62,7 +61,6 @@ from .samplers import ( DualSampler, DualSamplerCC, - DualSamplerWithObs, Sampler, SamplerDC, ) diff --git a/sup3r/preprocessing/batch_handlers/__init__.py b/sup3r/preprocessing/batch_handlers/__init__.py index d66b10126e..08bba8d6b8 100644 --- a/sup3r/preprocessing/batch_handlers/__init__.py +++ b/sup3r/preprocessing/batch_handlers/__init__.py @@ -11,5 +11,4 @@ BatchHandlerMom2SepSF, BatchHandlerMom2SF, DualBatchHandler, - DualBatchHandlerWithObs, ) diff --git a/sup3r/preprocessing/batch_handlers/factory.py b/sup3r/preprocessing/batch_handlers/factory.py index c43e9198a0..3a65b64bb7 100644 --- a/sup3r/preprocessing/batch_handlers/factory.py +++ b/sup3r/preprocessing/batch_handlers/factory.py @@ -17,7 +17,7 @@ from sup3r.preprocessing.collections.stats import StatsCollection from sup3r.preprocessing.samplers.base import Sampler from sup3r.preprocessing.samplers.cc import DualSamplerCC -from sup3r.preprocessing.samplers.dual import DualSampler, DualSamplerWithObs +from sup3r.preprocessing.samplers.dual import DualSampler from sup3r.preprocessing.utilities import ( check_signatures, get_class_kwargs, @@ -316,10 +316,6 @@ def stop(self): DualBatchQueue, DualSampler, name='DualBatchHandler' ) -DualBatchHandlerWithObs = BatchHandlerFactory( - DualBatchQueue, DualSamplerWithObs, name='DualBatchHandlerWithObs' -) - BatchHandlerCC = BatchHandlerFactory( DualBatchQueue, DualSamplerCC, name='BatchHandlerCC' ) diff --git a/sup3r/preprocessing/rasterizers/dual.py b/sup3r/preprocessing/rasterizers/dual.py index 777f6b5dc7..e968ff27e2 100644 --- a/sup3r/preprocessing/rasterizers/dual.py +++ b/sup3r/preprocessing/rasterizers/dual.py @@ -76,7 +76,9 @@ def __init__( self.s_enhance = s_enhance self.t_enhance = t_enhance if isinstance(data, tuple): - data = Sup3rDataset(low_res=data[0], high_res=data[1]) + data = {'low_res': data[0], 'high_res': data[1]} + if isinstance(data, dict): + data = Sup3rDataset(data) msg = ( 'The DualRasterizer requires either a data tuple with two ' 'members, low and high resolution in that order, or a ' diff --git a/sup3r/preprocessing/samplers/__init__.py b/sup3r/preprocessing/samplers/__init__.py index 545da5a3c3..e281616d56 100644 --- a/sup3r/preprocessing/samplers/__init__.py +++ b/sup3r/preprocessing/samplers/__init__.py @@ -8,4 +8,4 @@ from .base import Sampler from .cc import DualSamplerCC from .dc import SamplerDC -from .dual import DualSampler, DualSamplerWithObs +from .dual import DualSampler diff --git a/sup3r/preprocessing/samplers/dual.py b/sup3r/preprocessing/samplers/dual.py index f8a70be096..b60b549052 100644 --- a/sup3r/preprocessing/samplers/dual.py +++ b/sup3r/preprocessing/samplers/dual.py @@ -16,7 +16,10 @@ class DualSampler(Sampler): """Sampler for sampling from paired (or dual) datasets. Pairs consist of - low and high resolution data, which are contained by a Sup3rDataset.""" + low and high resolution data, which are contained by a Sup3rDataset. This + can also include extra observation data on the same grid as the + high-resolution data which has NaNs at points where observation data + doesn't exist. This will be used in an additional content loss term.""" def __init__( self, @@ -32,7 +35,7 @@ def __init__( ---------- data : Sup3rDataset A :class:`~sup3r.preprocessing.base.Sup3rDataset` instance with - low-res and high-res data members + low-res and high-res data members, and optionally an obs member. sample_shape : tuple Size of arrays to sample from the high-res data. The sample shape for the low-res sampler will be determined from the enhancement @@ -57,10 +60,15 @@ def __init__( """ msg = ( f'{self.__class__.__name__} requires a Sup3rDataset object ' - 'with `.low_res` and `.high_res` data members, in that order' + 'with `.low_res` and `.high_res` data members, and optionally an ' + '`.obs` member, in that order' ) - assert hasattr(data, 'low_res') and hasattr(data, 'high_res'), msg - assert data.low_res == data[0] and data.high_res == data[1], msg + check = hasattr(data, 'low_res') and hasattr(data, 'high_res') + check = check and data.low_res == data[0] and data.high_res == data[1] + if len(data) == 2: + check = check and (hasattr(data, 'obs') and data.obs == data[2]) + assert check, msg + super().__init__( data=data, sample_shape=sample_shape, batch_size=batch_size ) @@ -117,7 +125,8 @@ def check_for_consistent_shapes(self): def get_sample_index(self, n_obs=None): """Get paired sample index, consisting of index for the low res sample and the index for the high res sample with the same spatiotemporal - extent.""" + extent. Optionally includes an extra high res index if the sample data + includes observation data.""" n_obs = n_obs or self.batch_size spatial_slice = uniform_box_sampler( self.lr_data.shape, self.lr_sample_shape[:2] @@ -135,81 +144,8 @@ def get_sample_index(self, n_obs=None): for s in lr_index[2:-1] ] hr_index = (*hr_index, self.hr_features) - return (lr_index, hr_index) - -class DualSamplerWithObs(DualSampler): - """Dual Sampler which also samples from extra observation data. The - observation data is on the same grid as the high-resolution data but - includes NaNs at points where observation data doesn't exist. This will - be used in an additional content loss term.""" - - def __init__( - self, - data: Sup3rDataset, - sample_shape: Optional[tuple] = None, - batch_size: int = 16, - s_enhance: int = 1, - t_enhance: int = 1, - feature_sets: Optional[Dict] = None, - ): - """ - Parameters - ---------- - data : Sup3rDataset - A :class:`~sup3r.preprocessing.base.Sup3rDataset` instance with - low-res and high-res data members - sample_shape : tuple - Size of arrays to sample from the high-res data. The sample shape - for the low-res sampler will be determined from the enhancement - factors. - s_enhance : int - Spatial enhancement factor - t_enhance : int - Temporal enhancement factor - feature_sets : Optional[dict] - Optional dictionary describing how the full set of features is - split between `lr_only_features` and `hr_exo_features`. - - lr_only_features : list | tuple - List of feature names or patt*erns that should only be - included in the low-res training set and not the high-res - observations. - hr_exo_features : list | tuple - List of feature names or patt*erns that should be included - in the high-resolution observation but not expected to be - output from the generative model. An example is high-res - topography that is to be injected mid-network. - """ - - msg = ( - f'{self.__class__.__name__} requires a Sup3rDataset object ' - 'with `.low_res`, `.high_res`, and `.obs` data members, in that ' - 'order' - ) - assert ( - hasattr(data, 'low_res') - and hasattr(data, 'high_res') - and hasattr(data, 'obs') - ), msg - assert ( - data.low_res == data[0] - and data.high_res == data[1] - and data.obs == data[2] - ), msg - super().__init__( - data, - sample_shape=sample_shape, - batch_size=batch_size, - s_enhance=s_enhance, - t_enhance=t_enhance, - feature_sets=feature_sets, - ) - - def get_sample_index(self, n_obs=None): - """Get paired sample index, consisting of index for the low res sample - and the index for the high res sample with the same spatiotemporal - extent, with an additional index (same as the index for the high-res - data) for the observation data""" - lr_index, hr_index = super().get_sample_index(n_obs=n_obs) - return (lr_index, hr_index, hr_index) + sample_index = (lr_index, hr_index) + if hasattr(self.data, 'obs'): + sample_index += (hr_index,) + return sample_index diff --git a/tests/training/test_train_dual_with_obs.py b/tests/training/test_train_dual_with_obs.py index 01ab41e50b..48a9c7d676 100644 --- a/tests/training/test_train_dual_with_obs.py +++ b/tests/training/test_train_dual_with_obs.py @@ -11,10 +11,10 @@ from sup3r.preprocessing import ( Container, DataHandler, - DualBatchHandlerWithObs, + DualBatchHandler, DualRasterizer, ) -from sup3r.preprocessing.samplers import DualSamplerWithObs +from sup3r.preprocessing.samplers import DualSampler from sup3r.utilities.pytest.helpers import BatchHandlerTesterFactory TARGET_COORD = (39.01, -105.15) @@ -22,10 +22,112 @@ DualBatchHandlerWithObsTester = BatchHandlerTesterFactory( - DualBatchHandlerWithObs, DualSamplerWithObs + DualBatchHandler, DualSampler ) +@pytest.mark.parametrize( + [ + 'fp_gen', + 'fp_disc', + 's_enhance', + 't_enhance', + 'sample_shape', + 'mode', + ], + [ + (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16), 'lazy'), + (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16), 'eager'), + (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (20, 20, 1), 'lazy'), + (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (20, 20, 1), 'eager'), + ], +) +def test_train_h5_nc( + fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, mode, n_epoch=2 +): + """Test model training with a dual data handler / batch handler with h5 and + era as hr / lr datasets. Tests both spatiotemporal and spatial models.""" + + lr = 1e-5 + kwargs = { + 'features': FEATURES, + 'target': TARGET_COORD, + 'shape': (20, 20), + } + hr_handler = DataHandler( + pytest.FP_WTK, + **kwargs, + time_slice=slice(None, None, 1), + ) + + lr_handler = DataHandler( + pytest.FP_ERA, + features=FEATURES, + time_slice=slice(None, None, t_enhance), + ) + + dual_rasterizer = DualRasterizer( + data=(lr_handler.data, hr_handler.data), + s_enhance=s_enhance, + t_enhance=t_enhance, + ) + obs_data = dual_rasterizer.high_res.copy() + for feat in FEATURES: + tmp = np.full(obs_data[feat].shape, np.nan) + lat_ids = list(range(0, 20, 4)) + lon_ids = list(range(0, 20, 4)) + for ilat, ilon in itertools.product(lat_ids, lon_ids): + tmp[ilat, ilon, :] = obs_data[feat][ilat, ilon] + obs_data[feat] = (obs_data[feat].dims, tmp) + + dual_with_obs = Container( + data={ + 'low_res': dual_rasterizer.low_res, + 'high_res': dual_rasterizer.high_res, + 'obs': obs_data, + } + ) + + batch_handler = DualBatchHandlerWithObsTester( + train_containers=[dual_with_obs], + val_containers=[], + sample_shape=sample_shape, + batch_size=3, + s_enhance=s_enhance, + t_enhance=t_enhance, + n_batches=3, + mode=mode, + ) + + for batch in batch_handler: + assert hasattr(batch, 'obs') + assert not np.isnan(batch.obs).all() + assert np.isnan(batch.obs).any() + + Sup3rGan.seed() + model = Sup3rGan( + fp_gen, fp_disc, learning_rate=lr, loss='MeanAbsoluteError' + ) + + with tempfile.TemporaryDirectory() as td: + model_kwargs = { + 'input_resolution': {'spatial': '30km', 'temporal': '60min'}, + 'n_epoch': n_epoch, + 'weight_gen_advers': 0.0, + 'train_gen': True, + 'train_disc': False, + 'checkpoint_int': 1, + 'out_dir': os.path.join(td, 'test_{epoch}'), + } + + model.train(batch_handler, **model_kwargs) + + tlossg = model.history['train_loss_gen'].values + tlosso = model.history['train_loss_obs'].values + assert np.sum(np.diff(tlossg)) < 0 + assert np.sum(np.diff(tlosso)) < 0 + + @pytest.mark.parametrize( [ 'fp_gen', @@ -101,6 +203,11 @@ def test_train_coarse_h5( mode=mode, ) + for batch in batch_handler: + assert hasattr(batch, 'obs') + assert not np.isnan(batch.obs).all() + assert np.isnan(batch.obs).any() + Sup3rGan.seed() model = Sup3rGan( fp_gen, fp_disc, learning_rate=lr, loss='MeanAbsoluteError' From 019d022591c768823739a4d50ac4547e6be461d6 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Mon, 23 Dec 2024 07:30:58 -0800 Subject: [PATCH 007/122] examples added to DataHandler doc string. Some instructions on sup3rwind model training added to examples README. --- examples/sup3rwind/README.rst | 39 ++++++++++++- sup3r/preprocessing/base.py | 9 +-- sup3r/preprocessing/batch_queues/abstract.py | 9 +-- sup3r/preprocessing/cachers/base.py | 2 +- sup3r/preprocessing/data_handlers/base.py | 25 +++++++++ sup3r/preprocessing/rasterizers/dual.py | 13 +++-- sup3r/preprocessing/samplers/dual.py | 15 +++-- sup3r/utilities/utilities.py | 2 +- tests/data/extract_raster_wtk.py | 59 -------------------- 9 files changed, 85 insertions(+), 88 deletions(-) delete mode 100644 tests/data/extract_raster_wtk.py diff --git a/examples/sup3rwind/README.rst b/examples/sup3rwind/README.rst index 8ca30dc57c..4cb0c5fa31 100644 --- a/examples/sup3rwind/README.rst +++ b/examples/sup3rwind/README.rst @@ -2,7 +2,7 @@ Sup3rWind Examples ################### -Super-Resolution for Renewable Energy Resource Data with Wind from Reanalysis Data (Sup3rWind) is one application of the sup3r software. In this work, we train generative models to create high-resolution (2km 5-minute) wind data based on coarse (30km hourly) ERA5 data. The generative models and high-resolution output data is publicly available via the `Open Energy Data Initiative (OEDI) `__ and via HSDS at the bucket ``nrel-pds-hsds`` and path ``/nrel/wtk/sup3rwind``. This data covers recent historical time periods for an expanding selection of countries. +Super-Resolution for Renewable Energy Resource Data with Wind from Reanalysis Data (Sup3rWind) is one application of the sup3r software. In this work, we train generative models to create high-resolution (2km 5-minute) wind data based on coarse (30km hourly) ERA5 data. The generative models, high-resolution output data, and training data is publicly available via the `Open Energy Data Initiative (OEDI) `__ and via HSDS at the bucket ``nrel-pds-hsds`` and path ``/nrel/wtk/sup3rwind``. This data covers recent historical time periods for an expanding selection of countries. Sup3rWind Data Access ---------------------- @@ -11,8 +11,8 @@ The Sup3rWind data and models are publicly available in a public AWS S3 bucket. The Sup3rWind data is also loaded into `HSDS `__ so that you may stream the data via the `NREL developer API `__ or your own HSDS server. This is the best option if you're not going to want a full annual dataset. See these `rex instructions `__ for more details on how to access this data with HSDS and rex. -Example Sup3rWind Data Usage ------------------------------ +Sup3rWind Data Usage +--------------------- Sup3rWind data can be used in generally the same way as `Sup3rCC `__ data, with the condition that Sup3rWind includes only wind data and ancillary variables for modeling wind energy generation. Refer to the Sup3rCC `example notebook `__ for usage patterns. @@ -32,6 +32,39 @@ The process for running the Sup3rWind models is much the same as for `Sup3rCC `__. This data is for training the spatial enhancement models only. The 2024-01 `models `__ perform spatial enhancement in two steps, 3x from ERA5 to coarsened WTK and 5x from coarsened WTK to uncoarsened WTK. The currently used approach performs spatial enhancement in a single 15x step. + +For a given year and training domain, initialize low-resolution and high-resolution data handlers and wrap these in a dual rasterizer object. Do this for as many years and training regions as desired, and use these containers to initialize a batch handler. To train models for 3x spatial enhancement use ``hr_spatial_coarsen=5`` in the ``hr_dh``. To train models for 15x (the currently used approach) ``hr_spatial_coarsen=1``. (Refer to tests and docs for information on additional arguments, denoted by the ellipses):: + + from sup3r.preprocessing import DataHandler, DualBatchHandler, DualRasterizer + containers = [] + for tdir in training_dirs: + lr_dh = DataHandler(f"{tdir}/lr_*.h5", ...) + hr_dh = DataHandler(f"{tdir}/hr_*.h5", hr_spatial_coarsen=...) + container = DualRasterizer({'low_res': lr_dh, 'high_res': hr_dh}, ...) + containers.append(container) + bh = DualBatchHandler(train_containers=containers, ...) + +To train a 5x model use the ``hr_*.h5`` files for both the ``lr_dh`` and the ``hr_dh``. Use ``hr_spatial_coarsen=3`` in the ``lr_dh`` and ``hr_spatial_coarsen=1`` in the ``hr_dh``:: + + for tdir in training_dirs: + lr_dh = DataHandler(f"{tdir}/hr_*.h5", hr_spatial_coarsen=3, ...) + hr_dh = DataHandler(f"{tdir}/hr_*.h5", hr_spatial_coarsen=1, ...) + container = DualRasterizer({'low_res': lr_dh, 'high_res': hr_dh}, ...) + containers.append(container) + bh = DualBatchHandler(train_containers=containers, ...) + + +Initialize a 3x, 5x, or 15x spatial enhancement model, with 14 output channels, and train for the desired number of epochs. (The 3x and 5x generator configs can be copied from the ``model_params.json`` files in each OEDI model `directory `__. The 15x generator config can be created from the OEDI model configs by changing the spatial enhancement factor or from the configs in the repo by changing the enhancement factor and the number of output channels):: + + from sup3r.models import Sup3rGan + model = Sup3rGan(gen_layers="./gen_config.json", disc_layers="./disc_config.json", ...) + model.train(batch_handler, ...) + + Sup3rWind Versions ------------------- diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index 1cfa0c7eaa..a6e9074e92 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -116,6 +116,8 @@ class Sup3rDataset: from the high_res non coarsened variable. """ + DSET_NAMES = ('low_res', 'high_res', 'obs') + def __init__( self, **dsets: Mapping[str, Union[xr.Dataset, Sup3rX]], @@ -185,7 +187,7 @@ def rewrap(self, data): return data if len(data) == 1: return type(self)(high_res=data[0]) - return type(self)(**dict(zip(['low_res', 'high_res', 'obs'], data))) + return type(self)(**dict(zip(self.DSET_NAMES, data))) def sample(self, idx): """Get samples from ``self._ds`` members. idx should be either a tuple @@ -369,16 +371,15 @@ def wrap(self, data): if isinstance(data, dict): data = Sup3rDataset(**data) - default_names = ['low_res', 'high_res', 'obs'] if isinstance(data, tuple) and len(data) > 1: msg = ( f'{self.__class__.__name__}.data is being set with a ' f'{len(data)}-tuple without explicit dataset names. We will ' - f'assume name ordering: {default_names[:len(data)]}' + f'assume name ordering: {Sup3rDataset.DSET_NAMES[:len(data)]}' ) logger.warning(msg) warn(msg) - data = Sup3rDataset(**dict(zip(default_names, data))) + data = Sup3rDataset(**dict(zip(Sup3rDataset.DSET_NAMES, data))) elif not isinstance(data, Sup3rDataset): name = getattr(data, 'name', None) or 'high_res' data = Sup3rDataset(**{name: data}) diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index d5a6cfd958..4a6e475f3c 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -22,11 +22,7 @@ from sup3r.utilities.utilities import RANDOM_GENERATOR, Timer if TYPE_CHECKING: - from sup3r.preprocessing.samplers import ( - DualSampler, - DualSamplerWithObs, - Sampler, - ) + from sup3r.preprocessing.samplers import DualSampler, Sampler logger = logging.getLogger(__name__) @@ -41,8 +37,7 @@ class AbstractBatchQueue(Collection, ABC): def __init__( self, samplers: Union[ - List['Sampler'], List['DualSampler'], List['DualSamplerWithObs'] - ], + List['Sampler'], List['DualSampler']], batch_size: int = 16, n_batches: int = 64, s_enhance: int = 1, diff --git a/sup3r/preprocessing/cachers/base.py b/sup3r/preprocessing/cachers/base.py index be380b6738..4df526039c 100644 --- a/sup3r/preprocessing/cachers/base.py +++ b/sup3r/preprocessing/cachers/base.py @@ -330,7 +330,7 @@ def write_h5( ] if Dimension.TIME in data: - data[Dimension.TIME] = data[Dimension.TIME].astype(int) + data[Dimension.TIME] = data[Dimension.TIME].astype('int64') for dset in [*coord_names, *features]: data_var, chunksizes = cls.get_chunksizes(dset, data, chunks) diff --git a/sup3r/preprocessing/data_handlers/base.py b/sup3r/preprocessing/data_handlers/base.py index 452518d63a..3d8f73125d 100644 --- a/sup3r/preprocessing/data_handlers/base.py +++ b/sup3r/preprocessing/data_handlers/base.py @@ -145,6 +145,31 @@ def __init__( Dictionary of additional keyword args for :class:`~sup3r.preprocessing.rasterizers.Rasterizer`, used specifically for rasterizing flattened data + + Examples + -------- + Extract windspeed at 40m and 80m above the ground from files for u/v at + 10m and 100m. Windspeed will be interpolated from surrounding levels + using a log profile. ``dh`` will contain dask arrays of this data with + 10x10x50 chunk sizes. Data will be cached to files named + 'windspeed_40m.h5' and 'windspeed_80m.h5' in './cache_dir' with + 5x5x10 chunks on disk. + >>> cache_chunks = {'south_north': 5, 'west_east': 5, 'time': 10} + >>> load_chunks = {'south_north': 10, 'west_east': 10, 'time': 50} + >>> grid_size = (50, 50) + >>> lower_left_coordinate = (39.7, -105.2) + >>> dh = DataHandler( + ... file_paths=['./data_dir/u_10m.nc', './data_dir/u_100m.nc', + ... './data_dir/v_10m.nc', './data_dir/v_100m.nc'], + ... features=['windspeed_40m', 'windspeed_80m'], + ... shape=grid_size, time_slice=slice(0, 100), + ... target=lower_left_coordinate, hr_spatial_coarsen=2, + ... chunks=load_chunks, interp_kwargs={'method': 'log'}, + ... cache_kwargs={'cache_pattern': './cache_dir/{feature}.h5', + ... 'chunks': cache_chunks}) + + Derive more features from already initialized data handler: + >>> dh['windspeed_60m'] = dh.derive('windspeed_60m') """ # pylint: disable=line-too-long features = parse_to_list(features=features) diff --git a/sup3r/preprocessing/rasterizers/dual.py b/sup3r/preprocessing/rasterizers/dual.py index e968ff27e2..47706aa4e4 100644 --- a/sup3r/preprocessing/rasterizers/dual.py +++ b/sup3r/preprocessing/rasterizers/dual.py @@ -2,7 +2,7 @@ datasets""" import logging -from typing import Tuple, Union +from typing import Dict, Tuple, Union from warnings import warn import numpy as np @@ -38,7 +38,9 @@ class DualRasterizer(Container): @log_args def __init__( self, - data: Union[Sup3rDataset, Tuple[xr.Dataset, xr.Dataset]], + data: Union[ + Sup3rDataset, Tuple[xr.Dataset, xr.Dataset], Dict[str, xr.Dataset] + ], regrid_workers=1, regrid_lr=True, s_enhance=1, @@ -51,7 +53,8 @@ def __init__( Parameters ---------- - data : Sup3rDataset | Tuple[xr.Dataset, xr.Dataset] + data : Sup3rDataset | Tuple[xr.Dataset, xr.Dataset] | + Dict[str, xr.Dataset] A tuple of xr.Dataset instances. The first must be low-res and the second must be high-res data regrid_workers : int | None @@ -78,9 +81,9 @@ def __init__( if isinstance(data, tuple): data = {'low_res': data[0], 'high_res': data[1]} if isinstance(data, dict): - data = Sup3rDataset(data) + data = Sup3rDataset(**data) msg = ( - 'The DualRasterizer requires either a data tuple with two ' + 'The DualRasterizer requires a data tuple or dictionary with two ' 'members, low and high resolution in that order, or a ' f'Sup3rDataset instance. Received {type(data)}.' ) diff --git a/sup3r/preprocessing/samplers/dual.py b/sup3r/preprocessing/samplers/dual.py index b60b549052..44558b5458 100644 --- a/sup3r/preprocessing/samplers/dual.py +++ b/sup3r/preprocessing/samplers/dual.py @@ -63,10 +63,11 @@ def __init__( 'with `.low_res` and `.high_res` data members, and optionally an ' '`.obs` member, in that order' ) - check = hasattr(data, 'low_res') and hasattr(data, 'high_res') - check = check and data.low_res == data[0] and data.high_res == data[1] - if len(data) == 2: - check = check and (hasattr(data, 'obs') and data.obs == data[2]) + dnames = ['low_res', 'high_res', 'obs'][:len(data)] + check = ( + hasattr(data, dname) and getattr(data, dname) == data[i] + for i, dname in enumerate(dnames) + ) assert check, msg super().__init__( @@ -145,7 +146,5 @@ def get_sample_index(self, n_obs=None): ] hr_index = (*hr_index, self.hr_features) - sample_index = (lr_index, hr_index) - if hasattr(self.data, 'obs'): - sample_index += (hr_index,) - return sample_index + sample_index = (lr_index, hr_index, hr_index) + return sample_index[:len(self.data)] diff --git a/sup3r/utilities/utilities.py b/sup3r/utilities/utilities.py index 22210a35d9..498e664f52 100644 --- a/sup3r/utilities/utilities.py +++ b/sup3r/utilities/utilities.py @@ -27,7 +27,7 @@ def preprocess_datasets(dset): dset.indexes['time'], 'to_datetimeindex' ): dset['time'] = dset.indexes['time'].to_datetimeindex() - ti = dset['time'].astype(int) + ti = dset['time'].astype('int64') dset['time'] = ti if 'latitude' in dset.dims: dset = dset.swap_dims({'latitude': 'south_north'}) diff --git a/tests/data/extract_raster_wtk.py b/tests/data/extract_raster_wtk.py deleted file mode 100644 index ed826d1349..0000000000 --- a/tests/data/extract_raster_wtk.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Script to extract data subset in raster shape from flattened WTK h5 files. - -TODO: Is this worth keeping for any reason? -""" - -import matplotlib.pyplot as plt -from rex import init_logger -from rex.outputs import Outputs -from rex.resource_extraction.resource_extraction import WindX - -if __name__ == '__main__': - init_logger('rex', log_level='DEBUG') - - res_fp = '/datasets/WIND/conus/v1.0.0/wtk_conus_2013.h5' - fout = './test_wtk_co_2013.h5' - - dsets = ['windspeed_80m', 'windspeed_100m', 'winddirection_80m', - 'winddirection_100m', 'temperature_100m', 'pressure_100m'] - - target = (39.0, -105.15) - shape = (20, 20) - - with WindX(res_fp) as res: - meta = res.meta - raster_index_2d = res.get_raster_index(target, shape, max_delta=20) - - for d in ('elevation', 'latitude', 'longitude'): - data = meta[d].values[raster_index_2d] - a = plt.imshow(data) - plt.colorbar(a, label=d) - plt.savefig(d + '.png') - plt.close() - - raster_index = sorted(raster_index_2d.ravel()) - - attrs = {k: res.resource.attrs[k] for k in dsets} - chunks = dict.fromkeys(dsets) - dtypes = {k: res.resource.dtypes[k] for k in dsets} - meta = meta.iloc[raster_index].reset_index(drop=True) - time_index = res.time_index - shapes = {k: (len(time_index), len(meta)) for k in dsets} - print(shapes) - - Outputs.init_h5(fout, dsets, shapes, attrs, chunks, dtypes, meta, - time_index=time_index) - - with Outputs(fout, mode='a') as f: - for d in dsets: - f[d] = res[d, :, raster_index] - - with Outputs(fout, mode='r') as f: - meta = f.meta - for d in dsets: - data = f[d].mean(axis=0) - data = data.reshape(shape) - a = plt.imshow(data) - plt.colorbar(a, label=d) - plt.savefig(d + '.png') - plt.close() From 7ff2fbaf07b14385b325af61ee7b5ed6d731b846 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 26 Dec 2024 14:22:32 -0800 Subject: [PATCH 008/122] removed namedtuple from Sup3rDataset to make Sup3rDataset picklable. --- sup3r/models/abstract.py | 8 +++- sup3r/preprocessing/base.py | 25 ++++++++++- sup3r/preprocessing/batch_queues/abstract.py | 47 ++++++++++++-------- sup3r/preprocessing/cachers/base.py | 1 + sup3r/utilities/pytest/helpers.py | 27 +++++------ 5 files changed, 71 insertions(+), 37 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index ea47ec3c39..1fcd09b3cd 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -777,8 +777,8 @@ def finish_epoch( self.save(out_dir.format(epoch=epoch)) if extras is not None: - for k, v in extras.items(): - self._history.at[epoch, k] = safe_cast(v) + entry = np.vstack([safe_cast(v) for v in extras.values()]) + self._history.loc[epoch, list(extras)] = entry.T return stop @@ -811,6 +811,8 @@ def run_gradient_descent( current loss weight values. obs_data : tf.Tensor | None Optional observation data to use in additional content loss term. + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) optimizer : tf.keras.optimizers.Optimizer Optimizer class to use to update weights. This can be different if you're training just the generator or one of the discriminator @@ -1121,6 +1123,8 @@ def get_single_grad( current loss weight values. obs_data : tf.Tensor | None Optional observation data to use in additional content loss term. + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) device_name : None | str Optional tensorflow device name for GPU placement. Note that if a GPU is available, variables will be placed on that GPU even if diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index a6e9074e92..8a82efe3df 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -11,7 +11,6 @@ import logging import pprint from abc import ABCMeta -from collections import namedtuple from typing import Dict, Mapping, Tuple, Union from warnings import warn @@ -70,6 +69,28 @@ def __repr__(cls): return f"" +class DsetTuple: + """A simple class to mimic namedtuple behavior with dynamic attributes + while being serializable""" + + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __iter__(self): + return iter(self.__dict__.values()) + + def __getitem__(self, key): + if isinstance(key, int): + key = list(self.__dict__)[key] + return self.__dict__[key] + + def __len__(self): + return len(self.__dict__) + + def __repr__(self): + return f"DsetTuple({self.__dict__})" + + class Sup3rDataset: """Interface for interacting with one or two ``xr.Dataset`` instances. This is a wrapper around one or two ``Sup3rX`` objects so they work well @@ -149,7 +170,7 @@ def __init__( assert len(dset) == 1, msg dsets[name] = dset._ds[0] - self._ds = namedtuple('Dataset', list(dsets))(**dsets) + self._ds = DsetTuple(**dsets) def __iter__(self): yield from self._ds diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index 4a6e475f3c..9a07efb598 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -12,9 +12,9 @@ import time from abc import ABC, abstractmethod from collections import namedtuple -from concurrent.futures import ThreadPoolExecutor from typing import TYPE_CHECKING, List, Optional, Union +import dask import numpy as np import tensorflow as tf @@ -36,8 +36,7 @@ class AbstractBatchQueue(Collection, ABC): def __init__( self, - samplers: Union[ - List['Sampler'], List['DualSampler']], + samplers: Union[List['Sampler'], List['DualSampler']], batch_size: int = 16, n_batches: int = 64, s_enhance: int = 1, @@ -242,6 +241,24 @@ def running(self): and not self.queue.is_closed() ) + def sample_batches(self, n_batches) -> None: + """Sample N batches from samplers. Returns N batches which are then + used to fill the queue.""" + if n_batches == 1: + return [self.sample_batch()] + + tasks = [dask.delayed(self.sample_batch)() for _ in range(n_batches)] + logger.debug('Added %s sample_batch futures to %s queue.', + n_batches, + self._thread_name) + + if self.max_workers == 1: + batches = dask.compute(*tasks, scheduler='single-threaded') + else: + batches = dask.compute( + *tasks, scheduler='threads', num_workers=self.max_workers) + return batches + def enqueue_batches(self) -> None: """Callback function for queue thread. While training, the queue is checked for empty spots and filled. In the training thread, batches are @@ -249,16 +266,15 @@ def enqueue_batches(self) -> None: log_time = time.time() while self.running: needed = self.queue_cap - self.queue.size().numpy() - if needed == 1 or self.max_workers == 1: - self.enqueue_batch() - elif needed > 0: - with ThreadPoolExecutor(self.max_workers) as exe: - _ = [exe.submit(self.enqueue_batch) for _ in range(needed)] - logger.debug( - 'Added %s enqueue futures to %s queue.', - needed, - self._thread_name, - ) + + # no point in getting more than one batch at a time if + # max_workers == 1 + needed = 1 if needed > 0 and self.max_workers == 1 else needed + + if needed > 0: + for batch in self.sample_batches(n_batches=needed): + self.queue.enqueue(batch) + if time.time() > log_time + 10: logger.debug(self.log_queue_info()) log_time = time.time() @@ -322,11 +338,6 @@ def log_queue_info(self): self.queue_cap, ) - def enqueue_batch(self): - """Build batch and send to queue.""" - if self.running and self.queue.size().numpy() < self.queue_cap: - self.queue.enqueue(self.sample_batch()) - @property def lr_shape(self): """Shape of low resolution sample in a low-res / high-res pair. (e.g. diff --git a/sup3r/preprocessing/cachers/base.py b/sup3r/preprocessing/cachers/base.py index 4df526039c..82a4bd5c1c 100644 --- a/sup3r/preprocessing/cachers/base.py +++ b/sup3r/preprocessing/cachers/base.py @@ -330,6 +330,7 @@ def write_h5( ] if Dimension.TIME in data: + # int64 used explicity to avoid incorrect encoding as int32 data[Dimension.TIME] = data[Dimension.TIME].astype('int64') for dset in [*coord_names, *features]: diff --git a/sup3r/utilities/pytest/helpers.py b/sup3r/utilities/pytest/helpers.py index 3f9b320a51..bb9912c9fd 100644 --- a/sup3r/utilities/pytest/helpers.py +++ b/sup3r/utilities/pytest/helpers.py @@ -107,9 +107,17 @@ class DummySampler(Sampler): """Dummy container with random data.""" def __init__( - self, sample_shape, data_shape, features, batch_size, feature_sets=None + self, + sample_shape, + data_shape, + features, + batch_size, + feature_sets=None, + chunk_shape=None, ): data = make_fake_dset(data_shape, features=features) + if chunk_shape is not None: + data = data.chunk(chunk_shape) super().__init__( Sup3rDataset(high_res=data), sample_shape, @@ -314,10 +322,7 @@ def make_collect_chunks(td): out_files = [] for t, slice_hr in enumerate(t_slices_hr): for s, (s1_hr, s2_hr) in enumerate(product(s_slices_hr, s_slices_hr)): - out_file = out_pattern.format( - t=str(t).zfill(6), - s=str(s).zfill(6) - ) + out_file = out_pattern.format(t=str(t).zfill(6), s=str(s).zfill(6)) out_files.append(out_file) OutputHandlerH5._write_output( data[s1_hr, s2_hr, slice_hr, :], @@ -330,15 +335,7 @@ def make_collect_chunks(td): gids=gids[s1_hr, s2_hr], ) - return ( - out_files, - data, - ws_true, - wd_true, - features, - hr_lat_lon, - hr_times - ) + return (out_files, data, ws_true, wd_true, features, hr_lat_lon, hr_times) def make_fake_h5_chunks(td): @@ -436,7 +433,7 @@ def make_fake_h5_chunks(td): s_slices_lr, s_slices_hr, low_res_lat_lon, - low_res_times + low_res_times, ) From 5a483c44a4fd5a2cd695cad7731a75a80dbee351 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 27 Dec 2024 08:04:01 -0800 Subject: [PATCH 009/122] parallel batch queue test added. --- sup3r/preprocessing/base.py | 3 ++ sup3r/preprocessing/batch_handlers/factory.py | 8 +-- sup3r/preprocessing/batch_queues/abstract.py | 36 +++++++------ .../preprocessing/batch_queues/conditional.py | 14 ++---- sup3r/preprocessing/batch_queues/dual.py | 3 +- tests/batch_queues/test_bq_general.py | 50 +++++++++++++++++++ 6 files changed, 83 insertions(+), 31 deletions(-) diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index 8a82efe3df..e046438fa5 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -386,6 +386,9 @@ def wrap(self, data): if data is None: return data + if hasattr(data, 'data'): + data = data.data + if is_type_of(data, Sup3rDataset): return data diff --git a/sup3r/preprocessing/batch_handlers/factory.py b/sup3r/preprocessing/batch_handlers/factory.py index 3a65b64bb7..4037f22246 100644 --- a/sup3r/preprocessing/batch_handlers/factory.py +++ b/sup3r/preprocessing/batch_handlers/factory.py @@ -265,26 +265,26 @@ def init_samplers( """Initialize samplers from given data containers.""" train_samplers = [ self.SAMPLER( - data=c.data, + data=container, sample_shape=sample_shape, feature_sets=feature_sets, batch_size=batch_size, **sampler_kwargs, ) - for c in train_containers + for container in train_containers ] val_samplers = ( [] if val_containers is None else [ self.SAMPLER( - data=c.data, + data=container, sample_shape=sample_shape, feature_sets=feature_sets, batch_size=batch_size, **sampler_kwargs, ) - for c in val_containers + for container in val_containers ] ) return train_samplers, val_samplers diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index 9a07efb598..132a59efc0 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -11,13 +11,13 @@ import threading import time from abc import ABC, abstractmethod -from collections import namedtuple from typing import TYPE_CHECKING, List, Optional, Union import dask import numpy as np import tensorflow as tf +from sup3r.preprocessing.base import DsetTuple from sup3r.preprocessing.collections.base import Collection from sup3r.utilities.utilities import RANDOM_GENERATOR, Timer @@ -32,7 +32,7 @@ class AbstractBatchQueue(Collection, ABC): generator and maintains a queue of batches in a dedicated thread so the training routine can proceed as soon as batches are available.""" - Batch = namedtuple('Batch', ['low_res', 'high_res']) + BATCH_MEMBERS = ('low_res', 'high_res') def __init__( self, @@ -46,6 +46,7 @@ def __init__( max_workers: int = 1, thread_name: str = 'training', mode: str = 'lazy', + verbose: bool = False ): """ Parameters @@ -77,6 +78,8 @@ def __init__( Loading mode. Default is 'lazy', which only loads data into memory as batches are queued. 'eager' will load all data into memory right away. + verbose : bool + Whether to log timing information for batch steps. """ msg = ( f'{self.__class__.__name__} requires a list of samplers. ' @@ -106,6 +109,7 @@ def __init__( 'smoothing_ignore': [], 'smoothing': None, } + self.verbose = verbose self.timer = Timer() self.preflight() @@ -179,7 +183,7 @@ def transform(self, samples, **kwargs): high res samples. For a dual dataset queue this will just include smoothing.""" - def post_proc(self, samples) -> Batch: + def post_proc(self, samples) -> DsetTuple: """Performs some post proc on dequeued samples before sending out for training. Post processing can include coarsening on high-res data (if :class:`Collection` consists of :class:`Sampler` objects and not @@ -187,13 +191,12 @@ def post_proc(self, samples) -> Batch: Returns ------- - Batch : namedtuple - namedtuple with `low_res` and `high_res` attributes. Could also - include additional members for integration with - ``DualSamplerWithObs`` + Batch : DsetTuple + namedtuple-like object with `low_res` and `high_res` attributes. + Could also include `obs` member. """ tsamps = self.transform(samples, **self.transform_kwargs) - return self.Batch(**dict(zip(self.Batch._fields, tsamps))) + return DsetTuple(**dict(zip(self.BATCH_MEMBERS, tsamps))) def start(self) -> None: """Start thread to keep sample queue full for batches.""" @@ -221,7 +224,7 @@ def __iter__(self): self.start() return self - def get_batch(self) -> Batch: + def get_batch(self) -> DsetTuple: """Get batch from queue or directly from a ``Sampler`` through ``sample_batch``.""" if ( @@ -279,10 +282,10 @@ def enqueue_batches(self) -> None: logger.debug(self.log_queue_info()) log_time = time.time() - def __next__(self) -> Batch: + def __next__(self) -> DsetTuple: """Dequeue batch samples, squeeze if for a spatial only model, perform some post-proc like smoothing, coarsening, etc, and then send out for - training as a namedtuple of low_res / high_res arrays. + training as a namedtuple-like object of low_res / high_res arrays. Returns ------- @@ -300,11 +303,12 @@ def __next__(self) -> Batch: batch = self.post_proc(samples) self.timer.stop() self._batch_count += 1 - logger.debug( - 'Batch step %s finished in %s.', - self._batch_count, - self.timer.elapsed_str, - ) + if self.verbose: + logger.debug( + 'Batch step %s finished in %s.', + self._batch_count, + self.timer.elapsed_str, + ) else: raise StopIteration return batch diff --git a/sup3r/preprocessing/batch_queues/conditional.py b/sup3r/preprocessing/batch_queues/conditional.py index c3d3aa0b44..5ff1078c8d 100644 --- a/sup3r/preprocessing/batch_queues/conditional.py +++ b/sup3r/preprocessing/batch_queues/conditional.py @@ -2,12 +2,12 @@ import logging from abc import abstractmethod -from collections import namedtuple from typing import TYPE_CHECKING, Dict, List, Optional, Union import numpy as np from sup3r.models.conditional import Sup3rCondMom +from sup3r.preprocessing.base import DsetTuple from sup3r.preprocessing.utilities import numpy_if_tensor from .base import SingleBatchQueue @@ -22,10 +22,6 @@ class ConditionalBatchQueue(SingleBatchQueue): """BatchQueue class for conditional moment estimation.""" - ConditionalBatch = namedtuple( - 'ConditionalBatch', ['low_res', 'high_res', 'output', 'mask'] - ) - def __init__( self, samplers: Union[List['Sampler'], List['DualSampler']], @@ -160,14 +156,14 @@ def post_proc(self, samples): Returns ------- - namedtuple - Named tuple with `low_res`, `high_res`, `mask`, and `output` - attributes + DsetTuple + Namedtuple-like object with `low_res`, `high_res`, `mask`, and + `output` attributes """ lr, hr = self.transform(samples, **self.transform_kwargs) mask = self.make_mask(high_res=hr) output = self.make_output(samples=(lr, hr)) - return self.ConditionalBatch( + return DsetTuple( low_res=lr, high_res=hr, output=output, mask=mask ) diff --git a/sup3r/preprocessing/batch_queues/dual.py b/sup3r/preprocessing/batch_queues/dual.py index 4b64c593d9..9a89ce8bd1 100644 --- a/sup3r/preprocessing/batch_queues/dual.py +++ b/sup3r/preprocessing/batch_queues/dual.py @@ -2,7 +2,6 @@ interface with models.""" import logging -from collections import namedtuple from scipy.ndimage import gaussian_filter @@ -21,7 +20,7 @@ def __init__(self, samplers, **kwargs): -------- :class:`~sup3r.preprocessing.batch_queues.abstract.AbstractBatchQueue` """ - self.Batch = namedtuple('Batch', samplers[0]._fields) + self.BATCH_MEMBERS = samplers[0]._fields super().__init__(samplers, **kwargs) self.check_enhancement_factors() diff --git a/tests/batch_queues/test_bq_general.py b/tests/batch_queues/test_bq_general.py index 52b2ffb257..b130aa7b7f 100644 --- a/tests/batch_queues/test_bq_general.py +++ b/tests/batch_queues/test_bq_general.py @@ -12,6 +12,7 @@ DummyData, DummySampler, ) +from sup3r.utilities.utilities import Timer FEATURES = ['windspeed', 'winddirection'] @@ -53,6 +54,55 @@ def test_batch_queue(): batcher.stop() +def test_batch_queue_workers(): + """Check that using max_workers > 1 for a batch queue is faster than using + max_workers = 1.""" + + timer = Timer() + sample_shape = (10, 10, 20) + n_batches = 20 + batch_size = 10 + max_workers = 10 + n_epochs = 10 + chunk_shape = {'south_north': 20, 'west_east': 20, 'time': 40} + samplers = [ + DummySampler( + sample_shape, + data_shape=(100, 100, 1000), + batch_size=batch_size, + features=FEATURES, + chunk_shape=chunk_shape + ) + ] + batcher = SingleBatchQueue( + samplers=samplers, + n_batches=n_batches, + batch_size=batch_size, + max_workers=1, + ) + timer.start() + for _ in range(n_epochs): + _ = list(batcher) + timer.stop() + batcher.stop() + serial_time = timer.elapsed / (n_epochs * n_batches) + + batcher = SingleBatchQueue( + samplers=samplers, + n_batches=n_batches, + batch_size=batch_size, + max_workers=max_workers, + ) + timer.start() + for _ in range(n_epochs): + _ = list(batcher) + timer.stop() + batcher.stop() + parallel_time = timer.elapsed / (n_epochs * n_batches) + print(f'Parallel / Serial Time: {parallel_time} / {serial_time}') + assert parallel_time < serial_time + + def test_spatial_batch_queue(): """Smoke test for spatial batch queue. A batch queue returns batches for spatial models if the sample shapes have 1 for the time axis""" From 53bdf46382b0b9f428ab0d59304f6dbdc1ae0c63 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 27 Dec 2024 08:40:09 -0800 Subject: [PATCH 010/122] namedtuple -> DsetTuple missing attr fix --- sup3r/preprocessing/base.py | 18 ++++++++++++------ sup3r/preprocessing/batch_queues/dual.py | 4 ++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index e046438fa5..1e04cba44d 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -74,21 +74,27 @@ class DsetTuple: while being serializable""" def __init__(self, **kwargs): + self.dset_names = list(kwargs) self.__dict__.update(kwargs) + @property + def dsets(self): + """Dictionary with only dset names and associated values.""" + return {k: v for k, v in self.__dict__.items() if k in self.dset_names} + def __iter__(self): - return iter(self.__dict__.values()) + return iter(self.dsets.values()) def __getitem__(self, key): if isinstance(key, int): - key = list(self.__dict__)[key] - return self.__dict__[key] + key = list(self.dsets)[key] + return self.dsets[key] def __len__(self): - return len(self.__dict__) + return len(self.dsets) def __repr__(self): - return f"DsetTuple({self.__dict__})" + return f'DsetTuple({self.dsets})' class Sup3rDataset: @@ -237,7 +243,7 @@ def __getitem__(self, keys): if len(self._ds) == 1: return out[-1] if all(isinstance(o, Sup3rX) for o in out): - return type(self)(**dict(zip(self._ds._fields, out))) + return type(self)(**dict(zip(self._ds.dset_names, out))) return out @property diff --git a/sup3r/preprocessing/batch_queues/dual.py b/sup3r/preprocessing/batch_queues/dual.py index 9a89ce8bd1..b7e26bf94a 100644 --- a/sup3r/preprocessing/batch_queues/dual.py +++ b/sup3r/preprocessing/batch_queues/dual.py @@ -20,7 +20,7 @@ def __init__(self, samplers, **kwargs): -------- :class:`~sup3r.preprocessing.batch_queues.abstract.AbstractBatchQueue` """ - self.BATCH_MEMBERS = samplers[0]._fields + self.BATCH_MEMBERS = samplers[0].dset_names super().__init__(samplers, **kwargs) self.check_enhancement_factors() @@ -30,7 +30,7 @@ def __init__(self, samplers, **kwargs): def queue_shape(self): """Shape of objects stored in the queue.""" queue_shapes = [(self.batch_size, *self.lr_shape)] - hr_mems = len(self.Batch._fields) - 1 + hr_mems = len(self.BATCH_MEMBERS) - 1 queue_shapes += [(self.batch_size, *self.hr_shape)] * hr_mems return queue_shapes From f25beb0960f77dfd4b015ab9e000495f319a7668 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 27 Dec 2024 13:20:07 -0700 Subject: [PATCH 011/122] gust added to era download variables. len dunder added to ``Container`` class to enable passing ``xr.Dataset`` and like objects directly to ``BatchHandlers`` without needing to invoke ``.data`` --- sup3r/preprocessing/base.py | 3 +++ sup3r/preprocessing/names.py | 1 + 2 files changed, 4 insertions(+) diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index 1e04cba44d..4b3e9d681f 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -428,6 +428,9 @@ def shape(self): """Get shape of underlying data.""" return self.data.shape + def __len__(self): + return len(self.data) + def __contains__(self, vals): return vals in self.data diff --git a/sup3r/preprocessing/names.py b/sup3r/preprocessing/names.py index 0ade726c61..700dce81b7 100644 --- a/sup3r/preprocessing/names.py +++ b/sup3r/preprocessing/names.py @@ -162,6 +162,7 @@ def dims_4d_bc(cls): 'northward_turbulent_surface_stress', 'eastward_turbulent_surface_stress', 'sea_surface_temperature', + 'instantaneous_10m_wind_gust' ] # variables available on multiple pressure levels From 5e1012041dbd191af791954131e6263096d9fdfb Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sat, 28 Dec 2024 08:49:15 -0700 Subject: [PATCH 012/122] computing before reshaping is 2x faster. --- sup3r/preprocessing/samplers/base.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sup3r/preprocessing/samplers/base.py b/sup3r/preprocessing/samplers/base.py index dbf75f61f9..b824cd360b 100644 --- a/sup3r/preprocessing/samplers/base.py +++ b/sup3r/preprocessing/samplers/base.py @@ -194,10 +194,11 @@ def _reshape_samples(self, samples): new_shape[2] // self.batch_size, new_shape[-1], ] + out = compute_if_dask(samples) # (lats, lons, batch_size, times, feats) - out = np.reshape(samples, new_shape) + out = np.reshape(out, new_shape) # (batch_size, lats, lons, times, feats) - return compute_if_dask(np.transpose(out, axes=(2, 0, 1, 3, 4))) + return np.transpose(out, axes=(2, 0, 1, 3, 4)) def _stack_samples(self, samples): """Used to build batch arrays in the case of independent time samples From f01f136519889ea1399e1dbdbf58d9928ba8aea1 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sat, 28 Dec 2024 13:44:14 -0700 Subject: [PATCH 013/122] obs_index fix - sampler needs to use hr_out_features for the obs member. --- sup3r/models/base.py | 1 + sup3r/preprocessing/base.py | 2 - sup3r/preprocessing/batch_queues/abstract.py | 64 +++++++++++------ sup3r/preprocessing/collections/base.py | 2 - sup3r/preprocessing/samplers/dual.py | 3 +- tests/batch_handlers/test_bh_general.py | 76 ++++++++++++++++++++ tests/batch_queues/test_bq_general.py | 50 ------------- 7 files changed, 120 insertions(+), 78 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 0cb78b8de4..c6dbbd35fe 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -10,6 +10,7 @@ import numpy as np import pandas as pd import tensorflow as tf +from tensorflow.keras.losses import MeanAbsoluteError from sup3r.preprocessing.utilities import get_class_kwargs from sup3r.utilities import VERSION_RECORD diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index 4b3e9d681f..ce9d1d6c4e 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -451,8 +451,6 @@ def __setitem__(self, keys, data): def __getattr__(self, attr): """Check if attribute is available from ``.data``""" - if attr in dir(self): - return self.__getattribute__(attr) try: data = self.__getattribute__('_data') return getattr(data, attr) diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index 132a59efc0..8e2d2135c3 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -11,9 +11,9 @@ import threading import time from abc import ABC, abstractmethod +from concurrent.futures import ThreadPoolExecutor from typing import TYPE_CHECKING, List, Optional, Union -import dask import numpy as np import tensorflow as tf @@ -46,7 +46,7 @@ def __init__( max_workers: int = 1, thread_name: str = 'training', mode: str = 'lazy', - verbose: bool = False + verbose: bool = False, ): """ Parameters @@ -91,6 +91,7 @@ def __init__( self._queue_thread = None self._training_flag = threading.Event() self._thread_name = thread_name + self._thread_pool = ThreadPoolExecutor(max_workers=max_workers) self.mode = mode self.s_enhance = s_enhance self.t_enhance = t_enhance @@ -120,6 +121,22 @@ def queue_shape(self): this is (batch_size, *sample_shape, len(features)). For dual dataset queues this is [(batch_size, *lr_shape), (batch_size, *hr_shape)]""" + @property + def queue_len(self): + """Get number of batches in the queue.""" + return self.queue.size().numpy() + + @property + def queue_futures(self): + """Get number of scheduled futures that will eventually add batches to + the queue.""" + return self._thread_pool._work_queue.qsize() + + @property + def queue_free(self): + """Get number of free spots in the queue.""" + return self.queue_cap - self.queue_len + def get_queue(self): """Return FIFO queue for storing batches.""" return tf.queue.FIFOQueue( @@ -227,13 +244,9 @@ def __iter__(self): def get_batch(self) -> DsetTuple: """Get batch from queue or directly from a ``Sampler`` through ``sample_batch``.""" - if ( - self.mode == 'eager' - or self.queue_cap == 0 - or self.queue.size().numpy() == 0 - ): - return self.sample_batch() - return self.queue.dequeue() + if self.queue_len > 0 or self.queue_futures > 0: + return self.queue.dequeue() + return self.sample_batch() @property def running(self): @@ -250,17 +263,19 @@ def sample_batches(self, n_batches) -> None: if n_batches == 1: return [self.sample_batch()] - tasks = [dask.delayed(self.sample_batch)() for _ in range(n_batches)] - logger.debug('Added %s sample_batch futures to %s queue.', - n_batches, - self._thread_name) - if self.max_workers == 1: - batches = dask.compute(*tasks, scheduler='single-threaded') - else: - batches = dask.compute( - *tasks, scheduler='threads', num_workers=self.max_workers) - return batches + return [self.sample_batch() for _ in range(n_batches)] + + tasks = [ + self._thread_pool.submit(self.sample_batch) + for _ in range(n_batches) + ] + logger.debug( + 'Added %s sample_batch futures to %s queue.', + n_batches, + self._thread_name, + ) + return [task.result() for task in tasks] def enqueue_batches(self) -> None: """Callback function for queue thread. While training, the queue is @@ -268,8 +283,10 @@ def enqueue_batches(self) -> None: removed from the queue.""" log_time = time.time() while self.running: - needed = self.queue_cap - self.queue.size().numpy() - + needed = min( + self.queue_free - self.queue_futures, + self.n_batches - self._batch_count + ) # no point in getting more than one batch at a time if # max_workers == 1 needed = 1 if needed > 0 and self.max_workers == 1 else needed @@ -336,10 +353,11 @@ def sample_batch(self): def log_queue_info(self): """Log info about queue size.""" - return '{} queue length: {} / {}.'.format( + return '{} queue length: {} / {}, with {} futures'.format( self._thread_name.title(), - self.queue.size().numpy(), + self.queue_len, self.queue_cap, + self.queue_futures ) @property diff --git a/sup3r/preprocessing/collections/base.py b/sup3r/preprocessing/collections/base.py index f9f373bc92..736606a733 100644 --- a/sup3r/preprocessing/collections/base.py +++ b/sup3r/preprocessing/collections/base.py @@ -60,8 +60,6 @@ def container_weights(self): def __getattr__(self, attr): """Get attributes from self or the first container in the collection.""" - if attr in dir(self): - return self.__getattribute__(attr) return self.check_shared_attr(attr) def check_shared_attr(self, attr): diff --git a/sup3r/preprocessing/samplers/dual.py b/sup3r/preprocessing/samplers/dual.py index 44558b5458..a82cd986e3 100644 --- a/sup3r/preprocessing/samplers/dual.py +++ b/sup3r/preprocessing/samplers/dual.py @@ -144,7 +144,8 @@ def get_sample_index(self, n_obs=None): slice(s.start * self.t_enhance, s.stop * self.t_enhance) for s in lr_index[2:-1] ] + obs_index = (*hr_index, self.hr_out_features) hr_index = (*hr_index, self.hr_features) - sample_index = (lr_index, hr_index, hr_index) + sample_index = (lr_index, hr_index, obs_index) return sample_index[:len(self.data)] diff --git a/tests/batch_handlers/test_bh_general.py b/tests/batch_handlers/test_bh_general.py index 3ef40d806d..f00229f821 100644 --- a/tests/batch_handlers/test_bh_general.py +++ b/tests/batch_handlers/test_bh_general.py @@ -2,8 +2,11 @@ import copy +import dask.array as da import numpy as np +import pandas as pd import pytest +import xarray as xr from scipy.ndimage import gaussian_filter from sup3r.preprocessing import ( @@ -17,6 +20,7 @@ ) from sup3r.utilities.utilities import ( RANDOM_GENERATOR, + Timer, spatial_coarsening, temporal_coarsening, ) @@ -29,6 +33,78 @@ BatchHandlerTester = BatchHandlerTesterFactory(BatchHandler, SamplerTester) +def test_batch_handler_workers(): + """Check that it is faster to get batches with max_workers > 1 than with + max_workers = 1.""" + + timer = Timer() + n_lats = 200 + n_lons = 200 + sample_shape = (20, 20, 30) + chunk_shape = ( + 2 * sample_shape[0], + 2 * sample_shape[1], + 2 * sample_shape[-1], + ) + n_obs = 40 + max_workers = 5 + n_batches = 40 + + lons, lats = np.meshgrid( + np.linspace(0, 180, n_lats), np.linspace(40, 60, n_lons) + ) + time = pd.date_range('2023-01-01', '2023-05-01', freq='h') + u_arr = da.random.random((*lats.shape, len(time))) + v_arr = da.random.random((*lats.shape, len(time))) + ds = xr.Dataset( + coords={ + 'latitude': (('south_north', 'west_east'), lats), + 'longitude': (('south_north', 'west_east'), lons), + 'time': time, + }, + data_vars={ + 'u_100m': (('south_north', 'west_east', 'time'), u_arr), + 'v_100m': (('south_north', 'west_east', 'time'), v_arr), + }, + ) + ds = ds.chunk(dict(zip(['south_north', 'west_east', 'time'], chunk_shape))) + + batcher = BatchHandler( + [ds], + n_batches=n_batches, + batch_size=n_obs, + sample_shape=sample_shape, + max_workers=max_workers, + ) + timer.start() + for _ in range(10): + _ = list(batcher) + timer.stop() + parallel_time = timer.elapsed / (n_batches * 10) + batcher.stop() + + batcher = BatchHandler( + [ds], + n_batches=n_batches, + batch_size=n_obs, + sample_shape=sample_shape, + max_workers=1, + ) + timer.start() + for _ in range(10): + _ = list(batcher) + timer.stop() + serial_time = timer.elapsed / (n_batches * 10) + batcher.stop() + + print( + 'Elapsed (serial / parallel): {} / {}'.format( + serial_time, parallel_time + ) + ) + assert serial_time > parallel_time + + def test_eager_vs_lazy(): """Make sure eager and lazy loading agree. We use queue_cap = 0 here so there is no disagreement that results from dequeuing vs direct batch diff --git a/tests/batch_queues/test_bq_general.py b/tests/batch_queues/test_bq_general.py index b130aa7b7f..52b2ffb257 100644 --- a/tests/batch_queues/test_bq_general.py +++ b/tests/batch_queues/test_bq_general.py @@ -12,7 +12,6 @@ DummyData, DummySampler, ) -from sup3r.utilities.utilities import Timer FEATURES = ['windspeed', 'winddirection'] @@ -54,55 +53,6 @@ def test_batch_queue(): batcher.stop() -def test_batch_queue_workers(): - """Check that using max_workers > 1 for a batch queue is faster than using - max_workers = 1.""" - - timer = Timer() - sample_shape = (10, 10, 20) - n_batches = 20 - batch_size = 10 - max_workers = 10 - n_epochs = 10 - chunk_shape = {'south_north': 20, 'west_east': 20, 'time': 40} - samplers = [ - DummySampler( - sample_shape, - data_shape=(100, 100, 1000), - batch_size=batch_size, - features=FEATURES, - chunk_shape=chunk_shape - ) - ] - batcher = SingleBatchQueue( - samplers=samplers, - n_batches=n_batches, - batch_size=batch_size, - max_workers=1, - ) - timer.start() - for _ in range(n_epochs): - _ = list(batcher) - timer.stop() - batcher.stop() - serial_time = timer.elapsed / (n_epochs * n_batches) - - batcher = SingleBatchQueue( - samplers=samplers, - n_batches=n_batches, - batch_size=batch_size, - max_workers=max_workers, - ) - timer.start() - for _ in range(n_epochs): - _ = list(batcher) - timer.stop() - batcher.stop() - parallel_time = timer.elapsed / (n_epochs * n_batches) - print(f'Parallel / Serial Time: {parallel_time} / {serial_time}') - assert parallel_time < serial_time - - def test_spatial_batch_queue(): """Smoke test for spatial batch queue. A batch queue returns batches for spatial models if the sample shapes have 1 for the time axis""" From 01d9e60cbe0dedf52cdd3d8656ebe7d0cb7db72c Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 29 Dec 2024 06:48:39 -0800 Subject: [PATCH 014/122] split up ``calc_loss`` and ``calc_loss_obs`` --- sup3r/models/abstract.py | 158 ++++++++++++++++++++---------------- sup3r/models/base.py | 1 - sup3r/preprocessing/base.py | 5 +- 3 files changed, 92 insertions(+), 72 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 1fcd09b3cd..0f5752042e 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -18,6 +18,7 @@ from phygnn.layers.custom_layers import Sup3rAdder, Sup3rConcat from rex.utilities.utilities import safe_json_load from tensorflow.keras import optimizers +from tensorflow.keras.losses import MeanAbsoluteError import sup3r.utilities.loss_metrics from sup3r.preprocessing.data_handlers import ExoData @@ -782,6 +783,69 @@ def finish_epoch( return stop + def _get_parallel_grad( + self, + low_res, + hi_res_true, + training_weights, + obs_data=None, + **calc_loss_kwargs, + ): + """Compute gradient for one mini-batch of (low_res, hi_res_true) + across multiple GPUs""" + + futures = [] + lr_chunks = np.array_split(low_res, len(self.gpu_list)) + hr_true_chunks = np.array_split(hi_res_true, len(self.gpu_list)) + obs_data_chunks = ( + [None] * len(hr_true_chunks) + if obs_data is None + else np.array_split(obs_data, len(self.gpu_list)) + ) + split_mask = False + mask_chunks = None + if 'mask' in calc_loss_kwargs: + split_mask = True + mask_chunks = np.array_split( + calc_loss_kwargs['mask'], len(self.gpu_list) + ) + + with ThreadPoolExecutor(max_workers=len(self.gpu_list)) as exe: + for i in range(len(self.gpu_list)): + if split_mask: + calc_loss_kwargs['mask'] = mask_chunks[i] + futures.append( + exe.submit( + self.get_single_grad, + lr_chunks[i], + hr_true_chunks[i], + training_weights, + obs_data=obs_data_chunks[i], + device_name=f'/gpu:{i}', + **calc_loss_kwargs, + ) + ) + + # sum the gradients from each gpu to weight equally in + # optimizer momentum calculation + total_grad = None + for future in futures: + grad, loss_details = future.result() + if total_grad is None: + total_grad = grad + else: + for i, igrad in enumerate(grad): + total_grad[i] += igrad + + self.timer.stop() + logger.debug( + 'Finished %s gradient descent steps on %s GPUs in %s', + len(futures), + len(self.gpu_list), + self.timer.elapsed_str, + ) + return total_grad, loss_details + def run_gradient_descent( self, low_res, @@ -792,7 +856,6 @@ def run_gradient_descent( multi_gpu=False, **calc_loss_kwargs, ): - # pylint: disable=E0602 """Run gradient descent for one mini-batch of (low_res, hi_res_true) and update weights @@ -832,6 +895,7 @@ def run_gradient_descent( loss_details : dict Namespace of the breakdown of loss components """ + self.timer.start() if optimizer is None: optimizer = self.optimizer @@ -852,58 +916,15 @@ def run_gradient_descent( self.timer.elapsed_str, ) else: - futures = [] - lr_chunks = np.array_split(low_res, len(self.gpu_list)) - hr_true_chunks = np.array_split(hi_res_true, len(self.gpu_list)) - obs_data_chunks = ( - [None] * len(hr_true_chunks) - if obs_data is None - else np.array_split(obs_data, len(self.gpu_list)) + total_grad, loss_details = self._get_parallel_grad( + low_res, + hi_res_true, + training_weights, + obs_data, + **calc_loss_kwargs, ) - split_mask = False - mask_chunks = None - if 'mask' in calc_loss_kwargs: - split_mask = True - mask_chunks = np.array_split( - calc_loss_kwargs['mask'], len(self.gpu_list) - ) - - with ThreadPoolExecutor(max_workers=len(self.gpu_list)) as exe: - for i in range(len(self.gpu_list)): - if split_mask: - calc_loss_kwargs['mask'] = mask_chunks[i] - futures.append( - exe.submit( - self.get_single_grad, - lr_chunks[i], - hr_true_chunks[i], - training_weights, - obs_data=obs_data_chunks[i], - device_name=f'/gpu:{i}', - **calc_loss_kwargs, - ) - ) - - # sum the gradients from each gpu to weight equally in - # optimizer momentum calculation - total_grad = None - for future in futures: - grad, loss_details = future.result() - if total_grad is None: - total_grad = grad - else: - for i, igrad in enumerate(grad): - total_grad[i] += igrad - optimizer.apply_gradients(zip(total_grad, training_weights)) - self.timer.stop() - logger.debug( - 'Finished %s gradient descent steps on %s GPUs in %s', - len(futures), - len(self.gpu_list), - self.timer.elapsed_str, - ) return loss_details def _reshape_norm_exo(self, hi_res, hi_res_exo, exo_name, norm_in=True): @@ -1148,9 +1169,13 @@ def get_single_grad( hi_res_exo = self.get_hr_exo_input(hi_res_true) hi_res_gen = self._tf_generate(low_res, hi_res_exo) loss_out = self.calc_loss( - hi_res_true, hi_res_gen, obs_data=obs_data, **calc_loss_kwargs + hi_res_true, hi_res_gen, **calc_loss_kwargs ) loss, loss_details = loss_out + if obs_data is not None: + loss_obs = self.calc_loss_obs(obs_data, hi_res_gen) + loss += loss_obs + loss_details['loss_obs'] = loss_obs grad = tape.gradient(loss, training_weights) return grad, loss_details @@ -1159,36 +1184,33 @@ def calc_loss( self, hi_res_true, hi_res_gen, - obs_data=None, weight_gen_advers=0.001, train_gen=True, train_disc=False, ): """Calculate the GAN loss function using generated and true high - resolution data. + resolution data.""" + + @tf.function + def calc_loss_obs(self, obs_data, hi_res_gen): + """Calculate loss term for the observation data vs generated + high-resolution data Parameters ---------- - hi_res_true : tf.Tensor - Ground truth high resolution spatiotemporal data. + obs_data : tf.Tensor | None + Optional observation data to use in additional content loss term. hi_res_gen : tf.Tensor Superresolved high resolution spatiotemporal data generated by the generative model. - obs_data : tf.Tensor | None - Optional observation data to use in additional content loss term. - weight_gen_advers : float - Weight factor for the adversarial loss component of the generator - vs. the discriminator. - train_gen : bool - True if generator is being trained, then loss=loss_gen - train_disc : bool - True if disc is being trained, then loss=loss_disc Returns ------- loss : tf.Tensor - 0D tensor representing the loss value for the network being trained - (either generator or one of the discriminators) - loss_details : dict - Namespace of the breakdown of loss components + 0D tensor of observation loss """ + mask = tf.math.is_nan(obs_data) + return MeanAbsoluteError()( + obs_data[~mask], + hi_res_gen[..., : len(self.hr_out_features)][~mask], + ) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index c6dbbd35fe..0cb78b8de4 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -10,7 +10,6 @@ import numpy as np import pandas as pd import tensorflow as tf -from tensorflow.keras.losses import MeanAbsoluteError from sup3r.preprocessing.utilities import get_class_kwargs from sup3r.utilities import VERSION_RECORD diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index ce9d1d6c4e..7d81f9d273 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -379,7 +379,7 @@ def data(self, data): def wrap(self, data): """ Return a :class:`~.Sup3rDataset` object or tuple of such. This is a - tuple when the `.data` attribute belongs to a + tuple when the ``.data`` attribute belongs to a :class:`~.collections.base.Collection` object like :class:`~.batch_handlers.factory.BatchHandler`. Otherwise this is :class:`~.Sup3rDataset` object, which is either a wrapped 3-tuple, @@ -452,8 +452,7 @@ def __setitem__(self, keys, data): def __getattr__(self, attr): """Check if attribute is available from ``.data``""" try: - data = self.__getattribute__('_data') - return getattr(data, attr) + return getattr(self._data, attr) except Exception as e: msg = f'{self.__class__.__name__} object has no attribute "{attr}"' raise AttributeError(msg) from e From 507b15812d064863a9c8b9f3f89fcb8f9f42a377 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 29 Dec 2024 07:43:59 -0700 Subject: [PATCH 015/122] Optional run_qa flag in ``DualRasterizer``. Queue shape fix for queues with obs data --- sup3r/preprocessing/batch_queues/dual.py | 18 +++++++++++++----- sup3r/preprocessing/rasterizers/dual.py | 9 +++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/sup3r/preprocessing/batch_queues/dual.py b/sup3r/preprocessing/batch_queues/dual.py index b7e26bf94a..56b2b08d42 100644 --- a/sup3r/preprocessing/batch_queues/dual.py +++ b/sup3r/preprocessing/batch_queues/dual.py @@ -28,11 +28,19 @@ def __init__(self, samplers, **kwargs): @property def queue_shape(self): - """Shape of objects stored in the queue.""" - queue_shapes = [(self.batch_size, *self.lr_shape)] - hr_mems = len(self.BATCH_MEMBERS) - 1 - queue_shapes += [(self.batch_size, *self.hr_shape)] * hr_mems - return queue_shapes + """Shape of objects stored in the queue. Optionally includes shape of + observation data which would be included in an extra content loss + term""" + obs_shape = ( + *self.hr_shape[:-1], + len(self.containers[0].hr_out_features), + ) + queue_shapes = [ + (self.batch_size, *self.lr_shape), + (self.batch_size, *self.hr_shape), + (self.batch_size, *obs_shape), + ] + return queue_shapes[: len(self.BATCH_MEMBERS)] def check_enhancement_factors(self): """Make sure each DualSampler has the same enhancment factors and they diff --git a/sup3r/preprocessing/rasterizers/dual.py b/sup3r/preprocessing/rasterizers/dual.py index 47706aa4e4..a70f01b089 100644 --- a/sup3r/preprocessing/rasterizers/dual.py +++ b/sup3r/preprocessing/rasterizers/dual.py @@ -43,6 +43,7 @@ def __init__( ], regrid_workers=1, regrid_lr=True, + run_qa=False, s_enhance=1, t_enhance=1, lr_cache_kwargs=None, @@ -63,6 +64,9 @@ def __init__( Flag to regrid the low-res data to the high-res grid. This will take care of any minor inconsistencies in different projections. Disable this if the grids are known to be the same. + run_qa : bool + Flag to run qa on the regridded low-res data. This will check for + NaNs and fill them if there are not too many. s_enhance : int Spatial enhancement factor t_enhance : int @@ -135,7 +139,8 @@ def __init__( self.update_hr_data() super().__init__(data=(self.lr_data, self.hr_data)) - self.check_regridded_lr_data() + if run_qa: + self.check_regridded_lr_data() if lr_cache_kwargs is not None: Cacher(self.lr_data, lr_cache_kwargs) @@ -205,7 +210,7 @@ def update_lr_data(self): lr_coords_new = { Dimension.LATITUDE: self.lr_lat_lon[..., 0], Dimension.LONGITUDE: self.lr_lat_lon[..., 1], - Dimension.TIME: self.lr_data.indexes['time'][ + Dimension.TIME: self.lr_data.indexes[Dimension.TIME][ : self.lr_required_shape[2] ], } From af047da841c7d4ef51c156c25195c14d5a5fcb20 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 29 Dec 2024 08:27:46 -0800 Subject: [PATCH 016/122] ``run_qa=True`` default for ``DualRasterizer`` --- sup3r/preprocessing/rasterizers/dual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sup3r/preprocessing/rasterizers/dual.py b/sup3r/preprocessing/rasterizers/dual.py index a70f01b089..7b3df385db 100644 --- a/sup3r/preprocessing/rasterizers/dual.py +++ b/sup3r/preprocessing/rasterizers/dual.py @@ -43,7 +43,7 @@ def __init__( ], regrid_workers=1, regrid_lr=True, - run_qa=False, + run_qa=True, s_enhance=1, t_enhance=1, lr_cache_kwargs=None, From 1388cd7153ca38d2c5c167402628e124893a0da7 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 29 Dec 2024 12:13:09 -0800 Subject: [PATCH 017/122] better tracking of batch counting. (this can be tricky for parallel queueing, since batches can be sampled directly if there are none in the queue). --- sup3r/preprocessing/batch_queues/abstract.py | 41 ++++++++++---------- tests/batch_handlers/test_bh_general.py | 4 +- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index 8e2d2135c3..ce236bf430 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -124,7 +124,7 @@ def queue_shape(self): @property def queue_len(self): """Get number of batches in the queue.""" - return self.queue.size().numpy() + return self.queue.size().numpy() + self.queue_futures @property def queue_futures(self): @@ -132,11 +132,6 @@ def queue_futures(self): the queue.""" return self._thread_pool._work_queue.qsize() - @property - def queue_free(self): - """Get number of free spots in the queue.""" - return self.queue_cap - self.queue_len - def get_queue(self): """Return FIFO queue for storing batches.""" return tf.queue.FIFOQueue( @@ -237,16 +232,16 @@ def __len__(self): return self.n_batches def __iter__(self): - self._batch_count = 0 self.start() + self._batch_count = 0 return self def get_batch(self) -> DsetTuple: """Get batch from queue or directly from a ``Sampler`` through ``sample_batch``.""" - if self.queue_len > 0 or self.queue_futures > 0: - return self.queue.dequeue() - return self.sample_batch() + if self.mode == 'eager' or self.queue_cap == 0 or self.queue_len == 0: + return self.sample_batch() + return self.queue.dequeue() @property def running(self): @@ -277,19 +272,26 @@ def sample_batches(self, n_batches) -> None: ) return [task.result() for task in tasks] + @property + def needed_batches(self): + """Number of batches needed to either fill or the queue or hit the + epoch limit.""" + remaining = self.n_batches - self._batch_count - self.queue_len - 1 + return min(self.queue_cap - self.queue_len, remaining) + def enqueue_batches(self) -> None: """Callback function for queue thread. While training, the queue is checked for empty spots and filled. In the training thread, batches are removed from the queue.""" log_time = time.time() while self.running: - needed = min( - self.queue_free - self.queue_futures, - self.n_batches - self._batch_count - ) # no point in getting more than one batch at a time if # max_workers == 1 - needed = 1 if needed > 0 and self.max_workers == 1 else needed + needed = ( + 1 + if self.needed_batches > 0 and self.max_workers == 1 + else self.needed_batches + ) if needed > 0: for batch in self.sample_batches(n_batches=needed): @@ -312,6 +314,7 @@ def __next__(self) -> DsetTuple: if self._batch_count < self.n_batches: self.timer.start() samples = self.get_batch() + self._batch_count += 1 if self.sample_shape[2] == 1: if isinstance(samples, (list, tuple)): samples = tuple(s[..., 0, :] for s in samples) @@ -319,7 +322,6 @@ def __next__(self) -> DsetTuple: samples = samples[..., 0, :] batch = self.post_proc(samples) self.timer.stop() - self._batch_count += 1 if self.verbose: logger.debug( 'Batch step %s finished in %s.', @@ -353,11 +355,8 @@ def sample_batch(self): def log_queue_info(self): """Log info about queue size.""" - return '{} queue length: {} / {}, with {} futures'.format( - self._thread_name.title(), - self.queue_len, - self.queue_cap, - self.queue_futures + return '{} queue length: {} / {}'.format( + self._thread_name.title(), self.queue_len, self.queue_cap ) @property diff --git a/tests/batch_handlers/test_bh_general.py b/tests/batch_handlers/test_bh_general.py index f00229f821..b716646e5a 100644 --- a/tests/batch_handlers/test_bh_general.py +++ b/tests/batch_handlers/test_bh_general.py @@ -47,8 +47,8 @@ def test_batch_handler_workers(): 2 * sample_shape[-1], ) n_obs = 40 - max_workers = 5 - n_batches = 40 + max_workers = 20 + n_batches = 20 lons, lats = np.meshgrid( np.linspace(0, 180, n_lats), np.linspace(40, 60, n_lons) From 950ca9e77a09d95509ffc19cddf49f8984100de7 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 29 Dec 2024 15:41:00 -0800 Subject: [PATCH 018/122] missed compute call for slow batching. this was hidden by queueing and dequeueing since this would cast to tensors. --- sup3r/preprocessing/batch_queues/abstract.py | 4 ++-- sup3r/preprocessing/samplers/base.py | 16 ++++++++-------- sup3r/preprocessing/utilities.py | 2 ++ tests/batch_handlers/test_bh_general.py | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index ce236bf430..3e15d016a6 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -276,7 +276,7 @@ def sample_batches(self, n_batches) -> None: def needed_batches(self): """Number of batches needed to either fill or the queue or hit the epoch limit.""" - remaining = self.n_batches - self._batch_count - self.queue_len - 1 + remaining = self.n_batches - self._batch_count - self.queue_len return min(self.queue_cap - self.queue_len, remaining) def enqueue_batches(self) -> None: @@ -314,7 +314,6 @@ def __next__(self) -> DsetTuple: if self._batch_count < self.n_batches: self.timer.start() samples = self.get_batch() - self._batch_count += 1 if self.sample_shape[2] == 1: if isinstance(samples, (list, tuple)): samples = tuple(s[..., 0, :] for s in samples) @@ -322,6 +321,7 @@ def __next__(self) -> DsetTuple: samples = samples[..., 0, :] batch = self.post_proc(samples) self.timer.stop() + self._batch_count += 1 if self.verbose: logger.debug( 'Batch step %s finished in %s.', diff --git a/sup3r/preprocessing/samplers/base.py b/sup3r/preprocessing/samplers/base.py index b824cd360b..e576c15218 100644 --- a/sup3r/preprocessing/samplers/base.py +++ b/sup3r/preprocessing/samplers/base.py @@ -7,7 +7,6 @@ from typing import Dict, Optional, Tuple from warnings import warn -import dask.array as da import numpy as np from sup3r.preprocessing.base import Container @@ -194,9 +193,8 @@ def _reshape_samples(self, samples): new_shape[2] // self.batch_size, new_shape[-1], ] - out = compute_if_dask(samples) # (lats, lons, batch_size, times, feats) - out = np.reshape(out, new_shape) + out = np.reshape(samples, new_shape) # (batch_size, lats, lons, times, feats) return np.transpose(out, axes=(2, 0, 1, 3, 4)) @@ -223,25 +221,27 @@ def _stack_samples(self, samples): (batch_size, samp_shape[0], samp_shape[1], samp_shape[2], n_feats) """ if isinstance(samples[0], tuple): - lr = da.stack([s[0] for s in samples], axis=0) - hr = da.stack([s[1] for s in samples], axis=0) + lr = np.stack([s[0] for s in samples], axis=0) + hr = np.stack([s[1] for s in samples], axis=0) return (lr, hr) - return da.stack(samples, axis=0) + return np.stack(samples, axis=0) def _fast_batch(self): """Get batch of samples with adjacent time slices.""" out = self.data.sample(self.get_sample_index(n_obs=self.batch_size)) + out = compute_if_dask(out) if isinstance(out, tuple): return tuple(self._reshape_samples(o) for o in out) return self._reshape_samples(out) def _slow_batch(self): """Get batch of samples with random time slices.""" - samples = [ + out = [ self.data.sample(self.get_sample_index(n_obs=1)) for _ in range(self.batch_size) ] - return self._stack_samples(samples) + out = compute_if_dask(out) + return self._stack_samples(out) def _fast_batch_possible(self): return self.batch_size * self.sample_shape[2] <= self.data.shape[2] diff --git a/sup3r/preprocessing/utilities.py b/sup3r/preprocessing/utilities.py index 8a51fadbfd..f52e91b72c 100644 --- a/sup3r/preprocessing/utilities.py +++ b/sup3r/preprocessing/utilities.py @@ -228,6 +228,8 @@ def compute_if_dask(arr): compute_if_dask(arr.stop), compute_if_dask(arr.step), ) + if isinstance(arr, (tuple, list)): + return type(arr)(compute_if_dask(a) for a in arr) return arr.compute() if hasattr(arr, 'compute') else arr diff --git a/tests/batch_handlers/test_bh_general.py b/tests/batch_handlers/test_bh_general.py index b716646e5a..207a1f962f 100644 --- a/tests/batch_handlers/test_bh_general.py +++ b/tests/batch_handlers/test_bh_general.py @@ -47,8 +47,8 @@ def test_batch_handler_workers(): 2 * sample_shape[-1], ) n_obs = 40 - max_workers = 20 - n_batches = 20 + max_workers = 32 + n_batches = 40 lons, lats = np.meshgrid( np.linspace(0, 180, n_lats), np.linspace(40, 60, n_lons) From 0b489b6ba27ffd98fab7288add3b7d64f830680e Mon Sep 17 00:00:00 2001 From: bnb32 Date: Mon, 30 Dec 2024 07:12:20 -0800 Subject: [PATCH 019/122] Included convert to tensor in ``sample_batch``. Test for training with ``max_workers > 1``. --- sup3r/preprocessing/batch_queues/abstract.py | 5 +- sup3r/utilities/utilities.py | 2 + tests/batch_handlers/test_bh_general.py | 17 ++-- tests/rasterizers/test_dual.py | 10 ++- tests/training/test_train_dual.py | 6 +- tests/training/test_train_dual_with_obs.py | 4 +- tests/training/test_train_gan.py | 81 +++++++++++++++++++- 7 files changed, 106 insertions(+), 19 deletions(-) diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index 3e15d016a6..f9e2209039 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -351,7 +351,10 @@ def sample_batch(self): These samples are wrapped in an ``np.asarray`` call, so they have been loaded into memory. """ - return next(self.get_random_container()) + out = next(self.get_random_container()) + if not isinstance(out, tuple): + return tf.convert_to_tensor(out, dtype=tf.float32) + return tuple(tf.convert_to_tensor(o, dtype=tf.float32) for o in out) def log_queue_info(self): """Log info about queue size.""" diff --git a/sup3r/utilities/utilities.py b/sup3r/utilities/utilities.py index 498e664f52..d5fc1578c5 100644 --- a/sup3r/utilities/utilities.py +++ b/sup3r/utilities/utilities.py @@ -92,6 +92,8 @@ def stop(self): @property def elapsed(self): """Elapsed time between start and stop.""" + if self._stop is None: + return time.time() - self._start return self._stop - self._start @property diff --git a/tests/batch_handlers/test_bh_general.py b/tests/batch_handlers/test_bh_general.py index 207a1f962f..5d844b1c20 100644 --- a/tests/batch_handlers/test_bh_general.py +++ b/tests/batch_handlers/test_bh_general.py @@ -49,13 +49,14 @@ def test_batch_handler_workers(): n_obs = 40 max_workers = 32 n_batches = 40 + n_epochs = 3 lons, lats = np.meshgrid( np.linspace(0, 180, n_lats), np.linspace(40, 60, n_lons) ) time = pd.date_range('2023-01-01', '2023-05-01', freq='h') - u_arr = da.random.random((*lats.shape, len(time))) - v_arr = da.random.random((*lats.shape, len(time))) + u_arr = da.random.random((*lats.shape, len(time))).astype('float32') + v_arr = da.random.random((*lats.shape, len(time))).astype('float32') ds = xr.Dataset( coords={ 'latitude': (('south_north', 'west_east'), lats), @@ -75,12 +76,14 @@ def test_batch_handler_workers(): batch_size=n_obs, sample_shape=sample_shape, max_workers=max_workers, + means={'u_100m': 0, 'v_100m': 0}, + stds={'u_100m': 1, 'v_100m': 1}, ) timer.start() - for _ in range(10): + for _ in range(n_epochs): _ = list(batcher) timer.stop() - parallel_time = timer.elapsed / (n_batches * 10) + parallel_time = timer.elapsed / (n_batches * n_epochs) batcher.stop() batcher = BatchHandler( @@ -89,12 +92,14 @@ def test_batch_handler_workers(): batch_size=n_obs, sample_shape=sample_shape, max_workers=1, + means={'u_100m': 0, 'v_100m': 0}, + stds={'u_100m': 1, 'v_100m': 1}, ) timer.start() - for _ in range(10): + for _ in range(n_epochs): _ = list(batcher) timer.stop() - serial_time = timer.elapsed / (n_batches * 10) + serial_time = timer.elapsed / (n_batches * n_epochs) batcher.stop() print( diff --git a/tests/rasterizers/test_dual.py b/tests/rasterizers/test_dual.py index ed11feecba..725c2fdb05 100644 --- a/tests/rasterizers/test_dual.py +++ b/tests/rasterizers/test_dual.py @@ -30,7 +30,9 @@ def test_dual_rasterizer_shapes(full_shape=(20, 20)): ) pair_rasterizer = DualRasterizer( - (lr_container.data, hr_container.data), s_enhance=2, t_enhance=1 + {'low_res': lr_container.data, 'high_res': hr_container.data}, + s_enhance=2, + t_enhance=1, ) assert pair_rasterizer.lr_data.shape == ( pair_rasterizer.hr_data.shape[0] // 2, @@ -63,7 +65,9 @@ def test_dual_nan_fill(full_shape=(20, 20)): assert np.isnan(lr_container.data.as_array()).any() pair_rasterizer = DualRasterizer( - (lr_container.data, hr_container.data), s_enhance=1, t_enhance=1 + {'low_res': lr_container.data, 'high_res': hr_container.data}, + s_enhance=1, + t_enhance=1, ) assert not np.isnan(pair_rasterizer.lr_data.as_array()).any() @@ -89,7 +93,7 @@ def test_regrid_caching(full_shape=(20, 20)): lr_cache_pattern = os.path.join(td, 'lr_{feature}.h5') hr_cache_pattern = os.path.join(td, 'hr_{feature}.h5') pair_rasterizer = DualRasterizer( - (lr_container.data, hr_container.data), + {'low_res': lr_container.data, 'high_res': hr_container.data}, s_enhance=2, t_enhance=1, lr_cache_kwargs={'cache_pattern': lr_cache_pattern}, diff --git a/tests/training/test_train_dual.py b/tests/training/test_train_dual.py index f924439618..29e38a32b8 100644 --- a/tests/training/test_train_dual.py +++ b/tests/training/test_train_dual.py @@ -66,7 +66,7 @@ def test_train_h5_nc( # time indices conflict with t_enhance with pytest.raises(AssertionError): dual_rasterizer = DualRasterizer( - data=(lr_handler.data, hr_handler.data), + data={'low_res': lr_handler.data, 'high_res': hr_handler.data}, s_enhance=s_enhance, t_enhance=t_enhance, ) @@ -78,7 +78,7 @@ def test_train_h5_nc( ) dual_rasterizer = DualRasterizer( - data=(lr_handler.data, hr_handler.data), + data={'low_res': lr_handler.data, 'high_res': hr_handler.data}, s_enhance=s_enhance, t_enhance=t_enhance, ) @@ -158,7 +158,7 @@ def test_train_coarse_h5( ) dual_rasterizer = DualRasterizer( - data=(lr_handler.data, hr_handler.data), + data={'low_res': lr_handler.data, 'high_res': hr_handler.data}, s_enhance=s_enhance, t_enhance=t_enhance, ) diff --git a/tests/training/test_train_dual_with_obs.py b/tests/training/test_train_dual_with_obs.py index 48a9c7d676..0bf8244eec 100644 --- a/tests/training/test_train_dual_with_obs.py +++ b/tests/training/test_train_dual_with_obs.py @@ -67,7 +67,7 @@ def test_train_h5_nc( ) dual_rasterizer = DualRasterizer( - data=(lr_handler.data, hr_handler.data), + data={'low_res': lr_handler.data, 'high_res': hr_handler.data}, s_enhance=s_enhance, t_enhance=t_enhance, ) @@ -171,7 +171,7 @@ def test_train_coarse_h5( ) dual_rasterizer = DualRasterizer( - data=(lr_handler.data, hr_handler.data), + data={'low_res': lr_handler.data, 'high_res': hr_handler.data}, s_enhance=s_enhance, t_enhance=t_enhance, ) diff --git a/tests/training/test_train_gan.py b/tests/training/test_train_gan.py index 4ea2d95830..fa7b3bc5ce 100644 --- a/tests/training/test_train_gan.py +++ b/tests/training/test_train_gan.py @@ -11,6 +11,7 @@ from sup3r.models import Sup3rGan from sup3r.preprocessing import BatchHandler, DataHandler +from sup3r.utilities.utilities import Timer TARGET_COORD = (39.01, -105.15) FEATURES = ['u_100m', 'v_100m'] @@ -180,10 +181,13 @@ def test_train(fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, n_epoch=8): assert 'OptmGen/learning_rate' in model.history assert 'OptmDisc/learning_rate' in model.history - msg = ('Could not find OptmGen states in columns: ' - f'{sorted(model.history.columns)}') - check = [col.startswith('OptmGen/Adam/v') - for col in model.history.columns] + msg = ( + 'Could not find OptmGen states in columns: ' + f'{sorted(model.history.columns)}' + ) + check = [ + col.startswith('OptmGen/Adam/v') for col in model.history.columns + ] assert any(check), msg assert 'config_generator' in loaded.meta @@ -235,6 +239,75 @@ def test_train(fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, n_epoch=8): batch_handler.stop() +def test_train_workers(n_epoch=3): + """Test that model training with max_workers > 1 for the batch queue is + faster than for max_workers = 1.""" + + lr = 5e-5 + Sup3rGan.seed() + model = Sup3rGan( + pytest.ST_FP_GEN, + pytest.ST_FP_DISC, + learning_rate=lr, + loss='MeanAbsoluteError', + ) + + train_handler, val_handler = _get_handlers() + timer = Timer() + + with tempfile.TemporaryDirectory() as td: + batch_handler = BatchHandler( + train_containers=[train_handler], + val_containers=[val_handler], + sample_shape=(12, 12, 16), + batch_size=15, + s_enhance=3, + t_enhance=4, + n_batches=10, + means={'u_100m': 0, 'v_100m': 0}, + stds={'u_100m': 1, 'v_100m': 1}, + max_workers=10, + ) + + model_kwargs = { + 'input_resolution': {'spatial': '30km', 'temporal': '60min'}, + 'n_epoch': n_epoch, + 'weight_gen_advers': 0.0, + 'train_gen': True, + 'train_disc': False, + 'checkpoint_int': 1, + 'out_dir': os.path.join(td, 'test_{epoch}'), + } + + timer.start() + model.train(batch_handler, **model_kwargs) + parallel_time = timer.elapsed + + batch_handler = BatchHandler( + train_containers=[train_handler], + val_containers=[val_handler], + sample_shape=(12, 12, 16), + batch_size=15, + s_enhance=3, + t_enhance=4, + n_batches=10, + means={'u_100m': 0, 'v_100m': 0}, + stds={'u_100m': 1, 'v_100m': 1}, + max_workers=1, + ) + + timer.start() + model.train(batch_handler, **model_kwargs) + serial_time = timer.elapsed + + print( + 'Elapsed (parallel / serial): {} / {}'.format( + parallel_time, serial_time + ) + ) + assert parallel_time < serial_time + + def test_train_st_weight_update(n_epoch=2): """Test basic spatiotemporal model training with discriminators and adversarial loss updating.""" From d7ca6cdeccfd09ca594aa4f5a87221c480538461 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 31 Dec 2024 11:53:54 -0700 Subject: [PATCH 020/122] cc batch handler test fix --- sup3r/models/base.py | 5 ++ sup3r/preprocessing/accessor.py | 52 ++++++++--------- sup3r/preprocessing/batch_queues/abstract.py | 60 +++++++------------- sup3r/preprocessing/rasterizers/dual.py | 2 +- sup3r/preprocessing/utilities.py | 1 + tests/batch_handlers/test_bh_general.py | 6 +- tests/batch_handlers/test_bh_h5_cc.py | 10 ++-- tests/training/test_train_gan.py | 45 ++++++++------- 8 files changed, 87 insertions(+), 94 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 0cb78b8de4..683f42304d 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -812,6 +812,11 @@ def train( ) if stop: break + logger.info( + 'Finished training %s epochs in %s seconds', + n_epoch, + time.time() - t0, + ) batch_handler.stop() diff --git a/sup3r/preprocessing/accessor.py b/sup3r/preprocessing/accessor.py index 8eaa28d90e..aad25b8842 100644 --- a/sup3r/preprocessing/accessor.py +++ b/sup3r/preprocessing/accessor.py @@ -488,8 +488,8 @@ def size(self): def time_index(self): """Base time index for contained data.""" return ( - pd.to_datetime(self._ds.indexes['time']) - if 'time' in self._ds.indexes + pd.to_datetime(self._ds.indexes[Dimension.TIME]) + if Dimension.TIME in self._ds.indexes else None ) @@ -562,40 +562,40 @@ def flatten(self): """Flatten rasterized dataset so that there is only a single spatial dimension.""" if not self.flattened: - self._ds = self._ds.stack( - {Dimension.FLATTENED_SPATIAL: Dimension.dims_2d()} - ) - self._ds = self._ds.assign( - { - Dimension.FLATTENED_SPATIAL: np.arange( - len(self._ds[Dimension.FLATTENED_SPATIAL]) - ) - } - ) + dims = {Dimension.FLATTENED_SPATIAL: Dimension.dims_2d()} + self._ds = self._ds.stack(dims) + index = np.arange(len(self._ds[Dimension.FLATTENED_SPATIAL])) + self._ds = self._ds.assign({Dimension.FLATTENED_SPATIAL: index}) else: msg = 'Dataset is already flattened' logger.warning(msg) warn(msg) return self - def _qa(self, feature): + def _qa(self, feature, stats=None): """Get qa info for given feature.""" info = {} + stats = stats or ['nan_perc', 'std', 'mean', 'min', 'max'] logger.info('Running qa on feature: %s', feature) nan_count = 100 * np.isnan(self[feature].data).sum() nan_perc = nan_count / self[feature].size - info['nan_perc'] = compute_if_dask(nan_perc) - info['std'] = compute_if_dask(self[feature].std().data) - info['mean'] = compute_if_dask(self[feature].mean().data) - info['min'] = compute_if_dask(self[feature].min().data) - info['max'] = compute_if_dask(self[feature].max().data) + + for stat in stats: + logger.info('Running QA method %s on feature: %s', stat, feature) + if stat == 'nan_perc': + info['nan_perc'] = compute_if_dask(nan_perc) + else: + msg = f'Unknown QA method requested: {stat}' + assert hasattr(self[feature], stat), msg + qa_data = getattr(self[feature], stat)().data + info[stat] = compute_if_dask(qa_data) return info - def qa(self): - """Check NaNs and stats for all features.""" + def qa(self, stats=None): + """Check NaNs and the given stats for all features.""" qa_info = {} for f in self.features: - qa_info[f] = self._qa(f) + qa_info[f] = self._qa(f, stats=stats) return qa_info def __mul__(self, other): @@ -604,9 +604,8 @@ def __mul__(self, other): try: return type(self)(other * self._ds) except Exception as e: - raise NotImplementedError( - f'Multiplication not supported for type {type(other)}.' - ) from e + msg = f'Multiplication not supported for type {type(other)}.' + raise NotImplementedError(msg) from e def __rmul__(self, other): return self.__mul__(other) @@ -617,6 +616,5 @@ def __pow__(self, other): try: return type(self)(self._ds**other) except Exception as e: - raise NotImplementedError( - f'Exponentiation not supported for type {type(other)}.' - ) from e + msg = f'Exponentiation not supported for type {type(other)}.' + raise NotImplementedError(msg) from e diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index f9e2209039..c2c019d0d9 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -11,7 +11,7 @@ import threading import time from abc import ABC, abstractmethod -from concurrent.futures import ThreadPoolExecutor +from concurrent.futures import ThreadPoolExecutor, as_completed from typing import TYPE_CHECKING, List, Optional, Union import numpy as np @@ -232,14 +232,14 @@ def __len__(self): return self.n_batches def __iter__(self): - self.start() self._batch_count = 0 + self.start() return self def get_batch(self) -> DsetTuple: """Get batch from queue or directly from a ``Sampler`` through ``sample_batch``.""" - if self.mode == 'eager' or self.queue_cap == 0 or self.queue_len == 0: + if self.mode == 'eager' or self.queue_cap == 0: return self.sample_batch() return self.queue.dequeue() @@ -252,32 +252,24 @@ def running(self): and not self.queue.is_closed() ) - def sample_batches(self, n_batches) -> None: - """Sample N batches from samplers. Returns N batches which are then - used to fill the queue.""" - if n_batches == 1: - return [self.sample_batch()] - - if self.max_workers == 1: - return [self.sample_batch() for _ in range(n_batches)] - - tasks = [ - self._thread_pool.submit(self.sample_batch) - for _ in range(n_batches) - ] - logger.debug( - 'Added %s sample_batch futures to %s queue.', - n_batches, - self._thread_name, - ) - return [task.result() for task in tasks] + def _enqueue_batches(self, n_batches) -> None: + """Sample N batches and enqueue them as they are sampled.""" + if n_batches == 1 or self.max_workers == 1: + for _ in range(n_batches): + self.queue.enqueue(self.sample_batch()) - @property - def needed_batches(self): - """Number of batches needed to either fill or the queue or hit the - epoch limit.""" - remaining = self.n_batches - self._batch_count - self.queue_len - return min(self.queue_cap - self.queue_len, remaining) + else: + tasks = [ + self._thread_pool.submit(self.sample_batch) + for _ in range(n_batches) + ] + logger.debug( + 'Added %s sample_batch futures to %s queue.', + n_batches, + self._thread_name, + ) + for batch in as_completed(tasks): + self.queue.enqueue(batch.result()) def enqueue_batches(self) -> None: """Callback function for queue thread. While training, the queue is @@ -285,17 +277,9 @@ def enqueue_batches(self) -> None: removed from the queue.""" log_time = time.time() while self.running: - # no point in getting more than one batch at a time if - # max_workers == 1 - needed = ( - 1 - if self.needed_batches > 0 and self.max_workers == 1 - else self.needed_batches - ) - + needed = max(self.queue_cap - self.queue_len, 0) if needed > 0: - for batch in self.sample_batches(n_batches=needed): - self.queue.enqueue(batch) + self._enqueue_batches(n_batches=needed) if time.time() > log_time + 10: logger.debug(self.log_queue_info()) diff --git a/sup3r/preprocessing/rasterizers/dual.py b/sup3r/preprocessing/rasterizers/dual.py index 7b3df385db..2df70fa840 100644 --- a/sup3r/preprocessing/rasterizers/dual.py +++ b/sup3r/preprocessing/rasterizers/dual.py @@ -223,7 +223,7 @@ def check_regridded_lr_data(self): """Check for NaNs after regridding and do NN fill if needed.""" fill_feats = [] logger.info('Checking for NaNs after regridding') - qa_info = self.lr_data.qa() + qa_info = self.lr_data.qa(stats=['nan_perc']) for f in self.lr_data.features: nan_perc = qa_info[f]['nan_perc'] if nan_perc > 0: diff --git a/sup3r/preprocessing/utilities.py b/sup3r/preprocessing/utilities.py index f52e91b72c..db0dd5909a 100644 --- a/sup3r/preprocessing/utilities.py +++ b/sup3r/preprocessing/utilities.py @@ -411,6 +411,7 @@ def parse_keys( order. If keys is empty then we just want to return the coordinate data, so features will be set to just the coordinate names.""" + keys = list(keys) if isinstance(keys, set) else keys keys = keys if isinstance(keys, tuple) else (keys,) has_feats = is_type_of(keys[0], str) diff --git a/tests/batch_handlers/test_bh_general.py b/tests/batch_handlers/test_bh_general.py index 5d844b1c20..b0842b2f6d 100644 --- a/tests/batch_handlers/test_bh_general.py +++ b/tests/batch_handlers/test_bh_general.py @@ -46,9 +46,9 @@ def test_batch_handler_workers(): 2 * sample_shape[1], 2 * sample_shape[-1], ) - n_obs = 40 - max_workers = 32 - n_batches = 40 + n_obs = 10 + max_workers = 10 + n_batches = 10 n_epochs = 3 lons, lats = np.meshgrid( diff --git a/tests/batch_handlers/test_bh_h5_cc.py b/tests/batch_handlers/test_bh_h5_cc.py index 0fac7520ad..942e52eed4 100644 --- a/tests/batch_handlers/test_bh_h5_cc.py +++ b/tests/batch_handlers/test_bh_h5_cc.py @@ -1,6 +1,5 @@ """pytests for H5 climate change data batch handlers""" - import matplotlib.pyplot as plt import numpy as np import pytest @@ -370,10 +369,11 @@ def test_surf_min_max_vars(): assert batch.low_res.shape[-1] == len(surf_features) # compare daily avg temp vs min and max - assert (batch.low_res[..., 0] > batch.low_res[..., 2]).all() - assert (batch.low_res[..., 0] < batch.low_res[..., 3]).all() + blr = batch.low_res.numpy() + assert (blr[..., 0] > blr[..., 2]).all() + assert (blr[..., 0] < blr[..., 3]).all() # compare daily avg rh vs min and max - assert (batch.low_res[..., 1] > batch.low_res[..., 4]).all() - assert (batch.low_res[..., 1] < batch.low_res[..., 5]).all() + assert (blr[..., 1] > blr[..., 4]).all() + assert (blr[..., 1] < blr[..., 5]).all() batcher.stop() diff --git a/tests/training/test_train_gan.py b/tests/training/test_train_gan.py index fa7b3bc5ce..55fa44e202 100644 --- a/tests/training/test_train_gan.py +++ b/tests/training/test_train_gan.py @@ -239,34 +239,37 @@ def test_train(fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, n_epoch=8): batch_handler.stop() -def test_train_workers(n_epoch=3): +def test_train_workers(n_epoch=20): """Test that model training with max_workers > 1 for the batch queue is faster than for max_workers = 1.""" lr = 5e-5 + train_handler, val_handler = _get_handlers() + timer = Timer() + n_batches = 40 + batch_size = 40 + Sup3rGan.seed() model = Sup3rGan( - pytest.ST_FP_GEN, - pytest.ST_FP_DISC, + pytest.S_FP_GEN, + pytest.S_FP_DISC, learning_rate=lr, loss='MeanAbsoluteError', ) - train_handler, val_handler = _get_handlers() - timer = Timer() - with tempfile.TemporaryDirectory() as td: + batch_handler = BatchHandler( train_containers=[train_handler], val_containers=[val_handler], - sample_shape=(12, 12, 16), - batch_size=15, - s_enhance=3, - t_enhance=4, - n_batches=10, + sample_shape=(10, 10, 1), + batch_size=batch_size, + s_enhance=2, + t_enhance=1, + n_batches=n_batches, means={'u_100m': 0, 'v_100m': 0}, stds={'u_100m': 1, 'v_100m': 1}, - max_workers=10, + max_workers=5, ) model_kwargs = { @@ -275,22 +278,23 @@ def test_train_workers(n_epoch=3): 'weight_gen_advers': 0.0, 'train_gen': True, 'train_disc': False, - 'checkpoint_int': 1, + 'checkpoint_int': 10, 'out_dir': os.path.join(td, 'test_{epoch}'), } timer.start() model.train(batch_handler, **model_kwargs) - parallel_time = timer.elapsed + timer.stop() + parallel_time = timer.elapsed / n_epoch batch_handler = BatchHandler( train_containers=[train_handler], val_containers=[val_handler], - sample_shape=(12, 12, 16), - batch_size=15, - s_enhance=3, - t_enhance=4, - n_batches=10, + sample_shape=(10, 10, 1), + batch_size=batch_size, + s_enhance=2, + t_enhance=1, + n_batches=n_batches, means={'u_100m': 0, 'v_100m': 0}, stds={'u_100m': 1, 'v_100m': 1}, max_workers=1, @@ -298,7 +302,8 @@ def test_train_workers(n_epoch=3): timer.start() model.train(batch_handler, **model_kwargs) - serial_time = timer.elapsed + timer.stop() + serial_time = timer.elapsed / n_epoch print( 'Elapsed (parallel / serial): {} / {}'.format( From 07251bb97ea204d69b5991aeadccbe5742979c01 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 31 Dec 2024 14:17:03 -0700 Subject: [PATCH 021/122] added test for new disc with "valid" padding --- tests/conftest.py | 4 ++++ tests/training/test_train_gan.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 9c23755cf0..2f52610da3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -39,6 +39,10 @@ def pytest_configure(config): # pylint: disable=unused-argument # noqa: ARG001 pytest.ST_FP_DISC = os.path.join(TEST_DATA_DIR, 'config_disc_st_test.json') pytest.S_FP_DISC = os.path.join(TEST_DATA_DIR, 'config_disc_s_test.json') + pytest.ST_FP_DISC_PROD = os.path.join( + CONFIG_DIR, 'spatiotemporal/disc.json' + ) + pytest.FPS_GCM = [ os.path.join(TEST_DATA_DIR, 'ua_test.nc'), os.path.join(TEST_DATA_DIR, 'va_test.nc'), diff --git a/tests/training/test_train_gan.py b/tests/training/test_train_gan.py index 55fa44e202..ddd5610f05 100644 --- a/tests/training/test_train_gan.py +++ b/tests/training/test_train_gan.py @@ -43,6 +43,7 @@ def _get_handlers(): ['fp_gen', 'fp_disc', 's_enhance', 't_enhance', 'sample_shape'], [ (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16)), + (pytest.ST_FP_GEN, pytest.ST_FP_DISC_PROD, 3, 4, (12, 12, 16)), (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (10, 10, 1)), ], ) @@ -322,7 +323,7 @@ def test_train_st_weight_update(n_epoch=2): pytest.ST_FP_GEN, pytest.ST_FP_DISC, learning_rate=1e-4, - learning_rate_disc=4e-4, + learning_rate_disc=4e-4 ) train_handler, val_handler = _get_handlers() From 0b1a43d141c49c52fadbc9d296cc3e8b29d07411 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 1 Jan 2025 10:24:28 -0700 Subject: [PATCH 022/122] parallel sampling batch sampling test. --- sup3r/preprocessing/batch_queues/abstract.py | 40 +++++----- tests/batch_handlers/test_bh_general.py | 84 ++++++++++++++------ tests/training/test_train_gan.py | 6 +- 3 files changed, 85 insertions(+), 45 deletions(-) diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index c2c019d0d9..e98664e83a 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -252,24 +252,21 @@ def running(self): and not self.queue.is_closed() ) - def _enqueue_batches(self, n_batches) -> None: - """Sample N batches and enqueue them as they are sampled.""" + def sample_batches(self, n_batches) -> None: + """Sample given number of batches either in serial or with thread + pool.""" if n_batches == 1 or self.max_workers == 1: - for _ in range(n_batches): - self.queue.enqueue(self.sample_batch()) - - else: - tasks = [ - self._thread_pool.submit(self.sample_batch) - for _ in range(n_batches) - ] - logger.debug( - 'Added %s sample_batch futures to %s queue.', - n_batches, - self._thread_name, - ) - for batch in as_completed(tasks): - self.queue.enqueue(batch.result()) + return [self.sample_batch() for _ in range(n_batches)] + tasks = [ + self._thread_pool.submit(self.sample_batch) + for _ in range(n_batches) + ] + logger.debug( + 'Added %s sample_batch futures to %s queue.', + n_batches, + self._thread_name, + ) + return tasks def enqueue_batches(self) -> None: """Callback function for queue thread. While training, the queue is @@ -278,8 +275,15 @@ def enqueue_batches(self) -> None: log_time = time.time() while self.running: needed = max(self.queue_cap - self.queue_len, 0) + needed = min(self.max_workers, needed) if needed > 0: - self._enqueue_batches(n_batches=needed) + batches = self.sample_batches(n_batches=needed) + if needed > 1 and self.max_workers > 1: + for batch in as_completed(batches): + self.queue.enqueue(batch.result()) + else: + for batch in batches: + self.queue.enqueue(batch) if time.time() > log_time + 10: logger.debug(self.log_queue_info()) diff --git a/tests/batch_handlers/test_bh_general.py b/tests/batch_handlers/test_bh_general.py index b0842b2f6d..91f064ca5f 100644 --- a/tests/batch_handlers/test_bh_general.py +++ b/tests/batch_handlers/test_bh_general.py @@ -2,11 +2,8 @@ import copy -import dask.array as da import numpy as np -import pandas as pd import pytest -import xarray as xr from scipy.ndimage import gaussian_filter from sup3r.preprocessing import ( @@ -33,13 +30,12 @@ BatchHandlerTester = BatchHandlerTesterFactory(BatchHandler, SamplerTester) -def test_batch_handler_workers(): - """Check that it is faster to get batches with max_workers > 1 than with - max_workers = 1.""" +def test_batch_sampling_workers(): + """Check that it is faster to sample batches with max_workers > 1 than with + max_workers = 1. This does not include enqueueing and dequeueing.""" timer = Timer() - n_lats = 200 - n_lons = 200 + ds = DummyData((200, 200, 2000), ['u_100m', 'v_100m']) sample_shape = (20, 20, 30) chunk_shape = ( 2 * sample_shape[0], @@ -51,23 +47,65 @@ def test_batch_handler_workers(): n_batches = 10 n_epochs = 3 - lons, lats = np.meshgrid( - np.linspace(0, 180, n_lats), np.linspace(40, 60, n_lons) + ds = ds.chunk(dict(zip(['south_north', 'west_east', 'time'], chunk_shape))) + + batcher = BatchHandler( + [ds], + n_batches=n_batches, + batch_size=n_obs, + sample_shape=sample_shape, + max_workers=max_workers, + means={'u_100m': 0, 'v_100m': 0}, + stds={'u_100m': 1, 'v_100m': 1}, + ) + timer.start() + for _ in range(n_epochs): + batches = batcher.sample_batches(n_batches) + _ = [batch.result() for batch in batches] + timer.stop() + parallel_time = timer.elapsed / (n_batches * n_epochs) + batcher.stop() + + batcher = BatchHandler( + [ds], + n_batches=n_batches, + batch_size=n_obs, + sample_shape=sample_shape, + max_workers=1, + means={'u_100m': 0, 'v_100m': 0}, + stds={'u_100m': 1, 'v_100m': 1}, + ) + timer.start() + for _ in range(n_epochs): + _ = batcher.sample_batches(n_batches) + timer.stop() + serial_time = timer.elapsed / (n_batches * n_epochs) + batcher.stop() + + print( + 'Elapsed (serial / parallel): {} / {}'.format( + serial_time, parallel_time + ) ) - time = pd.date_range('2023-01-01', '2023-05-01', freq='h') - u_arr = da.random.random((*lats.shape, len(time))).astype('float32') - v_arr = da.random.random((*lats.shape, len(time))).astype('float32') - ds = xr.Dataset( - coords={ - 'latitude': (('south_north', 'west_east'), lats), - 'longitude': (('south_north', 'west_east'), lons), - 'time': time, - }, - data_vars={ - 'u_100m': (('south_north', 'west_east', 'time'), u_arr), - 'v_100m': (('south_north', 'west_east', 'time'), v_arr), - }, + assert serial_time > parallel_time + + +def test_batch_queue_workers(): + """Check that it is faster to queue batches with max_workers > 1 than with + max_workers = 1.""" + + timer = Timer() + ds = DummyData((200, 200, 2000), ['u_100m', 'v_100m']) + sample_shape = (20, 20, 30) + chunk_shape = ( + 2 * sample_shape[0], + 2 * sample_shape[1], + 2 * sample_shape[-1], ) + n_obs = 10 + max_workers = 10 + n_batches = 10 + n_epochs = 3 ds = ds.chunk(dict(zip(['south_north', 'west_east', 'time'], chunk_shape))) batcher = BatchHandler( diff --git a/tests/training/test_train_gan.py b/tests/training/test_train_gan.py index ddd5610f05..9894b1cd59 100644 --- a/tests/training/test_train_gan.py +++ b/tests/training/test_train_gan.py @@ -43,7 +43,6 @@ def _get_handlers(): ['fp_gen', 'fp_disc', 's_enhance', 't_enhance', 'sample_shape'], [ (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16)), - (pytest.ST_FP_GEN, pytest.ST_FP_DISC_PROD, 3, 4, (12, 12, 16)), (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (10, 10, 1)), ], ) @@ -240,7 +239,7 @@ def test_train(fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, n_epoch=8): batch_handler.stop() -def test_train_workers(n_epoch=20): +def test_train_workers(n_epoch=10): """Test that model training with max_workers > 1 for the batch queue is faster than for max_workers = 1.""" @@ -259,7 +258,6 @@ def test_train_workers(n_epoch=20): ) with tempfile.TemporaryDirectory() as td: - batch_handler = BatchHandler( train_containers=[train_handler], val_containers=[val_handler], @@ -323,7 +321,7 @@ def test_train_st_weight_update(n_epoch=2): pytest.ST_FP_GEN, pytest.ST_FP_DISC, learning_rate=1e-4, - learning_rate_disc=4e-4 + learning_rate_disc=4e-4, ) train_handler, val_handler = _get_handlers() From 81bad5cdc6c8cdd3dca66a250903efb9d8c35b17 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 2 Jan 2025 12:00:18 -0700 Subject: [PATCH 023/122] removed workers tests. max_workers > 1 still not consistently faster. just sampling is, except for macos, but training is not. --- sup3r/preprocessing/batch_queues/abstract.py | 2 +- tests/batch_handlers/test_bh_general.py | 168 ++++++++----------- tests/training/test_train_gan.py | 74 -------- 3 files changed, 67 insertions(+), 177 deletions(-) diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index e98664e83a..fc3a932a9c 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -239,7 +239,7 @@ def __iter__(self): def get_batch(self) -> DsetTuple: """Get batch from queue or directly from a ``Sampler`` through ``sample_batch``.""" - if self.mode == 'eager' or self.queue_cap == 0: + if self.mode == 'eager' or self.queue_cap == 0 or self.queue_len == 0: return self.sample_batch() return self.queue.dequeue() diff --git a/tests/batch_handlers/test_bh_general.py b/tests/batch_handlers/test_bh_general.py index 91f064ca5f..9c7270a616 100644 --- a/tests/batch_handlers/test_bh_general.py +++ b/tests/batch_handlers/test_bh_general.py @@ -1,14 +1,14 @@ """Smoke tests for batcher objects. Just make sure things run without errors""" - import copy +import os +import time +from tempfile import TemporaryDirectory import numpy as np import pytest from scipy.ndimage import gaussian_filter -from sup3r.preprocessing import ( - BatchHandler, -) +from sup3r.preprocessing import BatchHandler, DataHandler from sup3r.preprocessing.base import Container from sup3r.utilities.pytest.helpers import ( BatchHandlerTesterFactory, @@ -35,8 +35,7 @@ def test_batch_sampling_workers(): max_workers = 1. This does not include enqueueing and dequeueing.""" timer = Timer() - ds = DummyData((200, 200, 2000), ['u_100m', 'v_100m']) - sample_shape = (20, 20, 30) + sample_shape = (100, 100, 30) chunk_shape = ( 2 * sample_shape[0], 2 * sample_shape[1], @@ -44,108 +43,73 @@ def test_batch_sampling_workers(): ) n_obs = 10 max_workers = 10 - n_batches = 10 + n_batches = 50 n_epochs = 3 + chunks = dict(zip(['south_north', 'west_east', 'time'], chunk_shape)) - ds = ds.chunk(dict(zip(['south_north', 'west_east', 'time'], chunk_shape))) - - batcher = BatchHandler( - [ds], - n_batches=n_batches, - batch_size=n_obs, - sample_shape=sample_shape, - max_workers=max_workers, - means={'u_100m': 0, 'v_100m': 0}, - stds={'u_100m': 1, 'v_100m': 1}, - ) - timer.start() - for _ in range(n_epochs): - batches = batcher.sample_batches(n_batches) - _ = [batch.result() for batch in batches] - timer.stop() - parallel_time = timer.elapsed / (n_batches * n_epochs) - batcher.stop() - - batcher = BatchHandler( - [ds], - n_batches=n_batches, - batch_size=n_obs, - sample_shape=sample_shape, - max_workers=1, - means={'u_100m': 0, 'v_100m': 0}, - stds={'u_100m': 1, 'v_100m': 1}, - ) - timer.start() - for _ in range(n_epochs): - _ = batcher.sample_batches(n_batches) - timer.stop() - serial_time = timer.elapsed / (n_batches * n_epochs) - batcher.stop() + with TemporaryDirectory() as td: + ds = DummyData((200, 200, 2000), ['u_100m', 'v_100m']) + ds.to_netcdf(os.path.join(td, 'test.nc')) + ds = DataHandler(os.path.join(td, 'test.nc'), chunks=chunks) - print( - 'Elapsed (serial / parallel): {} / {}'.format( - serial_time, parallel_time + batcher = BatchHandler( + [ds], + n_batches=n_batches, + batch_size=n_obs, + sample_shape=sample_shape, + max_workers=max_workers, + means={'u_100m': 0, 'v_100m': 0}, + stds={'u_100m': 1, 'v_100m': 1}, ) - ) - assert serial_time > parallel_time - - -def test_batch_queue_workers(): - """Check that it is faster to queue batches with max_workers > 1 than with - max_workers = 1.""" + timer.start() + queue_time = 0 + for _ in range(n_epochs): + batches = batcher.sample_batches(n_batches) + batches = [batch.result() for batch in batches] + queue_start = time.time() + for batch in batches: + batcher.queue.enqueue(batch) + _ = batcher.queue.dequeue() + queue_time += (time.time() - queue_start) + timer.stop() + parallel_time = timer.elapsed / (n_batches * n_epochs) + parallel_queue_time = queue_time / (n_batches * n_epochs) + batcher.stop() - timer = Timer() - ds = DummyData((200, 200, 2000), ['u_100m', 'v_100m']) - sample_shape = (20, 20, 30) - chunk_shape = ( - 2 * sample_shape[0], - 2 * sample_shape[1], - 2 * sample_shape[-1], - ) - n_obs = 10 - max_workers = 10 - n_batches = 10 - n_epochs = 3 - ds = ds.chunk(dict(zip(['south_north', 'west_east', 'time'], chunk_shape))) - - batcher = BatchHandler( - [ds], - n_batches=n_batches, - batch_size=n_obs, - sample_shape=sample_shape, - max_workers=max_workers, - means={'u_100m': 0, 'v_100m': 0}, - stds={'u_100m': 1, 'v_100m': 1}, - ) - timer.start() - for _ in range(n_epochs): - _ = list(batcher) - timer.stop() - parallel_time = timer.elapsed / (n_batches * n_epochs) - batcher.stop() - - batcher = BatchHandler( - [ds], - n_batches=n_batches, - batch_size=n_obs, - sample_shape=sample_shape, - max_workers=1, - means={'u_100m': 0, 'v_100m': 0}, - stds={'u_100m': 1, 'v_100m': 1}, - ) - timer.start() - for _ in range(n_epochs): - _ = list(batcher) - timer.stop() - serial_time = timer.elapsed / (n_batches * n_epochs) - batcher.stop() - - print( - 'Elapsed (serial / parallel): {} / {}'.format( - serial_time, parallel_time + batcher = BatchHandler( + [ds], + n_batches=n_batches, + batch_size=n_obs, + sample_shape=sample_shape, + max_workers=1, + means={'u_100m': 0, 'v_100m': 0}, + stds={'u_100m': 1, 'v_100m': 1}, ) - ) - assert serial_time > parallel_time + timer.start() + queue_time = 0 + for _ in range(n_epochs): + batches = batcher.sample_batches(n_batches) + queue_start = time.time() + for batch in batches: + batcher.queue.enqueue(batch) + _ = batcher.queue.dequeue() + queue_time += time.time() - queue_start + timer.stop() + serial_time = timer.elapsed / (n_batches * n_epochs) + serial_queue_time = queue_time / (n_batches * n_epochs) + batcher.stop() + + print( + 'Elapsed total time (serial / parallel): {} / {}'.format( + serial_time, parallel_time + ) + ) + print( + 'Elapsed queue time (serial / parallel): {} / {}'.format( + serial_queue_time, parallel_queue_time + ) + ) + assert serial_time > parallel_time def test_eager_vs_lazy(): diff --git a/tests/training/test_train_gan.py b/tests/training/test_train_gan.py index 9894b1cd59..ce0860b3cf 100644 --- a/tests/training/test_train_gan.py +++ b/tests/training/test_train_gan.py @@ -11,7 +11,6 @@ from sup3r.models import Sup3rGan from sup3r.preprocessing import BatchHandler, DataHandler -from sup3r.utilities.utilities import Timer TARGET_COORD = (39.01, -105.15) FEATURES = ['u_100m', 'v_100m'] @@ -239,79 +238,6 @@ def test_train(fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, n_epoch=8): batch_handler.stop() -def test_train_workers(n_epoch=10): - """Test that model training with max_workers > 1 for the batch queue is - faster than for max_workers = 1.""" - - lr = 5e-5 - train_handler, val_handler = _get_handlers() - timer = Timer() - n_batches = 40 - batch_size = 40 - - Sup3rGan.seed() - model = Sup3rGan( - pytest.S_FP_GEN, - pytest.S_FP_DISC, - learning_rate=lr, - loss='MeanAbsoluteError', - ) - - with tempfile.TemporaryDirectory() as td: - batch_handler = BatchHandler( - train_containers=[train_handler], - val_containers=[val_handler], - sample_shape=(10, 10, 1), - batch_size=batch_size, - s_enhance=2, - t_enhance=1, - n_batches=n_batches, - means={'u_100m': 0, 'v_100m': 0}, - stds={'u_100m': 1, 'v_100m': 1}, - max_workers=5, - ) - - model_kwargs = { - 'input_resolution': {'spatial': '30km', 'temporal': '60min'}, - 'n_epoch': n_epoch, - 'weight_gen_advers': 0.0, - 'train_gen': True, - 'train_disc': False, - 'checkpoint_int': 10, - 'out_dir': os.path.join(td, 'test_{epoch}'), - } - - timer.start() - model.train(batch_handler, **model_kwargs) - timer.stop() - parallel_time = timer.elapsed / n_epoch - - batch_handler = BatchHandler( - train_containers=[train_handler], - val_containers=[val_handler], - sample_shape=(10, 10, 1), - batch_size=batch_size, - s_enhance=2, - t_enhance=1, - n_batches=n_batches, - means={'u_100m': 0, 'v_100m': 0}, - stds={'u_100m': 1, 'v_100m': 1}, - max_workers=1, - ) - - timer.start() - model.train(batch_handler, **model_kwargs) - timer.stop() - serial_time = timer.elapsed / n_epoch - - print( - 'Elapsed (parallel / serial): {} / {}'.format( - parallel_time, serial_time - ) - ) - assert parallel_time < serial_time - - def test_train_st_weight_update(n_epoch=2): """Test basic spatiotemporal model training with discriminators and adversarial loss updating.""" From ed2cc21672a22611f0afa1e1244e02f5eb6c05c3 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 3 Jan 2025 15:42:25 -0700 Subject: [PATCH 024/122] ``Sup3rGanWithObs`` model subclass. Other misc model refactoring. --- sup3r/models/__init__.py | 1 + sup3r/models/abstract.py | 124 +++----- sup3r/models/base.py | 26 +- sup3r/models/interface.py | 27 -- sup3r/models/with_obs.py | 352 +++++++++++++++++++++ tests/training/test_train_dual_with_obs.py | 10 +- 6 files changed, 417 insertions(+), 123 deletions(-) create mode 100644 sup3r/models/with_obs.py diff --git a/sup3r/models/__init__.py b/sup3r/models/__init__.py index 5d6b51344c..20fff799fb 100644 --- a/sup3r/models/__init__.py +++ b/sup3r/models/__init__.py @@ -6,6 +6,7 @@ from .multi_step import MultiStepGan, MultiStepSurfaceMetGan, SolarMultiStepGan from .solar_cc import SolarCC from .surface import SurfaceSpatialMetModel +from .with_obs import Sup3rGanWithObs SPATIAL_FIRST_MODELS = (MultiStepSurfaceMetGan, SolarMultiStepGan) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 0f5752042e..6841a72aee 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -18,13 +18,12 @@ from phygnn.layers.custom_layers import Sup3rAdder, Sup3rConcat from rex.utilities.utilities import safe_json_load from tensorflow.keras import optimizers -from tensorflow.keras.losses import MeanAbsoluteError import sup3r.utilities.loss_metrics from sup3r.preprocessing.data_handlers import ExoData from sup3r.preprocessing.utilities import numpy_if_tensor from sup3r.utilities import VERSION_RECORD -from sup3r.utilities.utilities import safe_cast +from sup3r.utilities.utilities import camel_to_underscore, safe_cast from .utilities import TensorboardMixIn @@ -783,25 +782,42 @@ def finish_epoch( return stop + def _sum_parallel_grad(self, futures, start_time): + """Sum gradient descent future results""" + + # sum the gradients from each gpu to weight equally in + # optimizer momentum calculation + total_grad = None + for future in futures: + grad, loss_details = future.result() + if total_grad is None: + total_grad = grad + else: + for i, igrad in enumerate(grad): + total_grad[i] += igrad + + msg = ( + f'Finished {len(futures)} gradient descent steps on ' + f'{len(self.gpu_list)} GPUs in {time.time() - start_time:.4f} ' + 'seconds' + ) + logger.info(msg) + return total_grad, loss_details + def _get_parallel_grad( self, low_res, hi_res_true, training_weights, - obs_data=None, **calc_loss_kwargs, ): """Compute gradient for one mini-batch of (low_res, hi_res_true) across multiple GPUs""" futures = [] + start_time = time.time() lr_chunks = np.array_split(low_res, len(self.gpu_list)) hr_true_chunks = np.array_split(hi_res_true, len(self.gpu_list)) - obs_data_chunks = ( - [None] * len(hr_true_chunks) - if obs_data is None - else np.array_split(obs_data, len(self.gpu_list)) - ) split_mask = False mask_chunks = None if 'mask' in calc_loss_kwargs: @@ -820,38 +836,17 @@ def _get_parallel_grad( lr_chunks[i], hr_true_chunks[i], training_weights, - obs_data=obs_data_chunks[i], device_name=f'/gpu:{i}', **calc_loss_kwargs, ) ) - - # sum the gradients from each gpu to weight equally in - # optimizer momentum calculation - total_grad = None - for future in futures: - grad, loss_details = future.result() - if total_grad is None: - total_grad = grad - else: - for i, igrad in enumerate(grad): - total_grad[i] += igrad - - self.timer.stop() - logger.debug( - 'Finished %s gradient descent steps on %s GPUs in %s', - len(futures), - len(self.gpu_list), - self.timer.elapsed_str, - ) - return total_grad, loss_details + return self._sum_parallel_grad(futures, start_time=start_time) def run_gradient_descent( self, low_res, hi_res_true, training_weights, - obs_data=None, optimizer=None, multi_gpu=False, **calc_loss_kwargs, @@ -872,10 +867,6 @@ def run_gradient_descent( training_weights : list A list of layer weights that are to-be-trained based on the current loss weight values. - obs_data : tf.Tensor | None - Optional observation data to use in additional content loss term. - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) optimizer : tf.keras.optimizers.Optimizer Optimizer class to use to update weights. This can be different if you're training just the generator or one of the discriminator @@ -895,32 +886,27 @@ def run_gradient_descent( loss_details : dict Namespace of the breakdown of loss components """ - - self.timer.start() if optimizer is None: optimizer = self.optimizer if not multi_gpu or len(self.gpu_list) < 2: + start_time = time.time() grad, loss_details = self.get_single_grad( low_res, hi_res_true, training_weights, - obs_data=obs_data, device_name=self.default_device, **calc_loss_kwargs, ) optimizer.apply_gradients(zip(grad, training_weights)) - self.timer.stop() - logger.debug( - 'Finished single gradient descent step in %s', - self.timer.elapsed_str, - ) + msg = ('Finished single gradient descent step in ' + f'{time.time() - start_time:.4f} seconds') + logger.debug(msg) else: total_grad, loss_details = self._get_parallel_grad( low_res, hi_res_true, training_weights, - obs_data, **calc_loss_kwargs, ) optimizer.apply_gradients(zip(total_grad, training_weights)) @@ -1116,13 +1102,25 @@ def _tf_generate(self, low_res, hi_res_exo=None): return hi_res + def _get_hr_exo_and_loss( + self, + low_res, + hi_res_true, + **calc_loss_kwargs, + ): + """Get high-resolution exogenous data, generate synthetic output, and + compute loss.""" + hi_res_exo = self.get_high_res_exo_input(hi_res_true) + hi_res_gen = self._tf_generate(low_res, hi_res_exo) + loss_out = self.calc_loss(hi_res_true, hi_res_gen, **calc_loss_kwargs) + return *loss_out, hi_res_gen + @tf.function def get_single_grad( self, low_res, hi_res_true, training_weights, - obs_data=None, device_name=None, **calc_loss_kwargs, ): @@ -1142,10 +1140,6 @@ def get_single_grad( training_weights : list A list of layer weights that are to-be-trained based on the current loss weight values. - obs_data : tf.Tensor | None - Optional observation data to use in additional content loss term. - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) device_name : None | str Optional tensorflow device name for GPU placement. Note that if a GPU is available, variables will be placed on that GPU even if @@ -1166,16 +1160,10 @@ def get_single_grad( watch_accessed_variables=False ) as tape: tape.watch(training_weights) - hi_res_exo = self.get_hr_exo_input(hi_res_true) - hi_res_gen = self._tf_generate(low_res, hi_res_exo) - loss_out = self.calc_loss( - hi_res_true, hi_res_gen, **calc_loss_kwargs + *loss_out, _ = self._get_hr_exo_and_loss( + low_res, hi_res_true, **calc_loss_kwargs ) loss, loss_details = loss_out - if obs_data is not None: - loss_obs = self.calc_loss_obs(obs_data, hi_res_gen) - loss += loss_obs - loss_details['loss_obs'] = loss_obs grad = tape.gradient(loss, training_weights) return grad, loss_details @@ -1190,27 +1178,3 @@ def calc_loss( ): """Calculate the GAN loss function using generated and true high resolution data.""" - - @tf.function - def calc_loss_obs(self, obs_data, hi_res_gen): - """Calculate loss term for the observation data vs generated - high-resolution data - - Parameters - ---------- - obs_data : tf.Tensor | None - Optional observation data to use in additional content loss term. - hi_res_gen : tf.Tensor - Superresolved high resolution spatiotemporal data generated by the - generative model. - - Returns - ------- - loss : tf.Tensor - 0D tensor of observation loss - """ - mask = tf.math.is_nan(obs_data) - return MeanAbsoluteError()( - obs_data[~mask], - hi_res_gen[..., : len(self.hr_out_features)][~mask], - ) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 683f42304d..08953a8851 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -750,13 +750,13 @@ def train( ) for epoch in epochs: + t_epoch = time.time() loss_details = self._train_epoch( batch_handler, weight_gen_advers, train_gen, train_disc, disc_loss_bounds, - loss_mean_window=loss_mean_window, multi_gpu=multi_gpu, ) loss_details.update( @@ -810,12 +810,18 @@ def train( early_stop_n_epoch, extras=extras, ) + logger.info( + 'Finished training epoch in {:.4f} seconds'.format( + time.time() - t_epoch + ) + ) if stop: break logger.info( - 'Finished training %s epochs in %s seconds', - n_epoch, - time.time() - t0, + 'Finished training {} epochs in {:.4f} seconds'.format( + n_epoch, + time.time() - t0, + ) ) batch_handler.stop() @@ -921,10 +927,10 @@ def calc_val_loss(self, batch_handler, weight_gen_advers): """ logger.debug('Starting end-of-epoch validation loss calculation...') for batch in batch_handler.val_data: - hi_res_exo = self.get_hr_exo_input(batch.high_res) - hi_res_gen = self._tf_generate(batch.low_res, hi_res_exo) - _, v_loss_details = self.calc_loss( - batch.high_res, hi_res_gen, weight_gen_advers=weight_gen_advers + _, v_loss_details, _, _ = self._get_hr_exo_and_loss( + batch.low_res, + batch.high_res, + weight_gen_advers=weight_gen_advers ) self._val_record = self.update_loss_details( self._val_record, @@ -1022,9 +1028,7 @@ def _train_batch( loss_details['disc_train_frac'] = float(trained_disc) return loss_details - def _post_batch( - self, ib, b_loss_details, n_batches, previous_means - ): + def _post_batch(self, ib, b_loss_details, n_batches, previous_means): """Update loss details after the current batch and write to log. Parameters diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index c81065f86f..e284cc21ac 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -9,7 +9,6 @@ from warnings import warn import numpy as np -import tensorflow as tf from phygnn import CustomNetwork from phygnn.layers.custom_layers import Sup3rAdder, Sup3rConcat @@ -355,32 +354,6 @@ def _combine_fwp_output(self, hi_res, exogenous_data=None): hi_res = np.concatenate((hi_res, exo_output), axis=-1) return hi_res - @tf.function - def _combine_loss_input(self, high_res_true, high_res_gen): - """Combine exogenous feature data from high_res_true with high_res_gen - for loss calculation - - Parameters - ---------- - high_res_true : tf.Tensor - Ground truth high resolution spatiotemporal data. - high_res_gen : tf.Tensor - Superresolved high resolution spatiotemporal data generated by the - generative model. - - Returns - ------- - high_res_gen : tf.Tensor - Same as input with exogenous data combined with high_res input - """ - if high_res_true.shape[-1] > high_res_gen.shape[-1]: - for feature in self.hr_exo_features: - f_idx = self.hr_exo_features.index(feature) - f_idx += len(self.hr_out_features) - exo_data = high_res_true[..., f_idx : f_idx + 1] - high_res_gen = tf.concat((high_res_gen, exo_data), axis=-1) - return high_res_gen - @property @abstractmethod def meta(self): diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py new file mode 100644 index 0000000000..e8ce3e417c --- /dev/null +++ b/sup3r/models/with_obs.py @@ -0,0 +1,352 @@ +"""Sup3r model with training on observation data.""" + +import logging +import time +from concurrent.futures import ThreadPoolExecutor + +import numpy as np +import tensorflow as tf +from tensorflow.keras.losses import MeanAbsoluteError + +from .base import Sup3rGan + +logger = logging.getLogger(__name__) + + +class Sup3rGanWithObs(Sup3rGan): + """Sup3r GAN model which incorporates observation data into content loss. + """ + + def _calc_val_loss(self, batch, weight_gen_advers, loss_details): + """Calculate the validation loss at the current state of model training + for a given batch + + Parameters + ---------- + batch : DsetTuple + Object with ``.high_res``, ``.low_res``, and ``.obs`` arrays + weight_gen_advers : float + Weight factor for the adversarial loss component of the generator + vs. the discriminator. + loss_details : dict + Namespace of the breakdown of loss components + + Returns + ------- + loss_details : dict + Same as input with updated val_* loss info + """ + val_exo_data = self.get_high_res_exo_input(batch.high_res) + high_res_gen = self._tf_generate(batch.low_res, val_exo_data) + _, v_loss_details = self.calc_loss( + batch.high_res, + high_res_gen, + weight_gen_advers=weight_gen_advers, + train_gen=False, + train_disc=False, + ) + v_loss_details['loss_obs'] = self.cal_loss_obs(batch.obs, high_res_gen) + + loss_details = self.update_loss_details( + loss_details, v_loss_details, len(batch), prefix='val_' + ) + return loss_details + + def _get_batch_loss_details( + self, + batch, + train_gen, + only_gen, + gen_too_good, + train_disc, + only_disc, + disc_too_good, + weight_gen_advers, + multi_gpu=False, + ): + """Get loss details for a given batch for the current epoch. + + Parameters + ---------- + batch : sup3r.preprocessing.base.DsetTuple + Object with ``.low_res``, ``.high_res``, and ``.obs`` arrays + train_gen : bool + Flag whether to train the generator for this set of epochs + only_gen : bool + Flag whether to only train the generator for this set of epochs + gen_too_good : bool + Flag whether to skip training the generator and only train the + discriminator, due to superior performance, for this batch. + train_disc : bool + Flag whether to train the discriminator for this set of epochs + only_disc : bool + Flag whether to only train the discriminator for this set of epochs + gen_too_good : bool + Flag whether to skip training the discriminator and only train the + generator, due to superior performance, for this batch. + weight_gen_advers : float + Weight factor for the adversarial loss component of the generator + vs. the discriminator. + multi_gpu : bool + Flag to break up the batch for parallel gradient descent + calculations on multiple gpus. If True and multiple GPUs are + present, each batch from the batch_handler will be divided up + between the GPUs and resulting gradients from each GPU will be + summed and then applied once per batch at the nominal learning + rate that the model and optimizer were initialized with. + If true and multiple gpus are found, ``default_device`` device + should be set to /gpu:0 + + Returns + ------- + loss_details : dict + Namespace of the breakdown of loss components for the given batch + """ + trained_gen = False + trained_disc = False + if only_gen or (train_gen and not gen_too_good): + trained_gen = True + b_loss_details = self.timer(self.run_gradient_descent)( + batch.low_res, + batch.high_res, + self.generator_weights, + obs_data=getattr(batch, 'obs', None), + weight_gen_advers=weight_gen_advers, + optimizer=self.optimizer, + train_gen=True, + train_disc=False, + multi_gpu=multi_gpu, + ) + + if only_disc or (train_disc and not disc_too_good): + trained_disc = True + b_loss_details = self.timer(self.run_gradient_descent)( + batch.low_res, + batch.high_res, + self.discriminator_weights, + weight_gen_advers=weight_gen_advers, + optimizer=self.optimizer_disc, + train_gen=False, + train_disc=True, + multi_gpu=multi_gpu, + ) + + b_loss_details['gen_trained_frac'] = float(trained_gen) + b_loss_details['disc_trained_frac'] = float(trained_disc) + return b_loss_details + + def _get_parallel_grad( + self, + low_res, + hi_res_true, + training_weights, + obs_data=None, + **calc_loss_kwargs, + ): + """Compute gradient for one mini-batch of (low_res, hi_res_true, + obs_data) across multiple GPUs. Can include observation data as well. + """ + + futures = [] + start_time = time.time() + lr_chunks = np.array_split(low_res, len(self.gpu_list)) + hr_true_chunks = np.array_split(hi_res_true, len(self.gpu_list)) + obs_data_chunks = ( + [None] * len(hr_true_chunks) + if obs_data is None + else np.array_split(obs_data, len(self.gpu_list)) + ) + split_mask = False + mask_chunks = None + if 'mask' in calc_loss_kwargs: + split_mask = True + mask_chunks = np.array_split( + calc_loss_kwargs['mask'], len(self.gpu_list) + ) + + with ThreadPoolExecutor(max_workers=len(self.gpu_list)) as exe: + for i in range(len(self.gpu_list)): + if split_mask: + calc_loss_kwargs['mask'] = mask_chunks[i] + futures.append( + exe.submit( + self.get_single_grad, + lr_chunks[i], + hr_true_chunks[i], + training_weights, + obs_data=obs_data_chunks[i], + device_name=f'/gpu:{i}', + **calc_loss_kwargs, + ) + ) + + return self._sum_parallel_grad(futures, start_time=start_time) + + def run_gradient_descent( + self, + low_res, + hi_res_true, + training_weights, + obs_data=None, + optimizer=None, + multi_gpu=False, + **calc_loss_kwargs, + ): + """Run gradient descent for one mini-batch of (low_res, hi_res_true) + and update weights + + Parameters + ---------- + low_res : np.ndarray + Real low-resolution data in a 4D or 5D array: + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + hi_res_true : np.ndarray + Real high-resolution data in a 4D or 5D array: + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + training_weights : list + A list of layer weights that are to-be-trained based on the + current loss weight values. + obs_data : tf.Tensor | None + Optional observation data to use in additional content loss term. + This needs to have NaNs where there is no observation data. + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + optimizer : tf.keras.optimizers.Optimizer + Optimizer class to use to update weights. This can be different if + you're training just the generator or one of the discriminator + models. Defaults to the generator optimizer. + multi_gpu : bool + Flag to break up the batch for parallel gradient descent + calculations on multiple gpus. If True and multiple GPUs are + present, each batch from the batch_handler will be divided up + between the GPUs and resulting gradients from each GPU will be + summed and then applied once per batch at the nominal learning + rate that the model and optimizer were initialized with. + calc_loss_kwargs : dict + Kwargs to pass to the self.calc_loss() method + + Returns + ------- + loss_details : dict + Namespace of the breakdown of loss components + """ + + self.timer.start() + if optimizer is None: + optimizer = self.optimizer + + if not multi_gpu or len(self.gpu_list) < 2: + grad, loss_details = self.get_single_grad( + low_res, + hi_res_true, + training_weights, + obs_data=obs_data, + device_name=self.default_device, + **calc_loss_kwargs, + ) + optimizer.apply_gradients(zip(grad, training_weights)) + self.timer.stop() + logger.debug( + 'Finished single gradient descent step in %s', + self.timer.elapsed_str, + ) + else: + total_grad, loss_details = self._get_parallel_grad( + low_res, + hi_res_true, + training_weights, + obs_data, + **calc_loss_kwargs, + ) + optimizer.apply_gradients(zip(total_grad, training_weights)) + + return loss_details + + @tf.function + def get_single_grad( + self, + low_res, + hi_res_true, + training_weights, + obs_data=None, + device_name=None, + **calc_loss_kwargs, + ): + """Run gradient descent for one mini-batch of (low_res, hi_res_true), + do not update weights, just return gradient details. + + Parameters + ---------- + low_res : np.ndarray + Real low-resolution data in a 4D or 5D array: + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + hi_res_true : np.ndarray + Real high-resolution data in a 4D or 5D array: + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + training_weights : list + A list of layer weights that are to-be-trained based on the + current loss weight values. + obs_data : tf.Tensor | None + Optional observation data to use in additional content loss term. + This needs to have NaNs where there is no observation data. + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + device_name : None | str + Optional tensorflow device name for GPU placement. Note that if a + GPU is available, variables will be placed on that GPU even if + device_name=None. + calc_loss_kwargs : dict + Kwargs to pass to the self.calc_loss() method + + Returns + ------- + grad : list + a list or nested structure of Tensors (or IndexedSlices, or None, + or CompositeTensor) representing the gradients for the + training_weights + loss_details : dict + Namespace of the breakdown of loss components + """ + with tf.device(device_name), tf.GradientTape( + watch_accessed_variables=False + ) as tape: + tape.watch(training_weights) + *loss_out, hi_res_gen = self._get_hr_exo_and_loss( + low_res, hi_res_true, **calc_loss_kwargs + ) + loss, loss_details = loss_out + if obs_data is not None: + loss_obs = self.calc_loss_obs(obs_data, hi_res_gen) + loss += loss_obs + loss_details['loss_obs'] = loss_obs + grad = tape.gradient(loss, training_weights) + return grad, loss_details + + @tf.function + def calc_loss_obs(self, obs_data, hi_res_gen): + """Calculate loss term for the observation data vs generated + high-resolution data + + Parameters + ---------- + obs_data : tf.Tensor | None + Observation data to use in additional content loss term. + This needs to have NaNs where there is no observation data. + hi_res_gen : tf.Tensor + Superresolved high resolution spatiotemporal data generated by the + generative model. + + Returns + ------- + loss : tf.Tensor + 0D tensor of observation loss + """ + mask = tf.math.is_nan(obs_data) + return MeanAbsoluteError()( + obs_data[~mask], + hi_res_gen[..., : len(self.hr_out_features)][~mask], + ) diff --git a/tests/training/test_train_dual_with_obs.py b/tests/training/test_train_dual_with_obs.py index 0bf8244eec..2399b21ba0 100644 --- a/tests/training/test_train_dual_with_obs.py +++ b/tests/training/test_train_dual_with_obs.py @@ -7,7 +7,7 @@ import numpy as np import pytest -from sup3r.models import Sup3rGan +from sup3r.models import Sup3rGanWithObs from sup3r.preprocessing import ( Container, DataHandler, @@ -104,8 +104,8 @@ def test_train_h5_nc( assert not np.isnan(batch.obs).all() assert np.isnan(batch.obs).any() - Sup3rGan.seed() - model = Sup3rGan( + Sup3rGanWithObs.seed() + model = Sup3rGanWithObs( fp_gen, fp_disc, learning_rate=lr, loss='MeanAbsoluteError' ) @@ -208,8 +208,8 @@ def test_train_coarse_h5( assert not np.isnan(batch.obs).all() assert np.isnan(batch.obs).any() - Sup3rGan.seed() - model = Sup3rGan( + Sup3rGanWithObs.seed() + model = Sup3rGanWithObs( fp_gen, fp_disc, learning_rate=lr, loss='MeanAbsoluteError' ) From 1ee7e3180084db2586eec1f09fee6c44fad60019 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 5 Jan 2025 09:09:29 -0700 Subject: [PATCH 025/122] moved ``_run`` method to bias correction interface ``AbstractBiasCorrection`` --- sup3r/models/utilities.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sup3r/models/utilities.py b/sup3r/models/utilities.py index 68e0f01021..dee3c51bc5 100644 --- a/sup3r/models/utilities.py +++ b/sup3r/models/utilities.py @@ -76,12 +76,11 @@ def __init__(self): @property def total_batches(self): """Record of total number of batches for logging.""" - if self._total_batches is None and self._history is None: - self._total_batches = 0 - elif self._history is None and 'total_batches' in self._history: - self._total_batches = self._history['total_batches'].values[-1] - elif self._total_batches is None and self._history is not None: - self._total_batches = 0 + if self._total_batches is None: + if self._history is not None and 'total_batches' in self._history: + self._total_batches = self._history['total_batches'].values[-1] + else: + self._total_batches = 0 return self._total_batches @total_batches.setter From e353ebc26957f11f66f28ed4a6f0b33bcba93de9 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 5 Jan 2025 16:28:37 -0700 Subject: [PATCH 026/122] moved ``_run`` method to bias correction interface ``AbstractBiasCorrection`` --- sup3r/preprocessing/derivers/base.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sup3r/preprocessing/derivers/base.py b/sup3r/preprocessing/derivers/base.py index 77a5ca528a..3c2ce06750 100644 --- a/sup3r/preprocessing/derivers/base.py +++ b/sup3r/preprocessing/derivers/base.py @@ -56,7 +56,7 @@ def __init__( a method to derive the feature in the registry. interp_kwargs : dict | None Dictionary of kwargs for level interpolation. Can include "method" - and "run_level_check". "method" specifies how to perform height + and "run_level_check" keys. Method specifies how to perform height interpolation. e.g. Deriving u_20m from u_10m and u_100m. Options are "linear" and "log". See :py:meth:`sup3r.preprocessing.derivers.Deriver.do_level_interpolation` @@ -65,7 +65,7 @@ def __init__( self.FEATURE_REGISTRY = FeatureRegistry super().__init__(data=data) - self.interp_kwargs = interp_kwargs or {} + self.interp_kwargs = interp_kwargs features = parse_to_list(data=data, features=features) new_features = [f for f in features if f not in self.data] for f in new_features: @@ -269,6 +269,7 @@ def get_single_level_data(self, feature): var_array = da.stack(var_list, axis=-1) sl_shape = (*var_array.shape[:-1], len(lev_list)) lev_array = da.broadcast_to(da.from_array(lev_list), sl_shape) + return var_array, lev_array def get_multi_level_data(self, feature): @@ -295,8 +296,8 @@ def get_multi_level_data(self, feature): assert can_calc_height or have_height, msg if can_calc_height: - lev_array = self.data['zg'] - self.data['topography'] - lev_array = lev_array.data + lev_array = self.data[['zg', 'topography']].as_array() + lev_array = lev_array[..., 0] - lev_array[..., 1] else: lev_array = da.broadcast_to( self.data[Dimension.HEIGHT].astype(np.float32), From 707a62432b4ddb27e318a11994aae600cea2df52 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 8 Jan 2025 11:10:40 -0700 Subject: [PATCH 027/122] fix: tensorboard issue with loss obs details --- sup3r/models/with_obs.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index e8ce3e417c..e15032e18a 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -319,10 +319,10 @@ def get_single_grad( low_res, hi_res_true, **calc_loss_kwargs ) loss, loss_details = loss_out - if obs_data is not None: - loss_obs = self.calc_loss_obs(obs_data, hi_res_gen) + loss_obs = self.calc_loss_obs(obs_data, hi_res_gen) + if not tf.reduce_any(tf.math.is_nan(loss_obs)): loss += loss_obs - loss_details['loss_obs'] = loss_obs + loss_details.update({'loss_obs': loss_obs}) grad = tape.gradient(loss, training_weights) return grad, loss_details @@ -345,8 +345,13 @@ def calc_loss_obs(self, obs_data, hi_res_gen): loss : tf.Tensor 0D tensor of observation loss """ - mask = tf.math.is_nan(obs_data) - return MeanAbsoluteError()( - obs_data[~mask], - hi_res_gen[..., : len(self.hr_out_features)][~mask], - ) + obs_loss = tf.constant(np.nan) + if obs_data is not None: + mask = tf.math.is_nan(obs_data) + masked_obs = obs_data[~mask] + if len(masked_obs) > 0: + obs_loss = MeanAbsoluteError()( + masked_obs, + hi_res_gen[..., : len(self.hr_out_features)][~mask], + ) + return obs_loss From 2816b8ad933f196b98fe383e35185dc1caf4c71d Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 19 Jan 2025 09:54:23 -0700 Subject: [PATCH 028/122] Adding obs loss to logging of loss gen --- sup3r/models/abstract.py | 15 +++++++++------ sup3r/models/with_obs.py | 17 ++++++++++------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 6841a72aee..922869d873 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -899,8 +899,10 @@ def run_gradient_descent( **calc_loss_kwargs, ) optimizer.apply_gradients(zip(grad, training_weights)) - msg = ('Finished single gradient descent step in ' - f'{time.time() - start_time:.4f} seconds') + msg = ( + 'Finished single gradient descent step in ' + f'{time.time() - start_time:.4f} seconds' + ) logger.debug(msg) else: total_grad, loss_details = self._get_parallel_grad( @@ -1112,8 +1114,10 @@ def _get_hr_exo_and_loss( compute loss.""" hi_res_exo = self.get_high_res_exo_input(hi_res_true) hi_res_gen = self._tf_generate(low_res, hi_res_exo) - loss_out = self.calc_loss(hi_res_true, hi_res_gen, **calc_loss_kwargs) - return *loss_out, hi_res_gen + loss, loss_details = self.calc_loss( + hi_res_true, hi_res_gen, **calc_loss_kwargs + ) + return loss, loss_details, hi_res_gen @tf.function def get_single_grad( @@ -1160,10 +1164,9 @@ def get_single_grad( watch_accessed_variables=False ) as tape: tape.watch(training_weights) - *loss_out, _ = self._get_hr_exo_and_loss( + loss, loss_details, _ = self._get_hr_exo_and_loss( low_res, hi_res_true, **calc_loss_kwargs ) - loss, loss_details = loss_out grad = tape.gradient(loss, training_weights) return grad, loss_details diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index e15032e18a..7fa3937c57 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -14,8 +14,7 @@ class Sup3rGanWithObs(Sup3rGan): - """Sup3r GAN model which incorporates observation data into content loss. - """ + """Sup3r GAN model with additional observation data content loss.""" def _calc_val_loss(self, batch, weight_gen_advers, loss_details): """Calculate the validation loss at the current state of model training @@ -133,6 +132,11 @@ def _get_batch_loss_details( b_loss_details['gen_trained_frac'] = float(trained_gen) b_loss_details['disc_trained_frac'] = float(trained_disc) + + if 'loss_obs' in b_loss_details: + loss_update = b_loss_details['loss_gen'] + loss_update += b_loss_details['loss_obs'] + b_loss_details.update({'loss_gen': loss_update}) return b_loss_details def _get_parallel_grad( @@ -315,10 +319,9 @@ def get_single_grad( watch_accessed_variables=False ) as tape: tape.watch(training_weights) - *loss_out, hi_res_gen = self._get_hr_exo_and_loss( + loss, loss_details, hi_res_gen = self._get_hr_exo_and_loss( low_res, hi_res_true, **calc_loss_kwargs ) - loss, loss_details = loss_out loss_obs = self.calc_loss_obs(obs_data, hi_res_gen) if not tf.reduce_any(tf.math.is_nan(loss_obs)): loss += loss_obs @@ -345,13 +348,13 @@ def calc_loss_obs(self, obs_data, hi_res_gen): loss : tf.Tensor 0D tensor of observation loss """ - obs_loss = tf.constant(np.nan) + loss_obs = tf.constant(np.nan) if obs_data is not None: mask = tf.math.is_nan(obs_data) masked_obs = obs_data[~mask] if len(masked_obs) > 0: - obs_loss = MeanAbsoluteError()( + loss_obs = MeanAbsoluteError()( masked_obs, hi_res_gen[..., : len(self.hr_out_features)][~mask], ) - return obs_loss + return loss_obs From 9a30089a058e0a826267f293bf2a6f7f6c24095e Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 21 Jan 2025 06:31:09 -0700 Subject: [PATCH 029/122] Adding ``loss_obs`` to ``loss_gen`` so the total loss shows in log output. --- sup3r/models/with_obs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 7fa3937c57..1e25014ecf 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -133,7 +133,9 @@ def _get_batch_loss_details( b_loss_details['gen_trained_frac'] = float(trained_gen) b_loss_details['disc_trained_frac'] = float(trained_disc) - if 'loss_obs' in b_loss_details: + if 'loss_obs' in b_loss_details and not tf.math.is_nan( + b_loss_details['loss_obs'] + ): loss_update = b_loss_details['loss_gen'] loss_update += b_loss_details['loss_obs'] b_loss_details.update({'loss_gen': loss_update}) From 1a66955bf940dac26f86d32862741b0d3eee7f81 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sat, 11 Jan 2025 11:22:34 -0700 Subject: [PATCH 030/122] generalized min pad width for padding slices so that this can accomodate models with increased receptive field and larger padding values. --- sup3r/pipeline/slicer.py | 4 +++- sup3r/pipeline/strategy.py | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/sup3r/pipeline/slicer.py b/sup3r/pipeline/slicer.py index 7bf0f1b234..dead974d23 100644 --- a/sup3r/pipeline/slicer.py +++ b/sup3r/pipeline/slicer.py @@ -702,7 +702,9 @@ def get_pad_width(self, chunk_index): check_boundary=True, ), self._get_pad_width( - ti_slice, len(self.dummy_time_index), self.temporal_pad + ti_slice, + len(self.dummy_time_index), + self.temporal_pad ), ) diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index a7a454438d..bbb172386d 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -261,6 +261,19 @@ def __post_init__(self): self.preflight() + def get_min_pad_width(self, model): + """Get the padding values applied in the first padding layer of the + model. This is used to determine the minimum width of padded slices + used to chunk the generator input.""" + pad_width = (1, 1, 1) + for layer in model._gen.layers: + if hasattr(layer, 'paddings'): + pad_width = np.max(layer.paddings, axis=1)[1:-1] + if len(pad_width) < 3: + pad_width = (*pad_width, 1) + break + return pad_width + @property def meta(self): """Meta data dictionary for the strategy. Used to add info to forward From a784290b1d63b17d5ae16ea9f94a8c473797a146 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 15 Jan 2025 09:00:58 -0700 Subject: [PATCH 031/122] min padding depends on the ``.paddings`` attribute of the ``FlexiblePadding`` layers in the generator model. Generalized current min padding to use these values. --- sup3r/pipeline/strategy.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index bbb172386d..218bc28f60 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -268,10 +268,12 @@ def get_min_pad_width(self, model): pad_width = (1, 1, 1) for layer in model._gen.layers: if hasattr(layer, 'paddings'): - pad_width = np.max(layer.paddings, axis=1)[1:-1] - if len(pad_width) < 3: - pad_width = (*pad_width, 1) - break + new_pw = np.max(layer.paddings, axis=1)[1:-1] + if len(new_pw) < 3: + new_pw = (*new_pw, 1) + pad_width = [ + np.max((new_pw[i], pad_width[i])) for i in range(3) + ] return pad_width @property From 827640a40f126324cece7c57dd6a2fb9b81e7927 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 21 Jan 2025 07:38:59 -0700 Subject: [PATCH 032/122] `max_paddings` method in `interface` instead of in `strategy.py`. --- sup3r/models/interface.py | 21 +++++++++++++++++++++ sup3r/pipeline/strategy.py | 15 --------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index e284cc21ac..25f63f410b 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -85,6 +85,27 @@ def input_dims(self): return self.models[0].input_dims return 5 + @property + def max_paddings(self): + """Get the maximum padding values used by the generator. This is used + to apply extra padding during forward passes if the raw input doesn't + meet the minimum input shape.""" + + paddings = (1, 1, 1) + if hasattr(self, '_gen'): + for layer in self._gen.layers: + if hasattr(layer, 'paddings'): + new_pw = np.max(layer.paddings, axis=1)[1:-1] + if len(new_pw) < 3: + new_pw = (*new_pw, 1) + paddings = [ + np.max((new_pw[i], paddings[i])) for i in range(3) + ] + return paddings + if hasattr(self, 'models'): + return self.models[0].max_paddings + return paddings + @property def is_5d(self): """Check if model expects spatiotemporal input""" diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index 218bc28f60..a7a454438d 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -261,21 +261,6 @@ def __post_init__(self): self.preflight() - def get_min_pad_width(self, model): - """Get the padding values applied in the first padding layer of the - model. This is used to determine the minimum width of padded slices - used to chunk the generator input.""" - pad_width = (1, 1, 1) - for layer in model._gen.layers: - if hasattr(layer, 'paddings'): - new_pw = np.max(layer.paddings, axis=1)[1:-1] - if len(new_pw) < 3: - new_pw = (*new_pw, 1) - pad_width = [ - np.max((new_pw[i], pad_width[i])) for i in range(3) - ] - return pad_width - @property def meta(self): """Meta data dictionary for the strategy. Used to add info to forward From a9a84c90651b621a5d561a8d9bb9e4827b685d31 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 22 Jan 2025 16:50:47 -0700 Subject: [PATCH 033/122] conditioning gan on observations with obs included through ``Sup3Fixer`` layer. --- sup3r/models/__init__.py | 2 +- sup3r/models/abstract.py | 24 +- sup3r/models/with_obs.py | 289 ++++++++++++++++++++++++- tests/conftest.py | 86 ++++++++ tests/training/test_train_exo.py | 4 - tests/training/test_train_fixed_obs.py | 65 ++++++ 6 files changed, 455 insertions(+), 15 deletions(-) create mode 100644 tests/training/test_train_fixed_obs.py diff --git a/sup3r/models/__init__.py b/sup3r/models/__init__.py index 20fff799fb..5e6bce4533 100644 --- a/sup3r/models/__init__.py +++ b/sup3r/models/__init__.py @@ -6,7 +6,7 @@ from .multi_step import MultiStepGan, MultiStepSurfaceMetGan, SolarMultiStepGan from .solar_cc import SolarCC from .surface import SurfaceSpatialMetModel -from .with_obs import Sup3rGanWithObs +from .with_obs import Sup3rGanFixedObs, Sup3rGanWithObs SPATIAL_FIRST_MODELS = (MultiStepSurfaceMetGan, SolarMultiStepGan) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 922869d873..ef45bf0ae6 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -15,7 +15,7 @@ import pandas as pd import tensorflow as tf from phygnn import CustomNetwork -from phygnn.layers.custom_layers import Sup3rAdder, Sup3rConcat +from phygnn.layers.custom_layers import Sup3rAdder, Sup3rConcat, Sup3rFixer from rex.utilities.utilities import safe_json_load from tensorflow.keras import optimizers @@ -1054,6 +1054,18 @@ def generate( return self._combine_fwp_output(hi_res, exogenous_data) + def _run_exo_layer(self, hi_res, hi_res_exo, layer): + """Run layer which requires handling of exogenous data, such as + ``Sup3rConcat``, ``Sup3rAdder``, or ``Sup3rFixer``""" + msg = (f'layer.name = {layer.name} does not match any features in ' + f'exogenous_data ({list(hi_res_exo)})') + assert layer.name in hi_res_exo, msg + hr_exo = hi_res_exo[layer.name] + if isinstance(layer, Sup3rFixer): + feature_index = self.obs_features.index(layer.name) + return layer(hi_res, hr_exo, feature_index=feature_index) + return layer(hi_res, hr_exo) + @tf.function def _tf_generate(self, low_res, hi_res_exo=None): """Use the generator model to generate high res data from low res input @@ -1085,14 +1097,8 @@ def _tf_generate(self, low_res, hi_res_exo=None): try: for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 - if isinstance(layer, (Sup3rAdder, Sup3rConcat)): - msg = ( - f'layer.name = {layer.name} does not match any ' - f'features in exogenous_data ({list(hi_res_exo)})' - ) - assert layer.name in hi_res_exo, msg - hr_exo = hi_res_exo[layer.name] - hi_res = layer(hi_res, hr_exo) + if isinstance(layer, (Sup3rAdder, Sup3rConcat, Sup3rFixer)): + hi_res = self._run_exo_layer(hi_res, hi_res_exo, layer) else: hi_res = layer(hi_res) except Exception as e: diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 1e25014ecf..d64d784c08 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -6,15 +6,20 @@ import numpy as np import tensorflow as tf +from phygnn.layers.custom_layers import Sup3rFixer from tensorflow.keras.losses import MeanAbsoluteError +from sup3r.utilities.utilities import RANDOM_GENERATOR + from .base import Sup3rGan logger = logging.getLogger(__name__) class Sup3rGanWithObs(Sup3rGan): - """Sup3r GAN model with additional observation data content loss.""" + """Sup3r GAN model with additional observation data content loss. This + model is useful for when observations are available for the training domain + but not for the production domain.""" def _calc_val_loss(self, batch, weight_gen_advers, loss_details): """Calculate the validation loss at the current state of model training @@ -360,3 +365,285 @@ def calc_loss_obs(self, obs_data, hi_res_gen): hi_res_gen[..., : len(self.hr_out_features)][~mask], ) return loss_obs + + +class Sup3rGanFixedObs(Sup3rGan): + """Sup3r GAN model which includes mid network observation fixing. This + model is useful for when production runs will be over a domain for which + observation data is available.""" + + def __init__(self, *args, obs_frac=None, **kwargs): + """ + Initialize the Sup3rGanFixedObs model. + + Parameters + ---------- + args : list + Positional args for ``Sup3rGan`` parent class. + obs_frac : dict + Fraction of the batch that should be "fixed" with observations. + Should include ``spatial`` key and optionally ``time`` key if this + is a spatiotemporal model. The values should correspond roughly to + the fraction of the production domain for which observations are + available (spatial) and the fraction of the full time period that + these cover. + kwargs : dict + Keyword arguments for the ``Sup3rGan`` parent class. + """ + self.obs_frac = {} if obs_frac is None else obs_frac + super().__init__(*args, **kwargs) + + @property + def obs_features(self): + """Get list of exogenous observation feature names the model uses. + These come from the names of the ``Sup3rFixer`` layers.""" + # pylint: disable=E1101 + features = [] + if hasattr(self, '_gen'): + features = [ + layer.name + for layer in self._gen.layers + if isinstance(layer, Sup3rFixer) + ] + return features + + @tf.function + def _get_single_grad( + self, + low_res, + hi_res_true, + obs_data, + training_weights, + device_name=None, + **calc_loss_kwargs, + ): + """Run gradient descent for one mini-batch of (low_res, hi_res_true), + do not update weights, just return gradient details. + + Parameters + ---------- + low_res : np.ndarray + Real low-resolution data in a 4D or 5D array: + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + hi_res_true : np.ndarray + Real high-resolution data in a 4D or 5D array: + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + obs_data : dict + Dictionary of masked "observation" data which will be added to the + dictionary of ``hi_res_exo`` data + training_weights : list + A list of layer weights that are to-be-trained based on the + current loss weight values. + device_name : None | str + Optional tensorflow device name for GPU placement. Note that if a + GPU is available, variables will be placed on that GPU even if + device_name=None. + calc_loss_kwargs : dict + Kwargs to pass to the self.calc_loss() method + + Returns + ------- + grad : list + a list or nested structure of Tensors (or IndexedSlices, or None, + or CompositeTensor) representing the gradients for the + training_weights + loss_details : dict + Namespace of the breakdown of loss components + """ + with tf.device(device_name), tf.GradientTape( + watch_accessed_variables=False + ) as tape: + tape.watch(training_weights) + loss, loss_details, _ = self._get_hr_exo_and_loss( + low_res, hi_res_true, obs_data, **calc_loss_kwargs + ) + grad = tape.gradient(loss, training_weights) + return grad, loss_details + + def get_single_grad( + self, + low_res, + hi_res_true, + training_weights, + device_name=None, + **calc_loss_kwargs, + ): + """Run gradient descent for one mini-batch of (low_res, hi_res_true), + do not update weights, just return gradient details. + + Parameters + ---------- + low_res : np.ndarray + Real low-resolution data in a 4D or 5D array: + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + hi_res_true : np.ndarray + Real high-resolution data in a 4D or 5D array: + (n_observations, spatial_1, spatial_2, features) + (n_observations, spatial_1, spatial_2, temporal, features) + training_weights : list + A list of layer weights that are to-be-trained based on the + current loss weight values. + device_name : None | str + Optional tensorflow device name for GPU placement. Note that if a + GPU is available, variables will be placed on that GPU even if + device_name=None. + calc_loss_kwargs : dict + Kwargs to pass to the self.calc_loss() method + + Returns + ------- + grad : list + a list or nested structure of Tensors (or IndexedSlices, or None, + or CompositeTensor) representing the gradients for the + training_weights + loss_details : dict + Namespace of the breakdown of loss components + """ + obs_data = self._get_masked_obs(hi_res_true) + return self._get_single_grad( + low_res, + hi_res_true, + obs_data, + training_weights, + device_name=device_name, + **calc_loss_kwargs, + ) + + def _get_obs_mask(self, hi_res): + """Define observation mask for the current batch. This is done + with a spatial mask and a temporal mask since often observation data + might be very sparse spatially but cover most of the full time period + for those locations.""" + spatial_frac = self.obs_frac['spatial'] + sp_mask = RANDOM_GENERATOR.choice( + [True, False], + size=hi_res.shape[1:3], + p=[1 - spatial_frac, spatial_frac], + ) + if self.is_4d: + return sp_mask + + time_frac = self.obs_frac['time'] + time_mask = RANDOM_GENERATOR.choice( + [True, False], + size=hi_res.shape[-2], + p=[1 - time_frac, time_frac], + ) + obs_mask = np.repeat( + sp_mask[..., None], hi_res.shape[-2], axis=-1 + ) + obs_mask[sp_mask] = time_mask + return obs_mask + + def init_weights(self, lr_shape, hr_shape, device=None): + """Initialize the generator and discriminator weights with device + placement. + + Parameters + ---------- + lr_shape : tuple + Shape of one batch of low res input data for sup3r resolution. Note + that the batch size (axis=0) must be included, but the actual batch + size doesnt really matter. + hr_shape : tuple + Shape of one batch of high res input data for sup3r resolution. + Note that the batch size (axis=0) must be included, but the actual + batch size doesnt really matter. + device : str | None + Option to place model weights on a device. If None, + self.default_device will be used. + """ + + if device is None: + device = self.default_device + + logger.info('Initializing model weights on device "{}"'.format(device)) + low_res = np.ones(lr_shape).astype(np.float32) + hi_res = np.ones(hr_shape).astype(np.float32) + + hr_exo_shape = hr_shape[:-1] + (1,) + hr_exo = np.ones(hr_exo_shape).astype(np.float32) + + with tf.device(device): + hr_exo_data = {} + for feature in self.hr_exo_features + self.obs_features: + hr_exo_data[feature] = hr_exo + _ = self._tf_generate(low_res, hr_exo_data) + _ = self._tf_discriminate(hi_res) + + @property + def model_params(self): + """ + Model parameters, used to save model to disc + + Returns + ------- + dict + """ + params = super().model_params + params['obs_frac'] = self.obs_frac + return params + + def _get_masked_obs(self, hi_res_true): + """Mask hi res data to act as sparse observation data.""" + obs_exo = {} + obs_mask = self._get_obs_mask(hi_res_true) + for feature in self.obs_features: + f_idx = self.hr_out_features.index(feature) + obs_fdata = hi_res_true[..., f_idx : f_idx + 1].copy() + obs_fdata[:, obs_mask] = np.nan + obs_exo[feature] = obs_fdata + return obs_exo + + def _get_hr_exo_and_loss( + self, + low_res, + hi_res_true, + obs_data, + **calc_loss_kwargs, + ): + """Get high-resolution exogenous data, generate synthetic output, and + compute loss. Includes artificially masking hi res data to act as + sparse observation data.""" + hi_res_exo = self.get_high_res_exo_input(hi_res_true) + hi_res_exo.update(obs_data) + hi_res_gen = self._tf_generate(low_res, hi_res_exo) + loss, loss_details = self.calc_loss( + hi_res_true, hi_res_gen, **calc_loss_kwargs + ) + return loss, loss_details, hi_res_gen + + def _calc_val_loss(self, batch, weight_gen_advers, loss_details): + """Calculate the validation loss at the current state of model training + for a given batch + + Parameters + ---------- + batch : DsetTuple + Object with ``.high_res`` and ``.low_res`` arrays + weight_gen_advers : float + Weight factor for the adversarial loss component of the generator + vs. the discriminator. + loss_details : dict + Namespace of the breakdown of loss components + + Returns + ------- + loss_details : dict + Same as input but now includes val_* loss info + """ + _, v_loss_details, _ = self._get_hr_exo_and_loss( + batch.low_res, + batch.high_res, + self._get_masked_obs(batch.high_res), + weight_gen_advers=weight_gen_advers, + train_gen=False, + train_disc=False, + ) + loss_details = self.update_loss_details( + loss_details, v_loss_details, len(batch), prefix='val_' + ) + return loss_details diff --git a/tests/conftest.py b/tests/conftest.py index 2f52610da3..3b2bceb56d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -66,6 +66,92 @@ def train_on_cpu(): os.environ['CUDA_VISIBLE_DEVICES'] = '-1' +@pytest.fixture(scope='package') +def gen_config_with_fixer(): + """Get generator config with custom fixer layer.""" + + def func(): + return [ + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 2, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 64, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 64, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + {'class': 'SpatialExpansion', 'spatial_mult': 2}, + {'class': 'Activation', 'activation': 'relu'}, + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 2, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + {'class': 'Sup3rFixer', 'name': 'u_10m'}, + {'class': 'Sup3rFixer', 'name': 'v_10m'}, + { + 'class': 'SkipConnection', 'name': 'b' + }, + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 2, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + { + 'class': 'SkipConnection', 'name': 'b' + }, + ] + + return func + + @pytest.fixture(scope='package') def gen_config_with_topo(): """Get generator config with custom topo layer.""" diff --git a/tests/training/test_train_exo.py b/tests/training/test_train_exo.py index ca5b0e9dae..02b283bf8a 100644 --- a/tests/training/test_train_exo.py +++ b/tests/training/test_train_exo.py @@ -2,7 +2,6 @@ import os import tempfile -import time import numpy as np import pytest @@ -69,7 +68,6 @@ def test_wind_hi_res_topo( gen_config_with_topo(CustomLayer), pytest.S_FP_DISC, learning_rate=1e-4 ) - start = time.time() with tempfile.TemporaryDirectory() as td: model.train( batcher, @@ -111,5 +109,3 @@ def test_wind_hi_res_topo( assert y.shape[1] == x.shape[1] * 2 assert y.shape[2] == x.shape[2] * 2 assert y.shape[3] == len(features) - len(lr_only_features) - 1 - - print(f'Elapsed: {time.time() - start}') diff --git a/tests/training/test_train_fixed_obs.py b/tests/training/test_train_fixed_obs.py new file mode 100644 index 0000000000..0176b98dc9 --- /dev/null +++ b/tests/training/test_train_fixed_obs.py @@ -0,0 +1,65 @@ +"""Test the training of super resolution GANs with exo data.""" + +import os +import tempfile + +import pytest + +from sup3r.models import Sup3rGanFixedObs +from sup3r.preprocessing import ( + BatchHandler, + DataHandler, +) + +SHAPE = (20, 20) +FEATURES_W = ['u_10m', 'v_10m'] +TARGET_W = (39.01, -105.15) + + +def test_fixed_wind_obs(gen_config_with_fixer): + """Test a special model which fixes observations mid network with + ``Sup3rFixer`` layer.""" + kwargs = { + 'file_paths': pytest.FP_WTK, + 'features': FEATURES_W, + 'target': TARGET_W, + 'shape': SHAPE, + } + + train_handler = DataHandler(**kwargs, time_slice=slice(None, 3000, 10)) + + val_handler = DataHandler(**kwargs, time_slice=slice(3000, None, 10)) + batcher = BatchHandler( + [train_handler], + [val_handler], + batch_size=2, + n_batches=1, + s_enhance=2, + t_enhance=1, + sample_shape=(20, 20, 1), + ) + + Sup3rGanFixedObs.seed() + + model = Sup3rGanFixedObs( + gen_config_with_fixer(), + pytest.S_FP_DISC, + obs_frac={'spatial': 0.1}, + learning_rate=1e-4, + ) + assert model.obs_features == ['u_10m', 'v_10m'] + with tempfile.TemporaryDirectory() as td: + model_kwargs = { + 'input_resolution': {'spatial': '16km', 'temporal': '3600min'}, + 'n_epoch': 3, + 'weight_gen_advers': 0.0, + 'train_gen': True, + 'train_disc': False, + 'checkpoint_int': None, + 'out_dir': os.path.join(td, 'test_{epoch}'), + } + + model.train(batcher, **model_kwargs) + + loaded = model.load(os.path.join(td, 'test_2')) + loaded.train(batcher, **model_kwargs) From ff4d7640d4e60598473564359aecc1c45ce543af Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 23 Jan 2025 14:33:59 -0700 Subject: [PATCH 034/122] simplified conditioning on obs with masked concat layer, so we don't have to worry about indices and can use existing methods for prepping exo data. --- sup3r/models/abstract.py | 45 +++++++++++++------------ sup3r/models/with_obs.py | 10 +++--- sup3r/preprocessing/collections/base.py | 2 +- sup3r/preprocessing/samplers/dual.py | 7 ++-- tests/conftest.py | 14 +++----- tests/training/test_train_fixed_obs.py | 38 +++++++++++++++++++-- 6 files changed, 73 insertions(+), 43 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index ef45bf0ae6..e6480bca1a 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -15,7 +15,11 @@ import pandas as pd import tensorflow as tf from phygnn import CustomNetwork -from phygnn.layers.custom_layers import Sup3rAdder, Sup3rConcat, Sup3rFixer +from phygnn.layers.custom_layers import ( + Sup3rAdder, + Sup3rConcat, + Sup3rConcatMasked, +) from rex.utilities.utilities import safe_json_load from tensorflow.keras import optimizers @@ -916,8 +920,8 @@ def run_gradient_descent( return loss_details def _reshape_norm_exo(self, hi_res, hi_res_exo, exo_name, norm_in=True): - """Reshape the hi_res_topo to match the hi_res tensor (if necessary) - and normalize (if requested). + """Reshape the hi_res_exo data to match the hi_res tensor (if + necessary) and normalize (if requested). Parameters ---------- @@ -1024,20 +1028,22 @@ def generate( try: for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 - if isinstance(layer, (Sup3rAdder, Sup3rConcat)): + if isinstance( + layer, (Sup3rAdder, Sup3rConcat, Sup3rConcatMasked) + ): msg = ( f'layer.name = {layer.name} does not match any ' 'features in exogenous_data ' f'({list(exogenous_data)})' ) assert layer.name in exogenous_data, msg - hi_res_exo = exogenous_data.get_combine_type_data( + hr_exo = exogenous_data.get_combine_type_data( layer.name, 'layer' ) - hi_res_exo = self._reshape_norm_exo( - hi_res, hi_res_exo, layer.name, norm_in=norm_in + hr_exo = self._reshape_norm_exo( + hi_res, hr_exo, layer.name, norm_in=norm_in ) - hi_res = layer(hi_res, hi_res_exo) + hi_res = layer(hi_res, hr_exo) else: hi_res = layer(hi_res) except Exception as e: @@ -1054,18 +1060,6 @@ def generate( return self._combine_fwp_output(hi_res, exogenous_data) - def _run_exo_layer(self, hi_res, hi_res_exo, layer): - """Run layer which requires handling of exogenous data, such as - ``Sup3rConcat``, ``Sup3rAdder``, or ``Sup3rFixer``""" - msg = (f'layer.name = {layer.name} does not match any features in ' - f'exogenous_data ({list(hi_res_exo)})') - assert layer.name in hi_res_exo, msg - hr_exo = hi_res_exo[layer.name] - if isinstance(layer, Sup3rFixer): - feature_index = self.obs_features.index(layer.name) - return layer(hi_res, hr_exo, feature_index=feature_index) - return layer(hi_res, hr_exo) - @tf.function def _tf_generate(self, low_res, hi_res_exo=None): """Use the generator model to generate high res data from low res input @@ -1097,8 +1091,15 @@ def _tf_generate(self, low_res, hi_res_exo=None): try: for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 - if isinstance(layer, (Sup3rAdder, Sup3rConcat, Sup3rFixer)): - hi_res = self._run_exo_layer(hi_res, hi_res_exo, layer) + if isinstance( + layer, (Sup3rAdder, Sup3rConcat, Sup3rConcatMasked) + ): + msg = ( + f'layer.name = {layer.name} does not match any ' + f'features in exogenous_data ({list(hi_res_exo)})' + ) + assert layer.name in hi_res_exo, msg + hi_res = layer(hi_res, hi_res_exo[layer.name]) else: hi_res = layer(hi_res) except Exception as e: diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index d64d784c08..a12f8c51e4 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -6,7 +6,7 @@ import numpy as np import tensorflow as tf -from phygnn.layers.custom_layers import Sup3rFixer +from phygnn.layers.custom_layers import Sup3rConcatMasked from tensorflow.keras.losses import MeanAbsoluteError from sup3r.utilities.utilities import RANDOM_GENERATOR @@ -396,14 +396,14 @@ def __init__(self, *args, obs_frac=None, **kwargs): @property def obs_features(self): """Get list of exogenous observation feature names the model uses. - These come from the names of the ``Sup3rFixer`` layers.""" + These come from the names of the ``Sup3rConcatMasked`` layers.""" # pylint: disable=E1101 features = [] if hasattr(self, '_gen'): features = [ layer.name for layer in self._gen.layers - if isinstance(layer, Sup3rFixer) + if isinstance(layer, Sup3rConcatMasked) ] return features @@ -592,7 +592,9 @@ def _get_masked_obs(self, hi_res_true): obs_exo = {} obs_mask = self._get_obs_mask(hi_res_true) for feature in self.obs_features: - f_idx = self.hr_out_features.index(feature) + # obs_features can include a _obs suffix to avoid conflict with + # fully gridded exo features + f_idx = self.hr_out_features.index(feature.strip('_obs')) obs_fdata = hi_res_true[..., f_idx : f_idx + 1].copy() obs_fdata[:, obs_mask] = np.nan obs_exo[feature] = obs_fdata diff --git a/sup3r/preprocessing/collections/base.py b/sup3r/preprocessing/collections/base.py index 736606a733..9b07a8f85a 100644 --- a/sup3r/preprocessing/collections/base.py +++ b/sup3r/preprocessing/collections/base.py @@ -44,7 +44,7 @@ def features(self): if not self._features: _ = [ self._features.append(f) - for f in np.concatenate([d.features for d in self.data]) + for f in np.concatenate([c.features for c in self.containers]) if f not in self._features ] return self._features diff --git a/sup3r/preprocessing/samplers/dual.py b/sup3r/preprocessing/samplers/dual.py index a82cd986e3..4f577369e2 100644 --- a/sup3r/preprocessing/samplers/dual.py +++ b/sup3r/preprocessing/samplers/dual.py @@ -63,7 +63,7 @@ def __init__( 'with `.low_res` and `.high_res` data members, and optionally an ' '`.obs` member, in that order' ) - dnames = ['low_res', 'high_res', 'obs'][:len(data)] + dnames = ['low_res', 'high_res', 'obs'][: len(data)] check = ( hasattr(data, dname) and getattr(data, dname) == data[i] for i, dname in enumerate(dnames) @@ -82,8 +82,9 @@ def __init__( feature_sets = feature_sets or {} self._lr_only_features = feature_sets.get('lr_only_features', []) self._hr_exo_features = feature_sets.get('hr_exo_features', []) - self.lr_features = list(self.lr_data.data_vars) self.features = self.get_features(feature_sets) + lr_feats = self.lr_data.features + self.lr_features = [f for f in lr_feats if f in self.features] self.s_enhance = s_enhance self.t_enhance = t_enhance self.check_for_consistent_shapes() @@ -148,4 +149,4 @@ def get_sample_index(self, n_obs=None): hr_index = (*hr_index, self.hr_features) sample_index = (lr_index, hr_index, obs_index) - return sample_index[:len(self.data)] + return sample_index[: len(self.data)] diff --git a/tests/conftest.py b/tests/conftest.py index 3b2bceb56d..48e05d0a08 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -67,8 +67,8 @@ def train_on_cpu(): @pytest.fixture(scope='package') -def gen_config_with_fixer(): - """Get generator config with custom fixer layer.""" +def gen_config_with_concat_masked(): + """Get generator config with custom concat masked layer.""" def func(): return [ @@ -126,11 +126,8 @@ def func(): 'activation': 'relu', }, {'class': 'Cropping2D', 'cropping': 4}, - {'class': 'Sup3rFixer', 'name': 'u_10m'}, - {'class': 'Sup3rFixer', 'name': 'v_10m'}, - { - 'class': 'SkipConnection', 'name': 'b' - }, + {'class': 'Sup3rConcatMasked', 'name': 'u_10m'}, + {'class': 'Sup3rConcatMasked', 'name': 'v_10m'}, { 'class': 'FlexiblePadding', 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], @@ -144,9 +141,6 @@ def func(): 'activation': 'relu', }, {'class': 'Cropping2D', 'cropping': 4}, - { - 'class': 'SkipConnection', 'name': 'b' - }, ] return func diff --git a/tests/training/test_train_fixed_obs.py b/tests/training/test_train_fixed_obs.py index 0176b98dc9..906856bcba 100644 --- a/tests/training/test_train_fixed_obs.py +++ b/tests/training/test_train_fixed_obs.py @@ -3,6 +3,7 @@ import os import tempfile +import numpy as np import pytest from sup3r.models import Sup3rGanFixedObs @@ -10,15 +11,16 @@ BatchHandler, DataHandler, ) +from sup3r.utilities.utilities import RANDOM_GENERATOR SHAPE = (20, 20) FEATURES_W = ['u_10m', 'v_10m'] TARGET_W = (39.01, -105.15) -def test_fixed_wind_obs(gen_config_with_fixer): +def test_fixed_wind_obs(gen_config_with_concat_masked): """Test a special model which fixes observations mid network with - ``Sup3rFixer`` layer.""" + ``Sup3rConcatMasked`` layer.""" kwargs = { 'file_paths': pytest.FP_WTK, 'features': FEATURES_W, @@ -42,7 +44,7 @@ def test_fixed_wind_obs(gen_config_with_fixer): Sup3rGanFixedObs.seed() model = Sup3rGanFixedObs( - gen_config_with_fixer(), + gen_config_with_concat_masked(), pytest.S_FP_DISC, obs_frac={'spatial': 0.1}, learning_rate=1e-4, @@ -63,3 +65,33 @@ def test_fixed_wind_obs(gen_config_with_fixer): loaded = model.load(os.path.join(td, 'test_2')) loaded.train(batcher, **model_kwargs) + + x = RANDOM_GENERATOR.uniform(0, 1, (4, 30, 30, len(FEATURES_W))) + u10m_obs = RANDOM_GENERATOR.uniform(0, 1, (4, 60, 60, 1)) + v10m_obs = RANDOM_GENERATOR.uniform(0, 1, (4, 60, 60, 1)) + mask = RANDOM_GENERATOR.choice([True, False], (60, 60, 1), p=[0.9, 0.1]) + u10m_obs[:, mask] = np.nan + v10m_obs[:, mask] = np.nan + + with pytest.raises(RuntimeError): + y = model.generate(x, exogenous_data=None) + + exo_tmp = { + 'u_10m': { + 'steps': [ + {'model': 0, 'combine_type': 'layer', 'data': u10m_obs} + ] + }, + 'v_10m': { + 'steps': [ + {'model': 0, 'combine_type': 'layer', 'data': v10m_obs} + ] + } + } + y = model.generate(x, exogenous_data=exo_tmp) + + assert y.dtype == np.float32 + assert y.shape[0] == x.shape[0] + assert y.shape[1] == x.shape[1] * 2 + assert y.shape[2] == x.shape[2] * 2 + assert y.shape[3] == len(FEATURES_W) From 7e43ae9a66c9761f3f1882d7c44207819d8944bb Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sat, 25 Jan 2025 13:40:52 -0700 Subject: [PATCH 035/122] Cleaned up ``Sup3rGanFixedObs`` to rely more on parent methods. Removed exo features from content loss calculations. --- sup3r/models/abstract.py | 29 +++- sup3r/models/with_obs.py | 218 ++++++------------------- tests/conftest.py | 4 +- tests/training/test_train_fixed_obs.py | 5 +- 4 files changed, 76 insertions(+), 180 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index e6480bca1a..dedb116e27 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -18,7 +18,8 @@ from phygnn.layers.custom_layers import ( Sup3rAdder, Sup3rConcat, - Sup3rConcatMasked, + Sup3rConcatObs, + Sup3rImpute, ) from rex.utilities.utilities import safe_json_load from tensorflow.keras import optimizers @@ -1029,21 +1030,27 @@ def generate( for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 if isinstance( - layer, (Sup3rAdder, Sup3rConcat, Sup3rConcatMasked) + layer, + (Sup3rAdder, Sup3rConcat, Sup3rConcatObs, Sup3rImpute), ): msg = ( f'layer.name = {layer.name} does not match any ' 'features in exogenous_data ' f'({list(exogenous_data)})' ) - assert layer.name in exogenous_data, msg + hr_feat = layer.name.replace('_obs', '') + assert hr_feat in exogenous_data, msg hr_exo = exogenous_data.get_combine_type_data( layer.name, 'layer' ) hr_exo = self._reshape_norm_exo( - hi_res, hr_exo, layer.name, norm_in=norm_in + hi_res, hr_exo, hr_feat, norm_in=norm_in, ) - hi_res = layer(hi_res, hr_exo) + if isinstance(layer, Sup3rImpute): + fidx = self.hr_out_features.index(hr_feat) + hi_res = layer(hi_res, hr_exo, fidx) + else: + hi_res = layer(hi_res, hr_exo) else: hi_res = layer(hi_res) except Exception as e: @@ -1092,14 +1099,22 @@ def _tf_generate(self, low_res, hi_res_exo=None): for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 if isinstance( - layer, (Sup3rAdder, Sup3rConcat, Sup3rConcatMasked) + layer, + (Sup3rAdder, Sup3rConcat, Sup3rConcatObs, Sup3rImpute), ): msg = ( f'layer.name = {layer.name} does not match any ' f'features in exogenous_data ({list(hi_res_exo)})' ) + hr_feat = layer.name.replace('_obs', '') assert layer.name in hi_res_exo, msg - hi_res = layer(hi_res, hi_res_exo[layer.name]) + hr_exo = hi_res_exo[hr_feat] + + if isinstance(layer, Sup3rImpute): + fidx = self.hr_out_features.index(hr_feat) + hi_res = layer(hi_res, hr_exo, fidx) + else: + hi_res = layer(hi_res, hr_exo) else: hi_res = layer(hi_res) except Exception as e: diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index a12f8c51e4..c8c6f4abfa 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -6,7 +6,7 @@ import numpy as np import tensorflow as tf -from phygnn.layers.custom_layers import Sup3rConcatMasked +from phygnn.layers.custom_layers import Sup3rConcatObs, Sup3rImpute from tensorflow.keras.losses import MeanAbsoluteError from sup3r.utilities.utilities import RANDOM_GENERATOR @@ -40,16 +40,15 @@ def _calc_val_loss(self, batch, weight_gen_advers, loss_details): loss_details : dict Same as input with updated val_* loss info """ - val_exo_data = self.get_high_res_exo_input(batch.high_res) - high_res_gen = self._tf_generate(batch.low_res, val_exo_data) - _, v_loss_details = self.calc_loss( + _, v_loss_details, hi_res_gen = self._get_hr_exo_and_loss( + batch.low_res, batch.high_res, - high_res_gen, weight_gen_advers=weight_gen_advers, train_gen=False, train_disc=False, ) - v_loss_details['loss_obs'] = self.cal_loss_obs(batch.obs, high_res_gen) + + v_loss_details['loss_obs'] = self.cal_loss_obs(batch.obs, hi_res_gen) loss_details = self.update_loss_details( loss_details, v_loss_details, len(batch), prefix='val_' @@ -396,121 +395,30 @@ def __init__(self, *args, obs_frac=None, **kwargs): @property def obs_features(self): """Get list of exogenous observation feature names the model uses. - These come from the names of the ``Sup3rConcatMasked`` layers.""" + These come from the names of the ``Sup3rObs`` layers.""" # pylint: disable=E1101 features = [] if hasattr(self, '_gen'): - features = [ - layer.name - for layer in self._gen.layers - if isinstance(layer, Sup3rConcatMasked) - ] + for layer in self._gen.layers: + check = isinstance(layer, (Sup3rConcatObs, Sup3rImpute)) + check = check and layer.name not in features + if check: + features.append(layer.name) return features - @tf.function - def _get_single_grad( - self, - low_res, - hi_res_true, - obs_data, - training_weights, - device_name=None, - **calc_loss_kwargs, - ): - """Run gradient descent for one mini-batch of (low_res, hi_res_true), - do not update weights, just return gradient details. - - Parameters - ---------- - low_res : np.ndarray - Real low-resolution data in a 4D or 5D array: - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - hi_res_true : np.ndarray - Real high-resolution data in a 4D or 5D array: - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - obs_data : dict - Dictionary of masked "observation" data which will be added to the - dictionary of ``hi_res_exo`` data - training_weights : list - A list of layer weights that are to-be-trained based on the - current loss weight values. - device_name : None | str - Optional tensorflow device name for GPU placement. Note that if a - GPU is available, variables will be placed on that GPU even if - device_name=None. - calc_loss_kwargs : dict - Kwargs to pass to the self.calc_loss() method - - Returns - ------- - grad : list - a list or nested structure of Tensors (or IndexedSlices, or None, - or CompositeTensor) representing the gradients for the - training_weights - loss_details : dict - Namespace of the breakdown of loss components - """ - with tf.device(device_name), tf.GradientTape( - watch_accessed_variables=False - ) as tape: - tape.watch(training_weights) - loss, loss_details, _ = self._get_hr_exo_and_loss( - low_res, hi_res_true, obs_data, **calc_loss_kwargs - ) - grad = tape.gradient(loss, training_weights) - return grad, loss_details + def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): + """Get loss for observation locations and for non observation + locations. - def get_single_grad( - self, - low_res, - hi_res_true, - training_weights, - device_name=None, - **calc_loss_kwargs, - ): - """Run gradient descent for one mini-batch of (low_res, hi_res_true), - do not update weights, just return gradient details. - - Parameters - ---------- - low_res : np.ndarray - Real low-resolution data in a 4D or 5D array: - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - hi_res_true : np.ndarray - Real high-resolution data in a 4D or 5D array: - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - training_weights : list - A list of layer weights that are to-be-trained based on the - current loss weight values. - device_name : None | str - Optional tensorflow device name for GPU placement. Note that if a - GPU is available, variables will be placed on that GPU even if - device_name=None. - calc_loss_kwargs : dict - Kwargs to pass to the self.calc_loss() method - - Returns - ------- - grad : list - a list or nested structure of Tensors (or IndexedSlices, or None, - or CompositeTensor) representing the gradients for the - training_weights - loss_details : dict - Namespace of the breakdown of loss components - """ - obs_data = self._get_masked_obs(hi_res_true) - return self._get_single_grad( - low_res, - hi_res_true, - obs_data, - training_weights, - device_name=device_name, - **calc_loss_kwargs, + Note: This assumes the loss function called by + ``calc_loss_gen_content`` works with the non-gridded masked data""" + loss_obs = self.calc_loss_gen_content( + hi_res_true[~obs_mask], hi_res_gen[~obs_mask] + ) + loss_non_obs = self.calc_loss_gen_content( + hi_res_true[obs_mask], hi_res_gen[obs_mask] ) + return loss_obs, loss_non_obs def _get_obs_mask(self, hi_res): """Define observation mask for the current batch. This is done @@ -518,25 +426,21 @@ def _get_obs_mask(self, hi_res): might be very sparse spatially but cover most of the full time period for those locations.""" spatial_frac = self.obs_frac['spatial'] - sp_mask = RANDOM_GENERATOR.choice( + obs_mask = RANDOM_GENERATOR.choice( [True, False], size=hi_res.shape[1:3], p=[1 - spatial_frac, spatial_frac], ) - if self.is_4d: - return sp_mask - - time_frac = self.obs_frac['time'] - time_mask = RANDOM_GENERATOR.choice( - [True, False], - size=hi_res.shape[-2], - p=[1 - time_frac, time_frac], - ) - obs_mask = np.repeat( - sp_mask[..., None], hi_res.shape[-2], axis=-1 - ) - obs_mask[sp_mask] = time_mask - return obs_mask + if self.is_5d: + sp_mask = obs_mask.copy() + time_frac = self.obs_frac['time'] + obs_mask = RANDOM_GENERATOR.choice( + [True, False], + size=hi_res.shape[1:-1], + p=[1 - time_frac, time_frac], + ) + obs_mask[sp_mask] = True + return np.repeat(obs_mask[None, ...], hi_res.shape[0], axis=0) def init_weights(self, lr_shape, hr_shape, device=None): """Initialize the generator and discriminator weights with device @@ -587,65 +491,39 @@ def model_params(self): params['obs_frac'] = self.obs_frac return params - def _get_masked_obs(self, hi_res_true): - """Mask hi res data to act as sparse observation data.""" - obs_exo = {} + def get_high_res_exo_input(self, hi_res_true): + """Mask high res data to act as sparse observation data. Add this to + the standard high res exo input""" + exo_data = super().get_high_res_exo_input(hi_res_true) obs_mask = self._get_obs_mask(hi_res_true) for feature in self.obs_features: # obs_features can include a _obs suffix to avoid conflict with # fully gridded exo features - f_idx = self.hr_out_features.index(feature.strip('_obs')) - obs_fdata = hi_res_true[..., f_idx : f_idx + 1].copy() - obs_fdata[:, obs_mask] = np.nan - obs_exo[feature] = obs_fdata - return obs_exo + f_idx = self.hr_out_features.index(feature.replace('_obs', '')) + exo_data[feature] = tf.where( + obs_mask, np.nan, hi_res_true[..., f_idx] + )[..., None] + exo_data['mask'] = obs_mask + return exo_data def _get_hr_exo_and_loss( self, low_res, hi_res_true, - obs_data, **calc_loss_kwargs, ): """Get high-resolution exogenous data, generate synthetic output, and compute loss. Includes artificially masking hi res data to act as sparse observation data.""" hi_res_exo = self.get_high_res_exo_input(hi_res_true) - hi_res_exo.update(obs_data) hi_res_gen = self._tf_generate(low_res, hi_res_exo) loss, loss_details = self.calc_loss( hi_res_true, hi_res_gen, **calc_loss_kwargs ) - return loss, loss_details, hi_res_gen - - def _calc_val_loss(self, batch, weight_gen_advers, loss_details): - """Calculate the validation loss at the current state of model training - for a given batch - - Parameters - ---------- - batch : DsetTuple - Object with ``.high_res`` and ``.low_res`` arrays - weight_gen_advers : float - Weight factor for the adversarial loss component of the generator - vs. the discriminator. - loss_details : dict - Namespace of the breakdown of loss components - - Returns - ------- - loss_details : dict - Same as input but now includes val_* loss info - """ - _, v_loss_details, _ = self._get_hr_exo_and_loss( - batch.low_res, - batch.high_res, - self._get_masked_obs(batch.high_res), - weight_gen_advers=weight_gen_advers, - train_gen=False, - train_disc=False, + loss_obs, loss_non_obs = self._get_loss_obs_comparison( + hi_res_true, hi_res_gen, hi_res_exo['mask'], ) - loss_details = self.update_loss_details( - loss_details, v_loss_details, len(batch), prefix='val_' + loss_details.update( + {'loss_obs': loss_obs, 'loss_non_obs': loss_non_obs} ) - return loss_details + return loss, loss_details, hi_res_gen diff --git a/tests/conftest.py b/tests/conftest.py index 48e05d0a08..bfa2722b65 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -126,8 +126,8 @@ def func(): 'activation': 'relu', }, {'class': 'Cropping2D', 'cropping': 4}, - {'class': 'Sup3rConcatMasked', 'name': 'u_10m'}, - {'class': 'Sup3rConcatMasked', 'name': 'v_10m'}, + {'class': 'Sup3rConcatObs', 'name': 'u_10m'}, + {'class': 'Sup3rConcatObs', 'name': 'v_10m'}, { 'class': 'FlexiblePadding', 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], diff --git a/tests/training/test_train_fixed_obs.py b/tests/training/test_train_fixed_obs.py index 906856bcba..539b7e7901 100644 --- a/tests/training/test_train_fixed_obs.py +++ b/tests/training/test_train_fixed_obs.py @@ -20,7 +20,7 @@ def test_fixed_wind_obs(gen_config_with_concat_masked): """Test a special model which fixes observations mid network with - ``Sup3rConcatMasked`` layer.""" + ``Sup3rConcatObs`` layer.""" kwargs = { 'file_paths': pytest.FP_WTK, 'features': FEATURES_W, @@ -49,6 +49,9 @@ def test_fixed_wind_obs(gen_config_with_concat_masked): obs_frac={'spatial': 0.1}, learning_rate=1e-4, ) + test_mask = model._get_obs_mask(np.zeros((1, 20, 20, 1, 1))) + frac = 1 - test_mask.sum() / test_mask.size + assert np.abs(0.1 - frac) < test_mask.size / (2 * np.sqrt(test_mask.size)) assert model.obs_features == ['u_10m', 'v_10m'] with tempfile.TemporaryDirectory() as td: model_kwargs = { From 81b3a760617963666c280e6805f26893403c498c Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 28 Jan 2025 10:08:37 -0700 Subject: [PATCH 036/122] removed additional gen loss term from obs loss. Need loss to be uniformly optimized, not just for obs locations --- sup3r/models/abstract.py | 4 +-- sup3r/models/with_obs.py | 60 +++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index dedb116e27..a74adac198 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -1139,7 +1139,7 @@ def _get_hr_exo_and_loss( loss, loss_details = self.calc_loss( hi_res_true, hi_res_gen, **calc_loss_kwargs ) - return loss, loss_details, hi_res_gen + return loss, loss_details, hi_res_gen, hi_res_exo @tf.function def get_single_grad( @@ -1186,7 +1186,7 @@ def get_single_grad( watch_accessed_variables=False ) as tape: tape.watch(training_weights) - loss, loss_details, _ = self._get_hr_exo_and_loss( + loss, loss_details, _, _ = self._get_hr_exo_and_loss( low_res, hi_res_true, **calc_loss_kwargs ) grad = tape.gradient(loss, training_weights) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index c8c6f4abfa..06554da18a 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -40,7 +40,7 @@ def _calc_val_loss(self, batch, weight_gen_advers, loss_details): loss_details : dict Same as input with updated val_* loss info """ - _, v_loss_details, hi_res_gen = self._get_hr_exo_and_loss( + _, v_loss_details, hi_res_gen, _ = self._get_hr_exo_and_loss( batch.low_res, batch.high_res, weight_gen_advers=weight_gen_advers, @@ -48,7 +48,7 @@ def _calc_val_loss(self, batch, weight_gen_advers, loss_details): train_disc=False, ) - v_loss_details['loss_obs'] = self.cal_loss_obs(batch.obs, hi_res_gen) + v_loss_details['loss_obs'] = self.calc_loss_obs(batch.obs, hi_res_gen) loss_details = self.update_loss_details( loss_details, v_loss_details, len(batch), prefix='val_' @@ -325,13 +325,17 @@ def get_single_grad( watch_accessed_variables=False ) as tape: tape.watch(training_weights) - loss, loss_details, hi_res_gen = self._get_hr_exo_and_loss( + loss, loss_details, hi_res_gen, _ = self._get_hr_exo_and_loss( low_res, hi_res_true, **calc_loss_kwargs ) loss_obs = self.calc_loss_obs(obs_data, hi_res_gen) - if not tf.reduce_any(tf.math.is_nan(loss_obs)): + loss_update = {'loss_obs': loss_obs} + if calc_loss_kwargs['train_gen'] and not tf.reduce_any( + tf.math.is_nan(loss_obs) + ): loss += loss_obs - loss_details.update({'loss_obs': loss_obs}) + loss_update['loss_gen'] = loss + loss_details.update(loss_update) grad = tape.gradient(loss, training_weights) return grad, loss_details @@ -408,15 +412,24 @@ def obs_features(self): def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): """Get loss for observation locations and for non observation - locations. - - Note: This assumes the loss function called by - ``calc_loss_gen_content`` works with the non-gridded masked data""" - loss_obs = self.calc_loss_gen_content( - hi_res_true[~obs_mask], hi_res_gen[~obs_mask] + locations.""" + + hr_true = [ + hi_res_true[..., self.hr_out_features.index(f)] + for f in self.obs_features + ] + hr_true = tf.stack(hr_true, axis=-1) + hr_gen = [ + hi_res_gen[..., self.hr_out_features.index(f)] + for f in self.obs_features + ] + hr_gen = tf.stack(hr_gen, axis=-1) + + loss_obs = MeanAbsoluteError()( + hr_true[~obs_mask], hr_gen[~obs_mask] ) - loss_non_obs = self.calc_loss_gen_content( - hi_res_true[obs_mask], hi_res_gen[obs_mask] + loss_non_obs = MeanAbsoluteError()( + hr_true[obs_mask], hr_gen[obs_mask] ) return loss_obs, loss_non_obs @@ -515,15 +528,18 @@ def _get_hr_exo_and_loss( """Get high-resolution exogenous data, generate synthetic output, and compute loss. Includes artificially masking hi res data to act as sparse observation data.""" - hi_res_exo = self.get_high_res_exo_input(hi_res_true) - hi_res_gen = self._tf_generate(low_res, hi_res_exo) - loss, loss_details = self.calc_loss( - hi_res_true, hi_res_gen, **calc_loss_kwargs + out = super()._get_hr_exo_and_loss( + low_res, hi_res_true, **calc_loss_kwargs ) + loss, loss_details, hi_res_gen, hi_res_exo = out loss_obs, loss_non_obs = self._get_loss_obs_comparison( - hi_res_true, hi_res_gen, hi_res_exo['mask'], - ) - loss_details.update( - {'loss_obs': loss_obs, 'loss_non_obs': loss_non_obs} + hi_res_true, + hi_res_gen, + hi_res_exo['mask'], ) - return loss, loss_details, hi_res_gen + loss_update = { + 'loss_obs': loss_obs, + 'loss_non_obs': loss_non_obs, + } + loss_details.update(loss_update) + return loss, loss_details, hi_res_gen, hi_res_exo From 543a75113cba823e6ca2805a665c0079e0cfe926 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 29 Jan 2025 08:08:26 -0700 Subject: [PATCH 037/122] spatial frac uniform sampling --- sup3r/models/with_obs.py | 23 ++++++++++--------- ...d_obs.py => test_train_conditioned_obs.py} | 0 2 files changed, 12 insertions(+), 11 deletions(-) rename tests/training/{test_train_fixed_obs.py => test_train_conditioned_obs.py} (100%) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 06554da18a..8ec67f8c26 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -389,7 +389,8 @@ def __init__(self, *args, obs_frac=None, **kwargs): is a spatiotemporal model. The values should correspond roughly to the fraction of the production domain for which observations are available (spatial) and the fraction of the full time period that - these cover. + these cover. For each batch a spatial frac will be selected by + uniformly selecting from the range ``(0, obs_frac['spatial'])`` kwargs : dict Keyword arguments for the ``Sup3rGan`` parent class. """ @@ -425,20 +426,15 @@ def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): ] hr_gen = tf.stack(hr_gen, axis=-1) - loss_obs = MeanAbsoluteError()( - hr_true[~obs_mask], hr_gen[~obs_mask] - ) - loss_non_obs = MeanAbsoluteError()( - hr_true[obs_mask], hr_gen[obs_mask] - ) + loss_obs = MeanAbsoluteError()(hr_true[~obs_mask], hr_gen[~obs_mask]) + loss_non_obs = MeanAbsoluteError()(hr_true[obs_mask], hr_gen[obs_mask]) return loss_obs, loss_non_obs - def _get_obs_mask(self, hi_res): + def _get_obs_mask(self, hi_res, spatial_frac, time_frac=None): """Define observation mask for the current batch. This is done with a spatial mask and a temporal mask since often observation data might be very sparse spatially but cover most of the full time period for those locations.""" - spatial_frac = self.obs_frac['spatial'] obs_mask = RANDOM_GENERATOR.choice( [True, False], size=hi_res.shape[1:3], @@ -446,7 +442,6 @@ def _get_obs_mask(self, hi_res): ) if self.is_5d: sp_mask = obs_mask.copy() - time_frac = self.obs_frac['time'] obs_mask = RANDOM_GENERATOR.choice( [True, False], size=hi_res.shape[1:-1], @@ -508,7 +503,13 @@ def get_high_res_exo_input(self, hi_res_true): """Mask high res data to act as sparse observation data. Add this to the standard high res exo input""" exo_data = super().get_high_res_exo_input(hi_res_true) - obs_mask = self._get_obs_mask(hi_res_true) + spatial_frac = RANDOM_GENERATOR.uniform(self.obs_frac['spatial']) + logger.info( + 'Using spatial_frac = %s for the current observation mask', + spatial_frac, + ) + time_frac = self.obs_frac.get('time', None) + obs_mask = self._get_obs_mask(hi_res_true, spatial_frac, time_frac) for feature in self.obs_features: # obs_features can include a _obs suffix to avoid conflict with # fully gridded exo features diff --git a/tests/training/test_train_fixed_obs.py b/tests/training/test_train_conditioned_obs.py similarity index 100% rename from tests/training/test_train_fixed_obs.py rename to tests/training/test_train_conditioned_obs.py From e1dd3052eabdca5f743085a4df10f991b4f35803 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 4 Feb 2025 10:07:32 -0700 Subject: [PATCH 038/122] new custom layers, which require special treatment based on naming, integrated --- sup3r/models/abstract.py | 30 +++++++++++++++++++++--------- sup3r/models/with_obs.py | 16 +++++++++------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index a74adac198..bf1f888150 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -16,9 +16,12 @@ import tensorflow as tf from phygnn import CustomNetwork from phygnn.layers.custom_layers import ( + MaskedSqueezeAndExcitation, + SparseAttention, Sup3rAdder, Sup3rConcat, Sup3rConcatObs, + Sup3rConcatObsBlock, Sup3rImpute, ) from rex.utilities.utilities import safe_json_load @@ -35,6 +38,17 @@ logger = logging.getLogger(__name__) +SUP3R_LAYERS = ( + Sup3rAdder, + Sup3rConcat, + Sup3rConcatObs, + Sup3rConcatObsBlock, + Sup3rImpute, + SparseAttention, + MaskedSqueezeAndExcitation +) + + # pylint: disable=E1101,W0201,E0203 class AbstractSingleModel(ABC, TensorboardMixIn): """ @@ -1029,10 +1043,7 @@ def generate( try: for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 - if isinstance( - layer, - (Sup3rAdder, Sup3rConcat, Sup3rConcatObs, Sup3rImpute), - ): + if isinstance(layer, SUP3R_LAYERS): msg = ( f'layer.name = {layer.name} does not match any ' 'features in exogenous_data ' @@ -1044,7 +1055,10 @@ def generate( layer.name, 'layer' ) hr_exo = self._reshape_norm_exo( - hi_res, hr_exo, hr_feat, norm_in=norm_in, + hi_res, + hr_exo, + hr_feat, + norm_in=norm_in, ) if isinstance(layer, Sup3rImpute): fidx = self.hr_out_features.index(hr_feat) @@ -1098,10 +1112,7 @@ def _tf_generate(self, low_res, hi_res_exo=None): try: for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 - if isinstance( - layer, - (Sup3rAdder, Sup3rConcat, Sup3rConcatObs, Sup3rImpute), - ): + if isinstance(layer, SUP3R_LAYERS): msg = ( f'layer.name = {layer.name} does not match any ' f'features in exogenous_data ({list(hi_res_exo)})' @@ -1115,6 +1126,7 @@ def _tf_generate(self, low_res, hi_res_exo=None): hi_res = layer(hi_res, hr_exo, fidx) else: hi_res = layer(hi_res, hr_exo) + else: hi_res = layer(hi_res) except Exception as e: diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 8ec67f8c26..bfb1eb6b57 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -6,7 +6,11 @@ import numpy as np import tensorflow as tf -from phygnn.layers.custom_layers import Sup3rConcatObs, Sup3rImpute +from phygnn.layers.custom_layers import ( + Sup3rConcatObs, + Sup3rConcatObsBlock, + Sup3rImpute, +) from tensorflow.keras.losses import MeanAbsoluteError from sup3r.utilities.utilities import RANDOM_GENERATOR @@ -405,7 +409,9 @@ def obs_features(self): features = [] if hasattr(self, '_gen'): for layer in self._gen.layers: - check = isinstance(layer, (Sup3rConcatObs, Sup3rImpute)) + check = isinstance( + layer, (Sup3rConcatObs, Sup3rConcatObsBlock, Sup3rImpute) + ) check = check and layer.name not in features if check: features.append(layer.name) @@ -503,11 +509,7 @@ def get_high_res_exo_input(self, hi_res_true): """Mask high res data to act as sparse observation data. Add this to the standard high res exo input""" exo_data = super().get_high_res_exo_input(hi_res_true) - spatial_frac = RANDOM_GENERATOR.uniform(self.obs_frac['spatial']) - logger.info( - 'Using spatial_frac = %s for the current observation mask', - spatial_frac, - ) + spatial_frac = RANDOM_GENERATOR.uniform(0, self.obs_frac['spatial']) time_frac = self.obs_frac.get('time', None) obs_mask = self._get_obs_mask(hi_res_true, spatial_frac, time_frac) for feature in self.obs_features: From 2f87f500596a896c48c10fa2e9f0c320003749d7 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 5 Feb 2025 11:42:02 -0700 Subject: [PATCH 039/122] fix: epoch always started with disc_loss = 0, resulting in disc never training first batch --- sup3r/models/base.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 08953a8851..f87ff4de0f 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -1099,7 +1099,6 @@ def _train_epoch( train_gen, train_disc, disc_loss_bounds, - loss_mean_window=None, multi_gpu=False, ): """Train the GAN for one epoch. @@ -1142,11 +1141,6 @@ def _train_epoch( loss_means = self._train_record.mean().to_dict() loss_means.setdefault('train_loss_disc', 0) loss_means.setdefault('train_loss_gen', 0) - loss_mean_window = ( - len(batch_handler) - if loss_mean_window is None - else loss_mean_window - ) only_gen = train_gen and not train_disc only_disc = train_disc and not train_gen From 335d0b7514faaea3a358ff59299f4d24d5e0c65b Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 5 Feb 2025 12:32:19 -0700 Subject: [PATCH 040/122] load check for disc training fix --- tests/training/test_train_gan.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/training/test_train_gan.py b/tests/training/test_train_gan.py index ce0860b3cf..fcee20b4ae 100644 --- a/tests/training/test_train_gan.py +++ b/tests/training/test_train_gan.py @@ -97,6 +97,15 @@ def test_train_disc( loaded.train(batch_handler, **model_kwargs) assert all(loaded.history['disc_train_frac'] == 1) + out_dir = os.path.join(td, 'st_gan') + model.save(out_dir) + loaded = model.load(out_dir) + + batch_handler = BatchHandler(**bh_kwargs) + + loaded.train(batch_handler, **model_kwargs) + assert all(loaded.history['train_disc_trained_frac'] == 1) + @pytest.mark.parametrize( ['fp_gen', 'fp_disc', 's_enhance', 't_enhance', 'sample_shape'], From d838135d85df8121d00993b88bfed99d1bd6607d Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 6 Feb 2025 09:54:45 -0700 Subject: [PATCH 041/122] added option to add extra content loss term for obs sites, with specified weight. --- sup3r/models/with_obs.py | 23 ++++++++++++++++---- tests/training/test_train_conditioned_obs.py | 1 + 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index bfb1eb6b57..17c387f521 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -379,7 +379,7 @@ class Sup3rGanFixedObs(Sup3rGan): model is useful for when production runs will be over a domain for which observation data is available.""" - def __init__(self, *args, obs_frac=None, **kwargs): + def __init__(self, *args, obs_frac=None, loss_obs_weight=None, **kwargs): """ Initialize the Sup3rGanFixedObs model. @@ -395,10 +395,15 @@ def __init__(self, *args, obs_frac=None, **kwargs): available (spatial) and the fraction of the full time period that these cover. For each batch a spatial frac will be selected by uniformly selecting from the range ``(0, obs_frac['spatial'])`` + loss_obs_weight : float + Value used to weight observation locations in extra content loss + term. e.g. The new content loss will include ``obs_loss_weight * + MAE(hi_res_gen[~obs_mask], hi_res_true[~obs_mask])`` kwargs : dict Keyword arguments for the ``Sup3rGan`` parent class. """ self.obs_frac = {} if obs_frac is None else obs_frac + self.loss_obs_weight = loss_obs_weight super().__init__(*args, **kwargs) @property @@ -436,17 +441,23 @@ def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): loss_non_obs = MeanAbsoluteError()(hr_true[obs_mask], hr_gen[obs_mask]) return loss_obs, loss_non_obs - def _get_obs_mask(self, hi_res, spatial_frac, time_frac=None): + def _get_obs_mask(self, hi_res, spatial_frac=None, time_frac=None): """Define observation mask for the current batch. This is done with a spatial mask and a temporal mask since often observation data might be very sparse spatially but cover most of the full time period for those locations.""" + spatial_frac = ( + self.obs_frac['spatial'] if spatial_frac is None else spatial_frac + ) obs_mask = RANDOM_GENERATOR.choice( [True, False], size=hi_res.shape[1:3], p=[1 - spatial_frac, spatial_frac], ) if self.is_5d: + time_frac = ( + self.obs_frac['time'] if time_frac is None else time_frac + ) sp_mask = obs_mask.copy() obs_mask = RANDOM_GENERATOR.choice( [True, False], @@ -503,6 +514,7 @@ def model_params(self): """ params = super().model_params params['obs_frac'] = self.obs_frac + params['loss_obs_weight'] = self.loss_obs_weight return params def get_high_res_exo_input(self, hi_res_true): @@ -513,8 +525,8 @@ def get_high_res_exo_input(self, hi_res_true): time_frac = self.obs_frac.get('time', None) obs_mask = self._get_obs_mask(hi_res_true, spatial_frac, time_frac) for feature in self.obs_features: - # obs_features can include a _obs suffix to avoid conflict with - # fully gridded exo features + # obs_features can include a _obs suffix to avoid name conflict + # with fully gridded exo features f_idx = self.hr_out_features.index(feature.replace('_obs', '')) exo_data[feature] = tf.where( obs_mask, np.nan, hi_res_true[..., f_idx] @@ -544,5 +556,8 @@ def _get_hr_exo_and_loss( 'loss_obs': loss_obs, 'loss_non_obs': loss_non_obs, } + if self.loss_obs_weight is not None and calc_loss_kwargs['train_gen']: + loss += self.loss_obs_weight * loss_obs + loss_update['loss_gen'] = loss loss_details.update(loss_update) return loss, loss_details, hi_res_gen, hi_res_exo diff --git a/tests/training/test_train_conditioned_obs.py b/tests/training/test_train_conditioned_obs.py index 539b7e7901..7ef2a8155e 100644 --- a/tests/training/test_train_conditioned_obs.py +++ b/tests/training/test_train_conditioned_obs.py @@ -47,6 +47,7 @@ def test_fixed_wind_obs(gen_config_with_concat_masked): gen_config_with_concat_masked(), pytest.S_FP_DISC, obs_frac={'spatial': 0.1}, + loss_obs_weight=0.1, learning_rate=1e-4, ) test_mask = model._get_obs_mask(np.zeros((1, 20, 20, 1, 1))) From 8d519e11e32692eb47eba44de376d39ac8a224e2 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 6 Feb 2025 10:37:21 -0700 Subject: [PATCH 042/122] added gen loss to previous epoch details, as part of running means --- sup3r/models/abstract.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index bf1f888150..b4a2d93824 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -640,6 +640,23 @@ def update_loss_details(record, new_data, max_batches, prefix=None): record.loc[new_index, key] = new_value return record.iloc[-max_batches:] + def _get_last_epoch_details(self): + """Get loss details from last epoch to use for continued running + averages""" + n_obs = loss_disc = loss_gen = 0 + if 'train_loss_disc' in self.history: + loss_disc = self.history['train_loss_disc'].ffill().values[-1] + if 'train_loss_gen' in self.history: + loss_gen = self.history['train_loss_gen'].ffill().values[-1] + if 'train_n_obs' in self.history: + n_obs = self.history['train_n_obs'].ffill().values[-1] + loss_details = { + 'n_obs': n_obs, + 'train_loss_disc': loss_disc, + 'train_loss_gen': loss_gen, + } + return loss_details + @staticmethod def log_loss_details(loss_details, level='INFO'): """Log the loss details to the module logger. From 8639ff9fa81bbb789f2d00fc3fca74f8d4cc62f0 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 6 Feb 2025 16:26:21 -0700 Subject: [PATCH 043/122] Using running dataframe records of training and validation batch loss details to compute running means. --- sup3r/models/abstract.py | 19 +----------------- sup3r/models/with_obs.py | 43 +++++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index b4a2d93824..d16de3d53d 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -45,7 +45,7 @@ Sup3rConcatObsBlock, Sup3rImpute, SparseAttention, - MaskedSqueezeAndExcitation + MaskedSqueezeAndExcitation, ) @@ -640,23 +640,6 @@ def update_loss_details(record, new_data, max_batches, prefix=None): record.loc[new_index, key] = new_value return record.iloc[-max_batches:] - def _get_last_epoch_details(self): - """Get loss details from last epoch to use for continued running - averages""" - n_obs = loss_disc = loss_gen = 0 - if 'train_loss_disc' in self.history: - loss_disc = self.history['train_loss_disc'].ffill().values[-1] - if 'train_loss_gen' in self.history: - loss_gen = self.history['train_loss_gen'].ffill().values[-1] - if 'train_n_obs' in self.history: - n_obs = self.history['train_n_obs'].ffill().values[-1] - loss_details = { - 'n_obs': n_obs, - 'train_loss_disc': loss_disc, - 'train_loss_gen': loss_gen, - } - return loss_details - @staticmethod def log_loss_details(loss_details, level='INFO'): """Log the loss details to the module logger. diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 17c387f521..f9895da181 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -25,39 +25,42 @@ class Sup3rGanWithObs(Sup3rGan): model is useful for when observations are available for the training domain but not for the production domain.""" - def _calc_val_loss(self, batch, weight_gen_advers, loss_details): + def calc_val_loss(self, batch_handler, weight_gen_advers): """Calculate the validation loss at the current state of model training - for a given batch Parameters ---------- - batch : DsetTuple - Object with ``.high_res``, ``.low_res``, and ``.obs`` arrays + batch_handler : sup3r.preprocessing.BatchHandler + BatchHandler object to iterate through weight_gen_advers : float Weight factor for the adversarial loss component of the generator vs. the discriminator. - loss_details : dict - Namespace of the breakdown of loss components Returns ------- loss_details : dict - Same as input with updated val_* loss info + Running mean for validation loss details """ - _, v_loss_details, hi_res_gen, _ = self._get_hr_exo_and_loss( - batch.low_res, - batch.high_res, - weight_gen_advers=weight_gen_advers, - train_gen=False, - train_disc=False, - ) - - v_loss_details['loss_obs'] = self.calc_loss_obs(batch.obs, hi_res_gen) + logger.debug('Starting end-of-epoch validation loss calculation...') + for batch in batch_handler.val_data: + _, v_loss_details, hi_res_gen, _ = self._get_hr_exo_and_loss( + batch.low_res, + batch.high_res, + weight_gen_advers=weight_gen_advers, + train_gen=False, + train_disc=False, + ) + v_loss_details['loss_obs'] = self.calc_loss_obs( + batch.obs, hi_res_gen + ) - loss_details = self.update_loss_details( - loss_details, v_loss_details, len(batch), prefix='val_' - ) - return loss_details + self._val_record = self.update_loss_details( + self._val_record, + v_loss_details, + len(batch_handler.val_data), + prefix='val_', + ) + return self._val_record.mean(axis=0) def _get_batch_loss_details( self, From ad0ef04cc7efdbe9427e005b7f33cf74e33446c0 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 7 Feb 2025 06:52:53 -0700 Subject: [PATCH 044/122] use index to append loss details record --- sup3r/preprocessing/batch_queues/abstract.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sup3r/preprocessing/batch_queues/abstract.py b/sup3r/preprocessing/batch_queues/abstract.py index fc3a932a9c..3ced2d7eac 100644 --- a/sup3r/preprocessing/batch_queues/abstract.py +++ b/sup3r/preprocessing/batch_queues/abstract.py @@ -285,7 +285,7 @@ def enqueue_batches(self) -> None: for batch in batches: self.queue.enqueue(batch) - if time.time() > log_time + 10: + if time.time() > log_time + 60: logger.debug(self.log_queue_info()) log_time = time.time() From 5e055eb62b4d4e51cac2b609e77132bff1d03360 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 7 Feb 2025 07:54:25 -0700 Subject: [PATCH 045/122] changed trained_frac naming - doesn't make sense to prefix these with 'train_' --- sup3r/models/with_obs.py | 4 ++-- tests/training/test_train_gan.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index f9895da181..9a7374e7d9 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -141,8 +141,8 @@ def _get_batch_loss_details( multi_gpu=multi_gpu, ) - b_loss_details['gen_trained_frac'] = float(trained_gen) - b_loss_details['disc_trained_frac'] = float(trained_disc) + b_loss_details['gen_train_frac'] = float(trained_gen) + b_loss_details['disc_train_frac'] = float(trained_disc) if 'loss_obs' in b_loss_details and not tf.math.is_nan( b_loss_details['loss_obs'] diff --git a/tests/training/test_train_gan.py b/tests/training/test_train_gan.py index fcee20b4ae..81a9c4a91f 100644 --- a/tests/training/test_train_gan.py +++ b/tests/training/test_train_gan.py @@ -104,7 +104,7 @@ def test_train_disc( batch_handler = BatchHandler(**bh_kwargs) loaded.train(batch_handler, **model_kwargs) - assert all(loaded.history['train_disc_trained_frac'] == 1) + assert all(loaded.history['disc_train_frac'] == 1) @pytest.mark.parametrize( From ecbde08dd1a53868b0840de9e3a4f40d012b6740 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 12 Feb 2025 09:33:54 -0700 Subject: [PATCH 046/122] added `loss_mean_window` arg, material derivative loss with extremes, and moved post batch logging to separate method --- sup3r/models/base.py | 1 + sup3r/models/with_obs.py | 1 + sup3r/utilities/loss_metrics.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index f87ff4de0f..42c127fe09 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -757,6 +757,7 @@ def train( train_gen, train_disc, disc_loss_bounds, + loss_mean_window=loss_mean_window, multi_gpu=multi_gpu, ) loss_details.update( diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 9a7374e7d9..19b64a7fd0 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -425,6 +425,7 @@ def obs_features(self): features.append(layer.name) return features + @tf.function def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): """Get loss for observation locations and for non observation locations.""" diff --git a/sup3r/utilities/loss_metrics.py b/sup3r/utilities/loss_metrics.py index 270a511cd8..1e9ec60a04 100644 --- a/sup3r/utilities/loss_metrics.py +++ b/sup3r/utilities/loss_metrics.py @@ -145,7 +145,7 @@ def __call__(self, x1, x2, sigma=1.0): return mmd -class MaterialDerivativeLoss(tf.keras.losses.Loss): +class MaterialDerivativeOnlyLoss(tf.keras.losses.Loss): """Loss class for the material derivative. This is the left hand side of the Navier-Stokes equation and is equal to internal + external forces divided by density. From 8fa47f03b74079647b6131d5a4a31edf0aea76b3 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 12 Feb 2025 11:03:33 -0700 Subject: [PATCH 047/122] material derivative loss test fix --- tests/utilities/test_loss_metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utilities/test_loss_metrics.py b/tests/utilities/test_loss_metrics.py index 5280ec8336..109314c577 100644 --- a/tests/utilities/test_loss_metrics.py +++ b/tests/utilities/test_loss_metrics.py @@ -267,7 +267,7 @@ def test_md_loss(): x = RANDOM_GENERATOR.random((6, 10, 10, 8, 3)) y = x.copy() - md_loss = MaterialDerivativeLoss() + md_loss = MaterialDerivativeLossOnly() u_div = md_loss._compute_md(x, fidx=0) v_div = md_loss._compute_md(x, fidx=1) From 13a8fe051db70ccfb16511ed5c744068d3feb346 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 12 Feb 2025 11:08:06 -0700 Subject: [PATCH 048/122] typo --- tests/utilities/test_loss_metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utilities/test_loss_metrics.py b/tests/utilities/test_loss_metrics.py index 109314c577..1fe5476ab8 100644 --- a/tests/utilities/test_loss_metrics.py +++ b/tests/utilities/test_loss_metrics.py @@ -267,7 +267,7 @@ def test_md_loss(): x = RANDOM_GENERATOR.random((6, 10, 10, 8, 3)) y = x.copy() - md_loss = MaterialDerivativeLossOnly() + md_loss = MaterialDerivativeOnlyLoss() u_div = md_loss._compute_md(x, fidx=0) v_div = md_loss._compute_md(x, fidx=1) From e145aaaac0703b99d55b1332ab27d3e1e0c0737d Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 13 Feb 2025 10:17:38 -0700 Subject: [PATCH 049/122] removing bespoke multi term loss functions which can now be created by providing the proper loss kwarg --- sup3r/utilities/loss_metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sup3r/utilities/loss_metrics.py b/sup3r/utilities/loss_metrics.py index 1e9ec60a04..270a511cd8 100644 --- a/sup3r/utilities/loss_metrics.py +++ b/sup3r/utilities/loss_metrics.py @@ -145,7 +145,7 @@ def __call__(self, x1, x2, sigma=1.0): return mmd -class MaterialDerivativeOnlyLoss(tf.keras.losses.Loss): +class MaterialDerivativeLoss(tf.keras.losses.Loss): """Loss class for the material derivative. This is the left hand side of the Navier-Stokes equation and is equal to internal + external forces divided by density. From bed86091e9ddc62a8d5ecd9106bb5354fe2daf5e Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 13 Feb 2025 10:36:49 -0700 Subject: [PATCH 050/122] Removing old loss functions in tests --- tests/utilities/test_loss_metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utilities/test_loss_metrics.py b/tests/utilities/test_loss_metrics.py index 1fe5476ab8..5280ec8336 100644 --- a/tests/utilities/test_loss_metrics.py +++ b/tests/utilities/test_loss_metrics.py @@ -267,7 +267,7 @@ def test_md_loss(): x = RANDOM_GENERATOR.random((6, 10, 10, 8, 3)) y = x.copy() - md_loss = MaterialDerivativeOnlyLoss() + md_loss = MaterialDerivativeLoss() u_div = md_loss._compute_md(x, fidx=0) v_div = md_loss._compute_md(x, fidx=1) From ab2f4e429e71a7d813a62f46a3f9c1f0ada5abe5 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 13 Feb 2025 13:31:05 -0700 Subject: [PATCH 051/122] adding obs loss to gen content in loss details --- sup3r/models/abstract.py | 2 +- sup3r/models/with_obs.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index d16de3d53d..1ab8f3084f 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -1146,7 +1146,7 @@ def _get_hr_exo_and_loss( ): """Get high-resolution exogenous data, generate synthetic output, and compute loss.""" - hi_res_exo = self.get_high_res_exo_input(hi_res_true) + hi_res_exo = self.get_hr_exo_input(hi_res_true) hi_res_gen = self._tf_generate(low_res, hi_res_exo) loss, loss_details = self.calc_loss( hi_res_true, hi_res_gen, **calc_loss_kwargs diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 19b64a7fd0..81f5070efb 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -521,10 +521,10 @@ def model_params(self): params['loss_obs_weight'] = self.loss_obs_weight return params - def get_high_res_exo_input(self, hi_res_true): + def get_hr_exo_input(self, hi_res_true): """Mask high res data to act as sparse observation data. Add this to the standard high res exo input""" - exo_data = super().get_high_res_exo_input(hi_res_true) + exo_data = super().get_hr_exo_input(hi_res_true) spatial_frac = RANDOM_GENERATOR.uniform(0, self.obs_frac['spatial']) time_frac = self.obs_frac.get('time', None) obs_mask = self._get_obs_mask(hi_res_true, spatial_frac, time_frac) @@ -561,7 +561,11 @@ def _get_hr_exo_and_loss( 'loss_non_obs': loss_non_obs, } if self.loss_obs_weight is not None and calc_loss_kwargs['train_gen']: - loss += self.loss_obs_weight * loss_obs + loss_obs *= self.loss_obs_weight + loss += loss_obs loss_update['loss_gen'] = loss + loss_update['loss_gen_content'] = ( + loss_details['loss_gen_content'] + loss_obs + ) loss_details.update(loss_update) return loss, loss_details, hi_res_gen, hi_res_exo From ec5431712fcacc97dece2c68e8cf893009e33fa0 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Mon, 17 Feb 2025 10:37:19 -0700 Subject: [PATCH 052/122] gen_loss_content functions now return total loss value and dictionary with loss values for each loss term. e.g. if loss consists of mae and mse the dict will include values for each. The dict is used to log values for each term. --- sup3r/models/base.py | 8 +++++--- sup3r/models/dc.py | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 42c127fe09..bfcf664333 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -10,6 +10,7 @@ import numpy as np import pandas as pd import tensorflow as tf +from scipy.stats.mstats import winsorize from sup3r.preprocessing.utilities import get_class_kwargs from sup3r.utilities import VERSION_RECORD @@ -1072,11 +1073,12 @@ def _post_batch(self, ib, b_loss_details, n_batches, previous_means): logger.debug( 'Batch {} out of {} has (gen / disc) loss of: ' '({:.2e} / {:.2e}). Running mean (gen / disc): ' - '({:.2e} / {:.2e}). Trained (gen / disc): ({} / {})'.format( + '({:.2e} / {:.2e}). Trained (gen / disc): ' + '({} / {})'.format( ib + 1, n_batches, - self._train_record['train_loss_gen'].values[-1], - self._train_record['train_loss_disc'].values[-1], + b_loss_details['loss_gen'], + b_loss_details['loss_disc'], gen_loss, disc_loss, trained_gen, diff --git a/sup3r/models/dc.py b/sup3r/models/dc.py index c305773503..0575716173 100644 --- a/sup3r/models/dc.py +++ b/sup3r/models/dc.py @@ -3,6 +3,7 @@ import logging import numpy as np +import tensorflow as tf from .base import Sup3rGan @@ -83,6 +84,7 @@ def calc_val_loss_gen_content(self, batch_handler): hi_res_true=batch.high_res, hi_res_gen=self._tf_generate(batch.low_res, exo_data), ) + loss = tf.reduce_sum(loss.values()) row = i // batch_handler.n_time_bins col = i % batch_handler.n_time_bins losses[row, col] = loss From 5a83f5598a504afa1d9d25a7818e2c1e942beca5 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 18 Feb 2025 08:12:36 -0700 Subject: [PATCH 053/122] Starting fwp integration of conditional obs models --- sup3r/models/conditional.py | 17 ++- tests/forward_pass/test_forward_pass_obs.py | 119 +++++++++++++++++++ tests/training/test_train_conditioned_obs.py | 3 +- 3 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 tests/forward_pass/test_forward_pass_obs.py diff --git a/sup3r/models/conditional.py b/sup3r/models/conditional.py index 18d6b5129c..dd7db810d9 100644 --- a/sup3r/models/conditional.py +++ b/sup3r/models/conditional.py @@ -180,12 +180,11 @@ def meta(self): @property def model_params(self): - """ - Model parameters, used to save model to disc + """Model parameters, used to save model to disc Returns ------- - dict + model_params: dict """ config_optm_g = self.get_optimizer_config(self.optimizer) @@ -239,9 +238,7 @@ def calc_loss_cond_mom(self, output_true, output_gen, mask): moment predictor """ - loss = self.loss_fun(output_true * mask, output_gen * mask) - - return loss + return self.loss_fun(output_true * mask, output_gen * mask) def calc_loss(self, output_true, output_gen, mask): """Calculate the total moment predictor loss @@ -352,7 +349,7 @@ def _train_epoch(self, batch_handler, multi_gpu=False): len(batch_handler), prefix='train_', ) - loss_details = self._train_record.mean(axis=0).to_dict() + loss_details = self._train_record.mean().to_dict() logger.debug( 'Batch {} out of {} has epoch-average ' @@ -458,9 +455,8 @@ def train( ) loss_details.update(self.calc_val_loss(batch_handler)) - msg = f'Epoch {epoch} of {epochs[-1]} ' - msg += 'gen train loss: {:.2e} '.format( - loss_details['train_loss_gen'] + msg = 'Epoch {} of {} gen train loss: {:.2e} '.format( + epoch, epochs[-1], loss_details['train_loss_gen'] ) if all(loss in loss_details for loss in ['val_loss_gen']): @@ -489,4 +485,5 @@ def train( if stop: break + batch_handler.stop() diff --git a/tests/forward_pass/test_forward_pass_obs.py b/tests/forward_pass/test_forward_pass_obs.py new file mode 100644 index 0000000000..fef5c72ab6 --- /dev/null +++ b/tests/forward_pass/test_forward_pass_obs.py @@ -0,0 +1,119 @@ +"""Test the training of super resolution GANs with exogenous observation +data.""" + +import os +import tempfile + +import numpy as np +import pytest + +from sup3r.models import Sup3rGanFixedObs +from sup3r.pipeline.forward_pass import ForwardPass, ForwardPassStrategy +from sup3r.utilities.pytest.helpers import make_fake_dset, make_fake_nc_file +from sup3r.utilities.utilities import RANDOM_GENERATOR + +SHAPE = (20, 20) +FEATURES_W = ['u_10m', 'v_10m'] +TARGET_W = (39.01, -105.15) + +target = (19.3, -123.5) +shape = (8, 8) +sample_shape = (8, 8, 6) +time_slice = slice(None, None, 1) +list_chunk_size = 10 +fwp_chunk_shape = (4, 4, 150) +s_enhance = 3 +t_enhance = 4 + + +@pytest.fixture(scope='module') +def input_files(tmpdir_factory): + """Dummy netcdf input files for :class:`ForwardPass`""" + + input_file = str(tmpdir_factory.mktemp('data').join('fwp_input.nc')) + make_fake_nc_file( + input_file, + shape=(100, 100, 8), + features=['u_10m', 'v_10m'], + ) + obs_file = str(tmpdir_factory.mktemp('data').join('fwp_obs.nc')) + dset = make_fake_dset( + shape=(100, 100, 8), + features=['u_10m', 'v_10m'], + ) + + mask = RANDOM_GENERATOR.choice( + [True, False], dset['u_10m'].shape, p=[0.9, 0.1] + ) + dset['u_10m'][mask] = np.nan + dset['v_10m'][mask] = np.nan + dset.to_netcdf(obs_file) + + return input_file, obs_file + + +def test_fwp_with_obs(input_files, gen_config_with_concat_masked): + """Test a special model trained to conditional output on input + observations.""" + + Sup3rGanFixedObs.seed() + + model = Sup3rGanFixedObs( + gen_config_with_concat_masked(), + pytest.S_FP_DISC, + obs_frac={'spatial': 0.1}, + loss_obs_weight=0.1, + learning_rate=1e-4, + input_resolution={'spatial': '16km', 'temporal': '3600min'}, + ) + + model.meta['lr_features'] = ['u_10m', 'v_10m'] + model.meta['hr_out_features'] = ['u_10m', 'v_10m'] + model.meta['s_enhance'] = 2 + model.meta['t_enhance'] = 1 + + with tempfile.TemporaryDirectory() as td: + model_dir = os.path.join(td, 'test') + model.save(model_dir) + + exo_handler_kwargs = { + 'u_10m': { + 'file_paths': input_files[0], + 'source_file': input_files[1], + 'target': target, + 'shape': shape, + 'cache_dir': td, + }, + 'v_10m': { + 'file_paths': input_files[0], + 'source_file': input_files[1], + 'target': target, + 'shape': shape, + 'cache_dir': td, + }, + } + + model_kwargs = {'model_dirs': [model_dir]} + + out_files = os.path.join(td, 'out_{file_id}.h5') + input_handler_kwargs = { + 'target': target, + 'shape': shape, + 'time_slice': time_slice, + } + handler = ForwardPassStrategy( + input_files, + model_kwargs=model_kwargs, + model_class='MultiStepGan', + fwp_chunk_shape=fwp_chunk_shape, + input_handler_kwargs=input_handler_kwargs, + spatial_pad=0, + temporal_pad=0, + out_pattern=out_files, + exo_handler_kwargs=exo_handler_kwargs, + max_nodes=1, + pass_workers=2, + ) + + forward_pass = ForwardPass(handler) + forward_pass.run(handler, node_index=0) diff --git a/tests/training/test_train_conditioned_obs.py b/tests/training/test_train_conditioned_obs.py index 7ef2a8155e..9ea2a380af 100644 --- a/tests/training/test_train_conditioned_obs.py +++ b/tests/training/test_train_conditioned_obs.py @@ -1,4 +1,5 @@ -"""Test the training of super resolution GANs with exo data.""" +"""Test the training of super resolution GANs with exogenous observation data. +""" import os import tempfile From c9606de2f116baec6560a651074b8b393a8dbfa3 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 18 Feb 2025 08:38:54 -0700 Subject: [PATCH 054/122] test fixes and missed function rename --- sup3r/models/conditional.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sup3r/models/conditional.py b/sup3r/models/conditional.py index dd7db810d9..2f2a99e93a 100644 --- a/sup3r/models/conditional.py +++ b/sup3r/models/conditional.py @@ -352,8 +352,8 @@ def _train_epoch(self, batch_handler, multi_gpu=False): loss_details = self._train_record.mean().to_dict() logger.debug( - 'Batch {} out of {} has epoch-average ' - 'gen loss of: {:.2e}. '.format( + 'Batch {} out of {} has epoch-average gen loss of: ' + '{:.2e}. '.format( ib, len(batch_handler), loss_details['train_loss_gen'] ) ) From 7aa6339f985ebeef56a2e8d5a9fccd4efddaab0f Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 18 Feb 2025 19:02:14 -0700 Subject: [PATCH 055/122] fix: multi-term loss for dc models and multiterm loss test --- sup3r/models/dc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sup3r/models/dc.py b/sup3r/models/dc.py index 0575716173..c305773503 100644 --- a/sup3r/models/dc.py +++ b/sup3r/models/dc.py @@ -3,7 +3,6 @@ import logging import numpy as np -import tensorflow as tf from .base import Sup3rGan @@ -84,7 +83,6 @@ def calc_val_loss_gen_content(self, batch_handler): hi_res_true=batch.high_res, hi_res_gen=self._tf_generate(batch.low_res, exo_data), ) - loss = tf.reduce_sum(loss.values()) row = i // batch_handler.n_time_bins col = i % batch_handler.n_time_bins losses[row, col] = loss From afda2b96b99ac075716bb8f3e5bf363332f2a83a Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 19 Feb 2025 09:53:41 -0700 Subject: [PATCH 056/122] ``min_width`` as optional kwarg instead of determined from generator layers. Cleaned up conditionals and doc strings. --- sup3r/models/base.py | 5 ++--- sup3r/pipeline/slicer.py | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index bfcf664333..282e39674b 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -10,7 +10,6 @@ import numpy as np import pandas as pd import tensorflow as tf -from scipy.stats.mstats import winsorize from sup3r.preprocessing.utilities import get_class_kwargs from sup3r.utilities import VERSION_RECORD @@ -1087,8 +1086,8 @@ def _post_batch(self, ib, b_loss_details, n_batches, previous_means): ) if all([not trained_gen, not trained_disc]): msg = ( - 'For some reason none of the GAN networks trained ' - 'during batch {} out of {}!'.format(ib, n_batches) + 'For some reason none of the GAN networks trained during ' + 'batch {} out of {}!'.format(ib, n_batches) ) logger.warning(msg) warn(msg) diff --git a/sup3r/pipeline/slicer.py b/sup3r/pipeline/slicer.py index dead974d23..7bf0f1b234 100644 --- a/sup3r/pipeline/slicer.py +++ b/sup3r/pipeline/slicer.py @@ -702,9 +702,7 @@ def get_pad_width(self, chunk_index): check_boundary=True, ), self._get_pad_width( - ti_slice, - len(self.dummy_time_index), - self.temporal_pad + ti_slice, len(self.dummy_time_index), self.temporal_pad ), ) From 5758a9747228e9d5e143ecd3207c624513527f3b Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 19 Feb 2025 11:08:11 -0700 Subject: [PATCH 057/122] renaming `_get_batch_loss_details`. moved weight into to start of `train` method. content on extractions in comments. --- sup3r/models/base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 282e39674b..35f04b9baa 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -1138,6 +1138,10 @@ def _train_epoch( lr_shape, hr_shape = batch_handler.shapes self.init_weights(lr_shape, hr_shape) + self.init_weights( + (1, *batch_handler.lr_shape), (1, *batch_handler.hr_shape) + ) + disc_th_low = np.min(disc_loss_bounds) disc_th_high = np.max(disc_loss_bounds) loss_means = self._train_record.mean().to_dict() From a31163df99ab832843101ae03df7fd10835f9a59 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 19 Feb 2025 11:13:49 -0700 Subject: [PATCH 058/122] don't need this `max_paddings` method since `min_width` is a user arg now --- sup3r/models/interface.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index 25f63f410b..e284cc21ac 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -85,27 +85,6 @@ def input_dims(self): return self.models[0].input_dims return 5 - @property - def max_paddings(self): - """Get the maximum padding values used by the generator. This is used - to apply extra padding during forward passes if the raw input doesn't - meet the minimum input shape.""" - - paddings = (1, 1, 1) - if hasattr(self, '_gen'): - for layer in self._gen.layers: - if hasattr(layer, 'paddings'): - new_pw = np.max(layer.paddings, axis=1)[1:-1] - if len(new_pw) < 3: - new_pw = (*new_pw, 1) - paddings = [ - np.max((new_pw[i], paddings[i])) for i in range(3) - ] - return paddings - if hasattr(self, 'models'): - return self.models[0].max_paddings - return paddings - @property def is_5d(self): """Check if model expects spatiotemporal input""" From 78eaacf66ff223afad1fd9b0a0b0d8305d31adc4 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sat, 22 Feb 2025 11:44:30 -0700 Subject: [PATCH 059/122] `if not self.generator_weights` condition added to obs model `init_weights` --- sup3r/models/with_obs.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 81f5070efb..d73e94de87 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -490,22 +490,25 @@ def init_weights(self, lr_shape, hr_shape, device=None): self.default_device will be used. """ - if device is None: - device = self.default_device - - logger.info('Initializing model weights on device "{}"'.format(device)) - low_res = np.ones(lr_shape).astype(np.float32) - hi_res = np.ones(hr_shape).astype(np.float32) - - hr_exo_shape = hr_shape[:-1] + (1,) - hr_exo = np.ones(hr_exo_shape).astype(np.float32) - - with tf.device(device): - hr_exo_data = {} - for feature in self.hr_exo_features + self.obs_features: - hr_exo_data[feature] = hr_exo - _ = self._tf_generate(low_res, hr_exo_data) - _ = self._tf_discriminate(hi_res) + if not self.generator_weights: + if device is None: + device = self.default_device + + logger.info( + 'Initializing model weights on device "{}"'.format(device) + ) + low_res = np.ones(lr_shape).astype(np.float32) + hi_res = np.ones(hr_shape).astype(np.float32) + + hr_exo_shape = hr_shape[:-1] + (1,) + hr_exo = np.ones(hr_exo_shape).astype(np.float32) + + with tf.device(device): + hr_exo_data = {} + for feature in self.hr_exo_features + self.obs_features: + hr_exo_data[feature] = hr_exo + _ = self._tf_generate(low_res, hr_exo_data) + _ = self._tf_discriminate(hi_res) @property def model_params(self): From 3c703bf13f658f699f3172f35c6f2aa3fa317551 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 2 Mar 2025 19:18:03 -0700 Subject: [PATCH 060/122] Removing `Sup3rGanWithObs` model. Keeping just `Sup3rGanFixedObs` model, for conditioning models on observations. --- sup3r/models/__init__.py | 2 +- sup3r/models/with_obs.py | 359 --------------------- tests/data/config_disc_st_test.json | 4 - tests/training/test_train_dual_with_obs.py | 232 ------------- 4 files changed, 1 insertion(+), 596 deletions(-) delete mode 100644 tests/training/test_train_dual_with_obs.py diff --git a/sup3r/models/__init__.py b/sup3r/models/__init__.py index 5e6bce4533..71fc829771 100644 --- a/sup3r/models/__init__.py +++ b/sup3r/models/__init__.py @@ -6,7 +6,7 @@ from .multi_step import MultiStepGan, MultiStepSurfaceMetGan, SolarMultiStepGan from .solar_cc import SolarCC from .surface import SurfaceSpatialMetModel -from .with_obs import Sup3rGanFixedObs, Sup3rGanWithObs +from .with_obs import Sup3rGanFixedObs SPATIAL_FIRST_MODELS = (MultiStepSurfaceMetGan, SolarMultiStepGan) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index d73e94de87..13da969551 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -1,8 +1,6 @@ """Sup3r model with training on observation data.""" import logging -import time -from concurrent.futures import ThreadPoolExecutor import numpy as np import tensorflow as tf @@ -20,363 +18,6 @@ logger = logging.getLogger(__name__) -class Sup3rGanWithObs(Sup3rGan): - """Sup3r GAN model with additional observation data content loss. This - model is useful for when observations are available for the training domain - but not for the production domain.""" - - def calc_val_loss(self, batch_handler, weight_gen_advers): - """Calculate the validation loss at the current state of model training - - Parameters - ---------- - batch_handler : sup3r.preprocessing.BatchHandler - BatchHandler object to iterate through - weight_gen_advers : float - Weight factor for the adversarial loss component of the generator - vs. the discriminator. - - Returns - ------- - loss_details : dict - Running mean for validation loss details - """ - logger.debug('Starting end-of-epoch validation loss calculation...') - for batch in batch_handler.val_data: - _, v_loss_details, hi_res_gen, _ = self._get_hr_exo_and_loss( - batch.low_res, - batch.high_res, - weight_gen_advers=weight_gen_advers, - train_gen=False, - train_disc=False, - ) - v_loss_details['loss_obs'] = self.calc_loss_obs( - batch.obs, hi_res_gen - ) - - self._val_record = self.update_loss_details( - self._val_record, - v_loss_details, - len(batch_handler.val_data), - prefix='val_', - ) - return self._val_record.mean(axis=0) - - def _get_batch_loss_details( - self, - batch, - train_gen, - only_gen, - gen_too_good, - train_disc, - only_disc, - disc_too_good, - weight_gen_advers, - multi_gpu=False, - ): - """Get loss details for a given batch for the current epoch. - - Parameters - ---------- - batch : sup3r.preprocessing.base.DsetTuple - Object with ``.low_res``, ``.high_res``, and ``.obs`` arrays - train_gen : bool - Flag whether to train the generator for this set of epochs - only_gen : bool - Flag whether to only train the generator for this set of epochs - gen_too_good : bool - Flag whether to skip training the generator and only train the - discriminator, due to superior performance, for this batch. - train_disc : bool - Flag whether to train the discriminator for this set of epochs - only_disc : bool - Flag whether to only train the discriminator for this set of epochs - gen_too_good : bool - Flag whether to skip training the discriminator and only train the - generator, due to superior performance, for this batch. - weight_gen_advers : float - Weight factor for the adversarial loss component of the generator - vs. the discriminator. - multi_gpu : bool - Flag to break up the batch for parallel gradient descent - calculations on multiple gpus. If True and multiple GPUs are - present, each batch from the batch_handler will be divided up - between the GPUs and resulting gradients from each GPU will be - summed and then applied once per batch at the nominal learning - rate that the model and optimizer were initialized with. - If true and multiple gpus are found, ``default_device`` device - should be set to /gpu:0 - - Returns - ------- - loss_details : dict - Namespace of the breakdown of loss components for the given batch - """ - trained_gen = False - trained_disc = False - if only_gen or (train_gen and not gen_too_good): - trained_gen = True - b_loss_details = self.timer(self.run_gradient_descent)( - batch.low_res, - batch.high_res, - self.generator_weights, - obs_data=getattr(batch, 'obs', None), - weight_gen_advers=weight_gen_advers, - optimizer=self.optimizer, - train_gen=True, - train_disc=False, - multi_gpu=multi_gpu, - ) - - if only_disc or (train_disc and not disc_too_good): - trained_disc = True - b_loss_details = self.timer(self.run_gradient_descent)( - batch.low_res, - batch.high_res, - self.discriminator_weights, - weight_gen_advers=weight_gen_advers, - optimizer=self.optimizer_disc, - train_gen=False, - train_disc=True, - multi_gpu=multi_gpu, - ) - - b_loss_details['gen_train_frac'] = float(trained_gen) - b_loss_details['disc_train_frac'] = float(trained_disc) - - if 'loss_obs' in b_loss_details and not tf.math.is_nan( - b_loss_details['loss_obs'] - ): - loss_update = b_loss_details['loss_gen'] - loss_update += b_loss_details['loss_obs'] - b_loss_details.update({'loss_gen': loss_update}) - return b_loss_details - - def _get_parallel_grad( - self, - low_res, - hi_res_true, - training_weights, - obs_data=None, - **calc_loss_kwargs, - ): - """Compute gradient for one mini-batch of (low_res, hi_res_true, - obs_data) across multiple GPUs. Can include observation data as well. - """ - - futures = [] - start_time = time.time() - lr_chunks = np.array_split(low_res, len(self.gpu_list)) - hr_true_chunks = np.array_split(hi_res_true, len(self.gpu_list)) - obs_data_chunks = ( - [None] * len(hr_true_chunks) - if obs_data is None - else np.array_split(obs_data, len(self.gpu_list)) - ) - split_mask = False - mask_chunks = None - if 'mask' in calc_loss_kwargs: - split_mask = True - mask_chunks = np.array_split( - calc_loss_kwargs['mask'], len(self.gpu_list) - ) - - with ThreadPoolExecutor(max_workers=len(self.gpu_list)) as exe: - for i in range(len(self.gpu_list)): - if split_mask: - calc_loss_kwargs['mask'] = mask_chunks[i] - futures.append( - exe.submit( - self.get_single_grad, - lr_chunks[i], - hr_true_chunks[i], - training_weights, - obs_data=obs_data_chunks[i], - device_name=f'/gpu:{i}', - **calc_loss_kwargs, - ) - ) - - return self._sum_parallel_grad(futures, start_time=start_time) - - def run_gradient_descent( - self, - low_res, - hi_res_true, - training_weights, - obs_data=None, - optimizer=None, - multi_gpu=False, - **calc_loss_kwargs, - ): - """Run gradient descent for one mini-batch of (low_res, hi_res_true) - and update weights - - Parameters - ---------- - low_res : np.ndarray - Real low-resolution data in a 4D or 5D array: - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - hi_res_true : np.ndarray - Real high-resolution data in a 4D or 5D array: - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - training_weights : list - A list of layer weights that are to-be-trained based on the - current loss weight values. - obs_data : tf.Tensor | None - Optional observation data to use in additional content loss term. - This needs to have NaNs where there is no observation data. - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - optimizer : tf.keras.optimizers.Optimizer - Optimizer class to use to update weights. This can be different if - you're training just the generator or one of the discriminator - models. Defaults to the generator optimizer. - multi_gpu : bool - Flag to break up the batch for parallel gradient descent - calculations on multiple gpus. If True and multiple GPUs are - present, each batch from the batch_handler will be divided up - between the GPUs and resulting gradients from each GPU will be - summed and then applied once per batch at the nominal learning - rate that the model and optimizer were initialized with. - calc_loss_kwargs : dict - Kwargs to pass to the self.calc_loss() method - - Returns - ------- - loss_details : dict - Namespace of the breakdown of loss components - """ - - self.timer.start() - if optimizer is None: - optimizer = self.optimizer - - if not multi_gpu or len(self.gpu_list) < 2: - grad, loss_details = self.get_single_grad( - low_res, - hi_res_true, - training_weights, - obs_data=obs_data, - device_name=self.default_device, - **calc_loss_kwargs, - ) - optimizer.apply_gradients(zip(grad, training_weights)) - self.timer.stop() - logger.debug( - 'Finished single gradient descent step in %s', - self.timer.elapsed_str, - ) - else: - total_grad, loss_details = self._get_parallel_grad( - low_res, - hi_res_true, - training_weights, - obs_data, - **calc_loss_kwargs, - ) - optimizer.apply_gradients(zip(total_grad, training_weights)) - - return loss_details - - @tf.function - def get_single_grad( - self, - low_res, - hi_res_true, - training_weights, - obs_data=None, - device_name=None, - **calc_loss_kwargs, - ): - """Run gradient descent for one mini-batch of (low_res, hi_res_true), - do not update weights, just return gradient details. - - Parameters - ---------- - low_res : np.ndarray - Real low-resolution data in a 4D or 5D array: - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - hi_res_true : np.ndarray - Real high-resolution data in a 4D or 5D array: - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - training_weights : list - A list of layer weights that are to-be-trained based on the - current loss weight values. - obs_data : tf.Tensor | None - Optional observation data to use in additional content loss term. - This needs to have NaNs where there is no observation data. - (n_observations, spatial_1, spatial_2, features) - (n_observations, spatial_1, spatial_2, temporal, features) - device_name : None | str - Optional tensorflow device name for GPU placement. Note that if a - GPU is available, variables will be placed on that GPU even if - device_name=None. - calc_loss_kwargs : dict - Kwargs to pass to the self.calc_loss() method - - Returns - ------- - grad : list - a list or nested structure of Tensors (or IndexedSlices, or None, - or CompositeTensor) representing the gradients for the - training_weights - loss_details : dict - Namespace of the breakdown of loss components - """ - with tf.device(device_name), tf.GradientTape( - watch_accessed_variables=False - ) as tape: - tape.watch(training_weights) - loss, loss_details, hi_res_gen, _ = self._get_hr_exo_and_loss( - low_res, hi_res_true, **calc_loss_kwargs - ) - loss_obs = self.calc_loss_obs(obs_data, hi_res_gen) - loss_update = {'loss_obs': loss_obs} - if calc_loss_kwargs['train_gen'] and not tf.reduce_any( - tf.math.is_nan(loss_obs) - ): - loss += loss_obs - loss_update['loss_gen'] = loss - loss_details.update(loss_update) - grad = tape.gradient(loss, training_weights) - return grad, loss_details - - @tf.function - def calc_loss_obs(self, obs_data, hi_res_gen): - """Calculate loss term for the observation data vs generated - high-resolution data - - Parameters - ---------- - obs_data : tf.Tensor | None - Observation data to use in additional content loss term. - This needs to have NaNs where there is no observation data. - hi_res_gen : tf.Tensor - Superresolved high resolution spatiotemporal data generated by the - generative model. - - Returns - ------- - loss : tf.Tensor - 0D tensor of observation loss - """ - loss_obs = tf.constant(np.nan) - if obs_data is not None: - mask = tf.math.is_nan(obs_data) - masked_obs = obs_data[~mask] - if len(masked_obs) > 0: - loss_obs = MeanAbsoluteError()( - masked_obs, - hi_res_gen[..., : len(self.hr_out_features)][~mask], - ) - return loss_obs - - class Sup3rGanFixedObs(Sup3rGan): """Sup3r GAN model which includes mid network observation fixing. This model is useful for when production runs will be over a domain for which diff --git a/tests/data/config_disc_st_test.json b/tests/data/config_disc_st_test.json index 8a4fd12fab..39083fb490 100644 --- a/tests/data/config_disc_st_test.json +++ b/tests/data/config_disc_st_test.json @@ -16,10 +16,6 @@ {"alpha": 0.2, "class": "LeakyReLU"}, {"class": "Conv3D", "filters": 256, "kernel_size": 3, "padding": "same", "strides": 2}, {"alpha": 0.2, "class": "LeakyReLU"}, - {"class": "Conv3D", "filters": 512, "kernel_size": 3, "padding": "same", "strides": 1}, - {"alpha": 0.2, "class": "LeakyReLU"}, - {"class": "Conv3D", "filters": 512, "kernel_size": 3, "padding": "same", "strides": 2}, - {"alpha": 0.2, "class": "LeakyReLU"}, {"class": "Flatten"}, {"class": "Dense", "units": 2048}, {"alpha": 0.2, "class": "LeakyReLU"}, diff --git a/tests/training/test_train_dual_with_obs.py b/tests/training/test_train_dual_with_obs.py deleted file mode 100644 index 2399b21ba0..0000000000 --- a/tests/training/test_train_dual_with_obs.py +++ /dev/null @@ -1,232 +0,0 @@ -"""Test the training of GANs with dual data handler""" - -import itertools -import os -import tempfile - -import numpy as np -import pytest - -from sup3r.models import Sup3rGanWithObs -from sup3r.preprocessing import ( - Container, - DataHandler, - DualBatchHandler, - DualRasterizer, -) -from sup3r.preprocessing.samplers import DualSampler -from sup3r.utilities.pytest.helpers import BatchHandlerTesterFactory - -TARGET_COORD = (39.01, -105.15) -FEATURES = ['u_100m', 'v_100m'] - - -DualBatchHandlerWithObsTester = BatchHandlerTesterFactory( - DualBatchHandler, DualSampler -) - - -@pytest.mark.parametrize( - [ - 'fp_gen', - 'fp_disc', - 's_enhance', - 't_enhance', - 'sample_shape', - 'mode', - ], - [ - (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16), 'lazy'), - (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16), 'eager'), - (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (20, 20, 1), 'lazy'), - (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (20, 20, 1), 'eager'), - ], -) -def test_train_h5_nc( - fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, mode, n_epoch=2 -): - """Test model training with a dual data handler / batch handler with h5 and - era as hr / lr datasets. Tests both spatiotemporal and spatial models.""" - - lr = 1e-5 - kwargs = { - 'features': FEATURES, - 'target': TARGET_COORD, - 'shape': (20, 20), - } - hr_handler = DataHandler( - pytest.FP_WTK, - **kwargs, - time_slice=slice(None, None, 1), - ) - - lr_handler = DataHandler( - pytest.FP_ERA, - features=FEATURES, - time_slice=slice(None, None, t_enhance), - ) - - dual_rasterizer = DualRasterizer( - data={'low_res': lr_handler.data, 'high_res': hr_handler.data}, - s_enhance=s_enhance, - t_enhance=t_enhance, - ) - obs_data = dual_rasterizer.high_res.copy() - for feat in FEATURES: - tmp = np.full(obs_data[feat].shape, np.nan) - lat_ids = list(range(0, 20, 4)) - lon_ids = list(range(0, 20, 4)) - for ilat, ilon in itertools.product(lat_ids, lon_ids): - tmp[ilat, ilon, :] = obs_data[feat][ilat, ilon] - obs_data[feat] = (obs_data[feat].dims, tmp) - - dual_with_obs = Container( - data={ - 'low_res': dual_rasterizer.low_res, - 'high_res': dual_rasterizer.high_res, - 'obs': obs_data, - } - ) - - batch_handler = DualBatchHandlerWithObsTester( - train_containers=[dual_with_obs], - val_containers=[], - sample_shape=sample_shape, - batch_size=3, - s_enhance=s_enhance, - t_enhance=t_enhance, - n_batches=3, - mode=mode, - ) - - for batch in batch_handler: - assert hasattr(batch, 'obs') - assert not np.isnan(batch.obs).all() - assert np.isnan(batch.obs).any() - - Sup3rGanWithObs.seed() - model = Sup3rGanWithObs( - fp_gen, fp_disc, learning_rate=lr, loss='MeanAbsoluteError' - ) - - with tempfile.TemporaryDirectory() as td: - model_kwargs = { - 'input_resolution': {'spatial': '30km', 'temporal': '60min'}, - 'n_epoch': n_epoch, - 'weight_gen_advers': 0.0, - 'train_gen': True, - 'train_disc': False, - 'checkpoint_int': 1, - 'out_dir': os.path.join(td, 'test_{epoch}'), - } - - model.train(batch_handler, **model_kwargs) - - tlossg = model.history['train_loss_gen'].values - tlosso = model.history['train_loss_obs'].values - assert np.sum(np.diff(tlossg)) < 0 - assert np.sum(np.diff(tlosso)) < 0 - - -@pytest.mark.parametrize( - [ - 'fp_gen', - 'fp_disc', - 's_enhance', - 't_enhance', - 'sample_shape', - 'mode', - ], - [ - (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16), 'lazy'), - (pytest.ST_FP_GEN, pytest.ST_FP_DISC, 3, 4, (12, 12, 16), 'eager'), - (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (20, 20, 1), 'lazy'), - (pytest.S_FP_GEN, pytest.S_FP_DISC, 2, 1, (20, 20, 1), 'eager'), - ], -) -def test_train_coarse_h5( - fp_gen, fp_disc, s_enhance, t_enhance, sample_shape, mode, n_epoch=2 -): - """Test model training with a dual data handler / batch handler with - additional sparse observation data used in extra content loss term. Tests - both spatiotemporal and spatial models.""" - - lr = 1e-5 - kwargs = { - 'features': FEATURES, - 'target': TARGET_COORD, - 'shape': (20, 20), - } - hr_handler = DataHandler( - pytest.FP_WTK, - **kwargs, - time_slice=slice(None, None, 1), - ) - - lr_handler = DataHandler( - pytest.FP_WTK, - **kwargs, - hr_spatial_coarsen=s_enhance, - time_slice=slice(None, None, t_enhance), - ) - - dual_rasterizer = DualRasterizer( - data={'low_res': lr_handler.data, 'high_res': hr_handler.data}, - s_enhance=s_enhance, - t_enhance=t_enhance, - ) - obs_data = dual_rasterizer.high_res.copy() - for feat in FEATURES: - tmp = np.full(obs_data[feat].shape, np.nan) - lat_ids = list(range(0, 20, 4)) - lon_ids = list(range(0, 20, 4)) - for ilat, ilon in itertools.product(lat_ids, lon_ids): - tmp[ilat, ilon, :] = obs_data[feat][ilat, ilon] - obs_data[feat] = (obs_data[feat].dims, tmp) - - dual_with_obs = Container( - data={ - 'low_res': dual_rasterizer.low_res, - 'high_res': dual_rasterizer.high_res, - 'obs': obs_data, - } - ) - - batch_handler = DualBatchHandlerWithObsTester( - train_containers=[dual_with_obs], - val_containers=[], - sample_shape=sample_shape, - batch_size=3, - s_enhance=s_enhance, - t_enhance=t_enhance, - n_batches=3, - mode=mode, - ) - - for batch in batch_handler: - assert hasattr(batch, 'obs') - assert not np.isnan(batch.obs).all() - assert np.isnan(batch.obs).any() - - Sup3rGanWithObs.seed() - model = Sup3rGanWithObs( - fp_gen, fp_disc, learning_rate=lr, loss='MeanAbsoluteError' - ) - - with tempfile.TemporaryDirectory() as td: - model_kwargs = { - 'input_resolution': {'spatial': '30km', 'temporal': '60min'}, - 'n_epoch': n_epoch, - 'weight_gen_advers': 0.0, - 'train_gen': True, - 'train_disc': False, - 'checkpoint_int': 1, - 'out_dir': os.path.join(td, 'test_{epoch}'), - } - - model.train(batch_handler, **model_kwargs) - - tlossg = model.history['train_loss_gen'].values - tlosso = model.history['train_loss_obs'].values - assert np.sum(np.diff(tlossg)) < 0 - assert np.sum(np.diff(tlosso)) < 0 From e0968ea8401b5968f270e6ceab8704de6fc1694b Mon Sep 17 00:00:00 2001 From: bnb32 Date: Mon, 3 Mar 2025 16:17:14 -0700 Subject: [PATCH 061/122] Removing tf.function decorator from some top level functions to prevent unneeded retracing. --- sup3r/models/abstract.py | 1 - sup3r/models/base.py | 6 ------ 2 files changed, 7 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 1ab8f3084f..e92efc6325 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -1153,7 +1153,6 @@ def _get_hr_exo_and_loss( ) return loss, loss_details, hi_res_gen, hi_res_exo - @tf.function def get_single_grad( self, low_res, diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 35f04b9baa..961b7d0ff8 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -632,7 +632,6 @@ def train( adaptive_update_bounds=(0.9, 0.99), adaptive_update_fraction=0.0, multi_gpu=False, - loss_mean_window=None, tensorboard_log=True, tensorboard_profile=False, ): @@ -698,10 +697,6 @@ def train( rate that the model and optimizer were initialized with. If true and multiple gpus are found, ``default_device`` device should be set to /gpu:0 - loss_mean_window : int - Number of batches to use to compute generator and discriminator - loss means, which are used to decide whether to train each network - for a given batch. Defaults to the number of batches in an epoch tensorboard_log : bool Whether to write log file for use with tensorboard. Log data can be viewed with ``tensorboard --logdir `` where ```` @@ -757,7 +752,6 @@ def train( train_gen, train_disc, disc_loss_bounds, - loss_mean_window=loss_mean_window, multi_gpu=multi_gpu, ) loss_details.update( From b80669117d8381a7c7e52c80460349f5cdf7abf3 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Mon, 3 Mar 2025 17:26:13 -0700 Subject: [PATCH 062/122] Removing unused experimental layers: `Sup3rImpute`, `Sup3rConcatObsBlock`, etc --- sup3r/models/abstract.py | 23 +++-------------------- sup3r/models/with_obs.py | 10 ++-------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index e92efc6325..7b899d064e 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -16,13 +16,9 @@ import tensorflow as tf from phygnn import CustomNetwork from phygnn.layers.custom_layers import ( - MaskedSqueezeAndExcitation, - SparseAttention, Sup3rAdder, Sup3rConcat, Sup3rConcatObs, - Sup3rConcatObsBlock, - Sup3rImpute, ) from rex.utilities.utilities import safe_json_load from tensorflow.keras import optimizers @@ -41,11 +37,7 @@ SUP3R_LAYERS = ( Sup3rAdder, Sup3rConcat, - Sup3rConcatObs, - Sup3rConcatObsBlock, - Sup3rImpute, - SparseAttention, - MaskedSqueezeAndExcitation, + Sup3rConcatObs ) @@ -1060,11 +1052,7 @@ def generate( hr_feat, norm_in=norm_in, ) - if isinstance(layer, Sup3rImpute): - fidx = self.hr_out_features.index(hr_feat) - hi_res = layer(hi_res, hr_exo, fidx) - else: - hi_res = layer(hi_res, hr_exo) + hi_res = layer(hi_res, hr_exo) else: hi_res = layer(hi_res) except Exception as e: @@ -1120,12 +1108,7 @@ def _tf_generate(self, low_res, hi_res_exo=None): hr_feat = layer.name.replace('_obs', '') assert layer.name in hi_res_exo, msg hr_exo = hi_res_exo[hr_feat] - - if isinstance(layer, Sup3rImpute): - fidx = self.hr_out_features.index(hr_feat) - hi_res = layer(hi_res, hr_exo, fidx) - else: - hi_res = layer(hi_res, hr_exo) + hi_res = layer(hi_res, hr_exo) else: hi_res = layer(hi_res) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 13da969551..227b9742d2 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -4,11 +4,7 @@ import numpy as np import tensorflow as tf -from phygnn.layers.custom_layers import ( - Sup3rConcatObs, - Sup3rConcatObsBlock, - Sup3rImpute, -) +from phygnn.layers.custom_layers import Sup3rConcatObs from tensorflow.keras.losses import MeanAbsoluteError from sup3r.utilities.utilities import RANDOM_GENERATOR @@ -58,9 +54,7 @@ def obs_features(self): features = [] if hasattr(self, '_gen'): for layer in self._gen.layers: - check = isinstance( - layer, (Sup3rConcatObs, Sup3rConcatObsBlock, Sup3rImpute) - ) + check = isinstance(layer, Sup3rConcatObs) check = check and layer.name not in features if check: features.append(layer.name) From 7dc4e40b9ef5af7f895a928809991d4e369d1a0a Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 6 Mar 2025 08:25:55 -0700 Subject: [PATCH 063/122] Added relativistic discriminator loss used in ESRGAN paper. --- sup3r/models/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 961b7d0ff8..e73d1b285c 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -521,7 +521,7 @@ def calc_loss_disc(disc_out_true, disc_out_gen): disc_out_true : tf.Tensor Raw discriminator outputs from the discriminator model predicting only on ground truth data hi_res_true (not on hi_res_gen). - disc_out_gen : tf.Tensor + disc_out_fake : tf.Tensor Raw discriminator outputs from the discriminator model predicting only on synthetic data hi_res_gen (not on hi_res_true). @@ -540,7 +540,7 @@ def calc_loss_disc(disc_out_true, disc_out_gen): loss_disc = tf.nn.sigmoid_cross_entropy_with_logits( logits=logits, labels=labels ) - return tf.reduce_mean(loss_disc) + return tf.reduce_mean(real_loss) + tf.reduce_mean(fake_loss) def update_adversarial_weights( self, From 020278d87bbc5c6a3143980094134a35dc6c0d1b Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 6 Mar 2025 10:09:07 -0700 Subject: [PATCH 064/122] Concatenating less / more realistic terms to get "mean" - disc getting half right should give 0.5 loss. --- sup3r/models/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index e73d1b285c..961b7d0ff8 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -521,7 +521,7 @@ def calc_loss_disc(disc_out_true, disc_out_gen): disc_out_true : tf.Tensor Raw discriminator outputs from the discriminator model predicting only on ground truth data hi_res_true (not on hi_res_gen). - disc_out_fake : tf.Tensor + disc_out_gen : tf.Tensor Raw discriminator outputs from the discriminator model predicting only on synthetic data hi_res_gen (not on hi_res_true). @@ -540,7 +540,7 @@ def calc_loss_disc(disc_out_true, disc_out_gen): loss_disc = tf.nn.sigmoid_cross_entropy_with_logits( logits=logits, labels=labels ) - return tf.reduce_mean(real_loss) + tf.reduce_mean(fake_loss) + return tf.reduce_mean(loss_disc) def update_adversarial_weights( self, From 57a5c4f8ae7addd0ac7bdcb326d7d912a1bc0d4d Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 18 Mar 2025 12:32:49 -0600 Subject: [PATCH 065/122] Changed `Sup3rGanFixedObs` to Sup3rGanWithObs` --- sup3r/models/__init__.py | 2 +- sup3r/models/abstract.py | 5 +- sup3r/models/with_obs.py | 8 +- sup3r/preprocessing/data_handlers/exo.py | 3 + tests/forward_pass/test_forward_pass_obs.py | 118 ++++++++++++++++--- tests/training/test_train_conditioned_obs.py | 6 +- 6 files changed, 113 insertions(+), 29 deletions(-) diff --git a/sup3r/models/__init__.py b/sup3r/models/__init__.py index 71fc829771..20fff799fb 100644 --- a/sup3r/models/__init__.py +++ b/sup3r/models/__init__.py @@ -6,7 +6,7 @@ from .multi_step import MultiStepGan, MultiStepSurfaceMetGan, SolarMultiStepGan from .solar_cc import SolarCC from .surface import SurfaceSpatialMetModel -from .with_obs import Sup3rGanFixedObs +from .with_obs import Sup3rGanWithObs SPATIAL_FIRST_MODELS = (MultiStepSurfaceMetGan, SolarMultiStepGan) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 7b899d064e..0b9c44c679 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -1041,15 +1041,14 @@ def generate( 'features in exogenous_data ' f'({list(exogenous_data)})' ) - hr_feat = layer.name.replace('_obs', '') - assert hr_feat in exogenous_data, msg + assert layer.name in exogenous_data, msg hr_exo = exogenous_data.get_combine_type_data( layer.name, 'layer' ) hr_exo = self._reshape_norm_exo( hi_res, hr_exo, - hr_feat, + layer.name, norm_in=norm_in, ) hi_res = layer(hi_res, hr_exo) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 227b9742d2..de970e0217 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -14,14 +14,14 @@ logger = logging.getLogger(__name__) -class Sup3rGanFixedObs(Sup3rGan): +class Sup3rGanWithObs(Sup3rGan): """Sup3r GAN model which includes mid network observation fixing. This model is useful for when production runs will be over a domain for which observation data is available.""" def __init__(self, *args, obs_frac=None, loss_obs_weight=None, **kwargs): """ - Initialize the Sup3rGanFixedObs model. + Initialize the Sup3rGanWithObs model. Parameters ---------- @@ -163,7 +163,9 @@ def get_hr_exo_input(self, hi_res_true): """Mask high res data to act as sparse observation data. Add this to the standard high res exo input""" exo_data = super().get_hr_exo_input(hi_res_true) - spatial_frac = RANDOM_GENERATOR.uniform(0, self.obs_frac['spatial']) + spatial_frac = RANDOM_GENERATOR.uniform( + low=0, high=self.obs_frac['spatial'] + ) time_frac = self.obs_frac.get('time', None) obs_mask = self._get_obs_mask(hi_res_true, spatial_frac, time_frac) for feature in self.obs_features: diff --git a/sup3r/preprocessing/data_handlers/exo.py b/sup3r/preprocessing/data_handlers/exo.py index 833a9f693d..24e9fc592f 100644 --- a/sup3r/preprocessing/data_handlers/exo.py +++ b/sup3r/preprocessing/data_handlers/exo.py @@ -371,10 +371,13 @@ def get_exo_steps(cls, feature, models): steps = [] for i, model in enumerate(models): is_sfc_model = model.__class__.__name__ == 'SurfaceSpatialMetModel' + obs_features = getattr(model, 'obs_features', []) if feature.lower() in _lowered(model.lr_features) or is_sfc_model: steps.append({'model': i, 'combine_type': 'input'}) if feature.lower() in _lowered(model.hr_exo_features): steps.append({'model': i, 'combine_type': 'layer'}) + if feature.lower() in _lowered(obs_features): + steps.append({'model': i, 'combine_type': 'layer'}) if ( feature.lower() in _lowered(model.hr_out_features) or is_sfc_model diff --git a/tests/forward_pass/test_forward_pass_obs.py b/tests/forward_pass/test_forward_pass_obs.py index fef5c72ab6..98be8e76b3 100644 --- a/tests/forward_pass/test_forward_pass_obs.py +++ b/tests/forward_pass/test_forward_pass_obs.py @@ -5,9 +5,11 @@ import tempfile import numpy as np +import pandas as pd import pytest +from rex import Outputs -from sup3r.models import Sup3rGanFixedObs +from sup3r.models import Sup3rGanWithObs from sup3r.pipeline.forward_pass import ForwardPass, ForwardPassStrategy from sup3r.utilities.pytest.helpers import make_fake_dset, make_fake_nc_file from sup3r.utilities.utilities import RANDOM_GENERATOR @@ -27,8 +29,8 @@ @pytest.fixture(scope='module') -def input_files(tmpdir_factory): - """Dummy netcdf input files for :class:`ForwardPass`""" +def input_file(tmpdir_factory): + """Dummy input for :class:`ForwardPass`""" input_file = str(tmpdir_factory.mktemp('data').join('fwp_input.nc')) make_fake_nc_file( @@ -36,6 +38,12 @@ def input_files(tmpdir_factory): shape=(100, 100, 8), features=['u_10m', 'v_10m'], ) + return input_file + + +@pytest.fixture(scope='module') +def nc_obs_file(tmpdir_factory): + """Dummy observation data saved to netcdf file""" obs_file = str(tmpdir_factory.mktemp('data').join('fwp_obs.nc')) dset = make_fake_dset( shape=(100, 100, 8), @@ -45,55 +53,127 @@ def input_files(tmpdir_factory): mask = RANDOM_GENERATOR.choice( [True, False], dset['u_10m'].shape, p=[0.9, 0.1] ) - dset['u_10m'][mask] = np.nan - dset['v_10m'][mask] = np.nan + u_10m = dset['u_10m'].values + v_10m = dset['v_10m'].values + u_10m[mask] = np.nan + v_10m[mask] = np.nan + dset['u_10m'] = (dset['u_10m'].dims, u_10m) + dset['v_10m'] = (dset['v_10m'].dims, v_10m) dset.to_netcdf(obs_file) - return input_file, obs_file + return obs_file + + +@pytest.fixture(scope='module') +def h5_obs_file(tmpdir_factory): + """Dummy observation data, flattened and sparsified and saved to h5""" + obs_file = str(tmpdir_factory.mktemp('data').join('fwp_obs.nc')) + dset = make_fake_dset( + shape=(100, 100, 8), + features=['u_10m', 'v_10m'], + ) + + mask = RANDOM_GENERATOR.choice( + [True, False], dset['u_10m'].shape[:-1], p=[0.9, 0.1] + ) + lats = dset.latitude.values[~mask].flatten() + lons = dset.longitude.values[~mask].flatten() + flat_shape = (len(dset.time), len(lats)) + u_10m = dset['u_10m'].values[~mask].reshape(flat_shape) + v_10m = dset['v_10m'].values[~mask].reshape(flat_shape) + + meta = pd.DataFrame({'latitude': lats, 'longitude': lons}) + + shapes = {'u_10m': flat_shape, 'v_10m': flat_shape} + attrs = {'u_10m': None, 'v_10m': None} + chunks = {'u_10m': None, 'v_10m': None} + dtypes = {'u_10m': 'float32', 'v_10m': 'float32'} + + Outputs.init_h5( + obs_file, + ['u_10m', 'v_10m'], + shapes, + attrs, + chunks, + dtypes, + meta=meta, + time_index=pd.DatetimeIndex(dset.time), + ) + with Outputs(obs_file, 'a') as out: + out['u_10m'] = u_10m + out['v_10m'] = v_10m + + return obs_file -def test_fwp_with_obs(input_files, gen_config_with_concat_masked): - """Test a special model trained to conditional output on input +@pytest.mark.parametrize('obs_file', ['nc_obs_file', 'h5_obs_file']) +def test_fwp_with_obs( + input_file, obs_file, gen_config_with_concat_masked, request +): + """Test a special model trained to condition output on input observations.""" - Sup3rGanFixedObs.seed() + obs_file = request.getfixturevalue(obs_file) + Sup3rGanWithObs.seed() - model = Sup3rGanFixedObs( + model = Sup3rGanWithObs( gen_config_with_concat_masked(), pytest.S_FP_DISC, obs_frac={'spatial': 0.1}, loss_obs_weight=0.1, learning_rate=1e-4, - input_resolution={'spatial': '16km', 'temporal': '3600min'}, ) - + model.meta['input_resolution'] = {'spatial': '16km', 'temporal': '3600min'} model.meta['lr_features'] = ['u_10m', 'v_10m'] model.meta['hr_out_features'] = ['u_10m', 'v_10m'] model.meta['s_enhance'] = 2 model.meta['t_enhance'] = 1 with tempfile.TemporaryDirectory() as td: + exo_tmp = { + 'u_10m': { + 'steps': [ + { + 'model': 0, + 'combine_type': 'layer', + 'data': np.ones((6, 20, 20, 1)), + } + ] + }, + 'v_10m': { + 'steps': [ + { + 'model': 0, + 'combine_type': 'layer', + 'data': np.ones((6, 20, 20, 1)), + } + ] + }, + } + _ = model.generate(np.ones((6, 10, 10, 2)), exogenous_data=exo_tmp) model_dir = os.path.join(td, 'test') model.save(model_dir) exo_handler_kwargs = { 'u_10m': { - 'file_paths': input_files[0], - 'source_file': input_files[1], + 'file_paths': input_file, + 'source_file': obs_file, 'target': target, 'shape': shape, 'cache_dir': td, + 's_enhance': 2, }, 'v_10m': { - 'file_paths': input_files[0], - 'source_file': input_files[1], + 'file_paths': input_file, + 'source_file': obs_file, 'target': target, 'shape': shape, 'cache_dir': td, + 's_enhance': 2, }, } - model_kwargs = {'model_dirs': [model_dir]} + model_kwargs = {'model_dir': model_dir} out_files = os.path.join(td, 'out_{file_id}.h5') input_handler_kwargs = { @@ -102,9 +182,9 @@ def test_fwp_with_obs(input_files, gen_config_with_concat_masked): 'time_slice': time_slice, } handler = ForwardPassStrategy( - input_files, + input_file, model_kwargs=model_kwargs, - model_class='MultiStepGan', + model_class='Sup3rGanWithObs', fwp_chunk_shape=fwp_chunk_shape, input_handler_kwargs=input_handler_kwargs, spatial_pad=0, diff --git a/tests/training/test_train_conditioned_obs.py b/tests/training/test_train_conditioned_obs.py index 9ea2a380af..34f62eb499 100644 --- a/tests/training/test_train_conditioned_obs.py +++ b/tests/training/test_train_conditioned_obs.py @@ -7,7 +7,7 @@ import numpy as np import pytest -from sup3r.models import Sup3rGanFixedObs +from sup3r.models import Sup3rGanWithObs from sup3r.preprocessing import ( BatchHandler, DataHandler, @@ -42,9 +42,9 @@ def test_fixed_wind_obs(gen_config_with_concat_masked): sample_shape=(20, 20, 1), ) - Sup3rGanFixedObs.seed() + Sup3rGanWithObs.seed() - model = Sup3rGanFixedObs( + model = Sup3rGanWithObs( gen_config_with_concat_masked(), pytest.S_FP_DISC, obs_frac={'spatial': 0.1}, From e99d7d850624ecf88c6bc724ebc2051f7ea66dc1 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 19 Mar 2025 13:02:48 -0600 Subject: [PATCH 066/122] Initial commit with `ObsRasterizer` for integrating sparse observations with the forward pass routine. --- sup3r/preprocessing/__init__.py | 1 + sup3r/preprocessing/rasterizers/__init__.py | 1 + sup3r/preprocessing/rasterizers/exo.py | 106 ++++++++++++++++++-- tests/conftest.py | 4 +- tests/forward_pass/test_forward_pass_obs.py | 29 +++--- 5 files changed, 115 insertions(+), 26 deletions(-) diff --git a/sup3r/preprocessing/__init__.py b/sup3r/preprocessing/__init__.py index 315e9b79aa..391eef6f8c 100644 --- a/sup3r/preprocessing/__init__.py +++ b/sup3r/preprocessing/__init__.py @@ -55,6 +55,7 @@ ExoRasterizer, ExoRasterizerH5, ExoRasterizerNC, + ObsRasterizer, Rasterizer, SzaRasterizer, ) diff --git a/sup3r/preprocessing/rasterizers/__init__.py b/sup3r/preprocessing/rasterizers/__init__.py index d1c8dff0a4..904e0bebd9 100644 --- a/sup3r/preprocessing/rasterizers/__init__.py +++ b/sup3r/preprocessing/rasterizers/__init__.py @@ -12,6 +12,7 @@ ExoRasterizer, ExoRasterizerH5, ExoRasterizerNC, + ObsRasterizer, SzaRasterizer, ) from .extended import Rasterizer diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index a2fd22b2d3..d777433728 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -6,7 +6,7 @@ import logging import os import shutil -from abc import ABC, abstractmethod +from abc import ABC from dataclasses import dataclass from typing import ClassVar, Optional, Union from warnings import warn @@ -128,9 +128,13 @@ def __post_init__(self): ) @property - @abstractmethod def source_data(self): - """Get the 1D array of source data from the source_file_h5""" + """Get the array of exogenous data from the source_file_h5""" + if self._source_data is None: + self._source_data = self.source_handler[self.feature].data + if 'time' not in self.source_handler[self.feature].dims: + self._source_data = self._source_data.data[:, None] + return self._source_data @property def source_handler(self): @@ -242,12 +246,12 @@ def get_distance_upper_bound(self): """Maximum distance (float) to map high-resolution data from source_file to the low-resolution file_paths input.""" if self.distance_upper_bound is None: - diff = da.diff(self.source_lat_lon, axis=0) - diff = da.median(diff, axis=0).max() - self.distance_upper_bound = diff + diff = da.diff(self.hr_lat_lon, axis=0) + diff = da.abs(da.median(diff, axis=0)).max() + self.distance_upper_bound = np.asarray(diff) logger.info( 'Set distance upper bound to {:.4f}'.format( - np.asarray(self.distance_upper_bound) + self.distance_upper_bound ) ) return self.distance_upper_bound @@ -285,8 +289,11 @@ def data(self): if not os.path.exists(cache_fp): tmp_fp = cache_fp + '.tmp' Cacher.write_netcdf( - tmp_fp, data, max_workers=self.max_workers, chunks=self.chunks, - verbose=self.verbose + tmp_fp, + data, + max_workers=self.max_workers, + chunks=self.chunks, + verbose=self.verbose, ) shutil.move(tmp_fp, cache_fp) logger.info('Moved %s to %s', tmp_fp, cache_fp) @@ -375,6 +382,85 @@ def source_lat_lon(self): return source_lat_lon +class ObsRasterizer(BaseExoRasterizer): + """Rasterizer for sparse spatiotemporal observation data""" + + @property + def source_handler(self): + """Get the Loader object that handles the exogenous data file.""" + feat = self.feature.replace('_obs', '') + msg = f'Getting {self.feature} for full domain from {self.source_file}' + if self._source_handler is None: + logger.info(msg) + self._source_handler = Loader( + file_paths=self.source_file, features=[feat] + ) + return self._source_handler + + @property + def source_data(self): + """Get the 1D array of exogenous data from the source_file_nc""" + feat = self.feature.replace('_obs', '') + return self.source_handler[feat].data + + @property + def tree(self): + """Get the KDTree built on the target lat lon data from the file_paths + input with s_enhance""" + if self._tree is None: + self._tree = KDTree(self.source_lat_lon.reshape((-1, 2))) + return self._tree + + @property + def nn(self): + """Get the nearest neighbor indices. This uses a single neighbor by + default""" + _, nn = self.tree.query( + self.hr_lat_lon.reshape((-1, 2)), + distance_upper_bound=self.get_distance_upper_bound(), + ) + return nn + + def get_data(self): + """Get a raster of source observation values corresponding to the + high-resolution grid (the file_paths input grid * s_enhance * + t_enhance). The shape is (lats, lons, time, 1) + """ + target_tmask = self.hr_time_index.isin(self.source_handler.time_index) + source_tmask = self.source_handler.time_index.isin(self.hr_time_index) + src_data = self.source_data.reshape((-1, self.source_data.shape[-1])) + out = np.full(self.hr_shape, np.nan) + out = out.reshape((-1, out.shape[-1])) + gid_mask = self.nn != src_data.shape[0] + src_data = src_data.vindex[self.nn[gid_mask]] + mask = gid_mask[:, None] & target_tmask + out[mask] = src_data[:, source_tmask].flatten() + hr_data = out.reshape(self.hr_shape) + logger.info( + 'Found {} observations within {:4f} of high-resolution grid ' + 'points.'.format(gid_mask.sum(), self.distance_upper_bound) + ) + logger.info( + 'Finished mapping raster from %s for "%s"', + self.source_file, + self.feature, + ) + cover_frac = (~np.isnan(hr_data)).sum() / hr_data.size + logger.info( + 'Observations cover {:.4f} of the high-res domain.'.format( + cover_frac + ) + ) + data_vars = { + self.feature: ( + Dimension.dims_3d(), + da.asarray(hr_data, dtype=np.float32), + ) + } + ds = xr.Dataset(coords=self.coords, data_vars=data_vars) + return Sup3rX(ds) + + class SzaRasterizer(BaseExoRasterizer): """SzaRasterizer for H5 files""" @@ -413,6 +499,8 @@ def __new__(cls, file_paths, source_file, feature, **kwargs): } if feature.lower() == 'sza': ExoClass = SzaRasterizer + elif feature.lower().endswith('_obs'): + ExoClass = ObsRasterizer else: ExoClass = cls.TypeSpecificClasses[get_source_type(source_file)] return ExoClass(**kwargs) diff --git a/tests/conftest.py b/tests/conftest.py index bfa2722b65..97cde5a239 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -126,8 +126,8 @@ def func(): 'activation': 'relu', }, {'class': 'Cropping2D', 'cropping': 4}, - {'class': 'Sup3rConcatObs', 'name': 'u_10m'}, - {'class': 'Sup3rConcatObs', 'name': 'v_10m'}, + {'class': 'Sup3rConcatObs', 'name': 'u_10m_obs'}, + {'class': 'Sup3rConcatObs', 'name': 'v_10m_obs'}, { 'class': 'FlexiblePadding', 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], diff --git a/tests/forward_pass/test_forward_pass_obs.py b/tests/forward_pass/test_forward_pass_obs.py index 98be8e76b3..aaa4134f44 100644 --- a/tests/forward_pass/test_forward_pass_obs.py +++ b/tests/forward_pass/test_forward_pass_obs.py @@ -11,7 +11,7 @@ from sup3r.models import Sup3rGanWithObs from sup3r.pipeline.forward_pass import ForwardPass, ForwardPassStrategy -from sup3r.utilities.pytest.helpers import make_fake_dset, make_fake_nc_file +from sup3r.utilities.pytest.helpers import make_fake_dset from sup3r.utilities.utilities import RANDOM_GENERATOR SHAPE = (20, 20) @@ -33,11 +33,12 @@ def input_file(tmpdir_factory): """Dummy input for :class:`ForwardPass`""" input_file = str(tmpdir_factory.mktemp('data').join('fwp_input.nc')) - make_fake_nc_file( - input_file, + dset = make_fake_dset( shape=(100, 100, 8), features=['u_10m', 'v_10m'], ) + dset = dset.coarsen(west_east=2, south_north=2).mean() + dset.to_netcdf(input_file) return input_file @@ -46,7 +47,7 @@ def nc_obs_file(tmpdir_factory): """Dummy observation data saved to netcdf file""" obs_file = str(tmpdir_factory.mktemp('data').join('fwp_obs.nc')) dset = make_fake_dset( - shape=(100, 100, 8), + shape=(100, 100, 20), features=['u_10m', 'v_10m'], ) @@ -67,20 +68,20 @@ def nc_obs_file(tmpdir_factory): @pytest.fixture(scope='module') def h5_obs_file(tmpdir_factory): """Dummy observation data, flattened and sparsified and saved to h5""" - obs_file = str(tmpdir_factory.mktemp('data').join('fwp_obs.nc')) + obs_file = str(tmpdir_factory.mktemp('data').join('fwp_obs.h5')) dset = make_fake_dset( - shape=(100, 100, 8), + shape=(100, 100, 20), features=['u_10m', 'v_10m'], ) mask = RANDOM_GENERATOR.choice( - [True, False], dset['u_10m'].shape[:-1], p=[0.9, 0.1] + [True, False], dset.latitude.values.shape, p=[0.95, 0.05] ) lats = dset.latitude.values[~mask].flatten() lons = dset.longitude.values[~mask].flatten() flat_shape = (len(dset.time), len(lats)) - u_10m = dset['u_10m'].values[~mask].reshape(flat_shape) - v_10m = dset['v_10m'].values[~mask].reshape(flat_shape) + u_10m = dset['u_10m'].values[:, ~mask].reshape(flat_shape) + v_10m = dset['v_10m'].values[:, ~mask].reshape(flat_shape) meta = pd.DataFrame({'latitude': lats, 'longitude': lons}) @@ -131,7 +132,7 @@ def test_fwp_with_obs( with tempfile.TemporaryDirectory() as td: exo_tmp = { - 'u_10m': { + 'u_10m_obs': { 'steps': [ { 'model': 0, @@ -140,7 +141,7 @@ def test_fwp_with_obs( } ] }, - 'v_10m': { + 'v_10m_obs': { 'steps': [ { 'model': 0, @@ -155,21 +156,19 @@ def test_fwp_with_obs( model.save(model_dir) exo_handler_kwargs = { - 'u_10m': { + 'u_10m_obs': { 'file_paths': input_file, 'source_file': obs_file, 'target': target, 'shape': shape, 'cache_dir': td, - 's_enhance': 2, }, - 'v_10m': { + 'v_10m_obs': { 'file_paths': input_file, 'source_file': obs_file, 'target': target, 'shape': shape, 'cache_dir': td, - 's_enhance': 2, }, } From 8130f525d0e540f6f08d02af1f573e8049bc6734 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 26 Mar 2025 08:22:00 -0600 Subject: [PATCH 067/122] moved `obs_features` to interface to use base `init_weights` method. Some new sup3r obs layers included. exo_handler_kwargs doc string updated. --- sup3r/models/abstract.py | 11 +- sup3r/models/base.py | 2 +- sup3r/models/interface.py | 29 +++- sup3r/models/with_obs.py | 140 ++++++++----------- sup3r/pipeline/strategy.py | 13 +- sup3r/preprocessing/rasterizers/exo.py | 14 +- tests/conftest.py | 86 ++++++++++++ tests/training/test_train_conditioned_obs.py | 37 ++--- 8 files changed, 217 insertions(+), 115 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 0b9c44c679..c0735664cd 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -19,6 +19,8 @@ Sup3rAdder, Sup3rConcat, Sup3rConcatObs, + Sup3rConcatWeightedObs, + Sup3rConcatWeightedObsWithEmbedding, ) from rex.utilities.utilities import safe_json_load from tensorflow.keras import optimizers @@ -37,7 +39,9 @@ SUP3R_LAYERS = ( Sup3rAdder, Sup3rConcat, - Sup3rConcatObs + Sup3rConcatObs, + Sup3rConcatWeightedObs, + Sup3rConcatWeightedObsWithEmbedding, ) @@ -1048,7 +1052,7 @@ def generate( hr_exo = self._reshape_norm_exo( hi_res, hr_exo, - layer.name, + layer.name.replace('_obs', ''), norm_in=norm_in, ) hi_res = layer(hi_res, hr_exo) @@ -1104,9 +1108,8 @@ def _tf_generate(self, low_res, hi_res_exo=None): f'layer.name = {layer.name} does not match any ' f'features in exogenous_data ({list(hi_res_exo)})' ) - hr_feat = layer.name.replace('_obs', '') assert layer.name in hi_res_exo, msg - hr_exo = hi_res_exo[hr_feat] + hr_exo = hi_res_exo[layer.name] hi_res = layer(hi_res, hr_exo) else: diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 961b7d0ff8..b3ff5e4f70 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -427,7 +427,7 @@ def init_weights(self, lr_shape, hr_shape, device=None): with tf.device(device): hr_exo_data = {} - for feature in self.hr_exo_features: + for feature in self.hr_exo_features + self.obs_features: hr_exo_data[feature] = hr_exo _ = self._tf_generate(low_res, hr_exo_data) _ = self._tf_discriminate(hi_res) diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index e284cc21ac..65b63fc31b 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -10,7 +10,13 @@ import numpy as np from phygnn import CustomNetwork -from phygnn.layers.custom_layers import Sup3rAdder, Sup3rConcat +from phygnn.layers.custom_layers import ( + Sup3rAdder, + Sup3rConcat, + Sup3rConcatObs, + Sup3rConcatWeightedObs, + Sup3rConcatWeightedObsWithEmbedding, +) from sup3r.preprocessing.data_handlers import ExoData from sup3r.utilities import VERSION_RECORD @@ -19,6 +25,13 @@ logger = logging.getLogger(__name__) +SUP3R_OBS_LAYERS = ( + Sup3rConcatObs, + Sup3rConcatWeightedObs, + Sup3rConcatWeightedObsWithEmbedding, +) + + class AbstractInterface(ABC): """ Abstract class to define the required interface for Sup3r model subclasses @@ -392,6 +405,20 @@ def hr_exo_features(self): ] return features + @property + def obs_features(self): + """Get list of exogenous observation feature names the model uses. + These come from the names of the ``Sup3rObs..`` layers.""" + # pylint: disable=E1101 + features = [] + if hasattr(self, '_gen'): + for layer in self._gen.layers: + check = isinstance(layer, SUP3R_OBS_LAYERS) + check = check and layer.name not in features + if check: + features.append(layer.name) + return features + @property def smoothing(self): """Value of smoothing parameter used in gaussian filtering of coarsened diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index de970e0217..ff1ad89150 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -4,7 +4,6 @@ import numpy as np import tensorflow as tf -from phygnn.layers.custom_layers import Sup3rConcatObs from tensorflow.keras.losses import MeanAbsoluteError from sup3r.utilities.utilities import RANDOM_GENERATOR @@ -15,11 +14,18 @@ class Sup3rGanWithObs(Sup3rGan): - """Sup3r GAN model which includes mid network observation fixing. This + """Sup3r GAN model which includes mid network observation fusion. This model is useful for when production runs will be over a domain for which observation data is available.""" - def __init__(self, *args, obs_frac=None, loss_obs_weight=None, **kwargs): + def __init__( + self, + *args, + onshore_obs_frac=None, + offshore_obs_frac=None, + loss_obs_weight=None, + **kwargs, + ): """ Initialize the Sup3rGanWithObs model. @@ -27,14 +33,19 @@ def __init__(self, *args, obs_frac=None, loss_obs_weight=None, **kwargs): ---------- args : list Positional args for ``Sup3rGan`` parent class. - obs_frac : dict - Fraction of the batch that should be "fixed" with observations. - Should include ``spatial`` key and optionally ``time`` key if this - is a spatiotemporal model. The values should correspond roughly to - the fraction of the production domain for which observations are - available (spatial) and the fraction of the full time period that - these cover. For each batch a spatial frac will be selected by - uniformly selecting from the range ``(0, obs_frac['spatial'])`` + onshore_obs_frac : dict + Fraction of the batch that should be treated as onshore + observations. Should include ``spatial`` key and optionally + ``time`` key if this is a spatiotemporal model. The values should + correspond roughly to the fraction of the production domain for + which onshore observations are available (spatial) and the fraction + of the full time period that these cover. For each batch a spatial + frac will be selected by uniformly selecting from the range ``(0, + obs_frac['spatial'])`` + offshore_obs_frac : dict + Same as ``onshore_obs_frac`` but for offshore observations. + Offshore observations are frequently sparser than onshore + observations. loss_obs_weight : float Value used to weight observation locations in extra content loss term. e.g. The new content loss will include ``obs_loss_weight * @@ -42,36 +53,27 @@ def __init__(self, *args, obs_frac=None, loss_obs_weight=None, **kwargs): kwargs : dict Keyword arguments for the ``Sup3rGan`` parent class. """ - self.obs_frac = {} if obs_frac is None else obs_frac + self.onshore_obs_frac = ( + {} if onshore_obs_frac is None else onshore_obs_frac + ) + self.offshore_obs_frac = ( + {} if offshore_obs_frac is None else offshore_obs_frac + ) self.loss_obs_weight = loss_obs_weight super().__init__(*args, **kwargs) - @property - def obs_features(self): - """Get list of exogenous observation feature names the model uses. - These come from the names of the ``Sup3rObs`` layers.""" - # pylint: disable=E1101 - features = [] - if hasattr(self, '_gen'): - for layer in self._gen.layers: - check = isinstance(layer, Sup3rConcatObs) - check = check and layer.name not in features - if check: - features.append(layer.name) - return features - @tf.function def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): """Get loss for observation locations and for non observation locations.""" hr_true = [ - hi_res_true[..., self.hr_out_features.index(f)] + hi_res_true[..., self.hr_out_features.index(f.replace('_obs', ''))] for f in self.obs_features ] hr_true = tf.stack(hr_true, axis=-1) hr_gen = [ - hi_res_gen[..., self.hr_out_features.index(f)] + hi_res_gen[..., self.hr_out_features.index(f.replace('_obs', ''))] for f in self.obs_features ] hr_gen = tf.stack(hr_gen, axis=-1) @@ -80,23 +82,15 @@ def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): loss_non_obs = MeanAbsoluteError()(hr_true[obs_mask], hr_gen[obs_mask]) return loss_obs, loss_non_obs - def _get_obs_mask(self, hi_res, spatial_frac=None, time_frac=None): - """Define observation mask for the current batch. This is done - with a spatial mask and a temporal mask since often observation data - might be very sparse spatially but cover most of the full time period - for those locations.""" - spatial_frac = ( - self.obs_frac['spatial'] if spatial_frac is None else spatial_frac - ) + def _get_obs_mask(self, hi_res, spatial_frac, time_frac=None): + """Get observation mask for a given spatial and temporal obs + fraction.""" obs_mask = RANDOM_GENERATOR.choice( [True, False], size=hi_res.shape[1:3], p=[1 - spatial_frac, spatial_frac], ) if self.is_5d: - time_frac = ( - self.obs_frac['time'] if time_frac is None else time_frac - ) sp_mask = obs_mask.copy() obs_mask = RANDOM_GENERATOR.choice( [True, False], @@ -106,44 +100,29 @@ def _get_obs_mask(self, hi_res, spatial_frac=None, time_frac=None): obs_mask[sp_mask] = True return np.repeat(obs_mask[None, ...], hi_res.shape[0], axis=0) - def init_weights(self, lr_shape, hr_shape, device=None): - """Initialize the generator and discriminator weights with device - placement. - - Parameters - ---------- - lr_shape : tuple - Shape of one batch of low res input data for sup3r resolution. Note - that the batch size (axis=0) must be included, but the actual batch - size doesnt really matter. - hr_shape : tuple - Shape of one batch of high res input data for sup3r resolution. - Note that the batch size (axis=0) must be included, but the actual - batch size doesnt really matter. - device : str | None - Option to place model weights on a device. If None, - self.default_device will be used. - """ - - if not self.generator_weights: - if device is None: - device = self.default_device - - logger.info( - 'Initializing model weights on device "{}"'.format(device) + def get_obs_mask(self, hi_res): + """Define observation mask for the current batch. This is done + with a spatial mask and a temporal mask since often observation data + might be very sparse spatially but cover most of the full time period + for those locations. This is also divided between onshore and offshore + regions""" + on_sf = RANDOM_GENERATOR.uniform( + low=0, high=self.onshore_obs_frac['spatial'] + ) + on_tf = self.onshore_obs_frac.get('time', None) + off_tf = self.offshore_obs_frac.get('time', None) + obs_mask = self._get_obs_mask(hi_res, on_sf, on_tf) + if 'topography' in self.hr_exo_features and self.offshore_obs_frac: + topo_idx = len(self.hr_out_features) + self.hr_exo_features.index( + 'topography' ) - low_res = np.ones(lr_shape).astype(np.float32) - hi_res = np.ones(hr_shape).astype(np.float32) - - hr_exo_shape = hr_shape[:-1] + (1,) - hr_exo = np.ones(hr_exo_shape).astype(np.float32) - - with tf.device(device): - hr_exo_data = {} - for feature in self.hr_exo_features + self.obs_features: - hr_exo_data[feature] = hr_exo - _ = self._tf_generate(low_res, hr_exo_data) - _ = self._tf_discriminate(hi_res) + topo = hi_res[..., topo_idx] + off_sf = RANDOM_GENERATOR.uniform( + low=0, high=self.offshore_obs_frac['spatial'] + ) + offshore_mask = self._get_obs_mask(hi_res, off_sf, off_tf) + obs_mask = tf.where(topo > 0, obs_mask, offshore_mask) + return obs_mask @property def model_params(self): @@ -155,7 +134,8 @@ def model_params(self): dict """ params = super().model_params - params['obs_frac'] = self.obs_frac + params['onshore_obs_frac'] = self.onshore_obs_frac + params['offshore_obs_frac'] = self.offshore_obs_frac params['loss_obs_weight'] = self.loss_obs_weight return params @@ -163,11 +143,7 @@ def get_hr_exo_input(self, hi_res_true): """Mask high res data to act as sparse observation data. Add this to the standard high res exo input""" exo_data = super().get_hr_exo_input(hi_res_true) - spatial_frac = RANDOM_GENERATOR.uniform( - low=0, high=self.obs_frac['spatial'] - ) - time_frac = self.obs_frac.get('time', None) - obs_mask = self._get_obs_mask(hi_res_true, spatial_frac, time_frac) + obs_mask = self.get_obs_mask(hi_res_true) for feature in self.obs_features: # obs_features can include a _obs suffix to avoid name conflict # with fully gridded exo features diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index a7a454438d..7b8b25680a 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -131,11 +131,14 @@ class ForwardPassStrategy: Dictionary of args to pass to :class:`~sup3r.preprocessing.data_handlers.ExoDataHandler` for extracting exogenous features for foward passes. This should be - a nested dictionary with keys for each exogenous feature. The - dictionaries corresponding to the feature names should include the path - to exogenous data source and the files used for input to the forward - passes, at minimum. Can also provide a dictionary of - ``input_handler_kwargs`` used for the handler which opens the + a nested dictionary with keys for each exogenous feature. If the + exogenous feature is sparse observation data, which will be rasterized + with :class:`~sup3r.preprocessing.rasterizers.ObsRasterizer`, the + feature name should include a "_obs" suffix. The dictionaries + corresponding to the feature names should include the path to exogenous + data source and the files used for input to the forward passes, at + minimum. Can also provide a dictionary of ``input_handler_kwargs`` used + for the handler which opens the exogenous data. e.g.:: {'topography': { 'source_file': ..., diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index d777433728..4610318515 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -491,18 +491,20 @@ class ExoRasterizer(BaseExoRasterizer, metaclass=Sup3rMeta): def __new__(cls, file_paths, source_file, feature, **kwargs): """Override parent class to return type specific class based on `source_file`""" - kwargs = { - 'file_paths': file_paths, - 'source_file': source_file, - 'feature': feature, - **kwargs, - } if feature.lower() == 'sza': ExoClass = SzaRasterizer elif feature.lower().endswith('_obs'): ExoClass = ObsRasterizer else: ExoClass = cls.TypeSpecificClasses[get_source_type(source_file)] + + kwargs = { + 'file_paths': file_paths, + 'source_file': source_file, + 'feature': feature.replace('_obs', ''), + **kwargs, + } + return ExoClass(**kwargs) _signature_objs = (BaseExoRasterizer,) diff --git a/tests/conftest.py b/tests/conftest.py index 97cde5a239..ae77d7548e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -146,6 +146,92 @@ def func(): return func +@pytest.fixture(scope='package') +def gen_config_with_concat_masked_weighted(): + """Get generator config with custom concat masked (and weighted) layer.""" + + def func(): + return [ + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 2, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 64, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 64, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + {'class': 'SpatialExpansion', 'spatial_mult': 2}, + {'class': 'Activation', 'activation': 'relu'}, + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 2, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + { + 'class': 'Sup3rConcatWeightedObsWithEmbedding', + 'name': 'u_10m_obs', + }, + { + 'class': 'Sup3rConcatWeightedObsWithEmbedding', + 'name': 'v_10m_obs', + }, + { + 'class': 'FlexiblePadding', + 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], + 'mode': 'REFLECT', + }, + { + 'class': 'Conv2DTranspose', + 'filters': 2, + 'kernel_size': 3, + 'strides': 1, + 'activation': 'relu', + }, + {'class': 'Cropping2D', 'cropping': 4}, + ] + + return func + + @pytest.fixture(scope='package') def gen_config_with_topo(): """Get generator config with custom topo layer.""" diff --git a/tests/training/test_train_conditioned_obs.py b/tests/training/test_train_conditioned_obs.py index 34f62eb499..23b75aada7 100644 --- a/tests/training/test_train_conditioned_obs.py +++ b/tests/training/test_train_conditioned_obs.py @@ -1,5 +1,5 @@ -"""Test the training of super resolution GANs with exogenous observation data. -""" +"""Test the training of super resolution GANs with exogenous observation +data.""" import os import tempfile @@ -19,9 +19,18 @@ TARGET_W = (39.01, -105.15) -def test_fixed_wind_obs(gen_config_with_concat_masked): +@pytest.mark.parametrize( + 'gen_config', + [ + 'gen_config_with_concat_masked', + 'gen_config_with_concat_masked_weighted', + ], +) +def test_fixed_wind_obs(gen_config, request): """Test a special model which fixes observations mid network with ``Sup3rConcatObs`` layer.""" + + gen_config = request.getfixturevalue(gen_config)() kwargs = { 'file_paths': pytest.FP_WTK, 'features': FEATURES_W, @@ -45,16 +54,16 @@ def test_fixed_wind_obs(gen_config_with_concat_masked): Sup3rGanWithObs.seed() model = Sup3rGanWithObs( - gen_config_with_concat_masked(), + gen_config, pytest.S_FP_DISC, - obs_frac={'spatial': 0.1}, + onshore_obs_frac={'spatial': 0.1}, loss_obs_weight=0.1, learning_rate=1e-4, ) - test_mask = model._get_obs_mask(np.zeros((1, 20, 20, 1, 1))) + test_mask = model.get_obs_mask(np.zeros((1, 20, 20, 1, 1))) frac = 1 - test_mask.sum() / test_mask.size assert np.abs(0.1 - frac) < test_mask.size / (2 * np.sqrt(test_mask.size)) - assert model.obs_features == ['u_10m', 'v_10m'] + assert model.obs_features == ['u_10m_obs', 'v_10m_obs'] with tempfile.TemporaryDirectory() as td: model_kwargs = { 'input_resolution': {'spatial': '16km', 'temporal': '3600min'}, @@ -82,16 +91,12 @@ def test_fixed_wind_obs(gen_config_with_concat_masked): y = model.generate(x, exogenous_data=None) exo_tmp = { - 'u_10m': { - 'steps': [ - {'model': 0, 'combine_type': 'layer', 'data': u10m_obs} - ] + 'u_10m_obs': { + 'steps': [{'model': 0, 'combine_type': 'layer', 'data': u10m_obs}] + }, + 'v_10m_obs': { + 'steps': [{'model': 0, 'combine_type': 'layer', 'data': v10m_obs}] }, - 'v_10m': { - 'steps': [ - {'model': 0, 'combine_type': 'layer', 'data': v10m_obs} - ] - } } y = model.generate(x, exogenous_data=exo_tmp) From bff8b71c580df5d06e291d193ffb3315d62b3ad5 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 1 Apr 2025 11:04:24 -0600 Subject: [PATCH 068/122] Clipped negative lower limit for observation mask sampling. Era downloader edit - don't add zg to every level variable download. --- sup3r/models/abstract.py | 36 ++++----- sup3r/models/interface.py | 2 + sup3r/models/with_obs.py | 24 +++--- sup3r/pipeline/utilities.py | 11 ++- sup3r/preprocessing/names.py | 2 + sup3r/preprocessing/rasterizers/exo.py | 2 +- sup3r/utilities/era_downloader.py | 38 +++++---- sup3r/utilities/loss_metrics.py | 85 +++++++++++++++++++++ tests/conftest.py | 4 +- tests/forward_pass/test_forward_pass_obs.py | 7 +- tests/loaders/test_file_loading.py | 2 +- 11 files changed, 154 insertions(+), 59 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index c0735664cd..20dfd831ee 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -18,6 +18,7 @@ from phygnn.layers.custom_layers import ( Sup3rAdder, Sup3rConcat, + Sup3rConcatEmbeddedObs, Sup3rConcatObs, Sup3rConcatWeightedObs, Sup3rConcatWeightedObsWithEmbedding, @@ -40,6 +41,7 @@ Sup3rAdder, Sup3rConcat, Sup3rConcatObs, + Sup3rConcatEmbeddedObs, Sup3rConcatWeightedObs, Sup3rConcatWeightedObsWithEmbedding, ) @@ -427,12 +429,12 @@ def _init_records(self): self._val_record = self._history[val_cols].iloc[-1:] self._val_record = self._val_record.reset_index(drop=True) - def get_hr_exo_input(self, high_res): - """Get exogenous feature data from high_res + def get_hr_exo_input(self, hi_res): + """Get exogenous feature data from hi_res Parameters ---------- - high_res : tf.Tensor + hi_res : tf.Tensor Ground truth high resolution spatiotemporal data. Returns @@ -445,33 +447,33 @@ def get_hr_exo_input(self, high_res): for feature in self.hr_exo_features: f_idx = self.hr_exo_features.index(feature) f_idx += len(self.hr_out_features) - exo_fdata = high_res[..., f_idx : f_idx + 1] + exo_fdata = hi_res[..., f_idx : f_idx + 1] exo_data[feature] = exo_fdata return exo_data @tf.function - def _combine_loss_input(self, high_res_true, high_res_gen): - """Combine exogenous feature data from high_res_true with high_res_gen + def _combine_loss_input(self, hi_res_true, hi_res_gen): + """Combine exogenous feature data from hi_res_true with hi_res_gen for loss calculation Parameters ---------- - high_res_true : tf.Tensor + hi_res_true : tf.Tensor Ground truth high resolution spatiotemporal data. - high_res_gen : tf.Tensor + hi_res_gen : tf.Tensor Superresolved high resolution spatiotemporal data generated by the generative model. Returns ------- - high_res_gen : tf.Tensor - Same as input with exogenous data combined with high_res input + hi_res_gen : tf.Tensor + Same as input with exogenous data combined with hi_res input """ - if high_res_true.shape[-1] > high_res_gen.shape[-1]: - exo_dict = self.get_hr_exo_input(high_res_true) + if hi_res_true.shape[-1] > hi_res_gen.shape[-1]: + exo_dict = self.get_hr_exo_input(hi_res_true) exo_data = [exo_dict[feat] for feat in self.hr_exo_features] - high_res_gen = tf.concat((high_res_gen, *exo_data), axis=-1) - return high_res_gen + hi_res_gen = tf.concat((hi_res_gen, *exo_data), axis=-1) + return hi_res_gen @classmethod def get_loss_fun(cls, loss): @@ -505,11 +507,11 @@ def get_loss_fun(cls, loss): loss_funcs = [cls._get_loss_fun({ln: loss[ln]}) for ln in lns] weights = copy.deepcopy(loss).pop('term_weights', [1.0] * len(lns)) - def loss_fun(x1, x2): + def loss_fun(hi_res_true, hi_res_gen): loss_details = {} loss = 0 for i, (ln, loss_func) in enumerate(zip(lns, loss_funcs)): - val = loss_func(x1, x2) + val = loss_func(hi_res_true, hi_res_gen) loss_details[camel_to_underscore(ln)] = val loss += weights[i] * val return loss, loss_details @@ -1082,7 +1084,7 @@ def _tf_generate(self, low_res, hi_res_exo=None): Real low-resolution data. The generator should always received normalized data with mean=0 stdev=1. hi_res_exo : dict - Dictionary of exogenous_data with same resolution as high_res data + Dictionary of exogenous_data with same resolution as hi_res data e.g. ``{'topography': np.array}`` The arrays in this dictionary should be a 4D array for spatial enhancement model or 5D array for a spatiotemporal enhancement diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index 65b63fc31b..d1b65c26f0 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -13,6 +13,7 @@ from phygnn.layers.custom_layers import ( Sup3rAdder, Sup3rConcat, + Sup3rConcatEmbeddedObs, Sup3rConcatObs, Sup3rConcatWeightedObs, Sup3rConcatWeightedObsWithEmbedding, @@ -27,6 +28,7 @@ SUP3R_OBS_LAYERS = ( Sup3rConcatObs, + Sup3rConcatEmbeddedObs, Sup3rConcatWeightedObs, Sup3rConcatWeightedObsWithEmbedding, ) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index ff1ad89150..7bda016ce3 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -66,20 +66,13 @@ def __init__( def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): """Get loss for observation locations and for non observation locations.""" - - hr_true = [ - hi_res_true[..., self.hr_out_features.index(f.replace('_obs', ''))] - for f in self.obs_features - ] - hr_true = tf.stack(hr_true, axis=-1) - hr_gen = [ - hi_res_gen[..., self.hr_out_features.index(f.replace('_obs', ''))] - for f in self.obs_features - ] - hr_gen = tf.stack(hr_gen, axis=-1) - - loss_obs = MeanAbsoluteError()(hr_true[~obs_mask], hr_gen[~obs_mask]) - loss_non_obs = MeanAbsoluteError()(hr_true[obs_mask], hr_gen[obs_mask]) + hr_true = hi_res_true[..., : len(self.hr_out_features)] + loss_obs = MeanAbsoluteError()( + hr_true[~obs_mask], hi_res_gen[~obs_mask] + ) + loss_non_obs = MeanAbsoluteError()( + hr_true[obs_mask], hi_res_gen[obs_mask] + ) return loss_obs, loss_non_obs def _get_obs_mask(self, hi_res, spatial_frac, time_frac=None): @@ -107,8 +100,9 @@ def get_obs_mask(self, hi_res): for those locations. This is also divided between onshore and offshore regions""" on_sf = RANDOM_GENERATOR.uniform( - low=0, high=self.onshore_obs_frac['spatial'] + low=-1e-6, high=self.onshore_obs_frac['spatial'] ) + on_sf = max(on_sf, 0) on_tf = self.onshore_obs_frac.get('time', None) off_tf = self.offshore_obs_frac.get('time', None) obs_mask = self._get_obs_mask(hi_res, on_sf, on_tf) diff --git a/sup3r/pipeline/utilities.py b/sup3r/pipeline/utilities.py index 398ed67069..57d4ee99db 100644 --- a/sup3r/pipeline/utilities.py +++ b/sup3r/pipeline/utilities.py @@ -10,16 +10,15 @@ def get_model(model_class, kwargs): """Instantiate model after check on class name.""" + msg = ( + 'Could not load requested model class "{}" from ' + 'sup3r.models, Make sure you typed in the model class ' + 'name correctly.'.format(model_class) + ) model_class = getattr(sup3r.models, model_class, None) if isinstance(kwargs, str): kwargs = {'model_dir': kwargs} - if model_class is None: - msg = ( - 'Could not load requested model class "{}" from ' - 'sup3r.models, Make sure you typed in the model class ' - 'name correctly.'.format(model_class) - ) logger.error(msg) raise KeyError(msg) return model_class.load(**kwargs, verbose=True) diff --git a/sup3r/preprocessing/names.py b/sup3r/preprocessing/names.py index 700dce81b7..d1cd95d89c 100644 --- a/sup3r/preprocessing/names.py +++ b/sup3r/preprocessing/names.py @@ -112,6 +112,7 @@ def dims_4d_bc(cls): 'isobaricInhPa': Dimension.PRESSURE_LEVEL, 'pressure_level': Dimension.PRESSURE_LEVEL, 'xtime': Dimension.TIME, + 'time_index': Dimension.TIME, 'valid_time': Dimension.TIME, 'west_east': Dimension.LONGITUDE, 'south_north': Dimension.LATITUDE @@ -129,6 +130,7 @@ def dims_4d_bc(cls): 'isobaricInhPa': Dimension.PRESSURE_LEVEL, 'pressure_level': Dimension.PRESSURE_LEVEL, 'xtime': Dimension.TIME, + 'time_index': Dimension.TIME, 'valid_time': Dimension.TIME } diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index 4610318515..d6ef398ecd 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -429,7 +429,7 @@ def get_data(self): target_tmask = self.hr_time_index.isin(self.source_handler.time_index) source_tmask = self.source_handler.time_index.isin(self.hr_time_index) src_data = self.source_data.reshape((-1, self.source_data.shape[-1])) - out = np.full(self.hr_shape, np.nan) + out = np.full(self.hr_shape, np.nan, dtype=np.float32) out = out.reshape((-1, out.shape[-1])) gid_mask = self.nn != src_data.shape[0] src_data = src_data.vindex[self.nn[gid_mask]] diff --git a/sup3r/utilities/era_downloader.py b/sup3r/utilities/era_downloader.py index 9593c0db92..352fd09fe0 100644 --- a/sup3r/utilities/era_downloader.py +++ b/sup3r/utilities/era_downloader.py @@ -30,7 +30,7 @@ SFC_VARS, Dimension, ) -from sup3r.preprocessing.utilities import log_args +from sup3r.preprocessing.utilities import log_args, ordered_dims # these are occasionally included in downloaded files, more often with cds-beta IGNORE_VARS = ('number', 'expver') @@ -196,13 +196,11 @@ def prep_var_lists(self, variables): ) if sfc_and_level_check: msg = ( - 'Both surface and pressure level variables were requested ' - 'without requesting "orog" and "zg". Adding these to the ' - 'download.' + f'Both surface and pressure level variables ({variables}) ' + 'were requested without requesting "orog" and "zg".' ) - logger.info(msg) - self.sfc_file_variables.append('geopotential') - self.level_file_variables.append('geopotential') + logger.warning(msg) + warn(msg) else: if 'orog' in variables: @@ -362,8 +360,13 @@ def process_surface_file(self): """Rename variables and convert geopotential to geopotential height.""" tmp_file = self.get_tmp_file(self.surface_file) ds = Loader(self.surface_file) - logger.info('Converting "z" var to "orog" for %s', self.surface_file) - ds = self.convert_z(ds, name='orog') + + if 'z' in ds.data_vars: + logger.info( + 'Converting "z" var to "orog" for %s', self.surface_file + ) + ds = self.convert_z(ds, name='orog') + ds = standardize_names(ds, ERA_NAME_MAP) ds = standardize_values(ds) @@ -396,11 +399,13 @@ def add_pressure(self, ds): # if trailing dimensions don't match this is for an ensemble # download - if len(pres) != ds['zg'].shape[-1]: - pres = np.repeat(pres[..., None], ds['zg'].shape[-1], axis=-1) + dims = {k: ds.dims[k] for k in ordered_dims(ds.dims)} + arr_shape = list(dims.values()) + if len(pres) != arr_shape[-1]: + pres = np.repeat(pres[..., None], arr_shape[-1], axis=-1) ds['pressure'] = ( - ds['zg'].dims, - da.broadcast_to(pres, ds['zg'].shape), + dims, + da.broadcast_to(pres, arr_shape), ) ds['pressure'].attrs['units'] = 'Pa' return ds @@ -430,8 +435,11 @@ def process_level_file(self): """Convert geopotential to geopotential height.""" tmp_file = self.get_tmp_file(self.level_file) ds = Loader(self.level_file) - logger.info('Converting "z" var to "zg" for %s', self.level_file) - ds = self.convert_z(ds, name='zg') + + if 'z' in ds.data_vars: + logger.info('Converting "z" var to "zg" for %s', self.level_file) + ds = self.convert_z(ds, name='zg') + ds = standardize_names(ds, ERA_NAME_MAP) ds = standardize_values(ds) ds = self.add_pressure(ds) diff --git a/sup3r/utilities/loss_metrics.py b/sup3r/utilities/loss_metrics.py index 270a511cd8..e6d5a416b5 100644 --- a/sup3r/utilities/loss_metrics.py +++ b/sup3r/utilities/loss_metrics.py @@ -4,6 +4,8 @@ import numpy as np import tensorflow as tf +from tensorflow.keras.applications import VGG16 +from tensorflow.keras.applications.vgg16 import preprocess_input from tensorflow.keras.losses import MeanAbsoluteError, MeanSquaredError @@ -640,3 +642,86 @@ def __call__(self, x1, x2): x2 = self._t_coarsen_sample(x2) return self._tf_loss(x1, x2) + ex_loss + + +class PerceptualLoss(tf.keras.losses.Loss): + """Perceptual loss that is calculated as MSE between feature maps of + ground truth and synthetic data""" + + def __init__(self, layer_names=None): + """ + Parameters + ---------- + layer_names : list | None + List of layer names in VGG16 to use to extract feature maps from + ground truth and synthetic data. Defaults to ['block1_conv2', + 'block2_conv2'] + """ + super().__init__() + # VGG16 for perceptual loss + vgg = VGG16(weights='imagenet', include_top=False) + vgg.trainable = False + self.layer_names = layer_names + if self.layer_names is None: + self.layer_names = ['block1_conv2', 'block2_conv2'] + vgg_outputs = [vgg.get_layer(name).output for name in self.layer_names] + self.feature_extractor = tf.keras.Model( + inputs=vgg.input, outputs=vgg_outputs + ) + + def _feature_loss(self, x1, x2): + """Calculate loss for a single feature. e.g. A single pair of tensors + each with only 3 channels""" + x1 = preprocess_input(x1) + x2 = preprocess_input(x2) + x1 = self.feature_extractor(x1) + x2 = self.feature_extractor(x2) + if len(self.layer_names) == 1: + x1 = [x1] + x2 = [x2] + loss = 0 + for x1_f, x2_f in zip(x1, x2): + loss += tf.reduce_mean(tf.square(x1_f - x2_f)) + return loss + + def __call__(self, x1, x2): + """Perceptual loss calculated on true and synthetic feature maps + + Parameters + ---------- + x1 : tf.tensor + Synthetic high-res generator output, shape is either of these: + (n_obs, spatial_1, spatial_2, features) + (n_obs, spatial_1, spatial_2, temporal, features) + x2 : tf.tensor + True high resolution data, shape is either of these: + (n_obs, spatial_1, spatial_2, features) + (n_obs, spatial_1, spatial_2, temporal, features) + + Returns + ------- + tf.tensor + 0D tensor loss value + """ + if len(x1.shape) == 5: + new_shape = ( + x1.shape[0] * x1.shape[3], + x1.shape[1], + x1.shape[2], + x1.shape[-1], + ) + x1 = tf.reshape(x1, new_shape) + x2 = tf.reshape(x2, new_shape) + + losses = [] + for i in range(x1.shape[-1]): + x1_f = x1[..., i] + x2_f = x2[..., i] + + # VGG input needs 3 RGB channels + x1_f = tf.stack([x1_f] * 3, axis=-1) + x2_f = tf.stack([x2_f] * 3, axis=-1) + + losses.append(self._feature_loss(x1_f, x2_f)) + + return tf.reduce_mean(losses) diff --git a/tests/conftest.py b/tests/conftest.py index ae77d7548e..374fd1e324 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -207,11 +207,11 @@ def func(): }, {'class': 'Cropping2D', 'cropping': 4}, { - 'class': 'Sup3rConcatWeightedObsWithEmbedding', + 'class': 'Sup3rConcatWeightedObs', 'name': 'u_10m_obs', }, { - 'class': 'Sup3rConcatWeightedObsWithEmbedding', + 'class': 'Sup3rConcatWeightedObs', 'name': 'v_10m_obs', }, { diff --git a/tests/forward_pass/test_forward_pass_obs.py b/tests/forward_pass/test_forward_pass_obs.py index aaa4134f44..f921c22214 100644 --- a/tests/forward_pass/test_forward_pass_obs.py +++ b/tests/forward_pass/test_forward_pass_obs.py @@ -120,7 +120,7 @@ def test_fwp_with_obs( model = Sup3rGanWithObs( gen_config_with_concat_masked(), pytest.S_FP_DISC, - obs_frac={'spatial': 0.1}, + onshore_obs_frac={'spatial': 0.1}, loss_obs_weight=0.1, learning_rate=1e-4, ) @@ -151,7 +151,10 @@ def test_fwp_with_obs( ] }, } - _ = model.generate(np.ones((6, 10, 10, 2)), exogenous_data=exo_tmp) + _ = model.generate( + np.ones((6, 10, 10, 2)), + exogenous_data=exo_tmp + ) model_dir = os.path.join(td, 'test') model.save(model_dir) diff --git a/tests/loaders/test_file_loading.py b/tests/loaders/test_file_loading.py index 59bdbf3406..b138f3e9b0 100644 --- a/tests/loaders/test_file_loading.py +++ b/tests/loaders/test_file_loading.py @@ -25,7 +25,7 @@ def test_time_independent_loading(): out_file = os.path.join(td, 'topo.nc') nc = make_fake_dset((20, 20, 1), features=['topography']) nc = nc.isel(time=0) - nc = nc.drop(Dimension.TIME) + nc = nc.drop_vars(Dimension.TIME) assert Dimension.TIME not in nc.dims assert Dimension.TIME not in nc.coords nc.to_netcdf(out_file, format='NETCDF4', engine='h5netcdf') From dbae7ffa8cde2513df1446a6cd854bf26b797238 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 1 Apr 2025 11:06:49 -0600 Subject: [PATCH 069/122] xarray future warning - changed ds.dims to ds.sizes --- sup3r/utilities/era_downloader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sup3r/utilities/era_downloader.py b/sup3r/utilities/era_downloader.py index 352fd09fe0..a526849137 100644 --- a/sup3r/utilities/era_downloader.py +++ b/sup3r/utilities/era_downloader.py @@ -399,7 +399,7 @@ def add_pressure(self, ds): # if trailing dimensions don't match this is for an ensemble # download - dims = {k: ds.dims[k] for k in ordered_dims(ds.dims)} + dims = {k: ds.sizes[k] for k in ordered_dims(ds.dims)} arr_shape = list(dims.values()) if len(pres) != arr_shape[-1]: pres = np.repeat(pres[..., None], arr_shape[-1], axis=-1) From f01d8b45077e64a6de10c401018dc48e31e06046 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 8 Apr 2025 20:17:41 -0600 Subject: [PATCH 070/122] example notebook for running sup3rwind models. added skin temp to era variables. some edits to rasterizers - all sub classes use same source_lat_lon, auto calculated max distance for KDTree should be divided by 2 --- examples/sup3rwind/running_sup3r_models.ipynb | 595 ++++++++++++++++++ sup3r/models/with_obs.py | 32 +- sup3r/preprocessing/accessor.py | 19 +- sup3r/preprocessing/names.py | 3 +- sup3r/preprocessing/rasterizers/exo.py | 41 +- sup3r/utilities/era_downloader.py | 1 + sup3r/utilities/utilities.py | 36 +- 7 files changed, 659 insertions(+), 68 deletions(-) create mode 100644 examples/sup3rwind/running_sup3r_models.ipynb diff --git a/examples/sup3rwind/running_sup3r_models.ipynb b/examples/sup3rwind/running_sup3r_models.ipynb new file mode 100644 index 0000000000..af70bf8e76 --- /dev/null +++ b/examples/sup3rwind/running_sup3r_models.ipynb @@ -0,0 +1,595 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How to run saved sup3r models on ERA5 data" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "import os\n", + "import xarray as xr\n", + "import hvplot.xarray\n", + "from rex import init_logger\n", + "from sup3r.models import Sup3rGanWithObs\n", + "from sup3r.preprocessing import DataHandler\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "os.environ['CUDA_VISIBLE_DEVICES'] = '-1'\n", + "\n", + "init_logger('sup3r', log_level='DEBUG')\n", + "init_logger(__name__, log_level='DEBUG')\n", + "logger = logging.getLogger(__name__)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + } + ], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load a model" + ] + }, + { + "cell_type": "code", + "execution_count": 288, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO - 2025-04-08 20:09:36,501 [base.py:180] : Loading GAN from disk in directory: /datasets/sup3rwind/models/experimental/15x_1x_14f_st_obs_unet_just_embed/gan_e1300\n", + "INFO - 2025-04-08 20:09:36,502 [base.py:186] : Active python environment versions: \n", + "{ 'cftime': '1.6.4',\n", + " 'dask': '2024.11.2',\n", + " 'h5netcdf': '1.3.0',\n", + " 'netCDF4': '1.6.5',\n", + " 'nrel-phygnn': '0.0.30',\n", + " 'nrel-rex': '0.2.99',\n", + " 'numpy': '1.26.4',\n", + " 'pandas': '2.2.2',\n", + " 'python': '3.11.9 (main, Apr 19 2024, 16:48:06) [GCC 11.2.0]',\n", + " 'sklearn': '1.5.1',\n", + " 'sup3r': '0.2.3.dev34+g9d6a51d2',\n", + " 'tensorflow': '2.15.1',\n", + " 'xarray': '2025.1.2'}\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO - 2025-04-08 20:09:36,522 [abstract.py:403] : Loading model from disk that was created with the following package versions: \n", + "{ 'cftime': '1.6.4',\n", + " 'dask': '2024.11.2',\n", + " 'h5netcdf': '1.3.0',\n", + " 'netCDF4': '1.6.5',\n", + " 'nrel-phygnn': '0.0.30',\n", + " 'nrel-rex': '0.2.99',\n", + " 'numpy': '1.26.4',\n", + " 'pandas': '2.2.2',\n", + " 'python': '3.11.9 (main, Apr 19 2024, 16:48:06) [GCC 11.2.0]',\n", + " 'sklearn': '1.5.1',\n", + " 'sup3r': '0.2.3.dev34+g9d6a51d2',\n", + " 'tensorflow': '2.15.1',\n", + " 'xarray': '2025.1.2'}\n" + ] + } + ], + "source": [ + "model_dir = '/datasets/sup3rwind/models/experimental/15x_1x_14f_st_obs_unet_just_embed/gan_e1300'\n", + "model = Sup3rGanWithObs.load(model_dir)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Double check what the model needs as input" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Models take mainly low resolution data" + ] + }, + { + "cell_type": "code", + "execution_count": 289, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['temperature_2m',\n", + " 'relativehumidity_2m',\n", + " 'pressure_0m',\n", + " 'ie',\n", + " 'zust',\n", + " 'slhf',\n", + " 'sshf',\n", + " 'd2m',\n", + " 'cape',\n", + " 'kx',\n", + " 'i10fg',\n", + " 'u_10m',\n", + " 'v_10m',\n", + " 'u_100m',\n", + " 'v_100m',\n", + " 'u_200m',\n", + " 'v_200m',\n", + " 'srl',\n", + " 'sza',\n", + " 'topography']" + ] + }, + "execution_count": 289, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.lr_features" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Models can also injest some high-resolution data (hr_exo_features)." + ] + }, + { + "cell_type": "code", + "execution_count": 290, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['srl', 'sza', 'topography']" + ] + }, + "execution_count": 290, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.hr_exo_features" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Finaly, some models can also injest observation data which is mostly NaN except for sparse locations" + ] + }, + { + "cell_type": "code", + "execution_count": 291, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['u_10m', 'v_10m', 'temperature_2m', 'relativehumidity_2m', 'pressure_0m']" + ] + }, + "execution_count": 291, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.obs_features" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Ok, so we need to load low-res data accordings to lr_features, and high-res data according to hr_exo_features and obs_features" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### We'll use some of the high-resolution training data as a proxy for \"observation data\"" + ] + }, + { + "cell_type": "code", + "execution_count": 292, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO - 2025-04-08 20:09:49,906 [utilities.py:125] : Initialized DataHandler with:\n", + "{ 'BaseLoader': None,\n", + " 'FeatureRegistry': None,\n", + " 'cache_kwargs': None,\n", + " 'chunks': 'auto',\n", + " 'features': [ 'temperature_2m',\n", + " 'relativehumidity_2m',\n", + " 'pressure_0m',\n", + " 'ie',\n", + " 'zust',\n", + " 'slhf',\n", + " 'sshf',\n", + " 'd2m',\n", + " 'cape',\n", + " 'kx',\n", + " 'i10fg',\n", + " 'u_10m',\n", + " 'v_10m',\n", + " 'u_100m',\n", + " 'v_100m',\n", + " 'u_200m',\n", + " 'v_200m',\n", + " 'srl',\n", + " 'sza',\n", + " 'topography'],\n", + " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/lr*.h5',\n", + " 'hr_spatial_coarsen': 1,\n", + " 'interp_kwargs': None,\n", + " 'load_features': 'all',\n", + " 'nan_method_kwargs': None,\n", + " 'res_kwargs': None,\n", + " 'shape': None,\n", + " 'target': None,\n", + " 'threshold': None,\n", + " 'time_roll': 0,\n", + " 'time_shift': None,\n", + " 'time_slice': slice(20, 30, None)}\n", + "DEBUG - 2025-04-08 20:09:49,910 [utilities.py:129] : Memory usage is 153.263 GB out of 269.755 GB\n", + "INFO - 2025-04-08 20:09:49,917 [utilities.py:125] : Initialized LoaderH5 with:\n", + "{ 'BASE_LOADER': ,\n", + " 'BaseLoader': None,\n", + " 'chunks': 'auto',\n", + " 'features': 'all',\n", + " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/lr*.h5',\n", + " 'res_kwargs': None}\n", + "DEBUG - 2025-04-08 20:09:49,917 [utilities.py:129] : Memory usage is 153.263 GB out of 269.755 GB\n", + "DEBUG - 2025-04-08 20:09:55,351 [h5.py:174] : Rechunking features with chunks: auto\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/bbenton/repos/rex/rex/resource.py:1287: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " datetime_index = pd.to_datetime(time_index.astype(str))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO - 2025-04-08 20:09:56,403 [base.py:153] : Getting raster index for target / shape: None / None\n", + "INFO - 2025-04-08 20:09:56,560 [base.py:224] : The distance between the closest coordinate: [ 15.127 -104.881] and the requested target: [ 15.127 -104.881] for files: ['/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_cape.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_d2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_i10fg.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_ie.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_kx.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_lsm.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_pressure_0m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_pressure_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_relativehumidity_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_relativehumidity_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_skt.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_slhf.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_srl.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_sshf.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_sst.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_sza.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_temperature_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_temperature_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_topography.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_u_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_u_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_u_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_v_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_v_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_v_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_zust.h5'] is 0.0.\n", + "INFO - 2025-04-08 20:09:58,684 [base.py:131] : Rasterizing data for target / shape: [ 15.127 -104.881] / [30 80]\n", + "INFO - 2025-04-08 20:09:59,652 [utilities.py:125] : Initialized DataHandler with:\n", + "{ 'BaseLoader': None,\n", + " 'FeatureRegistry': None,\n", + " 'cache_kwargs': None,\n", + " 'chunks': 'auto',\n", + " 'features': [ 'srl',\n", + " 'sza',\n", + " 'topography',\n", + " 'u_10m',\n", + " 'v_10m',\n", + " 'temperature_2m',\n", + " 'relativehumidity_2m',\n", + " 'pressure_0m'],\n", + " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/hr*.h5',\n", + " 'hr_spatial_coarsen': 1,\n", + " 'interp_kwargs': None,\n", + " 'load_features': 'all',\n", + " 'nan_method_kwargs': None,\n", + " 'res_kwargs': None,\n", + " 'shape': None,\n", + " 'target': None,\n", + " 'threshold': None,\n", + " 'time_roll': 0,\n", + " 'time_shift': None,\n", + " 'time_slice': slice(20, 30, None)}\n", + "DEBUG - 2025-04-08 20:09:59,653 [utilities.py:129] : Memory usage is 153.014 GB out of 269.755 GB\n", + "INFO - 2025-04-08 20:09:59,654 [utilities.py:125] : Initialized LoaderH5 with:\n", + "{ 'BASE_LOADER': ,\n", + " 'BaseLoader': None,\n", + " 'chunks': 'auto',\n", + " 'features': 'all',\n", + " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/hr*.h5',\n", + " 'res_kwargs': None}\n", + "DEBUG - 2025-04-08 20:09:59,655 [utilities.py:129] : Memory usage is 153.014 GB out of 269.755 GB\n", + "DEBUG - 2025-04-08 20:09:59,725 [h5.py:174] : Rechunking features with chunks: auto\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/bbenton/repos/rex/rex/resource.py:1287: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + " datetime_index = pd.to_datetime(time_index.astype(str))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO - 2025-04-08 20:10:00,012 [base.py:153] : Getting raster index for target / shape: None / None\n", + "INFO - 2025-04-08 20:10:00,069 [base.py:224] : The distance between the closest coordinate: [ 14.996 -105.005] and the requested target: [ 14.996 -105.005] for files: ['/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_pressure_0m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_relativehumidity_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_srl.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_sza.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_temperature_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_topography.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_120m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_160m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_40m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_80m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_120m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_160m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_40m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_80m.h5'] is 0.0.\n", + "INFO - 2025-04-08 20:10:00,263 [base.py:131] : Rasterizing data for target / shape: [ 14.996 -105.005] / [ 450 1200]\n" + ] + } + ], + "source": [ + "lr_data = DataHandler('/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/lr*.h5', time_slice=slice(20, 30),\n", + " features=model.lr_features)\n", + "hr_data = DataHandler('/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/hr*.h5', time_slice=slice(20, 30),\n", + " features=model.hr_exo_features+[feat.replace('_obs', '') for feat in model.obs_features])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now we need to pass the data to the model in the format it expects" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### The 'steps' list to accomodate possible multi-step models. 'combine_type' tells the model how to incorporate the exogenous data (e.g. whether it's part of a mid-network layer or incorporated as part of a multi-step forward pass)" + ] + }, + { + "cell_type": "code", + "execution_count": 293, + "metadata": {}, + "outputs": [], + "source": [ + "lr_input = lr_data.values[None, ...] # add batch dimension\n", + "\n", + "# simulate obs data by masking some of the high-res ground truth data\n", + "onshore_mask = model._get_obs_mask(\n", + " np.zeros((1, *hr_data.shape[:-1])), spatial_frac=1e-4, time_frac=0.8)\n", + "offshore_mask = model._get_obs_mask(\n", + " np.zeros((1, *hr_data.shape[:-1])), spatial_frac=1e-4, time_frac=0.5)\n", + "mask = np.where(hr_data['topography'].isel(time=0).values[None] > 0,\n", + " onshore_mask, offshore_mask)\n", + "\n", + "exo_input = {}\n", + "\n", + "for feature in model.hr_exo_features + model.obs_features:\n", + " if feature in model.obs_features:\n", + " data = np.where(\n", + " mask[0, ..., None], np.nan,\n", + " hr_data[feature.replace('_obs', '')].values)\n", + " else:\n", + " data = hr_data[feature].values\n", + " exo_input[feature] = {\n", + " 'steps': [\n", + " {'model': 0,\n", + " 'combine_type': 'layer',\n", + " 'data': data[None, ...][..., None],\n", + " }\n", + " ]\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now we can finally send the data through the model and generate output" + ] + }, + { + "cell_type": "code", + "execution_count": 294, + "metadata": {}, + "outputs": [], + "source": [ + "hr_output = model.generate(lr_input, exogenous_data=exo_input)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Double check ordering of output features" + ] + }, + { + "cell_type": "code", + "execution_count": 295, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['temperature_2m',\n", + " 'relativehumidity_2m',\n", + " 'pressure_0m',\n", + " 'u_10m',\n", + " 'v_10m',\n", + " 'u_40m',\n", + " 'v_40m',\n", + " 'u_80m',\n", + " 'v_80m',\n", + " 'u_100m',\n", + " 'v_100m',\n", + " 'u_120m',\n", + " 'v_120m',\n", + " 'u_160m',\n", + " 'v_160m',\n", + " 'u_200m',\n", + " 'v_200m']" + ] + }, + "execution_count": 295, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.hr_out_features" + ] + }, + { + "cell_type": "code", + "execution_count": 296, + "metadata": {}, + "outputs": [], + "source": [ + "dims = ('latitude', 'longitude', 'time')\n", + "coords = {\n", + " 'latitude': hr_data.latitude.values[:, 0],\n", + " 'longitude': hr_data.longitude.values[0, :],\n", + " 'time': hr_data.time\n", + "}\n", + "hr_out = xr.Dataset({feature: (dims, hr_output[0, ..., i]) for i, feature in enumerate(model.hr_out_features)})\n", + "hr_out = hr_out.assign_coords(coords)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's plot windspeed at 10m and 100m" + ] + }, + { + "cell_type": "code", + "execution_count": 297, + "metadata": {}, + "outputs": [], + "source": [ + "hr_out['windspeed_10m'] = (dims, np.hypot(hr_out['u_10m'].values, hr_out['v_10m'].values))\n", + "hr_out['windspeed_100m'] = (dims, np.hypot(hr_out['u_100m'].values, hr_out['v_100m'].values))" + ] + }, + { + "cell_type": "code", + "execution_count": 298, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b53e2b061b654627a7528931e92dcd89", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "BokehModel(combine_events=True, render_bundle={'docs_json': {'d9a0839b-df62-4939-8602-c3a67b9b838d': {'version…" + ] + }, + "execution_count": 298, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hr_out['windspeed_10m'].hvplot.quadmesh(\n", + " groupby='time', cmap='YlGnBu', clim=(0, 10),\n", + " rasterize=True,\n", + " widget_type=\"scrubber\",\n", + " widget_location=\"bottom\")" + ] + }, + { + "cell_type": "code", + "execution_count": 299, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a1eaad432ffa4db7b043814622287030", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "BokehModel(combine_events=True, render_bundle={'docs_json': {'dfed3acb-d406-4da4-909c-791dfb300de2': {'version…" + ] + }, + "execution_count": 299, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hr_out['windspeed_100m'].hvplot.quadmesh(\n", + " groupby='time', cmap='YlGnBu', clim=(0, 10),\n", + " rasterize=True,\n", + " widget_type=\"scrubber\",\n", + " widget_location=\"bottom\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "sup3r", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 7bda016ce3..6c2dc8ae4b 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -33,15 +33,17 @@ def __init__( ---------- args : list Positional args for ``Sup3rGan`` parent class. - onshore_obs_frac : dict + onshore_obs_frac : Dict[List] | Dict[float] Fraction of the batch that should be treated as onshore observations. Should include ``spatial`` key and optionally ``time`` key if this is a spatiotemporal model. The values should correspond roughly to the fraction of the production domain for which onshore observations are available (spatial) and the fraction - of the full time period that these cover. For each batch a spatial - frac will be selected by uniformly selecting from the range ``(0, - obs_frac['spatial'])`` + of the full time period that these cover. The values can be either + a list (for a lower and upper bound, respectively) or a single + float. For each batch a spatial frac will be selected by either + sampling uniformly between this lower and upper bound or just + using a single float. offshore_obs_frac : dict Same as ``onshore_obs_frac`` but for offshore observations. Offshore observations are frequently sparser than onshore @@ -99,10 +101,10 @@ def get_obs_mask(self, hi_res): might be very sparse spatially but cover most of the full time period for those locations. This is also divided between onshore and offshore regions""" - on_sf = RANDOM_GENERATOR.uniform( - low=-1e-6, high=self.onshore_obs_frac['spatial'] - ) - on_sf = max(on_sf, 0) + on_sf = self.onshore_obs_frac['spatial'] + if not isinstance(on_sf, (list, tuple)): + on_sf = [on_sf, on_sf] + on_sf = max(RANDOM_GENERATOR.uniform(*on_sf), 0) on_tf = self.onshore_obs_frac.get('time', None) off_tf = self.offshore_obs_frac.get('time', None) obs_mask = self._get_obs_mask(hi_res, on_sf, on_tf) @@ -111,9 +113,10 @@ def get_obs_mask(self, hi_res): 'topography' ) topo = hi_res[..., topo_idx] - off_sf = RANDOM_GENERATOR.uniform( - low=0, high=self.offshore_obs_frac['spatial'] - ) + off_sf = self.offshore_obs_frac['spatial'] + if not isinstance(off_sf, (list, tuple)): + off_sf = [off_sf, off_sf] + off_sf = max(RANDOM_GENERATOR.uniform(*off_sf), 0) offshore_mask = self._get_obs_mask(hi_res, off_sf, off_tf) obs_mask = tf.where(topo > 0, obs_mask, offshore_mask) return obs_mask @@ -142,9 +145,8 @@ def get_hr_exo_input(self, hi_res_true): # obs_features can include a _obs suffix to avoid name conflict # with fully gridded exo features f_idx = self.hr_out_features.index(feature.replace('_obs', '')) - exo_data[feature] = tf.where( - obs_mask, np.nan, hi_res_true[..., f_idx] - )[..., None] + tmp = tf.where(obs_mask, np.nan, hi_res_true[..., f_idx]) + exo_data[feature] = tmp[..., None] exo_data['mask'] = obs_mask return exo_data @@ -169,6 +171,8 @@ def _get_hr_exo_and_loss( loss_update = { 'loss_obs': loss_obs, 'loss_non_obs': loss_non_obs, + 'obs_frac': np.sum(~hi_res_exo['mask']) + / np.size(hi_res_exo['mask']), } if self.loss_obs_weight is not None and calc_loss_kwargs['train_gen']: loss_obs *= self.loss_obs_weight diff --git a/sup3r/preprocessing/accessor.py b/sup3r/preprocessing/accessor.py index aad25b8842..1eb9bb360f 100644 --- a/sup3r/preprocessing/accessor.py +++ b/sup3r/preprocessing/accessor.py @@ -23,6 +23,7 @@ ordered_dims, parse_keys, ) +from sup3r.utilities.utilities import nn_fill_array logger = logging.getLogger(__name__) @@ -372,18 +373,16 @@ def interpolate_na(self, **kwargs): ) self._ds[feat] = self._ds[feat].interpolate_na(**kwargs) else: - horiz = ( - self._ds[feat] - .chunk({Dimension.WEST_EAST: -1}) - .interpolate_na(dim=Dimension.WEST_EAST, **kwargs) + msg = ( + 'No dim given for interpolate_na. This will use nearest ' + 'neighbor fill, which could take some time.' ) - vert = ( - self._ds[feat] - .chunk({Dimension.SOUTH_NORTH: -1}) - .interpolate_na(dim=Dimension.SOUTH_NORTH, **kwargs) + logger.warning(msg) + warn(msg) + self._ds[feat] = ( + self._ds[feat].dims, + nn_fill_array(self._ds[feat].values), ) - new_var = (self._ds[feat].dims, (horiz.data + vert.data) / 2) - self._ds[feat] = new_var return self @staticmethod diff --git a/sup3r/preprocessing/names.py b/sup3r/preprocessing/names.py index d1cd95d89c..c48aa61525 100644 --- a/sup3r/preprocessing/names.py +++ b/sup3r/preprocessing/names.py @@ -164,7 +164,8 @@ def dims_4d_bc(cls): 'northward_turbulent_surface_stress', 'eastward_turbulent_surface_stress', 'sea_surface_temperature', - 'instantaneous_10m_wind_gust' + 'instantaneous_10m_wind_gust', + 'skin_temperature' ] # variables available on multiple pressure levels diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index d6ef398ecd..2095031dc0 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -186,10 +186,9 @@ def coords(self): @property def source_lat_lon(self): - """Get the 2D array (n, 2) of lat, lon data from the source_file_h5""" + """Get the 2D array (n, 2) of lat, lon data from the source_file""" if self._source_lat_lon is None: - with Loader(self.source_file) as res: - self._source_lat_lon = res.lat_lon + self._source_lat_lon = self.source_handler.lat_lon.reshape((-1, 2)) return self._source_lat_lon @property @@ -248,7 +247,10 @@ def get_distance_upper_bound(self): if self.distance_upper_bound is None: diff = da.diff(self.hr_lat_lon, axis=0) diff = da.abs(da.median(diff, axis=0)).max() - self.distance_upper_bound = np.asarray(diff) + + # divide by 2 - this should be distance to pixel edge rather than + # adjacent pixel center + self.distance_upper_bound = np.asarray(diff) / 2 logger.info( 'Set distance upper bound to {:.4f}'.format( self.distance_upper_bound @@ -375,12 +377,6 @@ def source_data(self): """Get the 1D array of exogenous data from the source_file_nc""" return self.source_handler[self.feature].data.flatten()[..., None] - @property - def source_lat_lon(self): - """Get the 2D array (n, 2) of lat, lon data from the source_file""" - source_lat_lon = self.source_handler.lat_lon.reshape((-1, 2)) - return source_lat_lon - class ObsRasterizer(BaseExoRasterizer): """Rasterizer for sparse spatiotemporal observation data""" @@ -403,24 +399,6 @@ def source_data(self): feat = self.feature.replace('_obs', '') return self.source_handler[feat].data - @property - def tree(self): - """Get the KDTree built on the target lat lon data from the file_paths - input with s_enhance""" - if self._tree is None: - self._tree = KDTree(self.source_lat_lon.reshape((-1, 2))) - return self._tree - - @property - def nn(self): - """Get the nearest neighbor indices. This uses a single neighbor by - default""" - _, nn = self.tree.query( - self.hr_lat_lon.reshape((-1, 2)), - distance_upper_bound=self.get_distance_upper_bound(), - ) - return nn - def get_data(self): """Get a raster of source observation values corresponding to the high-resolution grid (the file_paths input grid * s_enhance * @@ -431,10 +409,9 @@ def get_data(self): src_data = self.source_data.reshape((-1, self.source_data.shape[-1])) out = np.full(self.hr_shape, np.nan, dtype=np.float32) out = out.reshape((-1, out.shape[-1])) - gid_mask = self.nn != src_data.shape[0] - src_data = src_data.vindex[self.nn[gid_mask]] - mask = gid_mask[:, None] & target_tmask - out[mask] = src_data[:, source_tmask].flatten() + gid_mask = self.nn != out.shape[0] + src_data = src_data[gid_mask][:, source_tmask] + out[self.nn[gid_mask][:, None], target_tmask] = src_data hr_data = out.reshape(self.hr_shape) logger.info( 'Found {} observations within {:4f} of high-resolution grid ' diff --git a/sup3r/utilities/era_downloader.py b/sup3r/utilities/era_downloader.py index a526849137..48a90abc48 100644 --- a/sup3r/utilities/era_downloader.py +++ b/sup3r/utilities/era_downloader.py @@ -830,6 +830,7 @@ def _can_skip_file(cls, file): @classmethod def _combine_files(cls, files, outfile, chunks='auto', res_kwargs=None): if not os.path.exists(outfile): + os.makedirs(os.path.dirname(outfile), exist_ok=True) logger.info(f'Combining {files} into {outfile}.') try: res_kwargs = res_kwargs or {} diff --git a/sup3r/utilities/utilities.py b/sup3r/utilities/utilities.py index d5fc1578c5..49c8d821bf 100644 --- a/sup3r/utilities/utilities.py +++ b/sup3r/utilities/utilities.py @@ -22,6 +22,16 @@ def preprocess_datasets(dset): """Standardization preprocessing applied before datasets are concatenated by ``xr.open_mfdataset``""" + if 'latitude' in dset.dims: + dset = dset.swap_dims({'latitude': 'south_north'}) + if 'longitude' in dset.dims: + dset = dset.swap_dims({'longitude': 'west_east'}) + if 'valid_time' in dset: + dset = dset.rename({'valid_time': 'time'}) + if 'isobaricInhPa' in dset: + dset = dset.rename({'isobaricInhPa': 'level'}) + if 'orog' in dset: + dset = dset.rename({'orog': 'topography'}) if 'time' in dset and dset.time.size > 1: if 'time' in dset.indexes and hasattr( dset.indexes['time'], 'to_datetimeindex' @@ -29,27 +39,31 @@ def preprocess_datasets(dset): dset['time'] = dset.indexes['time'].to_datetimeindex() ti = dset['time'].astype('int64') dset['time'] = ti - if 'latitude' in dset.dims: - dset = dset.swap_dims({'latitude': 'south_north'}) - if 'longitude' in dset.dims: - dset = dset.swap_dims({'longitude': 'west_east'}) - # temporary to handle downloaded era files - if 'expver' in dset: - dset = dset.drop_vars('expver') - if 'number' in dset: - dset = dset.drop_vars('number') + + # sometimes these are included in prelim ERA5 data + dset = dset.drop_vars(['expver', 'number'], errors='ignore') return dset def xr_open_mfdataset(files, **kwargs): """Wrapper for xr.open_mfdataset with default opening options.""" - default_kwargs = {'engine': 'netcdf4'} + default_kwargs = { + 'engine': 'netcdf4', + 'coords': 'minimal', + 'compat': 'override', + } default_kwargs.update(kwargs) if isinstance(files, str): files = [files] - return xr.open_mfdataset( + out = xr.open_mfdataset( files, preprocess=preprocess_datasets, **default_kwargs ) + bad_dims = len(out['latitude'].dims) == 2 and ( + out['latitude'].dims != out['longitude'].dims + ) + if bad_dims: + out['longitude'] = (out['latitude'].dims, out['longitude'].values.T) + return out def safe_cast(o): From 2c534c63d7b226bccd520d4b8087d0099dd8d4cb Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 11 Apr 2025 08:43:18 -0600 Subject: [PATCH 071/122] lin interp threshold - was previously getting excessively large wind speeds sometimes --- sup3r/utilities/interpolation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sup3r/utilities/interpolation.py b/sup3r/utilities/interpolation.py index 79f0935182..e2bf4c08b8 100644 --- a/sup3r/utilities/interpolation.py +++ b/sup3r/utilities/interpolation.py @@ -58,11 +58,11 @@ def _lin_interp(cls, lev_samps, var_samps, level): """Linearly interpolate between levels.""" diff = da.map_blocks(lambda x, y: x - y, lev_samps[1], lev_samps[0]) alpha = da.where( - diff == 0, + np.abs(diff < 1e-3), # to avoid excessively large alpha values 0, da.map_blocks(lambda x, y: x / y, (level - lev_samps[0]), diff), ) - indices = 'ijk'[:lev_samps[0].ndim] + indices = 'ijk'[: lev_samps[0].ndim] return da.blockwise( lambda x, y, a: x * (1 - a) + y * a, indices, From 901baa0199402df408bf74d5b9215e1790683f6c Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 11 Apr 2025 08:45:24 -0600 Subject: [PATCH 072/122] Combined ExoRasterizerNC / H5. Added functionality for 3d regridding - used in ObsRasterizer --- sup3r/preprocessing/__init__.py | 3 +- sup3r/preprocessing/data_handlers/exo.py | 24 ++- sup3r/preprocessing/rasterizers/__init__.py | 8 +- sup3r/preprocessing/rasterizers/exo.py | 158 ++++++++++---------- tests/rasterizers/test_exo.py | 11 +- 5 files changed, 106 insertions(+), 98 deletions(-) diff --git a/sup3r/preprocessing/__init__.py b/sup3r/preprocessing/__init__.py index 391eef6f8c..87897fc054 100644 --- a/sup3r/preprocessing/__init__.py +++ b/sup3r/preprocessing/__init__.py @@ -51,10 +51,9 @@ from .loaders import Loader, LoaderH5, LoaderNC from .names import COORD_NAMES, DIM_NAMES, FEATURE_NAMES, Dimension from .rasterizers import ( + BaseExoRasterizer, DualRasterizer, ExoRasterizer, - ExoRasterizerH5, - ExoRasterizerNC, ObsRasterizer, Rasterizer, SzaRasterizer, diff --git a/sup3r/preprocessing/data_handlers/exo.py b/sup3r/preprocessing/data_handlers/exo.py index 24e9fc592f..11dfb5e3f9 100644 --- a/sup3r/preprocessing/data_handlers/exo.py +++ b/sup3r/preprocessing/data_handlers/exo.py @@ -6,6 +6,7 @@ import pathlib from dataclasses import dataclass from typing import TYPE_CHECKING, Optional, Union +from warnings import warn import numpy as np @@ -82,8 +83,29 @@ def __init__(self, steps): Each array in in 'data' key has 3D or 4D shape: (spatial_1, spatial_2, 1) (spatial_1, spatial_2, n_temporal, 1) + + If the 'combine_type' key is missing it is assumed to be 'layer', + and if the 'steps' key is missing it is assumed to be a single + step and will be converted to a list """ # noqa : D301 if isinstance(steps, dict): + for feat, entry in steps.items(): + msg = (f'ExoData entry for {feat} has no "steps" key. ' + 'Assuming this is for a single step.') + if 'steps' not in entry: + logger.warning(msg) + warn(msg) + steps_list = entry.get('steps', [entry]) + for i, step in enumerate(steps_list): + msg = (f'ExoData entry for {feat}, step #{i+1} has no ' + '"combine_type" key. Assuming this is for a ' + 'layer combination.') + if 'combine_type' not in step: + logger.warning(msg) + warn(msg) + step['combine_type'] = step.get('combine_type', 'layer') + steps_list[i] = step + steps[feat] = {'steps': steps_list} self.update(steps) else: @@ -204,7 +226,7 @@ def get_combine_type_data(self, feature, combine_type, model_step=None): combine_types = [step['combine_type'] for step in tmp['steps']] msg = ( 'Received exogenous_data without any combine_type ' - f'= "{combine_type}" steps' + f'= "{combine_type}" steps.' ) assert combine_type in combine_types, msg return tmp['steps'][combine_types.index(combine_type)]['data'] diff --git a/sup3r/preprocessing/rasterizers/__init__.py b/sup3r/preprocessing/rasterizers/__init__.py index 904e0bebd9..f0b0dc8f34 100644 --- a/sup3r/preprocessing/rasterizers/__init__.py +++ b/sup3r/preprocessing/rasterizers/__init__.py @@ -8,11 +8,5 @@ from .base import BaseRasterizer from .dual import DualRasterizer -from .exo import ( - ExoRasterizer, - ExoRasterizerH5, - ExoRasterizerNC, - ObsRasterizer, - SzaRasterizer, -) +from .exo import BaseExoRasterizer, ExoRasterizer, ObsRasterizer, SzaRasterizer from .extended import Rasterizer diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index 2095031dc0..4b15388275 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -8,7 +8,7 @@ import shutil from abc import ABC from dataclasses import dataclass -from typing import ClassVar, Optional, Union +from typing import Optional, Union from warnings import warn import dask.array as da @@ -24,12 +24,12 @@ from sup3r.preprocessing.derivers.utilities import SolarZenith from sup3r.preprocessing.loaders import Loader from sup3r.preprocessing.names import Dimension +from sup3r.preprocessing.utilities import compute_if_dask from sup3r.utilities.utilities import nn_fill_array from ..utilities import ( get_class_kwargs, get_input_handler_class, - get_source_type, log_args, ) @@ -58,8 +58,7 @@ class BaseExoRasterizer(ABC): the file_paths input. Accordingly, source_file should be a significantly higher resolution than file_paths. Warnings will be raised if the low-resolution pixels in file_paths do not have unique - nearest pixels from source_file. File format can be .h5 for - ExoRasterizerH5 or .nc for ExoRasterizerNC + nearest pixels from source_file. File format can be .h5 or .nc feature : str Name of exogenous feature to rasterize. s_enhance : int @@ -109,6 +108,7 @@ class BaseExoRasterizer(ABC): cache_dir: str = './exo_cache/' chunks: Optional[Union[str, dict]] = 'auto' distance_upper_bound: Optional[int] = None + fill_nans: bool = True max_workers: int = 1 verbose: bool = False @@ -129,11 +129,13 @@ def __post_init__(self): @property def source_data(self): - """Get the array of exogenous data from the source_file_h5""" + """Get the array of exogenous data from the source_file""" if self._source_data is None: self._source_data = self.source_handler[self.feature].data if 'time' not in self.source_handler[self.feature].dims: - self._source_data = self._source_data.data[:, None] + self._source_data = self._source_data[..., None] + shape = (-1, self._source_data.shape[-1]) + self._source_data = self.source_data.reshape(shape) return self._source_data @property @@ -305,24 +307,21 @@ def data(self): def get_data(self): """Get a raster of source values corresponding to the high-resolution grid (the file_paths input grid * s_enhance * - t_enhance). The shape is (lats, lons, 1) + t_enhance). The shape is (lats, lons, temporal) """ - assert ( - len(self.source_data.shape) == 2 and self.source_data.shape[1] == 1 - ) + assert len(self.source_data.shape) == 2 - df = pd.DataFrame( - {self.feature: self.source_data.flatten(), 'gid_target': self.nn} - ) - n_target = np.prod(self.hr_shape[:-1]) - df = df[df['gid_target'] != n_target] - df = df.sort_values('gid_target') - df = df.groupby('gid_target').mean() + if self.source_data.shape[1] == 1: + hr_data = self._get_data_2d() + dims = Dimension.dims_2d() - missing = set(np.arange(n_target)) - set(df.index) - if any(missing): + else: + hr_data = self._get_data_3d() + dims = Dimension.dims_3d() + + if np.isnan(hr_data).any() and self.fill_nans: msg = ( - f'{len(missing)} target pixels did not have unique ' + f'{np.isnan(hr_data).sum()} target pixels did not have unique ' f'high-resolution {self.feature} source data to map from. If ' 'there are a lot of target pixels missing source data this ' 'probably means the source data is not high enough ' @@ -330,13 +329,6 @@ def get_data(self): ) logger.warning(msg) warn(msg) - temp_df = pd.DataFrame( - {self.feature: np.nan}, index=sorted(missing) - ) - df = pd.concat((df, temp_df)).sort_index() - - hr_data = df[self.feature].values.reshape(self.hr_shape[:-1]) - if np.isnan(hr_data).any(): hr_data = nn_fill_array(hr_data) logger.info( @@ -346,36 +338,57 @@ def get_data(self): ) data_vars = { self.feature: ( - Dimension.dims_2d(), + dims, da.asarray(hr_data, dtype=np.float32), ) } ds = xr.Dataset(coords=self.coords, data_vars=data_vars) return Sup3rX(ds) + def _get_data_2d(self): + """Get a raster of source values corresponding to the + high-resolution grid (the file_paths input grid * s_enhance * + t_enhance). The shape is (lats, lons, 1) + """ + assert ( + len(self.source_data.shape) == 2 and self.source_data.shape[1] == 1 + ) -class ExoRasterizerH5(BaseExoRasterizer): - """ExoRasterizer for H5 files""" + df = pd.DataFrame({ + self.feature: self.source_data.flatten(), + 'gid_target': self.nn, + }) + n_target = np.prod(self.hr_shape[:-1]) + df = df[df['gid_target'] != n_target] + df = df.sort_values('gid_target') + df = df.groupby('gid_target').mean() - @property - def source_data(self): - """Get the 1D array of exogenous data from the source_file_h5""" - if self._source_data is None: - self._source_data = self.source_handler[self.feature] - if 'time' not in self.source_handler[self.feature].dims: - self._source_data = self._source_data.data[:, None] - else: - self._source_data = self._source_data.data[..., slice(0, 1)] - return self._source_data + missing = set(np.arange(n_target)) - set(df.index) + if any(missing): + temp_df = pd.DataFrame( + {self.feature: np.nan}, index=sorted(missing) + ) + df = pd.concat((df, temp_df)).sort_index() + return df[self.feature].values.reshape(self.hr_shape[:-1]) -class ExoRasterizerNC(BaseExoRasterizer): - """ExoRasterizer for netCDF files""" + def _get_data_3d(self): + """Get a raster of source observation values corresponding to the + high-resolution grid (the file_paths input grid * s_enhance * + t_enhance). The shape is (lats, lons, time) + """ + assert ( + len(self.source_data.shape) == 2 and self.source_data.shape[1] > 1 + ) - @property - def source_data(self): - """Get the 1D array of exogenous data from the source_file_nc""" - return self.source_handler[self.feature].data.flatten()[..., None] + target_tmask = self.hr_time_index.isin(self.source_handler.time_index) + source_tmask = self.source_handler.time_index.isin(self.hr_time_index) + out = np.full(self.hr_shape, np.nan, dtype=np.float32) + out = out.reshape((-1, out.shape[-1])) + gid_mask = self.nn != out.shape[0] + src_data = self.source_data[gid_mask][:, source_tmask] + out[self.nn[gid_mask][:, None], target_tmask] = src_data + return out.reshape(self.hr_shape) class ObsRasterizer(BaseExoRasterizer): @@ -395,47 +408,33 @@ def source_handler(self): @property def source_data(self): - """Get the 1D array of exogenous data from the source_file_nc""" + """Get the flattened observation data from the source_file""" feat = self.feature.replace('_obs', '') - return self.source_handler[feat].data + src = self.source_handler[feat].data + src = src.reshape((-1, src.shape[-1])) + return src - def get_data(self): + def _get_data_3d(self): """Get a raster of source observation values corresponding to the high-resolution grid (the file_paths input grid * s_enhance * - t_enhance). The shape is (lats, lons, time, 1) + t_enhance). The shape is (lats, lons, time) """ - target_tmask = self.hr_time_index.isin(self.source_handler.time_index) - source_tmask = self.source_handler.time_index.isin(self.hr_time_index) - src_data = self.source_data.reshape((-1, self.source_data.shape[-1])) - out = np.full(self.hr_shape, np.nan, dtype=np.float32) - out = out.reshape((-1, out.shape[-1])) - gid_mask = self.nn != out.shape[0] - src_data = src_data[gid_mask][:, source_tmask] - out[self.nn[gid_mask][:, None], target_tmask] = src_data - hr_data = out.reshape(self.hr_shape) + hr_data = super()._get_data_3d() + gid_mask = self.nn != np.prod(self.hr_shape[:-1]) logger.info( - 'Found {} observations within {:4f} of high-resolution grid ' - 'points.'.format(gid_mask.sum(), self.distance_upper_bound) - ) - logger.info( - 'Finished mapping raster from %s for "%s"', - self.source_file, - self.feature, + 'Found {} of {} observations within {:4f} degrees of high-' + 'resolution grid points.'.format( + gid_mask.sum(), len(gid_mask), self.distance_upper_bound + ) ) cover_frac = (~np.isnan(hr_data)).sum() / hr_data.size logger.info( - 'Observations cover {:.4f} of the high-res domain.'.format( - cover_frac + 'Observations cover {:.4e} of the high-res domain.'.format( + compute_if_dask(cover_frac) ) ) - data_vars = { - self.feature: ( - Dimension.dims_3d(), - da.asarray(hr_data, dtype=np.float32), - ) - } - ds = xr.Dataset(coords=self.coords, data_vars=data_vars) - return Sup3rX(ds) + self.fill_nans = False # override parent attribute to not fill nans + return hr_data class SzaRasterizer(BaseExoRasterizer): @@ -460,11 +459,6 @@ def get_data(self): class ExoRasterizer(BaseExoRasterizer, metaclass=Sup3rMeta): """Type agnostic `ExoRasterizer` class.""" - TypeSpecificClasses: ClassVar = { - 'nc': ExoRasterizerNC, - 'h5': ExoRasterizerH5, - } - def __new__(cls, file_paths, source_file, feature, **kwargs): """Override parent class to return type specific class based on `source_file`""" @@ -473,7 +467,7 @@ def __new__(cls, file_paths, source_file, feature, **kwargs): elif feature.lower().endswith('_obs'): ExoClass = ObsRasterizer else: - ExoClass = cls.TypeSpecificClasses[get_source_type(source_file)] + ExoClass = BaseExoRasterizer kwargs = { 'file_paths': file_paths, diff --git a/tests/rasterizers/test_exo.py b/tests/rasterizers/test_exo.py index 709ba27227..90d440d27b 100644 --- a/tests/rasterizers/test_exo.py +++ b/tests/rasterizers/test_exo.py @@ -11,12 +11,11 @@ from sup3r.postprocessing import RexOutputs from sup3r.preprocessing import ( + BaseExoRasterizer, Dimension, ExoData, ExoDataHandler, ExoRasterizer, - ExoRasterizerH5, - ExoRasterizerNC, ) from sup3r.utilities.utilities import RANDOM_GENERATOR, xr_open_mfdataset @@ -185,7 +184,7 @@ def test_srl_extraction_h5(s_enhance): 'cache_dir': f'{td}/exo_cache/', } - te = ExoRasterizerH5(**kwargs) + te = BaseExoRasterizer(**kwargs) te_gen = ExoRasterizer( **{k: v for k, v in kwargs.items() if k != 'cache_dir'} @@ -241,7 +240,7 @@ def test_topo_extraction_h5(s_enhance): 'cache_dir': f'{td}/exo_cache/', } - te = ExoRasterizerH5(**kwargs) + te = BaseExoRasterizer(**kwargs) te_gen = ExoRasterizer( **{k: v for k, v in kwargs.items() if k != 'cache_dir'} @@ -284,7 +283,7 @@ def test_bad_s_enhance(s_enhance=10): fp_exo_topo = make_topo_file(pytest.FP_WTK, td) with pytest.warns(UserWarning) as warnings: - te = ExoRasterizerH5( + te = BaseExoRasterizer( pytest.FP_WTK, fp_exo_topo, feature='topography', @@ -313,7 +312,7 @@ def test_topo_extraction_nc(): just makes sure that the data can be rasterized from a WRF file. """ with TemporaryDirectory() as td: - te = ExoRasterizerNC( + te = BaseExoRasterizer( pytest.FP_WRF, pytest.FP_WRF, feature='topography', From 29231ca729e517df12bbab2cdcc7f29242475eee Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 11 Apr 2025 08:49:10 -0600 Subject: [PATCH 073/122] Added some preproc to xr_open_mfdataset for handling problematic era downloads. --- sup3r/utilities/utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sup3r/utilities/utilities.py b/sup3r/utilities/utilities.py index 49c8d821bf..3a580e81d4 100644 --- a/sup3r/utilities/utilities.py +++ b/sup3r/utilities/utilities.py @@ -58,7 +58,7 @@ def xr_open_mfdataset(files, **kwargs): out = xr.open_mfdataset( files, preprocess=preprocess_datasets, **default_kwargs ) - bad_dims = len(out['latitude'].dims) == 2 and ( + bad_dims = 'latitude' in out and len(out['latitude'].dims) == 2 and ( out['latitude'].dims != out['longitude'].dims ) if bad_dims: From 5eec440422e7a22b9a27e9be0262c46651f16489 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 11 Apr 2025 08:49:37 -0600 Subject: [PATCH 074/122] Added check for correct number of model outputs --- sup3r/models/base.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index b3ff5e4f70..5d8504e42f 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -429,7 +429,13 @@ def init_weights(self, lr_shape, hr_shape, device=None): hr_exo_data = {} for feature in self.hr_exo_features + self.obs_features: hr_exo_data[feature] = hr_exo - _ = self._tf_generate(low_res, hr_exo_data) + out = self._tf_generate(low_res, hr_exo_data) + msg = ( + f'Number of model outputs {out.shape[-1]} does not ' + 'match the number of computed hr_out_features ' + f'{len(self.hr_out_features)}' + ) + assert out.shape[-1] == len(self.hr_out_features), msg _ = self._tf_discriminate(hi_res) @staticmethod From 7b68f16b9c516829167baefd15b8826aa5c7b633 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 11 Apr 2025 12:10:21 -0600 Subject: [PATCH 075/122] Adding time index to coords of 3d exo data --- sup3r/preprocessing/rasterizers/exo.py | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index 4b15388275..ff6a6e47e1 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -164,7 +164,8 @@ def cache_file(self): fn += f'{"_".join(map(str, self.input_handler.target))}_' fn += f'{"x".join(map(str, self.input_handler.grid_shape))}_' - if len(self.source_data.shape) == 3: + # add the time index to the filename if data is time dependent + if self.source_data.shape[-1] > 1: start = str(self.hr_time_index[0]) start = start.replace(':', '').replace('-', '').replace(' ', '') end = str(self.hr_time_index[-1]) @@ -184,6 +185,8 @@ def coords(self): coord: (Dimension.dims_2d(), self.hr_lat_lon[..., i]) for i, coord in enumerate(Dimension.coords_2d()) } + if self.source_data.shape[1] > 1: + coords['time'] = self.hr_time_index return coords @property @@ -332,23 +335,18 @@ def get_data(self): hr_data = nn_fill_array(hr_data) logger.info( - 'Finished mapping raster from %s for "%s"', - self.source_file, - self.feature, + f'Finished mapping raster from {self.source_file} for ' + f'"{self.feature}"', ) - data_vars = { - self.feature: ( - dims, - da.asarray(hr_data, dtype=np.float32), - ) - } - ds = xr.Dataset(coords=self.coords, data_vars=data_vars) - return Sup3rX(ds) + data_vars = (dims, da.asarray(hr_data, dtype=np.float32)) + data_vars = {self.feature: data_vars} + return Sup3rX(xr.Dataset(coords=self.coords, data_vars=data_vars)) def _get_data_2d(self): """Get a raster of source values corresponding to the high-resolution grid (the file_paths input grid * s_enhance * - t_enhance). The shape is (lats, lons, 1) + t_enhance). This is used for time independent exogenous data + like topography. The shape is (lats, lons, 1) """ assert ( len(self.source_data.shape) == 2 and self.source_data.shape[1] == 1 @@ -373,9 +371,10 @@ def _get_data_2d(self): return df[self.feature].values.reshape(self.hr_shape[:-1]) def _get_data_3d(self): - """Get a raster of source observation values corresponding to the - high-resolution grid (the file_paths input grid * s_enhance * - t_enhance). The shape is (lats, lons, time) + """Get a raster of source values for spatiotemporal exogeneous + data corresponding to the high-resolution grid (the file_paths input + grid * s_enhance * t_enhance) and high-resolution time index. + The shape is (lats, lons, time) """ assert ( len(self.source_data.shape) == 2 and self.source_data.shape[1] > 1 @@ -409,10 +408,11 @@ def source_handler(self): @property def source_data(self): """Get the flattened observation data from the source_file""" - feat = self.feature.replace('_obs', '') - src = self.source_handler[feat].data - src = src.reshape((-1, src.shape[-1])) - return src + if self._source_data is None: + feat = self.feature.replace('_obs', '') + src = self.source_handler[feat].data + self._source_data = src.reshape((-1, src.shape[-1])) + return self._source_data def _get_data_3d(self): """Get a raster of source observation values corresponding to the From c5c60784aadbc3af6255cd682aa6845588e824ab Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 11 Apr 2025 18:03:02 -0600 Subject: [PATCH 076/122] simpler running sup3r models notebook --- examples/sup3rwind/running_sup3r_models.ipynb | 629 ++++++++++-------- 1 file changed, 370 insertions(+), 259 deletions(-) diff --git a/examples/sup3rwind/running_sup3r_models.ipynb b/examples/sup3rwind/running_sup3r_models.ipynb index af70bf8e76..3f22fd44b9 100644 --- a/examples/sup3rwind/running_sup3r_models.ipynb +++ b/examples/sup3rwind/running_sup3r_models.ipynb @@ -4,37 +4,165 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## How to run saved sup3r models on ERA5 data" + "## How to run saved Sup3rWind models on ERA5 input data" ] }, { "cell_type": "code", - "execution_count": 79, - "metadata": {}, - "outputs": [], - "source": [ - "import logging\n", - "import os\n", - "import xarray as xr\n", - "import hvplot.xarray\n", - "from rex import init_logger\n", - "from sup3r.models import Sup3rGanWithObs\n", - "from sup3r.preprocessing import DataHandler\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "os.environ['CUDA_VISIBLE_DEVICES'] = '-1'\n", - "\n", - "init_logger('sup3r', log_level='DEBUG')\n", - "init_logger(__name__, log_level='DEBUG')\n", - "logger = logging.getLogger(__name__)" - ] - }, - { - "cell_type": "code", - "execution_count": 55, + "execution_count": 63, "metadata": {}, "outputs": [ + { + "data": { + "text/html": [ + " \n", + "
\n", + " \n", + " Loading BokehJS ...\n", + "
\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "'use strict';\n(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\nconst JS_MIME_TYPE = 'application/javascript';\n const HTML_MIME_TYPE = 'text/html';\n const EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n const CLASS_NAME = 'output_bokeh rendered_html';\n\n /**\n * Render data to the DOM node\n */\n function render(props, node) {\n const script = document.createElement(\"script\");\n node.appendChild(script);\n }\n\n /**\n * Handle when an output is cleared or removed\n */\n function handleClearOutput(event, handle) {\n function drop(id) {\n const view = Bokeh.index.get_by_id(id)\n if (view != null) {\n view.model.document.clear()\n Bokeh.index.delete(view)\n }\n }\n\n const cell = handle.cell;\n\n const id = cell.output_area._bokeh_element_id;\n const server_id = cell.output_area._bokeh_server_id;\n\n // Clean up Bokeh references\n if (id != null) {\n drop(id)\n }\n\n if (server_id !== undefined) {\n // Clean up Bokeh references\n const cmd_clean = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n cell.notebook.kernel.execute(cmd_clean, {\n iopub: {\n output: function(msg) {\n const id = msg.content.text.trim()\n drop(id)\n }\n }\n });\n // Destroy server and session\n const cmd_destroy = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n cell.notebook.kernel.execute(cmd_destroy);\n }\n }\n\n /**\n * Handle when a new output is added\n */\n function handleAddOutput(event, handle) {\n const output_area = handle.output_area;\n const output = handle.output;\n\n // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n if ((output.output_type != \"display_data\") || (!Object.prototype.hasOwnProperty.call(output.data, EXEC_MIME_TYPE))) {\n return\n }\n\n const toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n\n if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n // store reference to embed id on output_area\n output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n }\n if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n const bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n const script_attrs = bk_div.children[0].attributes;\n for (let i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n }\n\n function register_renderer(events, OutputArea) {\n\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n const toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n const props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[toinsert.length - 1]);\n element.append(toinsert);\n return toinsert\n }\n\n /* Handle when an output is cleared or removed */\n events.on('clear_output.CodeCell', handleClearOutput);\n events.on('delete.Cell', handleClearOutput);\n\n /* Handle when a new output is added */\n events.on('output_added.OutputArea', handleAddOutput);\n\n /**\n * Register the mime type and append_mime function with output_area\n */\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n /* Is output safe? */\n safe: true,\n /* Index of renderer in `output_area.display_order` */\n index: 0\n });\n }\n\n // register the mime type if in Jupyter Notebook environment and previously unregistered\n if (root.Jupyter !== undefined) {\n const events = require('base/js/events');\n const OutputArea = require('notebook/js/outputarea').OutputArea;\n\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n }\n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n const NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"
    \\n\"+\n \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n \"
  • use INLINE resources instead, as so:
  • \\n\"+\n \"
\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded(error = null) {\n const el = document.getElementById(\"a37d220a-7445-4ecf-beca-4bf51a504846\");\n if (el != null) {\n const html = (() => {\n if (typeof root.Bokeh === \"undefined\") {\n if (error == null) {\n return \"BokehJS is loading ...\";\n } else {\n return \"BokehJS failed to load.\";\n }\n } else {\n const prefix = `BokehJS ${root.Bokeh.version}`;\n if (error == null) {\n return `${prefix} successfully loaded.`;\n } else {\n return `${prefix} encountered errors while loading and may not function as expected.`;\n }\n }\n })();\n el.innerHTML = html;\n\n if (error != null) {\n const wrapper = document.createElement(\"div\");\n wrapper.style.overflow = \"auto\";\n wrapper.style.height = \"5em\";\n wrapper.style.resize = \"vertical\";\n const content = document.createElement(\"div\");\n content.style.fontFamily = \"monospace\";\n content.style.whiteSpace = \"pre-wrap\";\n content.style.backgroundColor = \"rgb(255, 221, 221)\";\n content.textContent = error.stack ?? error.toString();\n wrapper.append(content);\n el.append(wrapper);\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(() => display_loaded(error), 100);\n }\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error(url) {\n console.error(\"failed to load \" + url);\n }\n\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.0/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\"];\n const css_urls = [];\n\n const inline_js = [ function(Bokeh) {\n /* BEGIN bokeh.min.js */\n 'use strict';\n /*!\n * Copyright (c) Anaconda, Inc., and Bokeh Contributors\n * All rights reserved.\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n * \n * Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n * \n * Neither the name of Anaconda nor the names of any contributors\n * may be used to endorse or promote products derived from this software\n * without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n (function(root, factory) {\n const bokeh = factory();\n bokeh.__bokeh__ = true;\n if (typeof root.Bokeh === \"undefined\" || typeof root.Bokeh.__bokeh__ === \"undefined\") {\n root.Bokeh = bokeh;\n }\n const Bokeh = root.Bokeh;\n Bokeh[bokeh.version] = bokeh;\n })(this, function() {\n let define;\n const parent_require = typeof require === \"function\" && require\n return (function(modules, entry, aliases, externals) {\n if (aliases === undefined) aliases = {};\n if (externals === undefined) externals = {};\n\n const cache = {};\n\n const normalize = function(name) {\n if (typeof name === \"number\")\n return name;\n\n if (name === \"bokehjs\")\n return entry;\n\n if (!externals[name]) {\n const prefix = \"@bokehjs/\"\n if (name.slice(0, prefix.length) === prefix)\n name = name.slice(prefix.length)\n }\n\n const alias = aliases[name]\n if (alias != null)\n return alias;\n\n const trailing = name.length > 0 && name[name.length-1] === \"/\";\n const index = aliases[name + (trailing ? \"\" : \"/\") + \"index\"];\n if (index != null)\n return index;\n\n return name;\n }\n\n const require = function(name) {\n let mod = cache[name];\n if (!mod) {\n const id = normalize(name);\n\n mod = cache[id];\n if (!mod) {\n if (!modules[id]) {\n if (externals[id] === false || (externals[id] == true && parent_require)) {\n try {\n mod = {exports: externals[id] ? parent_require(id) : {}};\n cache[id] = cache[name] = mod;\n return mod.exports;\n } catch (e) {}\n }\n\n const err = new Error(\"Cannot find module '\" + name + \"'\");\n err.code = 'MODULE_NOT_FOUND';\n throw err;\n }\n\n mod = {exports: {}};\n cache[id] = cache[name] = mod;\n\n function __esModule() {\n Object.defineProperty(mod.exports, \"__esModule\", {value: true});\n }\n\n function __esExport(name, value) {\n Object.defineProperty(mod.exports, name, {\n enumerable: true, get: function () { return value; }\n });\n }\n\n modules[id].call(mod.exports, require, mod, mod.exports, __esModule, __esExport);\n } else {\n cache[name] = mod;\n }\n }\n\n return mod.exports;\n }\n require.resolve = function(name) {\n return \"\"\n }\n\n const main = require(entry);\n main.require = require;\n\n if (typeof Proxy !== \"undefined\") {\n // allow Bokeh.loader[\"@bokehjs/module/name\"] syntax\n main.loader = new Proxy({}, {\n get: function(_obj, module) {\n return require(module);\n }\n });\n }\n\n main.register_plugin = function(plugin_modules, plugin_entry, plugin_aliases, plugin_externals) {\n if (plugin_aliases === undefined) plugin_aliases = {};\n if (plugin_externals === undefined) plugin_externals = {};\n\n for (let name in plugin_modules) {\n modules[name] = plugin_modules[name];\n }\n\n for (let name in plugin_aliases) {\n aliases[name] = plugin_aliases[name];\n }\n\n for (let name in plugin_externals) {\n externals[name] = plugin_externals[name];\n }\n\n const plugin = require(plugin_entry);\n\n for (let name in plugin) {\n main[name] = plugin[name];\n }\n\n return plugin;\n }\n\n return main;\n })\n ([\n function _(t,_,n,o,r){o();t(1).__exportStar(t(2),n),t(76)},\n function _(e,t,r,n,o){n();var a=function(e,t){return a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},a(e,t)};function i(e,t){if(\"function\"!=typeof t&&null!==t)throw new TypeError(\"Class extends value \"+String(t)+\" is not a constructor or null\");function r(){this.constructor=e}a(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}r.__extends=i;function c(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(null!=e&&\"function\"==typeof Object.getOwnPropertySymbols){var o=0;for(n=Object.getOwnPropertySymbols(e);o=0;c--)(o=e[c])&&(i=(a<3?o(i):a>3?o(t,r,i):o(t,r))||i);return a>3&&i&&Object.defineProperty(t,r,i),i}function u(e,t){return function(r,n){t(r,n,e)}}function f(e,t){if(\"object\"==typeof Reflect&&\"function\"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function l(e,t,r,n){return new(r||(r=Promise))((function(o,a){function i(e){try{s(n.next(e))}catch(e){a(e)}}function c(e){try{s(n.throw(e))}catch(e){a(e)}}function s(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(i,c)}s((n=n.apply(e,t||[])).next())}))}function p(e,t){var r,n,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:c(0),throw:c(1),return:c(2)},\"function\"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function c(c){return function(s){return function(c){if(r)throw new TypeError(\"Generator is already executing.\");for(;a&&(a=0,c[0]&&(i=0)),i;)try{if(r=1,n&&(o=2&c[0]?n.return:c[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,c[1])).done)return o;switch(n=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return i.label++,{value:c[1],done:!1};case 5:i.label++,n=c[1],c=[0];continue;case 7:c=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==c[0]&&2!==c[0])){i=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?\"Object is not iterable.\":\"Symbol.iterator is not defined.\")}function d(e,t){var r=\"function\"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i}function h(){for(var e=[],t=0;t1||c(e,t)}))})}function c(e,t){try{(r=o[e](t)).value instanceof w?Promise.resolve(r.value.v).then(s,u):f(a[0][2],r)}catch(e){f(a[0][3],e)}var r}function s(e){c(\"next\",e)}function u(e){c(\"throw\",e)}function f(e,t){e(t),a.shift(),a.length&&c(a[0][0],a[0][1])}}function g(e){var t,r;return t={},n(\"next\"),n(\"throw\",(function(e){throw e})),n(\"return\"),t[Symbol.iterator]=function(){return this},t;function n(n,o){t[n]=e[n]?function(t){return(r=!r)?{value:w(e[n](t)),done:!1}:o?o(t):t}:o}}function O(e){if(!Symbol.asyncIterator)throw new TypeError(\"Symbol.asyncIterator is not defined.\");var t,r=e[Symbol.asyncIterator];return r?r.call(e):(e=_(e),t={},n(\"next\"),n(\"throw\"),n(\"return\"),t[Symbol.asyncIterator]=function(){return this},t);function n(r){t[r]=e[r]&&function(t){return new Promise((function(n,o){(function(e,t,r,n){Promise.resolve(n).then((function(t){e({value:t,done:r})}),t)})(n,o,(t=e[r](t)).done,t.value)}))}}}function j(e,t){return Object.defineProperty?Object.defineProperty(e,\"raw\",{value:t}):e.raw=t,e}r.__assign=function(){return r.__assign=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=0;y--){var _={};for(var d in n)_[d]=\"access\"===d?{}:n[d];for(var d in n.access)_.access[d]=n.access[d];_.addInitializer=function(e){if(p)throw new TypeError(\"Cannot add initializers after decoration has completed\");a.push(i(e||null))};var h=(0,r[y])(\"accessor\"===s?{get:l.get,set:l.set}:l[u],_);if(\"accessor\"===s){if(void 0===h)continue;if(null===h||\"object\"!=typeof h)throw new TypeError(\"Object expected\");(c=i(h.get))&&(l.get=c),(c=i(h.set))&&(l.set=c),(c=i(h.init))&&o.unshift(c)}else(c=i(h))&&(\"field\"===s?o.unshift(c):l[u]=c)}f&&Object.defineProperty(f,n.name,l),p=!0},r.__runInitializers=function(e,t,r){for(var n=arguments.length>2,o=0;o{(0,b.assert)(e instanceof z.ModelEvent),this.event_manager.trigger(e)}))}[f.equals](e,t){return this==e}get all_models(){return new Set(this._all_models.values())}get is_idle(){for(const e of this._roots)if(!this._idle_roots.has(e))return!1;return!0}notify_idle(e){this._idle_roots.add(e),this.is_idle&&(r.logger.info(`document idle at ${Date.now()-this._init_timestamp} ms`),this.event_manager.send_event(new S.DocumentReady),this.idle.emit())}clear(){this._push_all_models_freeze();try{for(;this._roots.length>0;)this.remove_root(this._roots[0])}finally{this._pop_all_models_freeze()}}interactive_start(e,t=null){null==this._interactive_plot&&(this._interactive_plot=e,this._interactive_plot.trigger_event(new S.LODStart)),this._interactive_finalize=t,this._interactive_timestamp=Date.now()}interactive_stop(){null!=this._interactive_plot&&(this._interactive_plot.trigger_event(new S.LODEnd),null!=this._interactive_finalize&&this._interactive_finalize()),this._interactive_plot=null,this._interactive_timestamp=null,this._interactive_finalize=null}interactive_duration(){return null==this._interactive_timestamp?-1:Date.now()-this._interactive_timestamp}destructively_move(e){if(e===this)throw new Error(\"Attempted to overwrite a document with itself\");e.clear();const t=(0,g.copy)(this._roots);this.clear();for(const e of t)if(null!=e.document)throw new Error(`Somehow we didn't detach ${e}`);if(0!=this._all_models.size)throw new Error(`this._all_models still had stuff in it: ${this._all_models}`);for(const s of t)e.add_root(s);e.set_title(this._title)}_push_all_models_freeze(){this._all_models_freeze_count+=1}_pop_all_models_freeze(){this._all_models_freeze_count-=1,0===this._all_models_freeze_count&&this._recompute_all_models()}_invalidate_all_models(){r.logger.debug(\"invalidating document models\"),0===this._all_models_freeze_count&&this._recompute_all_models()}_recompute_all_models(){let e=new Set;for(const t of this._roots)e=p.union(e,t.references());const t=new Set(this._all_models.values()),s=p.difference(t,e),o=p.difference(e,t),n=new Map;for(const t of e)n.set(t.id,t);for(const e of s)e.detach_document();for(const e of o)e.attach_document(this),this._new_models.add(e);this._all_models=n}roots(){return this._roots}_add_roots(...e){if(0==(e=e.filter((e=>!this._roots.includes(e)))).length)return!1;this._push_all_models_freeze();try{this._roots.push(...e)}finally{this._pop_all_models_freeze()}return!0}_remove_root(e){const t=this._roots.indexOf(e);if(t<0)return!1;this._push_all_models_freeze();try{this._roots.splice(t,1)}finally{this._pop_all_models_freeze()}return!0}_set_title(e){const t=e!=this._title;return t&&(this._title=e),t}add_root(e,{sync:t}={}){if(this._add_roots(e)){const s=new E.RootAddedEvent(this,e);s.sync=t??!0,this._trigger_on_change(s)}}remove_root(e,{sync:t}={}){if(this._remove_root(e)){const s=new E.RootRemovedEvent(this,e);s.sync=t??!0,this._trigger_on_change(s)}}set_title(e,{sync:t}={}){if(this._set_title(e)){const s=new E.TitleChangedEvent(this,e);s.sync=t??!0,this._trigger_on_change(s)}}title(){return this._title}get_model_by_id(e){return this._all_models.get(e)??null}get_model_by_name(e){const t=[];for(const s of this._all_models.values())s instanceof k.Model&&s.name==e&&t.push(s);switch(t.length){case 0:return null;case 1:return t[0];default:throw new Error(`Multiple models are named '${e}'`)}}on_message(e,t){const s=this._message_callbacks.get(e);null==s?this._message_callbacks.set(e,new Set([t])):s.add(t)}remove_on_message(e,t){this._message_callbacks.get(e)?.delete(t)}_trigger_on_message(e,t){const s=this._message_callbacks.get(e);if(null!=s)for(const e of s)e(t)}on_change(e,t=!1){this._callbacks.has(e)||this._callbacks.set(e,t)}remove_on_change(e){this._callbacks.delete(e)}_trigger_on_change(e){for(const[t,s]of this._callbacks)if(!s&&e instanceof E.DocumentEventBatch)for(const s of e.events)t(s);else t(e)}_trigger_on_event(e){const t=this._document_callbacks.get(e.event_name);if(null!=t)for(const s of t)(0,w.execute)(s,this,e)}on_event(e,...t){const s=(0,u.isString)(e)?e:e.prototype.event_name,o=this._document_callbacks.get(s)??[],n=t;this._document_callbacks.set(s,[...o,...n])}to_json_string(e=!0){return JSON.stringify(this.to_json(e))}to_json(e=!0){const t=new c.Serializer({include_defaults:e}).encode(this._roots);return{version:l.version,title:this._title,roots:t}}static from_json_string(e,t){const s=JSON.parse(e);return M.from_json(s,t)}static _handle_version(e){null==e.version&&r.logger.warn(\"'version' field is missing\");const t=e.version??\"0.0.0\",s=d.Version.from(t),o=d.Version.from(l.version),n=`new document using Bokeh ${t} and BokehJS ${l.version}`;(0,f.is_equal)(s,o)?r.logger.debug(n):r.logger.warn(`Bokeh/BokehJS version mismatch: ${n}`)}static from_json(e,t){r.logger.debug(\"Creating Document from JSON\"),M._handle_version(e);const s=new a.ModelResolver(_.default_resolver);if(null!=e.defs){new h.Deserializer(s).decode(e.defs)}const o=new M({resolver:s});o._push_all_models_freeze();const n=e=>t?.push(e);o.on_change(n,!0);const i=new h.Deserializer(s,o._all_models,(e=>e.attach_document(o))),l=i.decode(e.roots),c=null!=e.callbacks?i.decode(e.callbacks):{};o.remove_on_change(n);for(const[e,t]of(0,v.entries)(c))o.on_event(e,...t);for(const e of l)o.add_root(e);return null!=e.title&&o.set_title(e.title),o._pop_all_models_freeze(),o}replace_with_json(e){M.from_json(e).destructively_move(this)}create_json_patch(e){for(const t of e)if(t.document!=this)throw new Error(\"Cannot create a patch using events from a different document\");const t=new Map;for(const e of this._all_models.values())this._new_models.has(e)||t.set(e,e.ref());const s={events:new c.Serializer({references:t,binary:!0}).encode(e)};return this._new_models.clear(),s}apply_json_patch(e,t=new Map){this._push_all_models_freeze();const s=new h.Deserializer(this._resolver,this._all_models,(e=>e.attach_document(this))).decode(e.events,t);for(const e of s)switch(e.kind){case\"MessageSent\":{const{msg_type:t,msg_data:s}=e;this._trigger_on_message(t,s);break}case\"ModelChanged\":{const{model:t,attr:s,new:o}=e;t.setv({[s]:o},{sync:!1});break}case\"ColumnDataChanged\":{const{model:t,attr:s,data:o,cols:n}=e;if(null!=n){const e=(0,v.dict)(o),n=(0,v.dict)(t.property(s).get_value());for(const[t,s]of n)e.has(t)||e.set(t,s)}t.setv({data:o},{sync:!1,check_eq:!1});break}case\"ColumnsStreamed\":{const{model:t,attr:s,data:o,rollover:n}=e,i=t.property(s);t.stream_to(i,o,n,{sync:!1});break}case\"ColumnsPatched\":{const{model:t,attr:s,patches:o}=e,n=t.property(s);t.patch_to(n,o,{sync:!1});break}case\"RootAdded\":this.add_root(e.model,{sync:!1});break;case\"RootRemoved\":this.remove_root(e.model,{sync:!1});break;case\"TitleChanged\":this.set_title(e.title,{sync:!1});break;default:throw new Error(`unknown patch event type '${e.kind}'`)}this._pop_all_models_freeze()}}s.Document=M,M.__name__=\"Document\"},\n function _(e,r,t,n,s){n();const l=e(8),o=e(9),i=e(8),u=e(14),f=e(46);t.default_resolver=new f.ModelResolver(null),t.Models=new Proxy(t.default_resolver,{get(e,r,t){if((0,i.isString)(r)){const t=e.get(r);if(null!=t)return t}return Reflect.get(e,r,t)},has(e,r){if((0,i.isString)(r)){if(null!=e.get(r))return!0}return Reflect.has(e,r)},ownKeys:e=>e.names,getOwnPropertyDescriptor(e,r){if((0,i.isString)(r)){const t=e.get(r);if(null!=t)return{configurable:!0,enumerable:!0,writable:!1,value:t}}return Reflect.getOwnPropertyDescriptor(e,r)}}),t.register_models=function(e,r=!1){for(const s of(0,l.isArray)(e)?e:(0,o.values)(e))n=s,(0,l.isObject)(n)&&n.prototype instanceof u.HasProps&&t.default_resolver.register(s,r);var n}},\n function _(n,t,r,i,e){i();\n // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n // Underscore may be freely distributed under the MIT license.\n const{toString:o}=Object.prototype;function u(n){return null==n}function c(n){return null!=n}function s(n){return!0===n||!1===n||\"[object Boolean]\"===o.call(n)}function f(n){return\"[object Number]\"===o.call(n)}function l(n){return\"[object String]\"===o.call(n)}function a(n){return\"symbol\"==typeof n}function b(n){const t=typeof n;return\"function\"===t||\"object\"===t&&!!n}function y(n){return b(n)&&(u(n.constructor)||n.constructor===Object)}function j(n){return b(n)&&Symbol.iterator in n}r.is_undefined=function(n){return void 0===n},r.is_defined=function(n){return void 0!==n},r.is_nullish=u,r.isNull=function(n){return null==n},r.isNotNull=c,r.non_null=c,r.isBoolean=s,r.isNumber=f,r.isInteger=function(n){return f(n)&&Number.isInteger(n)},r.isString=l,r.isSymbol=a,r.isPrimitive=function(n){return null===n||s(n)||f(n)||l(n)||a(n)},r.isFunction=function(n){switch(o.call(n)){case\"[object Function]\":case\"[object AsyncFunction]\":case\"[object GeneratorFunction]\":case\"[object AsyncGeneratorFunction]\":return!0;default:return!1}},r.isArray=function(n){return Array.isArray(n)},r.isArrayOf=function(n,t){for(const r of n)if(!t(r))return!1;return!0},r.isArrayableOf=function(n,t){for(const r of n)if(!t(r))return!1;return!0},r.isTypedArray=function(n){return ArrayBuffer.isView(n)&&!(n instanceof DataView)},r.isObject=b,r.isBasicObject=function(n){return b(n)&&u(n.constructor)},r.isPlainObject=y,r.isDict=function(n){return n instanceof Map||y(n)},r.isIterable=j,r.isArrayable=function(n){return j(n)&&\"length\"in n}},\n function _(e,t,n,s,i){var o;s();const r=e(8),c=e(10);function a(e){return e instanceof Map?[...e.keys()]:Object.keys(e)}function u(e){return e instanceof Map?[...e.values()]:Object.values(e)}function b(e){return e instanceof Map?[...e.entries()]:Object.entries(e)}function l(e){return e instanceof Map?e.size:Object.keys(e).length}n.assign=Object.assign,n.extend=n.assign,n.to_object=function(e){return(0,r.isPlainObject)(e)?e:Object.fromEntries(e)},n.keys=a,n.values=u,n.entries=b,n.typed_keys=Object.keys,n.typed_values=Object.values,n.typed_entries=Object.entries,n.clone=function(e){return e instanceof Map?new Map(e):{...e}},n.merge=function(e,t){const n=new Map,s=[...e.keys(),...t.keys()];for(const i of s){const s=e.get(i),o=t.get(i),r=void 0===s?[]:s,a=void 0===o?[]:o;n.set(i,(0,c.union)(r,a))}return n},n.size=l,n.is_empty=function(e){return 0==l(e)};const{hasOwnProperty:j}=Object.prototype;class h{constructor(e){this[o]=\"PlainObjectProxy\",this.obj=e}clear(){for(const e of this.keys())delete this.obj[e]}delete(e){const t=this.has(e);return t&&delete this.obj[e],t}has(e){return j.call(this.obj,e)}get(e){return this.has(e)?this.obj[e]:void 0}set(e,t){return this.obj[e]=t,this}get size(){return l(this.obj)}[(o=Symbol.toStringTag,Symbol.iterator)](){return this.entries()}*keys(){yield*a(this.obj)}*values(){yield*u(this.obj)}*entries(){yield*b(this.obj)}forEach(e,t){for(const[n,s]of this.entries())e.call(t,s,n,this)}}n.PlainObjectProxy=h,h.__name__=\"PlainObjectProxy\",n.dict=function(e){return(0,r.isPlainObject)(e)?new h(e):e}},\n function _(n,t,e,r,o){r();\n // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n // Underscore may be freely distributed under the MIT license.\n const i=n(11),c=n(12),s=n(8),u=n(13);var f=n(13);o(\"map\",f.map),o(\"reduce\",f.reduce),o(\"min\",f.min),o(\"min_by\",f.min_by),o(\"max\",f.max),o(\"max_by\",f.max_by),o(\"sum\",f.sum),o(\"cumsum\",f.cumsum),o(\"every\",f.every),o(\"some\",f.some),o(\"find\",f.find),o(\"find_last\",f.find_last),o(\"find_index\",f.find_index),o(\"find_last_index\",f.find_last_index),o(\"sorted_index\",f.sorted_index),o(\"is_empty\",f.is_empty),o(\"includes\",f.includes),o(\"contains\",f.contains),o(\"sort_by\",f.sort_by);const{slice:l}=Array.prototype;function a(n){return l.call(n)}function h(n,t,e=1){(0,c.assert)(e>0,\"'step' must be a positive number\"),null==t&&(t=n,n=0);const{max:r,ceil:o,abs:i}=Math,s=n<=t?e:-e,u=r(o(i(t-n)/e),0),f=new Array(u);for(let t=0;t=0?t:n.length+t]},e.zip=function(...n){if(0==n.length)return[];const t=(0,u.min)(n.map((n=>n.length))),e=n.length,r=new Array(t);for(let o=0;on.length))),r=Array(e);for(let n=0;nn[t]))},e.argmax=function(n){return(0,u.max_by)(h(n.length),(t=>n[t]))},e.uniq=function(n){const t=new Set;for(const e of n)t.add(e);return[...t]},e.uniq_by=function(n,t){const e=[],r=[];for(const o of n){const n=t(o);(0,u.includes)(r,n)||(r.push(n),e.push(o))}return e},e._union=m,e.union=function(...n){return[...m(n)]},e.intersection=function(n,...t){const e=[];n:for(const r of n)if(!(0,u.includes)(e,r)){for(const n of t)if(!(0,u.includes)(n,r))continue n;e.push(r)}return e},e.difference=function(n,...t){const e=m(t);return(0,u.filter)(n,(n=>!e.has(n)))},e.symmetric_difference=function(n,t){const e=new Set(n),r=new Set(t),o=[];for(const n of e)r.has(n)||o.push(n);for(const n of r)e.has(n)||o.push(n);return o},e.remove_at=function(n,t){(0,c.assert)((0,s.isInteger)(t)&&t>=0);const e=a(n);return e.splice(t,1),e},e.remove=function(n,t){d(n,(n=>n==t))},e.remove_by=d,e.clear=function(n){n.splice(0,n.length)},e.split=function(n,t){const e=[],r=n.length;let o=0,i=0;for(;i=t)return n.slice(0,t);{const r=new Array(t-n.length);return void 0!==e&&r.fill(e),n.concat(r)}}},\n function _(n,t,r,o,e){o();const a=n(8),c=n(12),{PI:u,abs:i,sign:f,sqrt:l}=Math;function s(n){if(0==n)return 0;for(;n<=0;)n+=2*u;for(;n>2*u;)n-=2*u;return n}function h(n,t){return s(n-t)}function g(n,t,r=\"anticlock\"){return-(\"anticlock\"==r?1:-1)*n*m(t)}function m(n){switch(n){case\"deg\":return u/180;case\"rad\":return 1;case\"grad\":return u/200;case\"turn\":return 2*u}}function _(n,t){for(n=Math.abs(n),t=Math.abs(t);0!=t;)[n,t]=[t,n%t];return n}r.PI=u,r.abs=i,r.sqrt=l,r.angle_norm=s,r.angle_dist=h,r.angle_between=function(n,t,r,o=!1){const e=h(t,r);if(0==e)return!1;if(e==2*u)return!0;const a=s(n),c=h(t,a)<=e&&h(a,r)<=e;return o?!c:c},r.randomIn=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},r.atan2=function(n,t){return Math.atan2(t[1]-n[1],t[0]-n[0])},r.radians=function(n){return n*(u/180)},r.degrees=function(n){return n/(u/180)},r.compute_angle=g,r.resolve_angle=g,r.invert_angle=function(n,t,r=\"anticlock\"){return-(\"anticlock\"==r?1:-1)*n/m(t)},r.to_radians_coeff=m,r.minmax=function(n,t){return n<=t?[n,t]:[t,n]},r.clamp=function(n,t,r){return nr?r:n},r.cycle=function(n,t,r){return n>r?t:n=0;e--)r+=o*n[e],o*=t;return r}},\n function _(r,e,n,o,s){o();class a extends Error{}n.AssertionError=a,a.__name__=\"AssertionError\";class t extends Error{}function c(r,e){if(!(!0===r||!1!==r&&r()))throw new a(e??\"Assertion failed\")}n.UnreachableError=t,t.__name__=\"UnreachableError\",n.assert=c,n.assert_debug=function(r,e){\"undefined\"!=typeof DEBUG&&DEBUG&&c(r,e)},n.unreachable=function(r){throw new t(`unreachable code${null!=r?`: ${r}`:\"\"}`)}},\n function _(n,t,e,r,o){r();const i=n(11),c=n(12),{floor:u}=Math;function f(n){const t=n.length;if(0==t)return!0;let e=n[0];for(let r=1;ro&&(t=o),null==e||e>o-t?e=o-t:e<0&&(e=0);const i=o-e+r.length,c=new n.constructor(i);let u=0;for(;u0?0:r-1;for(;o>=0&&of(n))),(0,c.assert)(0<=r&&o<=n.length);rf(n))),(0,c.assert)(0<=r&&o<=n.length);rn),e,r)}function p(n,t,e,r,o){const i=(o-e)/(r-t);let c=i*(n-t)+e;return isFinite(c)||(c=i*(n-r)+o,isFinite(c)||e!=o||(c=e)),c}function w(n,t){if(nt[t.length-1])return t.length;if(1==t.length)return 0;let e=0,r=t.length-1;for(;r-e!=1;){const o=e+Math.floor((r-e)/2);n>=t[o]?e=o:r=o}return e}e.is_empty=function(n){return 0==n.length},e.is_sorted=f,e.copy=l,e.splice=s,e.head=a,e.insert=function(n,t,e){return s(n,e,0,t)},e.append=function(n,t){return s(n,n.length,0,t)},e.prepend=function(n,t){return s(n,0,0,t)},e.index_of=function(n,t){return n.indexOf(t)},e.includes=h,e.contains=h,e.subselect=function(n,t){const e=t.length,r=new n.constructor(e);for(let o=0;o({index:e,key:t(n)})));return e.sort(((n,t)=>{const e=n.key,r=t.key;if(e!==r){if(e>r)return 1;if(en[e[r].index]))},e.min=function(n){let t=1/0;for(const e of n)!isNaN(e)&&et&&(t=e);return t},e.minmax=function(n){let t=1/0,e=-1/0;for(const r of n)isNaN(r)||(re&&(e=r));return[t,e]},e.minmax2=function(n,t){let e,r,o=1/0,i=-1/0,c=1/0,u=-1/0;const f=Math.min(n.length,t.length);for(let l=0;li&&(i=e),ru&&(u=r));return[o,i,c,u]},e.min_by=function(n,t){if(0==n.length)throw new Error(\"min_by() called with an empty array\");let e=n[0],r=t(e,0);for(let o=1,i=n.length;or&&(e=i,r=c)}return e},e.sum=function(n){let t=0;for(let e=0,r=n.length;et[r]=n+e),0),t},e.every=function(n,t){for(const e of n)if(!t(e))return!1;return!0},e.some=function(n,t){for(const e of n)if(t(e))return!0;return!1},e.find_index=_(1),e.find_last_index=_(-1),e.find=function(n,t){const r=(0,e.find_index)(n,t);return-1==r?void 0:n[r]},e.find_last=function(n,t){const r=(0,e.find_last_index)(n,t);return-1==r?void 0:n[r]},e.bisect_left_by=y,e.bisect_right_by=m,e.bisect_left=x,e.bisect_right=function(n,t,e=0,r){return m(n,t,(n=>n),e,r)},e.binary_search=function(n,t){const e=x(n,t);return e!=n.length&&n[e]==t?e:null},e.sorted_index=x,e.bin_counts=function(n,t){const r=t.length-1,o=Array(r).fill(0);for(let c=0;c(n-t)/r))}},\n function _(t,e,s,n,i){var r;n();const o=t(1),c=t(15),a=t(17),h=o.__importStar(t(18)),_=o.__importStar(t(21)),u=t(12),l=t(40),f=t(9),p=t(8),d=t(32),g=t(41),y=t(26),m=t(42),v=t(31),w=o.__importStar(t(21)),b=t(28),S=t(43),$=new WeakMap;class x extends((0,c.Signalable)()){get is_syncable(){return!0}get type(){return this.constructor.__qualified__}static get __qualified__(){let t=$.get(this);if(null==t){const{__module__:e,__name__:s}=this;t=null!=e?`${e}.${s}`:s,$.set(this,t)}return t}static set __qualified__(t){$.set(this,t)}get[Symbol.toStringTag](){return this.constructor.__qualified__}static _fix_default(t,e){if(void 0===t||t===h.unset)return()=>h.unset;if((0,p.isFunction)(t))return t;if((0,p.isPrimitive)(t))return()=>t;{const e=new v.Cloner;return()=>e.clone(t)}}static define(t){for(const[e,s]of(0,f.entries)((0,p.isFunction)(t)?t(w):t)){if(e in this.prototype._props)throw new Error(`attempted to redefine property '${this.prototype.type}.${e}'`);if(e in this.prototype)throw new Error(`attempted to redefine attribute '${this.prototype.type}.${e}'`);Object.defineProperty(this.prototype,e,{get(){return this.properties[e].get_value()},set(t){return this.setv({[e]:t}),this},configurable:!1,enumerable:!0});const[t,n,i={}]=s,r={type:t,default_value:this._fix_default(n,e),options:i};this.prototype._props={...this.prototype._props,[e]:r}}}static internal(t){const e={};for(const[s,n]of(0,f.entries)((0,p.isFunction)(t)?t(w):t)){const[t,i,r={}]=n;e[s]=[t,i,{...r,internal:!0}]}this.define(e)}static mixins(t){function e(t,e){const s={};for(const[n,i]of(0,f.entries)(e))s[t+n]=i;return s}const s={},n=[];for(const i of(0,p.isArray)(t)?t:[t])if((0,p.isArray)(i)){const[t,r]=i;(0,f.extend)(s,e(t,r)),n.push([t,r])}else{const t=i;(0,f.extend)(s,t),n.push([\"\",t])}this.define(s),this.prototype._mixins=[...this.prototype._mixins,...n]}static override(t){for(const[e,s]of(0,f.entries)(t)){const t=this._fix_default(s,e);if(!(e in this.prototype._props))throw new Error(`attempted to override nonexistent '${this.prototype.type}.${e}'`);const n=this.prototype._props[e],i={...this.prototype._props};i[e]={...n,default_value:t},this.prototype._props=i}}static toString(){return this.__qualified__}toString(){return`${this.type}(${this.id})`}property(t){if(t in this.properties)return this.properties[t];throw new Error(`unknown property ${this.type}.${t}`)}get attributes(){const t={};for(const e of this)e.is_unset||(t[e.attr]=e.get_value());return t}[v.clone](t){const e=new Map;for(const s of this)s.dirty&&e.set(s.attr,t.clone(s.get_value()));return new this.constructor(e)}[y.equals](t,e){for(const s of this){const n=t.property(s.attr);if(!e.eq(s.get_value(),n.get_value()))return!1}return!0}[m.pretty](t){const e=t.token,s=[];for(const n of this)if(n.dirty){const i=n.get_value();s.push(`${n.attr}${e(\":\")} ${t.to_string(i)}`)}return`${this.constructor.__qualified__}${e(\"(\")}${e(\"{\")}${s.join(`${e(\",\")} `)}${e(\"}\")}${e(\")\")}`}[d.serialize](t){const e=this.ref();t.add_ref(this,e);const s={};for(const e of this)if(e.syncable&&(t.include_defaults||e.dirty)&&(!e.readonly||!e.is_unset)){const n=e.get_value();s[e.attr]=t.encode(n)}const{type:n,id:i}=this,r={type:\"object\",name:n,id:i};return(0,f.is_empty)(s)?r:{...r,attributes:s}}constructor(t={}){super(),this.document=null,this.destroyed=new c.Signal0(this,\"destroyed\"),this.change=new c.Signal0(this,\"change\"),this.transformchange=new c.Signal0(this,\"transformchange\"),this.exprchange=new c.Signal0(this,\"exprchange\"),this.streaming=new c.Signal0(this,\"streaming\"),this.patching=new c.Signal(this,\"patching\"),this.properties={},this._watchers=new WeakMap,this._pending=!1,this._changing=!1;const e=(0,p.isPlainObject)(t)&&\"id\"in t;this.id=e?t.id:(0,l.unique_id)();for(const[t,{type:e,default_value:s,options:n}]of(0,f.entries)(this._props)){let i;if(e instanceof h.PropertyAlias){const s=this.properties[e.attr];if(void 0===s)throw new Error(`can't resolve ${e.attr} before ${t} to create an alias`);Object.defineProperty(this.properties,t,{get:()=>s,configurable:!1,enumerable:!1})}else i=e instanceof _.Kind?new h.PrimitiveProperty(this,t,e,s,n):new e(this,t,_.Any,s,n),this.properties[t]=i}e?(0,u.assert)(1==(0,f.keys)(t).length,\"'id' cannot be used together with property initializers\"):(this.initialize_props(t),this.finalize(),this.connect_signals())}initialize_props(t){const e=(0,f.dict)(t),s=new Set;for(const t of this){const n=e.get(t.attr);t.initialize(n),s.add(t.attr)}for(const[t,n]of e)s.has(t)||this.property(t).set_value(n)}finalize(){this.initialize()}initialize(){}assert_initialized(){for(const t of this)t.syncable&&!t.readonly&&t.get_value()}connect_signals(){for(const t of this){if(!(t instanceof h.VectorSpec||t instanceof h.ScalarSpec))continue;if(t.is_unset)continue;const e=t.get_value();null!=e.transform&&this.connect(e.transform.change,(()=>this.transformchange.emit())),(0,b.isExpr)(e)&&this.connect(e.expr.change,(()=>this.exprchange.emit()))}}disconnect_signals(){c.Signal.disconnect_receiver(this)}destroy(){this.disconnect_signals(),this.destroyed.emit()}clone(t){const e=(new v.Cloner).clone(this);return null!=t&&e.setv(t),e}_clear_watchers(){this._watchers=new WeakMap}changed_for(t){const e=this._watchers.get(t);return this._watchers.set(t,!1),e??!0}_setv(t,e){const s=e.check_eq,n=new Set,i=this._changing;this._changing=!0;for(const[e,i]of t)!1!==s&&!e.is_unset&&(0,y.is_equal)(e.get_value(),i)||(e.set_value(i),n.add(e));n.size>0&&(this._clear_watchers(),this._pending=!0);for(const t of n)t.change.emit();if(!i){if(!e.no_change)for(;this._pending;)this._pending=!1,this.change.emit();this._pending=!1,this._changing=!1}return n}setv(t,e={}){const s=(0,f.entries)(t);if(0==s.length)return;if(e.silent){this._clear_watchers();for(const[t,e]of s)this.properties[t].set_value(e);return}const n=new Map,i=new Map;for(const[t,e]of s){const s=this.properties[t];n.set(s,e),i.set(s,s.is_unset?void 0:s.get_value())}const r=this._setv(n,e),{document:o}=this;if(null!=o){const t=[];for(const[e,s]of i)r.has(e)&&t.push([e,s,e.get_value()]);for(const[e,s,n]of t)if(e.may_have_refs&&this._needs_invalidate(s,n)){o._invalidate_all_models();break}const s=e.sync??!0;this._push_changes(t,s)}}ref(){return{id:this.id}}*[Symbol.iterator](){yield*(0,f.values)(this.properties)}*syncable_properties(){for(const t of this)t.syncable&&(yield t)}*own_properties(){const t=Object.getPrototypeOf(this),e=Object.getPrototypeOf(t),s=new Set((0,f.keys)(e._props));for(const t of this)s.has(t.attr)||(yield t)}static _value_record_references(t,e,s){if(!(0,p.isObject)(t)||!(0,a.may_have_refs)(t))return;const{recursive:n}=s;if(t instanceof r){if(!e.has(t)&&(e.add(t),n))for(const s of t.syncable_properties())if(!s.is_unset&&s.may_have_refs){const t=s.get_value();r._value_record_references(t,e,{recursive:n})}}else if((0,p.isIterable)(t))for(const s of t)r._value_record_references(s,e,{recursive:n});else if((0,p.isPlainObject)(t))for(const s of(0,f.values)(t))r._value_record_references(s,e,{recursive:n})}static references(t,e){const s=new Set;return r._value_record_references(t,s,e),s}references(){return r.references(this,{recursive:!0})}_doc_attached(){}_doc_detached(){}attach_document(t){if(null!=this.document){if(this.document==t)return;throw new Error(\"models must be owned by only a single document\")}this.document=t,this._doc_attached()}detach_document(){this._doc_detached(),this.document=null}_needs_invalidate(t,e){const s=new Set;r._value_record_references(e,s,{recursive:!1});const n=new Set;r._value_record_references(t,n,{recursive:!1});for(const t of s)if(!n.has(t))return!0;for(const t of n)if(!s.has(t))return!0;return!1}_push_changes(t,e){if(!this.is_syncable)return;const{document:s}=this;if(null==s)return;const n=[];for(const[i,,r]of t)if(i.syncable){const t=new g.ModelChangedEvent(s,this,i.attr,r);t.sync=e,n.push(t)}if(0!=n.length){let t;1==n.length?[t]=n:t=new g.DocumentEventBatch(s,n),s._trigger_on_change(t)}}on_change(t,e){for(const s of(0,p.isArray)(t)?t:[t])this.connect(s.change,e)}stream_to(t,e,s,{sync:n}={}){const i=t.get_value();if((0,S.stream_to_columns)(i,e,s),this._clear_watchers(),t.set_value(i),this.streaming.emit(),null!=this.document){const i=new g.ColumnsStreamedEvent(this.document,this,t.attr,e,s);i.sync=n??!0,this.document._trigger_on_change(i)}}patch_to(t,e,{sync:s}={}){const n=t.get_value(),i=(0,S.patch_to_columns)(n,e);if(this._clear_watchers(),t.set_value(n),this.patching.emit([...i]),null!=this.document){const n=new g.ColumnsPatchedEvent(this.document,this,t.attr,e);n.sync=s??!0,this.document._trigger_on_change(n)}}}s.HasProps=x,(r=x).prototype._props={},r.prototype._mixins=[]},\n function _(n,e,t,s,r){s();const l=n(16),i=n(10);class o{constructor(n,e){this.sender=n,this.name=e}connect(n,e=null){t.receivers_for_sender.has(this.sender)||t.receivers_for_sender.set(this.sender,[]);const s=t.receivers_for_sender.get(this.sender);if(null!=f(s,this,n,e))return!1;const r=e??n;u.has(r)||u.set(r,[]);const l=u.get(r),i={signal:this,slot:n,context:e};return s.push(i),l.push(i),!0}disconnect(n,e=null){const s=t.receivers_for_sender.get(this.sender);if(null==s||0===s.length)return!1;const r=f(s,this,n,e);if(null==r)return!1;const l=e??n,i=u.get(l);return r.signal=null,g(s),g(i),!0}emit(n){const e=t.receivers_for_sender.get(this.sender)??[];for(const{signal:t,slot:s,context:r}of e)t===this&&s.call(r,n,this.sender)}}t.Signal=o,o.__name__=\"Signal\";class c extends o{emit(){super.emit(void 0)}}t.Signal0=c,c.__name__=\"Signal0\",function(n){n.disconnect_between=function(n,e){const s=t.receivers_for_sender.get(n);if(null==s||0===s.length)return;const r=u.get(e);if(null!=r&&0!==r.length){for(const e of r){if(null==e.signal)return;e.signal.sender===n&&(e.signal=null)}g(s),g(r)}},n.disconnect_sender=function(n){const e=t.receivers_for_sender.get(n);if(null!=e&&0!==e.length){for(const n of e){if(null==n.signal)return;const e=n.context??n.slot;n.signal=null,g(u.get(e))}g(e)}},n.disconnect_receiver=function(n,e,s){const r=u.get(n);if(null!=r&&0!==r.length){for(const n of r){if(null==n.signal)return;if(null!=e&&n.slot!=e)continue;const r=n.signal.sender;null!=s&&s.has(r)||(n.signal=null,g(t.receivers_for_sender.get(r)))}g(r)}},n.disconnect_all=function(n){const e=t.receivers_for_sender.get(n);if(null!=e&&0!==e.length){for(const n of e)n.signal=null;g(e)}const s=u.get(n);if(null!=s&&0!==s.length){for(const n of s)n.signal=null;g(s)}}}(o||(t.Signal=o={})),t.Signalable=function(){return class{connect(n,e){return n.connect(e,this)}disconnect(n,e){return n.disconnect(e,this)}}},t.receivers_for_sender=new WeakMap;const u=new WeakMap;function f(n,e,t,s){return(0,i.find)(n,(n=>n.signal===e&&n.slot===t&&n.context===s))}const a=new Set;function g(n){0==a.size&&(async()=>{await(0,l.defer)(),function(){for(const n of a)(0,i.remove_by)(n,(n=>null==n.signal));a.clear()}()})(),a.add(n)}},\n function _(e,n,t,o,r){o();const s=new MessageChannel,i=new Map;s.port1.onmessage=e=>{const n=e.data,t=i.get(n);if(null!=t)try{t()}finally{i.delete(n)}};let a=1;function l(e){return new Promise((n=>setTimeout(n,e)))}t.defer=function(){return new Promise((e=>{const n=a++;i.set(n,e),s.port2.postMessage(n)}))},t.delay=l,t.poll=async function(e,n=50,t=500){for(;!e()&&t>=0;)await l(n),t-=n},t.paint=function(){return new Promise((e=>{requestAnimationFrame((()=>e()))}))},t.idle=function(){return new Promise((e=>{requestIdleCallback((()=>e()))}))}},\n function _(n,s,r,e,t){e();const i=n(8);function f(n){return r.has_refs in n}function c(n){return(0,i.isObject)(n)&&f(n)}r.is_ref=function(n){return(0,i.isPlainObject)(n)&&\"id\"in n&&!(\"type\"in n)},r.has_refs=Symbol(\"has_refs\"),r.is_HasRefs=c,r.may_have_refs=function(n){if(f(n))return n[r.has_refs];const s=n.constructor;return!c(s)||s[r.has_refs]}},\n function _(e,t,r,n,a){n(),r.TextBaselineSpec=r.TextAlignSpec=r.FontStyleSpec=r.FontSizeSpec=r.FontSpec=r.LineDashSpec=r.LineCapSpec=r.LineJoinSpec=r.MarkerSpec=r.ArraySpec=r.NullStringSpec=r.StringSpec=r.AnySpec=r.NDArraySpec=void 0;const s=e(1),i=e(15),l=e(19),_=s.__importStar(e(20)),o=e(24),c=e(10),u=e(13),d=e(11),S=e(22),p=e(27),h=e(8),m=e(28),f=e(29),v=e(30),y=e(38),x=e(12),g=e(32),A=e(39);function w(e){try{return JSON.stringify(e)}catch{return e.toString()}}function z(e){return(0,h.isPlainObject)(e)&&(void 0===e.value?0:1)+(void 0===e.field?0:1)+(void 0===e.expr?0:1)==1}a(\"Uniform\",A.Uniform),a(\"UniformScalar\",A.UniformScalar),a(\"UniformVector\",A.UniformVector),r.isSpec=z;let b=null;r.use_theme=function(e=null){b=e},r.unset=Symbol(\"unset\");class C extends Error{}r.UnsetValueError=C,C.__name__=\"UnsetValueError\";class F{get syncable(){return!this.internal}get is_unset(){return this._value===r.unset}get initialized(){return this._initialized}initialize(e=r.unset){if(this._initialized)throw new Error(\"already initialized\");let t=r.unset;if(e!==r.unset)t=e,this._dirty=!0;else{const e=this._default_override();if(e!==r.unset)t=e;else{let e=!1;if(null!=b){const r=b.get(this.obj,this.attr);void 0!==r&&(t=r,e=!0)}e||(t=this.default_value(this.obj))}}t!==r.unset?(null!=this.kind.coerce&&(t=this.kind.coerce(t)),this._update(t)):this._value=r.unset,this._initialized=!0}get_value(){if(this._value!==r.unset)return this._value;throw new C(`${this.obj}.${this.attr} is unset`)}set_value(e){this._initialized?(this._update(e),this._dirty=!0):this.initialize(e),y.diagnostics.report(this)}_default_override(){return r.unset}get dirty(){return this._dirty}constructor(e,t,n,a,s={}){this._value=r.unset,this._initialized=!1,this._dirty=!1,this.obj=e,this.attr=t,this.kind=n,this.default_value=a,this.change=new i.Signal0(this.obj,\"change\"),this.internal=s.internal??!1,this.readonly=s.readonly??!1,this.convert=s.convert,this.on_update=s.on_update,this.may_have_refs=n.may_have_refs()}_update(e){if(this.validate(e),null!=this.convert){const t=this.convert(e,this.obj);void 0!==t&&(e=t)}this._value=e,this.on_update?.(e,this.obj)}toString(){return`Prop(${this.obj}.${this.attr}, value: ${w(this._value)})`}normalize(e){return e}validate(e){if(!this.valid(e))throw new Error(`${this.obj}.${this.attr} given invalid value: ${w(e)}`)}valid(e){return this.kind.valid(e)}}r.Property=F,F.__name__=\"Property\";class q{constructor(e){this.attr=e}}r.PropertyAlias=q,q.__name__=\"PropertyAlias\",r.Alias=function(e){return new q(e)};class N extends F{}r.PrimitiveProperty=N,N.__name__=\"PrimitiveProperty\";class U extends N{_default_override(){return f.settings.dev?\"Bokeh\":r.unset}}r.Font=U,U.__name__=\"Font\";class $ extends F{constructor(){super(...arguments),this._value=r.unset}get_value(){if(this._value!==r.unset)return this._value;throw new Error(`${this.obj}.${this.attr} is unset`)}_update(e){if(z(e)?this._value=e:this._value={value:e},(0,h.isPlainObject)(this._value)){const{_value:e}=this;this._value[g.serialize]=t=>{const{value:r,field:n,expr:a,transform:s,units:i}=e;return t.encode_struct(void 0!==r?{type:\"value\",value:r,transform:s,units:i}:void 0!==n?{type:\"field\",field:n,transform:s,units:i}:{type:\"expr\",expr:a,transform:s,units:i})}}(0,m.isValue)(this._value)&&this.validate(this._value.value)}materialize(e){return e}scalar(e,t){return new A.UniformScalar(e,t)}uniform(e){const t=this.get_value(),r=e.get_length()??1;if((0,m.isExpr)(t)){const{expr:n,transform:a}=t;let s=n.compute(e);return null!=a&&(s=a.compute(s)),s=this.materialize(s),this.scalar(s,r)}{const{value:e,transform:n}=t;let a=e;return null!=n&&(a=n.compute(a)),a=this.materialize(a),this.scalar(a,r)}}}r.ScalarSpec=$,$.__name__=\"ScalarSpec\";class j extends ${}r.AnyScalar=j,j.__name__=\"AnyScalar\";class D extends ${}r.DictScalar=D,D.__name__=\"DictScalar\";class B extends ${}r.ColorScalar=B,B.__name__=\"ColorScalar\";class E extends ${}r.NumberScalar=E,E.__name__=\"NumberScalar\";class L extends ${}r.StringScalar=L,L.__name__=\"StringScalar\";class P extends ${}r.NullStringScalar=P,P.__name__=\"NullStringScalar\";class T extends ${}r.ArrayScalar=T,T.__name__=\"ArrayScalar\";class V extends ${}r.LineJoinScalar=V,V.__name__=\"LineJoinScalar\";class k extends ${}r.LineCapScalar=k,k.__name__=\"LineCapScalar\";class J extends ${}r.LineDashScalar=J,J.__name__=\"LineDashScalar\";class X extends ${_default_override(){return f.settings.dev?\"Bokeh\":r.unset}}r.FontScalar=X,X.__name__=\"FontScalar\";class Y extends ${}r.FontSizeScalar=Y,Y.__name__=\"FontSizeScalar\";class G extends ${}r.FontStyleScalar=G,G.__name__=\"FontStyleScalar\";class I extends ${}r.TextAlignScalar=I,I.__name__=\"TextAlignScalar\";class O extends ${}r.TextBaselineScalar=O,O.__name__=\"TextBaselineScalar\";class R extends F{constructor(){super(...arguments),this._value=r.unset}get_value(){if(this._value!==r.unset)return this._value;throw new Error(`${this.obj}.${this.attr} is unset`)}_update(e){if(z(e)?this._value=e:this._value={value:e},(0,h.isPlainObject)(this._value)){const{_value:e}=this;this._value[g.serialize]=t=>{const{value:r,field:n,expr:a,transform:s,units:i}=e;return t.encode_struct(void 0!==r?{type:\"value\",value:r,transform:s,units:i}:void 0!==n?{type:\"field\",field:n,transform:s,units:i}:{type:\"expr\",expr:a,transform:s,units:i})}}(0,m.isValue)(this._value)&&this.validate(this._value.value)}materialize(e){return e}v_materialize(e){return e}scalar(e,t){return new A.UniformScalar(e,t)}vector(e){return new A.UniformVector(e)}uniform(e){const t=this.get_value(),r=e.get_length()??1;if((0,m.isField)(t)){const{field:n,transform:a}=t;let s=e.get_column(n);if(null!=s)return null!=a&&(s=a.v_compute(s)),s=this.v_materialize(s),this.vector(s);{const e=`attempted to retrieve property array for nonexistent field '${n}'`;if(f.settings.force_fields)throw new Error(e);return l.logger.warn(e),this.scalar(null,r)}}if((0,m.isExpr)(t)){const{expr:r,transform:n}=t;let a=r.v_compute(e);return null!=n&&(a=n.v_compute(a)),a=this.v_materialize(a),this.vector(a)}if((0,m.isValue)(t)){const{value:e,transform:n}=t;let a=e;return null!=n&&(a=n.compute(a)),a=this.materialize(a),this.scalar(a,r)}(0,x.unreachable)()}array(e){let t;const r=e.get_length()??1,n=this.get_value();if((0,m.isField)(n)){const{field:a}=n,s=e.get_column(a);if(null!=s)t=this.normalize(s);else{const e=`attempted to retrieve property array for nonexistent field '${a}'`;if(f.settings.force_fields)throw new Error(e);l.logger.warn(e);const n=new Float64Array(r);n.fill(NaN),t=n}}else if((0,m.isExpr)(n)){const{expr:r}=n;t=this.normalize(r.v_compute(e))}else{const e=this.normalize([n.value])[0];if((0,h.isNumber)(e)){const n=new Float64Array(r);n.fill(e),t=n}else t=(0,c.repeat)(e,r)}const{transform:a}=n;return null!=a&&(t=a.v_compute(t)),t}}r.VectorSpec=R,R.__name__=\"VectorSpec\";class M extends R{}r.DataSpec=M,M.__name__=\"DataSpec\";class H extends R{constructor(){super(...arguments),this._value=r.unset}_update(e){if(super._update(e),this._value!==r.unset){const{units:e}=this._value;if(null!=e&&!(0,c.includes)(this.valid_units,e))throw new Error(`units must be one of ${this.valid_units.join(\", \")}; got: ${e}`)}}get units(){return this._value!==r.unset?this._value.units??this.default_units:this.default_units}set units(e){if(this._value===r.unset)throw new Error(`${this.obj}.${this.attr} is unset`);e!=this.default_units?this._value.units=e:delete this._value.units}}r.UnitsSpec=H,H.__name__=\"UnitsSpec\";class K extends H{array(e){return new Float64Array(super.array(e))}}r.NumberUnitsSpec=K,K.__name__=\"NumberUnitsSpec\";class Q extends M{}r.BaseCoordinateSpec=Q,Q.__name__=\"BaseCoordinateSpec\";class W extends Q{}r.CoordinateSpec=W,W.__name__=\"CoordinateSpec\";class Z extends Q{}r.CoordinateSeqSpec=Z,Z.__name__=\"CoordinateSeqSpec\";class ee extends Q{}r.CoordinateSeqSeqSeqSpec=ee,ee.__name__=\"CoordinateSeqSeqSeqSpec\";class te extends W{constructor(){super(...arguments),this.dimension=\"x\"}}r.XCoordinateSpec=te,te.__name__=\"XCoordinateSpec\";class re extends W{constructor(){super(...arguments),this.dimension=\"y\"}}r.YCoordinateSpec=re,re.__name__=\"YCoordinateSpec\";class ne extends Z{constructor(){super(...arguments),this.dimension=\"x\"}}r.XCoordinateSeqSpec=ne,ne.__name__=\"XCoordinateSeqSpec\";class ae extends Z{constructor(){super(...arguments),this.dimension=\"y\"}}r.YCoordinateSeqSpec=ae,ae.__name__=\"YCoordinateSeqSpec\";class se extends ee{constructor(){super(...arguments),this.dimension=\"x\"}}r.XCoordinateSeqSeqSeqSpec=se,se.__name__=\"XCoordinateSeqSeqSeqSpec\";class ie extends ee{constructor(){super(...arguments),this.dimension=\"y\"}}r.YCoordinateSeqSeqSeqSpec=ie,ie.__name__=\"YCoordinateSeqSeqSeqSpec\";class le extends K{get default_units(){return\"rad\"}get valid_units(){return[..._.AngleUnits]}materialize(e){return e*-(0,d.to_radians_coeff)(this.units)}v_materialize(e){const t=-(0,d.to_radians_coeff)(this.units),r=new Float32Array(e.length);return(0,u.mul)(e,t,r),r}array(e){throw new Error(\"not supported\")}}r.AngleSpec=le,le.__name__=\"AngleSpec\";class _e extends K{get default_units(){return\"data\"}get valid_units(){return[..._.SpatialUnits]}}r.DistanceSpec=_e,_e.__name__=\"DistanceSpec\";class oe extends _e{materialize(e){return e??NaN}}r.NullDistanceSpec=oe,oe.__name__=\"NullDistanceSpec\";class ce extends M{v_materialize(e){return new Uint8Array(e)}array(e){return new Uint8Array(super.array(e))}}r.BooleanSpec=ce,ce.__name__=\"BooleanSpec\";class ue extends M{v_materialize(e){return(0,h.isTypedArray)(e)?e:new Int32Array(e)}array(e){return new Int32Array(super.array(e))}}r.IntSpec=ue,ue.__name__=\"IntSpec\";class de extends M{v_materialize(e){return(0,h.isTypedArray)(e)?e:new Float64Array(e)}array(e){return new Float64Array(super.array(e))}}r.NumberSpec=de,de.__name__=\"NumberSpec\";class Se extends de{valid(e){return(0,h.isNumber)(e)&&e>=0}}r.ScreenSizeSpec=Se,Se.__name__=\"ScreenSizeSpec\";class pe extends M{materialize(e){return(0,S.encode_rgba)((0,S.color2rgba)(e))}v_materialize(e){if(!(0,v.is_NDArray)(e))return this._from_css_array(e);if(\"uint32\"==e.dtype&&1==e.dimension)return(0,p.to_big_endian)(e);if(\"uint8\"==e.dtype&&1==e.dimension){const[t]=e.shape,r=new o.RGBAArray(4*t);let n=0;for(const t of e)r[n++]=t,r[n++]=t,r[n++]=t,r[n++]=255;return new o.ColorArray(r.buffer)}if(\"uint8\"==e.dtype&&2==e.dimension){const[t,r]=e.shape;if(4==r)return new o.ColorArray(e.buffer);if(3==r){const n=new o.RGBAArray(4*t);for(let a=0,s=0;a0)return e in i?i[e]:i[e]=new _(e,l);throw new TypeError(\"Logger.get() expects a non-empty string name and an optional log-level\")}constructor(e,l=_.INFO){this._name=e,this.set_level(l)}get level(){return this.get_level()}get_level(){return this._log_level}set_level(e){if(e instanceof v)this._log_level=e;else{if(!_.log_levels.hasOwnProperty(e))throw new Error(\"Logger.set_level() expects a log-level object or a string name of a log-level\");this._log_level=_.log_levels[e]}const l=`[${this._name}]`;for(const{level:e,method:o}of(0,r.values)(_.log_levels))e\",\"*\"),t.BuiltinFormatter=(0,a.Enum)(\"raw\",\"basic\",\"numeral\",\"printf\",\"datetime\"),t.HTTPMethod=(0,a.Enum)(\"POST\",\"GET\"),t.HexTileOrientation=(0,a.Enum)(\"pointytop\",\"flattop\"),t.HoverMode=(0,a.Enum)(\"mouse\",\"hline\",\"vline\"),t.ImageOrigin=(0,a.Enum)(\"bottom_left\",\"top_left\",\"bottom_right\",\"top_right\"),t.LatLon=(0,a.Enum)(\"lat\",\"lon\"),t.LegendClickPolicy=(0,a.Enum)(\"none\",\"hide\",\"mute\"),t.LegendLocation=t.Anchor,t.LineCap=(0,a.Enum)(\"butt\",\"round\",\"square\"),t.LineDash=(0,a.Enum)(\"solid\",\"dashed\",\"dotted\",\"dotdash\",\"dashdot\"),t.LineJoin=(0,a.Enum)(\"miter\",\"round\",\"bevel\"),t.LinePolicy=(0,a.Enum)(\"prev\",\"next\",\"nearest\",\"interp\",\"none\"),t.Location=(0,a.Enum)(\"above\",\"below\",\"left\",\"right\"),t.Logo=(0,a.Enum)(\"normal\",\"grey\"),t.MapType=(0,a.Enum)(\"satellite\",\"roadmap\",\"terrain\",\"hybrid\"),t.MarkerType=(0,a.Enum)(\"asterisk\",\"circle\",\"circle_cross\",\"circle_dot\",\"circle_x\",\"circle_y\",\"cross\",\"dash\",\"diamond\",\"diamond_cross\",\"diamond_dot\",\"dot\",\"hex\",\"hex_dot\",\"inverted_triangle\",\"plus\",\"square\",\"square_cross\",\"square_dot\",\"square_pin\",\"square_x\",\"star\",\"star_dot\",\"triangle\",\"triangle_dot\",\"triangle_pin\",\"x\",\"y\"),t.MutedPolicy=(0,a.Enum)(\"show\",\"ignore\"),t.Orientation=(0,a.Enum)(\"vertical\",\"horizontal\"),t.OutlineShapeName=(0,a.Enum)(\"none\",\"box\",\"rectangle\",\"square\",\"circle\",\"ellipse\",\"trapezoid\",\"parallelogram\",\"diamond\",\"triangle\"),t.OutputBackend=(0,a.Enum)(\"canvas\",\"svg\",\"webgl\"),t.PaddingUnits=(0,a.Enum)(\"percent\",\"absolute\"),t.Place=(0,a.Enum)(\"above\",\"below\",\"left\",\"right\",\"center\"),t.PointPolicy=(0,a.Enum)(\"snap_to_data\",\"follow_mouse\",\"none\"),t.RadiusDimension=(0,a.Enum)(\"x\",\"y\",\"max\",\"min\"),t.RenderLevel=(0,a.Enum)(\"image\",\"underlay\",\"glyph\",\"guide\",\"annotation\",\"overlay\"),t.ResetPolicy=(0,a.Enum)(\"standard\",\"event_only\"),t.ResolutionType=(0,a.Enum)(\"microseconds\",\"milliseconds\",\"seconds\",\"minsec\",\"minutes\",\"hourmin\",\"hours\",\"days\",\"months\",\"years\"),t.RoundingFunction=(0,a.Enum)(\"round\",\"nearest\",\"floor\",\"rounddown\",\"ceil\",\"roundup\"),t.ScrollbarPolicy=(0,a.Enum)(\"auto\",\"visible\",\"hidden\"),t.RegionSelectionMode=(0,a.Enum)(\"replace\",\"append\",\"intersect\",\"subtract\",\"xor\"),t.SelectionMode=(0,a.Enum)(...t.RegionSelectionMode,\"toggle\"),t.Side=(0,a.Enum)(\"above\",\"below\",\"left\",\"right\"),t.SizingMode=(0,a.Enum)(\"stretch_width\",\"stretch_height\",\"stretch_both\",\"scale_width\",\"scale_height\",\"scale_both\",\"fixed\",\"inherit\"),t.Sort=(0,a.Enum)(\"ascending\",\"descending\"),t.SpatialUnits=(0,a.Enum)(\"screen\",\"data\"),t.StartEnd=(0,a.Enum)(\"start\",\"end\"),t.StepMode=(0,a.Enum)(\"after\",\"before\",\"center\"),t.TapBehavior=(0,a.Enum)(\"select\",\"inspect\"),t.TapGesture=(0,a.Enum)(\"tap\",\"doubletap\"),t.TextAlign=(0,a.Enum)(\"left\",\"right\",\"center\"),t.TextBaseline=(0,a.Enum)(\"top\",\"middle\",\"bottom\",\"alphabetic\",\"hanging\",\"ideographic\"),t.TextureRepetition=(0,a.Enum)(\"repeat\",\"repeat_x\",\"repeat_y\",\"no_repeat\"),t.LabelOrientation=(0,a.Enum)(\"vertical\",\"horizontal\",\"parallel\",\"normal\"),t.TooltipAttachment=(0,a.Enum)(\"horizontal\",\"vertical\",\"left\",\"right\",\"above\",\"below\"),t.UpdateMode=(0,a.Enum)(\"replace\",\"append\"),t.VerticalAlign=(0,a.Enum)(\"top\",\"middle\",\"bottom\"),t.ToolIcon=(0,a.Enum)(\"append_mode\",\"arrow_down_to_bar\",\"arrow_up_from_bar\",\"auto_box_zoom\",\"bold\",\"box_edit\",\"box_select\",\"box_zoom\",\"caret_down\",\"caret_left\",\"caret_right\",\"caret_up\",\"check\",\"chevron_down\",\"chevron_left\",\"chevron_right\",\"chevron_up\",\"clear_selection\",\"copy\",\"crosshair\",\"delete\",\"freehand_draw\",\"fullscreen\",\"help\",\"hover\",\"intersect_mode\",\"invert_selection\",\"italic\",\"lasso_select\",\"line_edit\",\"maximize\",\"minimize\",\"pan\",\"pin\",\"point_draw\",\"pointer\",\"poly_draw\",\"poly_edit\",\"polygon_select\",\"range\",\"redo\",\"replace_mode\",\"reset\",\"save\",\"see_off\",\"see_on\",\"settings\",\"square\",\"square_check\",\"subtract_mode\",\"tap_select\",\"text_align_center\",\"text_align_left\",\"text_align_right\",\"undo\",\"unknown\",\"unpin\",\"wheel_pan\",\"wheel_zoom\",\"x_box_select\",\"x_box_zoom\",\"x_grip\",\"x_pan\",\"xor_mode\",\"y_box_select\",\"y_box_zoom\",\"y_grip\",\"y_pan\",\"zoom_in\",\"zoom_out\")},\n function _(t,e,r,n,s){n();const i=t(1).__importStar(t(8)),a=t(22),_=t(9),o=t(17),u=globalThis.Map,l=globalThis.Set,y=globalThis.Node;class d{}r.Kind=d,d.__name__=\"Kind\",function(t){class e extends d{may_have_refs(){return!1}}e.__name__=\"Primitive\",t.Primitive=e;class r extends e{valid(t){return void 0!==t}toString(){return\"Any\"}may_have_refs(){return!0}}r.__name__=\"Any\",t.Any=r;class n extends e{valid(t){return void 0!==t}toString(){return\"Unknown\"}may_have_refs(){return!0}}n.__name__=\"Unknown\",t.Unknown=n;class s extends e{valid(t){return i.isBoolean(t)}toString(){return\"Bool\"}}s.__name__=\"Bool\",t.Bool=s;class c extends d{constructor(t){super(),this.obj_type=t}valid(t){return t instanceof this.obj_type}toString(){const t=this.obj_type;return`Ref(${t.__name__??t.toString()})`}may_have_refs(){const{obj_type:t}=this;return!(o.has_refs in t)||t[o.has_refs]}}c.__name__=\"Ref\",t.Ref=c;class p extends d{valid(t){return i.isObject(t)}toString(){return\"AnyRef\"}may_have_refs(){return!0}}p.__name__=\"AnyRef\",t.AnyRef=p;class h extends e{valid(t){return i.isNumber(t)}toString(){return\"Float\"}}h.__name__=\"Float\",t.Float=h;class m extends h{valid(t){return super.valid(t)&&i.isInteger(t)}toString(){return\"Int\"}}m.__name__=\"Int\",t.Int=m;class v extends h{valid(t){return super.valid(t)&&0<=t&&t<=1}toString(){return\"Percent\"}}v.__name__=\"Percent\",t.Percent=v;class S extends d{constructor(t){super(),this.types=t,this.types=t}valid(t){return this.types.some((e=>e.valid(t)))}toString(){return`Or(${this.types.map((t=>t.toString())).join(\", \")})`}may_have_refs(){return this.types.some((t=>t.may_have_refs()))}}S.__name__=\"Or\",t.Or=S;class f extends d{constructor(t,e){super(),this.types=[t,e]}valid(t){return this.types.some((e=>e.valid(t)))}toString(){return`And(${this.types.map((t=>t.toString())).join(\", \")})`}may_have_refs(){return this.types.some((t=>t.may_have_refs()))}}f.__name__=\"And\",t.And=f;class g extends d{constructor(t){super(),this.types=t,this.types=t}valid(t){if(!i.isArray(t))return!1;for(let e=0;et.toString())).join(\", \")})`}may_have_refs(){return this.types.some((t=>t.may_have_refs()))}}g.__name__=\"Tuple\",t.Tuple=g;class b extends d{constructor(t){super(),this.struct_type=t}valid(t){if(!i.isPlainObject(t))return!1;const e=new _.PlainObjectProxy(this.struct_type);for(const r of(0,_.keys)(t))if(!e.has(r))return!1;for(const[r,n]of e){const e=t[r];if(!n.valid(e))return!1}return!0}toString(){return`Struct({${(0,_.typed_entries)(this.struct_type).map((([t,e])=>`${t.toString()}: ${e}`)).join(\", \")}})`}may_have_refs(){return(0,_.typed_values)(this.struct_type).some((t=>t.may_have_refs()))}}b.__name__=\"Struct\",t.Struct=b;class x extends d{constructor(t){super(),this.struct_type=t}valid(t){if(!i.isPlainObject(t))return!1;const e=new _.PlainObjectProxy(t),r=new _.PlainObjectProxy(this.struct_type);for(const t of e.keys())if(!r.has(t))return!1;for(const[t,n]of r){const r=e.get(t);if(void 0!==r&&!n.valid(r))return!1}return!0}toString(){return`Struct({${(0,_.typed_entries)(this.struct_type).map((([t,e])=>`${t.toString()}?: ${e}`)).join(\", \")}})`}may_have_refs(){return(0,_.typed_values)(this.struct_type).some((t=>t.may_have_refs()))}}x.__name__=\"PartialStruct\",t.PartialStruct=x;class w extends d{constructor(t){super(),this.item_type=t}valid(t){return i.isIterable(t)}toString(){return`Iterable(${this.item_type.toString()})`}may_have_refs(){return this.item_type.may_have_refs()}}w.__name__=\"Iterable\",t.Iterable=w;class K extends d{constructor(t){super(),this.item_type=t}valid(t){return i.isArray(t)||i.isTypedArray(t)}toString(){return`Arrayable(${this.item_type.toString()})`}may_have_refs(){return this.item_type.may_have_refs()}}K.__name__=\"Arrayable\",t.Arrayable=K;class N extends d{constructor(t){super(),this.item_type=t}valid(t){return i.isArray(t)&&t.every((t=>this.item_type.valid(t)))}toString(){return`List(${this.item_type.toString()})`}may_have_refs(){return this.item_type.may_have_refs()}}N.__name__=\"List\",t.List=N;class A extends N{valid(t){return super.valid(t)&&0!=t.length}toString(){return`NonEmptyList(${this.item_type.toString()})`}}A.__name__=\"NonEmptyList\",t.NonEmptyList=A;class P extends e{valid(t){return null===t}toString(){return\"Null\"}}P.__name__=\"Null\",t.Null=P;class $ extends d{constructor(t){super(),this.base_type=t}valid(t){return null===t||this.base_type.valid(t)}toString(){return`Nullable(${this.base_type.toString()})`}may_have_refs(){return this.base_type.may_have_refs()}}$.__name__=\"Nullable\",t.Nullable=$;class j extends d{constructor(t){super(),this.base_type=t}valid(t){return void 0===t||this.base_type.valid(t)}toString(){return`Opt(${this.base_type.toString()})`}may_have_refs(){return this.base_type.may_have_refs()}}j.__name__=\"Opt\",t.Opt=j;class O extends d{valid(t){return t instanceof ArrayBuffer}toString(){return\"Bytes\"}may_have_refs(){return!1}}O.__name__=\"Bytes\",t.Bytes=O;class F extends e{valid(t){return i.isString(t)}toString(){return\"Str\"}}F.__name__=\"Str\",t.Str=F;class L extends F{constructor(t){super(),this.regex=t}valid(t){return super.valid(t)&&this.regex.test(t)}toString(){return`Regex(${this.regex.toString()})`}}L.__name__=\"Regex\",t.Regex=L;class R extends e{constructor(t){super(),this.values=new l(t)}valid(t){return this.values.has(t)}*[Symbol.iterator](){yield*this.values}toString(){return`Enum(${[...this.values].map((t=>t.toString())).join(\", \")})`}}R.__name__=\"Enum\",t.Enum=R;class B extends d{constructor(t){super(),this.item_type=t}valid(t){if(!(t instanceof u||i.isPlainObject(t)))return!1;for(const e of(0,_.values)(t))if(!this.item_type.valid(e))return!1;return!0}toString(){return`Dict(${this.item_type.toString()})`}may_have_refs(){return this.item_type.may_have_refs()}}B.__name__=\"Dict\",t.Dict=B;class I extends d{constructor(t,e){super(),this.key_type=t,this.item_type=e}coerce(t){return i.isPlainObject(t)&&(0,_.is_empty)(t)?new u:t}valid(t){if(!(t instanceof u))return!1;for(const[e,r]of t.entries())if(!this.key_type.valid(e)||!this.item_type.valid(r))return!1;return!0}toString(){return`Mapping(${this.key_type.toString()}, ${this.item_type.toString()})`}may_have_refs(){return this.key_type.may_have_refs()||this.item_type.may_have_refs()}}I.__name__=\"Mapping\",t.Mapping=I;class k extends d{constructor(t){super(),this.item_type=t}valid(t){if(!(t instanceof l))return!1;for(const e of t)if(!this.item_type.valid(e))return!1;return!0}toString(){return`Set(${this.item_type.toString()})`}may_have_refs(){return this.item_type.may_have_refs()}}k.__name__=\"Set\",t.Set=k;class C extends d{valid(t){return(0,a.is_Color)(t)}toString(){return\"Color\"}may_have_refs(){return!1}}C.__name__=\"Color\",t.Color=C;class E extends F{toString(){return\"CSSLength\"}}E.__name__=\"CSSLength\",t.CSSLength=E;class T extends d{valid(t){return i.isFunction(t)}toString(){return\"Func(...)\"}may_have_refs(){return!1}}T.__name__=\"Func\",t.Func=T;class M extends d{constructor(t){super(),this.base_type=t}valid(t){return this.base_type.valid(t)&&t>=0}toString(){return`NonNegative(${this.base_type.toString()})`}may_have_refs(){return this.base_type.may_have_refs()}}M.__name__=\"NonNegative\",t.NonNegative=M;class D extends d{constructor(t){super(),this.base_type=t}valid(t){return this.base_type.valid(t)&&t>0}toString(){return`Positive(${this.base_type.toString()})`}may_have_refs(){return this.base_type.may_have_refs()}}D.__name__=\"Positive\",t.Positive=D;class U extends d{valid(t){return t instanceof y}toString(){return\"Node\"}may_have_refs(){return!1}}U.__name__=\"Node\",t.Node=U}(r.Kinds||(r.Kinds={})),r.Any=new r.Kinds.Any,r.Unknown=new r.Kinds.Unknown,r.Bool=new r.Kinds.Bool,r.Float=new r.Kinds.Float,r.Int=new r.Kinds.Int,r.Bytes=new r.Kinds.Bytes,r.Str=new r.Kinds.Str;r.Regex=t=>new r.Kinds.Regex(t),r.Null=new r.Kinds.Null;r.Nullable=t=>new r.Kinds.Nullable(t);r.Opt=t=>new r.Kinds.Opt(t);r.Or=(...t)=>new r.Kinds.Or(t);r.And=(t,e)=>new r.Kinds.And(t,e);r.Tuple=(...t)=>new r.Kinds.Tuple(t);r.Struct=t=>new r.Kinds.Struct(t);r.PartialStruct=t=>new r.Kinds.PartialStruct(t);r.Iterable=t=>new r.Kinds.Iterable(t);r.Arrayable=t=>new r.Kinds.Arrayable(t);r.List=t=>new r.Kinds.List(t);r.NonEmptyList=t=>new r.Kinds.NonEmptyList(t);r.Dict=t=>new r.Kinds.Dict(t);r.Mapping=(t,e)=>new r.Kinds.Mapping(t,e);r.Set=t=>new r.Kinds.Set(t);r.Enum=(...t)=>new r.Kinds.Enum(t);r.Ref=t=>new r.Kinds.Ref(t);r.AnyRef=()=>new r.Kinds.AnyRef;r.Func=()=>new r.Kinds.Func,r.Node=new r.Kinds.Node;r.NonNegative=t=>new r.Kinds.NonNegative(t);r.Positive=t=>new r.Kinds.Positive(t),r.Percent=new r.Kinds.Percent,r.Alpha=r.Percent,r.Color=new r.Kinds.Color,r.Auto=(0,r.Enum)(\"auto\"),r.CSSLength=new r.Kinds.CSSLength,r.FontSize=r.Str,r.Font=r.Str,r.Angle=r.Float,r.Boolean=r.Bool,r.String=r.Str,r.Number=r.Float,r.Array=r.List,r.Map=r.Mapping,r.Function=r.Func},\n function _(n,r,t,e,s){e();const u=n(23),c=n(11),i=n(8),{round:o,sqrt:l}=Math;function a(n){return(0,c.clamp)(o(n),0,255)}function f(){return[0,0,0,0]}function g(n){return[n>>24&255,n>>16&255,n>>8&255,255&n]}function b(n,r=1){const[t,e,s,u]=(()=>{if(null==n)return[0,0,0,0];if((0,i.isInteger)(n))return g(n);if((0,i.isString)(n))return p(n)??[0,0,0,0];if(2==n.length){const[r,t]=n;return b(r,t)}{const[r,t,e,s=1]=n;return[r,t,e,a(255*s)]}})();return[t,e,s,a(r*u)]}t.byte=a,t.transparent=f,t.encode_rgba=function([n,r,t,e]){return n<<24|r<<16|t<<8|e},t.decode_rgba=g,t.color2rgba=b;const h={0:\"0\",1:\"1\",2:\"2\",3:\"3\",4:\"4\",5:\"5\",6:\"6\",7:\"7\",8:\"8\",9:\"9\",10:\"a\",11:\"b\",12:\"c\",13:\"d\",14:\"e\",15:\"f\"};function d(n){return h[n>>4]+h[15&n]}function $([n,r,t,e]){return`rgba(${n}, ${r}, ${t}, ${e/255})`}t.rgba2css=$,t.color2css=function(n,r){const[t,e,s,u]=b(n,r);return $([t,e,s,u])},t.color2hex=function(n,r){const[t,e,s,u]=b(n,r),c=`#${d(t)}${d(e)}${d(s)}`;return 255==u?c:`${c}${d(u)}`},t.color2hexrgb=function(n){const[r,t,e]=b(n);return`#${d(r)}${d(t)}${d(e)}`};const m=/^rgba?\\(\\s*(?[^\\s,]+?)\\s+(?[^\\s,]+?)\\s+(?[^\\s,]+?)(?:\\s*\\/\\s*(?[^\\s,]+?))?\\s*\\)$/,N=/^rgba?\\(\\s*(?[^\\s,]+?)\\s*,\\s*(?[^\\s,]+?)\\s*,\\s*(?[^\\s,]+?)(?:\\s*,\\s*(?[^\\s,]+?))?\\s*\\)$/,_=(()=>{const n=document.createElement(\"canvas\");n.width=1,n.height=1;const r=n.getContext(\"2d\"),t=r.createLinearGradient(0,0,1,1);return n=>{r.fillStyle=t,r.fillStyle=n;const e=r.fillStyle;return e!=t?e:null}})();function p(n){if(\"\"==(n=n.trim().toLowerCase()))return null;if(\"transparent\"==n)return[0,0,0,0];if((0,u.is_named_color)(n))return g(u.named_colors[n]);if(\"#\"==n[0]){const r=Number(`0x${n.substring(1)}`);if(isNaN(r))return null;switch(n.length-1){case 3:{const n=r>>8&15,t=r>>4&15,e=15&r;return[n<<4|n,t<<4|t,e<<4|e,255]}case 4:{const n=r>>12&15,t=r>>8&15,e=r>>4&15,s=15&r;return[n<<4|n,t<<4|t,e<<4|e,s<<4|s]}case 6:return[r>>16&255,r>>8&255,255&r,255];case 8:return[r>>24&255,r>>16&255,r>>8&255,255&r]}}else if(n.startsWith(\"rgb\")){const r=n.match(m)??n.match(N);if(null!=r?.groups){let{r:n,g:t,b:e,a:s=\"1\"}=r.groups;const u=n.endsWith(\"%\"),c=t.endsWith(\"%\"),i=e.endsWith(\"%\"),o=s.endsWith(\"%\");if(!(u&&c&&i)&&(u||c||i))return null;u&&(n=n.slice(0,-1)),c&&(t=t.slice(0,-1)),i&&(e=e.slice(0,-1)),o&&(s=s.slice(0,-1));let l=Number(n),f=Number(t),g=Number(e),b=Number(s);return isNaN(l+f+g+b)?null:(u&&(l=l/100*255),c&&(f=f/100*255),i&&(g=g/100*255),b=255*(o?b/100:b),l=a(l),f=a(f),g=a(g),b=a(b),[l,f,g,b])}}else{const r=_(n);if(null!=r)return p(r)}return null}t.css4_parse=p,t.is_Color=function(n){return!!(0,i.isInteger)(n)||(!(!(0,i.isString)(n)||null==p(n))||!(!(0,i.isArray)(n)||3!=n.length&&4!=n.length))},t.is_dark=function([n,r,t]){return 1-(.299*n+.587*r+.114*t)/255>=.6},t.brightness=function(n){const[r,t,e]=b(n);return l(.299*r**2+.587*t**2+.114*e**2)/255},t.luminance=function(n){const[r,t,e]=b(n);return(.2126*r**2.2+.7152*t**2.2+.0722*e**2.2)/255**2.2}},\n function _(e,r,l,a,i){a();l.named_colors={aliceblue:4042850303,antiquewhite:4209760255,aqua:16777215,aquamarine:2147472639,azure:4043309055,beige:4126530815,bisque:4293182719,black:255,blanchedalmond:4293643775,blue:65535,blueviolet:2318131967,brown:2771004159,burlywood:3736635391,cadetblue:1604231423,chartreuse:2147418367,chocolate:3530104575,coral:4286533887,cornflowerblue:1687547391,cornsilk:4294499583,crimson:3692313855,cyan:16777215,darkblue:35839,darkcyan:9145343,darkgoldenrod:3095792639,darkgray:2846468607,darkgreen:6553855,darkgrey:2846468607,darkkhaki:3182914559,darkmagenta:2332068863,darkolivegreen:1433087999,darkorange:4287365375,darkorchid:2570243327,darkred:2332033279,darksalmon:3918953215,darkseagreen:2411499519,darkslateblue:1211993087,darkslategray:793726975,darkslategrey:793726975,darkturquoise:13554175,darkviolet:2483082239,deeppink:4279538687,deepskyblue:12582911,dimgray:1768516095,dimgrey:1768516095,dodgerblue:512819199,firebrick:2988581631,floralwhite:4294635775,forestgreen:579543807,fuchsia:4278255615,gainsboro:3705462015,ghostwhite:4177068031,gold:4292280575,goldenrod:3668254975,gray:2155905279,green:8388863,greenyellow:2919182335,grey:2155905279,honeydew:4043305215,hotpink:4285117695,indianred:3445382399,indigo:1258324735,ivory:4294963455,khaki:4041641215,lavender:3873897215,lavenderblush:4293981695,lawngreen:2096890111,lemonchiffon:4294626815,lightblue:2916673279,lightcoral:4034953471,lightcyan:3774873599,lightgoldenrodyellow:4210742015,lightgray:3553874943,lightgreen:2431553791,lightgrey:3553874943,lightpink:4290167295,lightsalmon:4288707327,lightseagreen:548580095,lightskyblue:2278488831,lightslategray:2005441023,lightslategrey:2005441023,lightsteelblue:2965692159,lightyellow:4294959359,lime:16711935,limegreen:852308735,linen:4210091775,magenta:4278255615,maroon:2147483903,mediumaquamarine:1724754687,mediumblue:52735,mediumorchid:3126187007,mediumpurple:2473647103,mediumseagreen:1018393087,mediumslateblue:2070474495,mediumspringgreen:16423679,mediumturquoise:1221709055,mediumvioletred:3340076543,midnightblue:421097727,mintcream:4127193855,mistyrose:4293190143,moccasin:4293178879,navajowhite:4292783615,navy:33023,oldlace:4260751103,olive:2155872511,olivedrab:1804477439,orange:4289003775,orangered:4282712319,orchid:3664828159,palegoldenrod:4008225535,palegreen:2566625535,paleturquoise:2951671551,palevioletred:3681588223,papayawhip:4293907967,peachpuff:4292524543,peru:3448061951,pink:4290825215,plum:3718307327,powderblue:2967529215,purple:2147516671,rebeccapurple:1714657791,red:4278190335,rosybrown:3163525119,royalblue:1097458175,saddlebrown:2336560127,salmon:4202722047,sandybrown:4104413439,seagreen:780883967,seashell:4294307583,sienna:2689740287,silver:3233857791,skyblue:2278484991,slateblue:1784335871,slategray:1887473919,slategrey:1887473919,snow:4294638335,springgreen:16744447,steelblue:1182971135,tan:3535047935,teal:8421631,thistle:3636451583,tomato:4284696575,turquoise:1088475391,violet:4001558271,wheat:4125012991,white:4294967295,whitesmoke:4126537215,yellow:4294902015,yellowgreen:2597139199},l.is_named_color=function(e){return e in l.named_colors}},\n function _(r,t,n,o,a){o(),n.GeneratorFunction=Object.getPrototypeOf((function*(){})).constructor,n.AsyncGeneratorFunction=Object.getPrototypeOf((async function*(){})).constructor,n.ColorArray=Uint32Array,n.RGBAArray=Uint8ClampedArray,n.infer_type=function(r,t){return r instanceof Float64Array||r instanceof Array||t instanceof Float64Array||t instanceof Array?Float64Array:Float32Array},n.ScreenArray=Float32Array,n.to_screen=function(r){return r instanceof Float32Array?r:Float32Array.from(r)},a(\"Indices\",r(25).BitSet)},\n function _(t,s,r,e,i){var n,o;e();const _=t(26),a=t(12),h=t(17);class c{constructor(t,s=0){this[n]=\"BitSet\",this._count=null,this.size=t,this._nwords=Math.ceil(t/c._word_length),0==s||1==s?(this._array=new Uint32Array(this._nwords),1==s&&this._array.fill(4294967295)):((0,a.assert)(s.length==this._nwords,\"Initializer size mismatch\"),this._array=s)}clone(){return new c(this.size,new Uint32Array(this._array))}[(n=Symbol.toStringTag,o=h.has_refs,_.equals)](t,s){if(!s.eq(this.size,t.size))return!1;const{_nwords:r}=this,e=this.size%c._word_length,i=0==e?r:r-1;for(let s=0;s>>5,r=31&t;return 1==(this._array[s]>>r&1)}set(t,s=!0){this._check_bounds(t),this._count=null;const r=t>>>5,e=31&t;s?this._array[r]|=1<>>t&1)&&(e+=1)}return e}*ones(){const{_array:t,_nwords:s,size:r}=this;for(let e=0,i=0;i>>t&1)&&(yield e);else e+=c._word_length}}*zeros(){const{_array:t,_nwords:s,size:r}=this;for(let e=0,i=0;i>>t&1||(yield e);else e+=c._word_length}}_check_size(t){(0,a.assert)(this.size==t.size,`Size mismatch (${this.size} != ${t.size})`)}invert(){for(let t=0;t>>0}add(t){this._check_size(t);for(let s=0;s0;)if(n[c]===t)return o[c]===e;n.push(t),o.push(e);const l=(()=>{if(a(t)&&a(e))return t[r.equals](e,this);switch(s){case\"[object Array]\":case\"[object Uint8Array]\":case\"[object Int8Array]\":case\"[object Uint16Array]\":case\"[object Int16Array]\":case\"[object Uint32Array]\":case\"[object Int32Array]\":case\"[object Float32Array]\":case\"[object Float64Array]\":return this.arrays(t,e);case\"[object Map]\":return this.maps(t,e);case\"[object Set]\":return this.sets(t,e);case\"[object Object]\":if(t.constructor==e.constructor&&(null==t.constructor||t.constructor===Object))return this.objects(t,e);case\"[object Function]\":if(t.constructor==e.constructor&&t.constructor===Function)return this.eq(`${t}`,`${e}`)}if(\"undefined\"!=typeof Node&&t instanceof Node)return this.nodes(t,e);throw new u(`can't compare objects of type ${s}`)})();return n.pop(),o.pop(),l}numbers(t,e){return Object.is(t,e)}arrays(t,e){const{length:r}=t;if(r!=e.length)return!1;for(let s=0;s0,i.is_little_endian=(()=>{const n=new ArrayBuffer(4),t=new Uint8Array(n);new Uint32Array(n)[1]=168496141;let i=!0;return 10==t[4]&&11==t[5]&&12==t[6]&&13==t[7]&&(i=!1),i})(),i.BYTE_ORDER=i.is_little_endian?\"little\":\"big\",i.to_big_endian=function(n){if(i.is_little_endian){const t=new Uint32Array(n.length),i=new DataView(t.buffer);let e=0;for(const t of n)i.setUint32(e,t),e+=4;return t}return n}},\n function _(n,i,r,t,e){t();const u=n(8),f=n(9);function c(n,i){if(!(0,u.isPlainObject)(n))return!1;if(!(i in n))return!1;let r=(0,f.size)(n)-1;return\"transform\"in n&&(r-=1),\"units\"in n&&(r-=1),0==r}function o(n){return c(n,\"value\")}function s(n){return c(n,\"field\")}function l(n){return c(n,\"expr\")}r.isValue=o,r.isField=s,r.isExpr=l,r.isVectorized=function(n){return o(n)||s(n)||l(n)}},\n function _(e,t,r,s,_){s();class i{constructor(){this._dev=!1,this._wireframe=!1,this._force_webgl=!1,this._force_fields=!1}set dev(e){this._dev=e}get dev(){return this._dev}set wireframe(e){this._wireframe=e}get wireframe(){return this._wireframe}set force_webgl(e){this._force_webgl=e}get force_webgl(){return this._force_webgl}set force_fields(e){this._force_fields=e}get force_fields(){return this._force_fields}}r.Settings=i,i.__name__=\"Settings\",r.settings=new i},\n function _(e,t,s,r,n){var a,i,h,u,o,l,c,p,y,_;r();const A=e(8),d=e(27),g=e(26),f=e(31),m=e(32),w=Symbol(\"__ndarray__\");function N(e,t){return{type:\"ndarray\",array:t.encode(\"object\"==e.dtype?Array.from(e):e.buffer),order:d.BYTE_ORDER,dtype:e.dtype,shape:e.shape}}class D extends Uint8Array{constructor(e,t){super(e),this[a]=!0,this.dtype=\"bool\",this.shape=t??(O(e)?e.shape:[this.length]),this.dimension=this.shape.length}[(a=w,g.equals)](e,t){return t.eq(this.shape,e.shape)&&t.arrays(this,e)}[f.clone](e){return new D(this,e.clone(this.shape))}[m.serialize](e){return N(this,e)}get(e){return 1==this[e]}}s.BoolNDArray=D,D.__name__=\"BoolNDArray\";class q extends Uint8Array{constructor(e,t){super(e),this[i]=!0,this.dtype=\"uint8\",this.shape=t??(O(e)?e.shape:[this.length]),this.dimension=this.shape.length}[(i=w,g.equals)](e,t){return t.eq(this.shape,e.shape)&&t.arrays(this,e)}[f.clone](e){return new q(this,e.clone(this.shape))}[m.serialize](e){return N(this,e)}get(e){return this[e]}}s.Uint8NDArray=q,q.__name__=\"Uint8NDArray\";class b extends Int8Array{constructor(e,t){super(e),this[h]=!0,this.dtype=\"int8\",this.shape=t??(O(e)?e.shape:[this.length]),this.dimension=this.shape.length}[(h=w,g.equals)](e,t){return t.eq(this.shape,e.shape)&&t.arrays(this,e)}[f.clone](e){return new b(this,e.clone(this.shape))}[m.serialize](e){return N(this,e)}get(e){return this[e]}}s.Int8NDArray=b,b.__name__=\"Int8NDArray\";class U extends Uint16Array{constructor(e,t){super(e),this[u]=!0,this.dtype=\"uint16\",this.shape=t??(O(e)?e.shape:[this.length]),this.dimension=this.shape.length}[(u=w,g.equals)](e,t){return t.eq(this.shape,e.shape)&&t.arrays(this,e)}[f.clone](e){return new U(this,e.clone(this.shape))}[m.serialize](e){return N(this,e)}get(e){return this[e]}}s.Uint16NDArray=U,U.__name__=\"Uint16NDArray\";class I extends Int16Array{constructor(e,t){super(e),this[o]=!0,this.dtype=\"int16\",this.shape=t??(O(e)?e.shape:[this.length]),this.dimension=this.shape.length}[(o=w,g.equals)](e,t){return t.eq(this.shape,e.shape)&&t.arrays(this,e)}[f.clone](e){return new I(this,e.clone(this.shape))}[m.serialize](e){return N(this,e)}get(e){return this[e]}}s.Int16NDArray=I,I.__name__=\"Int16NDArray\";class x extends Uint32Array{constructor(e,t){super(e),this[l]=!0,this.dtype=\"uint32\",this.shape=t??(O(e)?e.shape:[this.length]),this.dimension=this.shape.length}[(l=w,g.equals)](e,t){return t.eq(this.shape,e.shape)&&t.arrays(this,e)}[f.clone](e){return new x(this,e.clone(this.shape))}[m.serialize](e){return N(this,e)}get(e){return this[e]}}s.Uint32NDArray=x,x.__name__=\"Uint32NDArray\";class z extends Int32Array{constructor(e,t){super(e),this[c]=!0,this.dtype=\"int32\",this.shape=t??(O(e)?e.shape:[this.length]),this.dimension=this.shape.length}[(c=w,g.equals)](e,t){return t.eq(this.shape,e.shape)&&t.arrays(this,e)}[f.clone](e){return new z(this,e.clone(this.shape))}[m.serialize](e){return N(this,e)}get(e){return this[e]}}s.Int32NDArray=z,z.__name__=\"Int32NDArray\";class F extends Float32Array{constructor(e,t){super(e),this[p]=!0,this.dtype=\"float32\",this.shape=t??(O(e)?e.shape:[this.length]),this.dimension=this.shape.length}[(p=w,g.equals)](e,t){return t.eq(this.shape,e.shape)&&t.arrays(this,e)}[f.clone](e){return new F(this,e.clone(this.shape))}[m.serialize](e){return N(this,e)}get(e){return this[e]}}s.Float32NDArray=F,F.__name__=\"Float32NDArray\";class j extends Float64Array{constructor(e,t){super(e),this[y]=!0,this.dtype=\"float64\",this.shape=t??(O(e)?e.shape:[this.length]),this.dimension=this.shape.length}[(y=w,g.equals)](e,t){return t.eq(this.shape,e.shape)&&t.arrays(this,e)}[f.clone](e){return new j(this,e.clone(this.shape))}[m.serialize](e){return N(this,e)}get(e){return this[e]}}s.Float64NDArray=j,j.__name__=\"Float64NDArray\";class B extends Array{get shape(){return this._shape??[this.length]}get dimension(){return this.shape.length}constructor(e,t){const s=e instanceof ArrayBuffer?new Float64Array(e):e;if(super((0,A.isNumber)(s)?s:s.length),this[_]=!0,this.dtype=\"object\",!(0,A.isNumber)(s))for(let e=0;e{switch(!0){case e instanceof Uint8Array:return\"uint8\";case e instanceof Int8Array:return\"int8\";case e instanceof Uint16Array:return\"uint16\";case e instanceof Int16Array:return\"int16\";case e instanceof Uint32Array:return\"uint32\";case e instanceof Int32Array:return\"int32\";case e instanceof Float32Array:return\"float32\";case e instanceof ArrayBuffer:case e instanceof Float64Array:return\"float64\";default:return\"object\"}})()),t){case\"bool\":return new D(e,s);case\"uint8\":return new q(e,s);case\"int8\":return new b(e,s);case\"uint16\":return new U(e,s);case\"int16\":return new I(e,s);case\"uint32\":return new x(e,s);case\"int32\":return new z(e,s);case\"float32\":return new F(e,s);case\"float64\":return new j(e,s);case\"object\":return new B(e,s)}}},\n function _(n,e,t,o,r){o();const i=n(9),c=n(8);function l(n){return(0,c.isObject)(n)&&t.clone in n}t.clone=Symbol(\"clone\"),t.is_Cloneable=l;class s extends Error{}t.CloningError=s,s.__name__=\"CloningError\";class a{constructor(){}clone(n){if(l(n))return n[t.clone](this);if((0,c.isPrimitive)(n))return n;if((0,c.isArray)(n)){const e=n.length,t=new Array(e);for(let o=0;o[this.clone(n),this.clone(e)])));if(n instanceof Set)return new Set([...n].map((n=>this.clone(n))));throw new s(`${Object.prototype.toString.call(n)} is not cloneable`)}}t.Cloner=a,a.__name__=\"Cloner\"},\n function _(r,e,i,a,f){a();const o=r(1);var l=r(33);f(\"Serializer\",l.Serializer),f(\"SerializationError\",l.SerializationError),f(\"serialize\",l.serialize);var t=r(35);f(\"Buffer\",t.Buffer),f(\"Base64Buffer\",t.Base64Buffer),o.__exportStar(r(37),i)},\n function _(e,r,t,n,i){n();const s=e(12),a=e(9),c=e(8),o=e(34),u=e(27),l=e(35);t.serialize=Symbol(\"serialize\");class f extends Error{}t.SerializationError=f,f.__name__=\"SerializationError\";class y{constructor(e){this.value=e}to_json(){return JSON.stringify(this.value)}}y.__name__=\"Serialized\";class d{constructor(e){this._circular=new WeakSet,this.binary=e?.binary??!1,this.include_defaults=e?.include_defaults??!1;const r=e?.references;this._references=null!=r?new Map(r):new Map}get_ref(e){return this._references.get(e)}add_ref(e,r){(0,s.assert)(!this._references.has(e)),this._references.set(e,r)}to_serializable(e){return new y(this.encode(e))}encode(e){const r=this.get_ref(e);if(null!=r)return r;if(!(0,c.isObject)(e))return this._encode(e);this._circular.has(e)&&this.error(\"circular reference\"),this._circular.add(e);try{return this._encode(e)}finally{this._circular.delete(e)}}_encode(e){if(function(e){return(0,c.isObject)(e)&&t.serialize in e}(e))return e[t.serialize](this);if((0,c.isArray)(e)){const r=e.length,t=new Array(r);for(let n=0;n[this.encode(e),this.encode(r)]))]}}if(null===e||(0,c.isBoolean)(e)||(0,c.isString)(e))return e;if((0,c.isNumber)(e))return isNaN(e)?{type:\"number\",value:\"nan\"}:isFinite(e)?e:{type:\"number\",value:(e<0?\"-\":\"+\")+\"inf\"};if(e instanceof Date){return{type:\"date\",iso:e.toISOString()}}if(e instanceof Set)return 0==e.size?{type:\"set\"}:{type:\"set\",entries:[...(0,o.map)(e.values(),(e=>this.encode(e)))]};if(e instanceof Map)return 0==e.size?{type:\"map\"}:{type:\"map\",entries:[...(0,o.map)(e.entries(),(([e,r])=>[this.encode(e),this.encode(r)]))]};if((0,c.isSymbol)(e)&&null!=e.description)return{type:\"symbol\",name:e.description};throw new f(`${Object.prototype.toString.call(e)} is not serializable`)}encode_struct(e){const r={};for(const[t,n]of(0,a.entries)(e))void 0!==n&&(r[t]=this.encode(n));return r}error(e){throw new f(e)}_encode_typed_array(e){const r=this.encode(e.buffer),t=(()=>{switch(e.constructor){case Uint8Array:return\"uint8\";case Int8Array:return\"int8\";case Uint16Array:return\"uint16\";case Int16Array:return\"int16\";case Uint32Array:return\"uint32\";case Int32Array:return\"int32\";case Float32Array:return\"float32\";case Float64Array:return\"float64\";default:this.error(`can't serialize typed array of type '${e[Symbol.toStringTag]}'`)}})();return{type:\"typed_array\",array:r,order:u.BYTE_ORDER,dtype:t}}}t.Serializer=d,d.__name__=\"Serializer\"},\n function _(n,o,t,e,f){e();const i=n(10),r=n(12);var l=n(13);function*c(n){const o=n.length;for(let t=0;t=0);for(const t of n)0==o?yield t:o-=1}function*a(n,o){const t=n.length;if(o>t)return;const e=(0,i.range)(o);for(yield e.map((o=>n[o]));;){let f;for(const n of c((0,i.range)(o)))if(e[n]!=n+t-o){f=n;break}if(null==f)return;e[f]+=1;for(const n of(0,i.range)(f+1,o))e[n]=e[n-1]+1;yield e.map((o=>n[o]))}}f(\"min\",l.min),f(\"max\",l.max),t.range=function*(n,o,t=1){(0,r.assert)(t>0);const{abs:e,ceil:f,max:i}=Math;null==o&&(o=n,n=0);const l=n<=o?t:-t,c=i(f(e(o-n)/t),0);for(let o=0;o=0);let t=0;for(const e of n){if(!(t++String.fromCharCode(t)));return btoa(e.join(\"\"))},e.base64_to_buffer=function(t){const n=atob(t),e=n.length,r=new Uint8Array(e);for(let t=0,o=e;t\"'`])/g,(e=>{switch(e){case\"&\":return\"&\";case\"<\":return\"<\";case\">\":return\">\";case'\"':return\""\";case\"'\":return\"'\";case\"`\":return\"`\";default:return e}}))},r.unescape=function(e){return e.replace(/&(amp|lt|gt|quot|#x27|#x60);/g,((e,t)=>{switch(t){case\"amp\":return\"&\";case\"lt\":return\"<\";case\"gt\":return\">\";case\"quot\":return'\"';case\"#x27\":return\"'\";case\"#x60\":return\"`\";default:return t}}))},r.use_strict=function(e){return`'use strict';\\n${e}`},r.to_fixed=function(e,t){return e.toFixed(t).replace(/(\\.[0-9]*?)0+$/,\"$1\").replace(/\\.$/,\"\")},r.insert_text_on_position=function(e,t,r){const n=[];return n.push(e.slice(0,t)),n.push(r),n.push(e.slice(t)),n.join(\"\")}},\n function _(e,t,s,n,a){n();const i=e(26),r=e(32);class d{constructor(e){this.sync=!0,this.document=e}get[Symbol.toStringTag](){return this.constructor.__name__}[i.equals](e,t){return t.eq(this.document,e.document)}}s.DocumentEvent=d,d.__name__=\"DocumentEvent\";class o extends d{constructor(e,t){super(e),this.events=t}[i.equals](e,t){return super[i.equals](e,t)&&t.eq(this.events,e.events)}}s.DocumentEventBatch=o,o.__name__=\"DocumentEventBatch\";class l extends d{}s.DocumentChangedEvent=l,l.__name__=\"DocumentChangedEvent\";class h extends l{constructor(e,t,s){super(e),this.kind=\"MessageSent\",this.msg_type=t,this.msg_data=s}[i.equals](e,t){return super[i.equals](e,t)&&t.eq(this.msg_type,e.msg_type)&&t.eq(this.msg_data,e.msg_data)}[r.serialize](e){return{kind:this.kind,msg_type:this.msg_type,msg_data:e.encode(this.msg_data)}}}s.MessageSentEvent=h,h.__name__=\"MessageSentEvent\";class u extends l{constructor(e,t,s,n){super(e),this.kind=\"ModelChanged\",this.model=t,this.attr=s,this.value=n}[i.equals](e,t){return super[i.equals](e,t)&&t.eq(this.model,e.model)&&t.eq(this.attr,e.attr)&&t.eq(this.value,e.value)}[r.serialize](e){return{kind:this.kind,model:this.model.ref(),attr:this.attr,new:e.encode(this.value)}}}s.ModelChangedEvent=u,u.__name__=\"ModelChangedEvent\";class m extends l{constructor(e,t,s,n,a){super(e),this.kind=\"ColumnDataChanged\",this.model=t,this.attr=s,this.data=n,this.cols=a}[i.equals](e,t){return super[i.equals](e,t)&&t.eq(this.model,e.model)&&t.eq(this.attr,e.attr)&&t.eq(this.data,e.data)&&t.eq(this.cols,e.cols)}[r.serialize](e){return{kind:this.kind,model:this.model.ref(),attr:this.attr,data:e.encode(this.data),cols:this.cols}}}s.ColumnDataChangedEvent=m,m.__name__=\"ColumnDataChangedEvent\";class c extends l{constructor(e,t,s,n,a){super(e),this.kind=\"ColumnsStreamed\",this.model=t,this.attr=s,this.data=n,this.rollover=a}[i.equals](e,t){return super[i.equals](e,t)&&t.eq(this.model,e.model)&&t.eq(this.attr,e.attr)&&t.eq(this.data,e.data)&&t.eq(this.rollover,e.rollover)}[r.serialize](e){return{kind:this.kind,model:this.model.ref(),attr:this.attr,data:e.encode(this.data),rollover:this.rollover}}}s.ColumnsStreamedEvent=c,c.__name__=\"ColumnsStreamedEvent\";class _ extends l{constructor(e,t,s,n){super(e),this.kind=\"ColumnsPatched\",this.model=t,this.attr=s,this.patches=n}[i.equals](e,t){return super[i.equals](e,t)&&t.eq(this.model,e.model)&&t.eq(this.attr,e.attr)&&t.eq(this.patches,e.patches)}[r.serialize](e){return{kind:this.kind,attr:this.attr,model:this.model.ref(),patches:e.encode(this.patches)}}}s.ColumnsPatchedEvent=_,_.__name__=\"ColumnsPatchedEvent\";class q extends l{constructor(e,t){super(e),this.kind=\"TitleChanged\",this.title=t}[i.equals](e,t){return super[i.equals](e,t)&&t.eq(this.title,e.title)}[r.serialize](e){return{kind:this.kind,title:this.title}}}s.TitleChangedEvent=q,q.__name__=\"TitleChangedEvent\";class v extends l{constructor(e,t){super(e),this.kind=\"RootAdded\",this.model=t}[i.equals](e,t){return super[i.equals](e,t)&&t.eq(this.model,e.model)}[r.serialize](e){return{kind:this.kind,model:e.encode(this.model)}}}s.RootAddedEvent=v,v.__name__=\"RootAddedEvent\";class p extends l{constructor(e,t){super(e),this.kind=\"RootRemoved\",this.model=t}[i.equals](e,t){return super[i.equals](e,t)&&t.eq(this.model,e.model)}[r.serialize](e){return{kind:this.kind,model:this.model.ref()}}}s.RootRemovedEvent=p,p.__name__=\"RootRemovedEvent\"},\n function _(t,r,i,e,n){e();const s=t(8),o=t(9);i.pretty=Symbol(\"pretty\");class c{constructor(t){this.visited=new Set,this.precision=t?.precision}to_string(t){if((0,s.isObject)(t)){if(this.visited.has(t))return\"\";this.visited.add(t)}return function(t){return(0,s.isObject)(t)&&i.pretty in t}(t)?t[i.pretty](this):(0,s.isBoolean)(t)?this.boolean(t):(0,s.isNumber)(t)?this.number(t):(0,s.isString)(t)?this.string(t):(0,s.isArray)(t)?this.array(t):(0,s.isIterable)(t)?this.iterable(t):(0,s.isPlainObject)(t)?this.object(t):(0,s.isSymbol)(t)?this.symbol(t):t instanceof ArrayBuffer?this.array_buffer(t):`${t}`}token(t){return t}boolean(t){return`${t}`}number(t){return null!=this.precision?t.toFixed(this.precision):`${t}`}string(t){const r=t.includes(\"'\"),i=t.includes('\"');return r&&i?`\\`${t.replace(/`/g,\"\\\\`\")}\\``:i?`'${t}'`:`\"${t}\"`}symbol(t){return t.toString()}array(t){const r=this.token,i=[];for(const r of t)i.push(this.to_string(r));return`${r(\"[\")}${i.join(`${r(\",\")} `)}${r(\"]\")}`}iterable(t){const r=this.token,i=Object(t)[Symbol.toStringTag]??\"Object\",e=this.array(t);return`${i}${r(\"(\")}${e}${r(\")\")}`}object(t){const r=this.token,i=[];for(const[e,n]of(0,o.entries)(t))i.push(`${e}${r(\":\")} ${this.to_string(n)}`);return`${r(\"{\")}${i.join(`${r(\",\")} `)}${r(\"}\")}`}array_buffer(t){return`ArrayBuffer(#${t.byteLength})`}}i.Printer=c,c.__name__=\"Printer\",i.to_string=function(t,r){return new c(r).to_string(t)}},\n function _(t,r,n,e,o){e();const s=t(1),c=t(8),u=t(9),l=t(44),i=s.__importStar(t(45));function a(t,r,n){if((0,c.isArray)(t)&&(0,c.isArray)(r)){const e=t.concat(r);return null!=n&&e.length>n?e.slice(-n):e}const e=t.length+r.length;if(null!=n&&e>n){const o=e-n,s=t.length,u=(()=>{if(t.length{if((0,c.isTypedArray)(t))return t.constructor;if((0,c.isTypedArray)(r))return r.constructor;throw new Error(\"unsupported array types\")})())(n);return e.set(t,0),e}return t})();for(let t=o,r=s;t{if((0,c.isTypedArray)(t))return t;if((0,c.isTypedArray)(r))return new r.constructor(t);throw new Error(\"unsupported array types\")})();return i.concat(n,r)}}function f(t,r){let n,e,o;return(0,c.isNumber)(t)?(n=t,o=t+1,e=1):(n=null!=t.start?t.start:0,o=null!=t.stop?t.stop:r,e=null!=t.step?t.step:1),[n,o,e]}function p(t,r){const n=new Set;let e=!1;for(const[o,s]of r){let r,u,l,i;if((0,c.isArray)(o)){const[e]=o;n.add(e),r=t[e].shape,u=t[e],i=s,2===o.length?(r=[1,r[0]],l=[o[0],0,o[1]]):l=o}else(0,c.isNumber)(o)?(i=[s],n.add(o)):(i=s,e=!0),l=[0,0,o],r=[1,t.length],u=t;let a=0;const[p,d,y]=f(l[1],r[0]),[h,_,g]=f(l[2],r[1]);for(let t=p;t{try{return this._decode(e)}finally{t=new Set(this._finalizable),this._decoding=!1,this._buffers.clear(),this._finalizable.clear()}})();for(const e of t)this.finalize?.(e),e.finalize(),e.assert_initialized();for(const e of t)e.connect_signals();return s}_decode(e){if((0,l.isArray)(e))return this._decode_plain_array(e);if(!(0,l.isPlainObject)(e))return e;if(!(0,l.isString)(e.type))return(0,l.isString)(e.id)?this._decode_ref(e):this._decode_plain_object(e);{const r=h.get(e.type);if(null!=r)return r(e,this);switch(e.type){case\"ref\":return this._decode_ref(e);case\"symbol\":return this._decode_symbol(e);case\"number\":return this._decode_number(e);case\"array\":return this._decode_array(e);case\"set\":return this._decode_set(e);case\"map\":return this._decode_map(e);case\"bytes\":return this._decode_bytes(e);case\"slice\":return this._decode_slice(e);case\"date\":return this._decode_date(e);case\"value\":return this._decode_value(e);case\"field\":return this._decode_field(e);case\"expr\":return this._decode_expr(e);case\"typed_array\":return this._decode_typed_array(e);case\"ndarray\":return this._decode_ndarray(e);case\"object\":return(0,l.isString)(e.id)?this._decode_object_ref(e):this._decode_object(e);default:this.error(`unable to decode an object of type '${e.type}'`)}}}_decode_symbol(e){this.error(`can't resolve named symbol '${e.name}'`)}_decode_number(e){if(\"value\"in e){const{value:r}=e;if((0,l.isString)(r))switch(r){case\"nan\":return NaN;case\"+inf\":return 1/0;case\"-inf\":return-1/0}else if((0,l.isNumber)(r))return r}this.error(`invalid number representation '${e}'`)}_decode_plain_array(e){return(0,c.map)(e,(e=>this._decode(e)))}_decode_plain_object(e){const r={};for(const[t,s]of(0,a.entries)(e))r[t]=this._decode(s);return r}_decode_array(e){const r=[];for(const t of e.entries??[])r.push(this._decode(t));return r}_decode_set(e){const r=new Set;for(const t of e.entries??[])r.add(this._decode(t));return r}_decode_map(e){const r=(0,c.map)(e.entries??[],(([e,r])=>[this._decode(e),this._decode(r)]));return r.every((([e,r])=>(0,l.isString)(e)))?Object.fromEntries(r):new Map(r)}_decode_bytes(e){const{data:r}=e;if(!(0,d.is_ref)(r))return(0,l.isString)(r)?(0,u.base64_to_buffer)(r):r.buffer;{const e=this._buffers.get(r.id);if(null!=e)return e;this.error(`buffer for id=${r.id} not found`)}}_decode_slice(e){const r=this._decode(e.start),t=this._decode(e.stop),s=this._decode(e.step);return new f.Slice({start:r,stop:t,step:s})}_decode_date(e){const r=this._decode(e.iso);return new Date(r)}_decode_value(e){return{value:this._decode(e.value),transform:null!=e.transform?this._decode(e.transform):void 0,units:null!=e.units?this._decode(e.units):void 0}}_decode_field(e){return{field:this._decode(e.field),transform:null!=e.transform?this._decode(e.transform):void 0,units:null!=e.units?this._decode(e.units):void 0}}_decode_expr(e){return{expr:this._decode(e.expr),transform:null!=e.transform?this._decode(e.transform):void 0,units:null!=e.units?this._decode(e.units):void 0}}_decode_typed_array(e){const{array:r,order:t,dtype:s}=e,n=this._decode(r);switch(t!=_.BYTE_ORDER&&(0,u.swap)(n,s),s){case\"uint8\":return new Uint8Array(n);case\"int8\":return new Int8Array(n);case\"uint16\":return new Uint16Array(n);case\"int16\":return new Int16Array(n);case\"uint32\":return new Uint32Array(n);case\"int32\":return new Int32Array(n);case\"float32\":return new Float32Array(n);case\"float64\":return new Float64Array(n);default:this.error(`unsupported dtype '${s}'`)}}_decode_ndarray(e){const{array:r,order:t,dtype:s,shape:n}=e,i=this._decode(r);return i instanceof ArrayBuffer&&t!=_.BYTE_ORDER&&(0,u.swap)(i,s),(0,o.ndarray)(i,{dtype:s,shape:n})}_decode_object(e){const{type:r,attributes:t}=e,s=this._resolve_type(r);return null!=t?new s(this._decode(t)):new s}_decode_ref(e){const r=this.references.get(e.id);if(null!=r)return r;this.error(`reference ${e.id} isn't known`)}_decode_object_ref(e){const{id:r,name:t,attributes:s}=e,n=this.references.get(r);if(null==n){const e=new(this._resolve_type(t))({id:r});this.references.set(r,e);const n=this._decode(s??{});return e.initialize_props(n),this._finalizable.add(e),e}if(n.type==t){const e=this._decode(s??{});return n.setv(e,{sync:!1}),n}this.error(`type mismatch for an existing reference '${n}', expected '${t}'`)}error(e){throw new y(e)}warning(e){i.logger.warn(e)}_resolve_type(e){const r=this.resolver.get(e);if(null!=r)return r;this.error(`could not resolve type '${e}', which could be due to a widget or a custom model not being registered before first usage`)}}t.Deserializer=p,p.__name__=\"Deserializer\"},\n function _(t,s,e,i,n){i();const c=t(32);class l{constructor({start:t,stop:s,step:e}={}){this.start=t??null,this.stop=s??null,this.step=e??null}[c.serialize](t){return{type:\"slice\",start:t.encode(this.start),stop:t.encode(this.stop),step:t.encode(this.step)}}}e.Slice=l,l.__name__=\"Slice\"},\n function _(e,r,s,t,i){t();const a=e(26),n=/^(?\\d+)\\.(?\\d+)\\.(?\\d+)(?:(?-dev\\.|-rc\\.|.dev|rc)(?\\d+))?(?:\\+(?\\d+)\\..+)?$/;var o;(o=s.ReleaseType||(s.ReleaseType={}))[o.Dev=0]=\"Dev\",o[o.Candidate=1]=\"Candidate\",o[o.Release=2]=\"Release\";class c{constructor(e,r,t,i=s.ReleaseType.Release,a=0,n=0){this.major=e,this.minor=r,this.patch=t,this.type=i,this.revision=a,this.build=n}static from(e){return function(e){const r=n.exec(e);if(null==r||null==r.groups)return null;const{groups:t}=r,i=Number(t.major),a=Number(t.minor),o=Number(t.patch),l=(()=>{switch(t.type){case\"-dev.\":case\".dev\":return s.ReleaseType.Dev;case\"-rc.\":case\"rc\":return s.ReleaseType.Candidate;default:return s.ReleaseType.Release}})(),u=void 0===t.revision?0:Number(t.revision),d=void 0===t.build?0:Number(t.build);return new c(i,a,o,l,u,d)}(e)}toString(){const{major:e,minor:r,patch:t,type:i,revision:a,build:n}=this;let o=`${e}.${r}.${t}`;switch(i){case s.ReleaseType.Dev:o+=`-dev.${a}`;case s.ReleaseType.Candidate:o+=`-rc.${a}`;case s.ReleaseType.Release:}return 0!=n&&(o+=`+${n}`),o}[a.equals](e){const{major:r,minor:s,patch:t,type:i,revision:a}=this;return r==e.major&&s==e.minor&&t==e.patch&&i==e.type&&a==e.revision}}s.Version=c,c.__name__=\"Version\"},\n function _(n,e,t,c,u){c();const i=n(8);t.execute=function(n,e,...t){return(0,i.isFunction)(n)?n(e,...t):n.execute(e,...t)}},\n function _(e,t,s,n,c){var i;n();const r=e(14),a=e(8),l=e(9),o=e(26),_=e(19),h=e(50);class u extends r.HasProps{get is_syncable(){return this.syncable}[o.equals](e,t){return(!!t.structural||t.eq(this.id,e.id))&&super[o.equals](e,t)}constructor(e){super(e)}initialize(){super.initialize(),this._js_callbacks=new Map}connect_signals(){super.connect_signals(),this._update_property_callbacks(),this.connect(this.properties.js_property_callbacks.change,(()=>this._update_property_callbacks())),this.connect(this.properties.js_event_callbacks.change,(()=>this._update_event_callbacks())),this.connect(this.properties.subscribed_events.change,(()=>this._update_event_callbacks()))}_process_event(e){for(const t of(0,l.dict)(this.js_event_callbacks).get(e.event_name)??[])(0,h.execute)(t,e);null!=this.document&&this.subscribed_events.has(e.event_name)&&this.document.event_manager.send_event(e)}trigger_event(e){null!=this.document&&(e.origin=this,this.document.event_manager.trigger(e))}_update_event_callbacks(){null!=this.document?this.document.event_manager.subscribed_models.add(this):_.logger.warn(\"WARNING: Document not defined for updating event callbacks\")}_update_property_callbacks(){const e=e=>{const[t,s=null]=e.split(\":\");return null!=s?this.properties[s][t]:this[t]};for(const[t,s]of this._js_callbacks){const n=e(t);for(const e of s)this.disconnect(n,e)}this._js_callbacks.clear();for(const[t,s]of(0,l.dict)(this.js_property_callbacks)){const n=s.map((e=>()=>(0,h.execute)(e,this)));this._js_callbacks.set(t,n);const c=e(t);for(const e of n)this.connect(c,e)}}_doc_attached(){0==this.js_event_callbacks.size&&0==this.subscribed_events.size||this._update_event_callbacks()}_doc_detached(){this.document.event_manager.subscribed_models.delete(this)}select(e){if((0,a.isString)(e))return[...this.references()].filter((t=>t instanceof i&&t.name===e));if((0,a.isPlainObject)(e)&&\"type\"in e)return[...this.references()].filter((t=>t.type==e.type));if(e.prototype instanceof r.HasProps)return[...this.references()].filter((t=>t instanceof e));throw new Error(`invalid selector ${e}`)}select_one(e){const t=this.select(e);switch(t.length){case 0:return null;case 1:return t[0];default:throw new Error(`found multiple objects matching the given selector ${e}`)}}get_one(e){const t=this.select_one(e);if(null!=t)return t;throw new Error(`could not find any objects matching the given selector ${e}`)}on_event(e,t){const s=(0,a.isString)(e)?e:e.prototype.event_name,n=(0,l.dict)(this.js_event_callbacks),c=n.get(s)??[];n.set(s,[...c,t])}}s.Model=u,i=u,u.__name__=\"Model\",i.define((({Any:e,Unknown:t,Bool:s,Str:n,List:c,Set:i,Dict:r,Nullable:a})=>({tags:[c(t),[]],name:[a(n),null],js_property_callbacks:[r(c(e)),{}],js_event_callbacks:[r(c(e)),{}],subscribed_events:[i(n),new globalThis.Set],syncable:[s,!0]})))},\n function _(e,n,r,t,o){t();const s=e(1),c=e(51),u=s.__importStar(e(21)),a=e(8),i=e(9);r.decode_def=function(e,n){var r;function t(e){if((0,a.isString)(e))switch(e){case\"Any\":return u.Any;case\"Unknown\":return u.Unknown;case\"Bool\":return u.Bool;case\"Float\":return u.Float;case\"Int\":return u.Int;case\"Bytes\":return u.Bytes;case\"Str\":return u.Str;case\"Null\":return u.Null}else switch(e[0]){case\"Regex\":{const[,n,r]=e;return u.Regex(new RegExp(n,r))}case\"Nullable\":{const[,n]=e;return u.Nullable(t(n))}case\"Or\":{const[,n,...r]=e;return u.Or(t(n),...r.map(t))}case\"Tuple\":{const[,n,...r]=e;return u.Tuple(t(n),...r.map(t))}case\"List\":{const[,n]=e;return u.List(t(n))}case\"Struct\":{const[,...n]=e,r=n.map((([e,n])=>[e,t(n)]));return u.Struct((0,i.to_object)(r))}case\"Dict\":{const[,n]=e;return u.Dict(t(n))}case\"Mapping\":{const[,n,r]=e;return u.Mapping(t(n),t(r))}case\"Enum\":{const[,...n]=e;return u.Enum(...n)}case\"Ref\":{const[,r]=e,t=n.resolver.get(r.id);if(null!=t)return u.Ref(t);throw new Error(`${r.id} wasn't defined before referencing it`)}case\"AnyRef\":return u.AnyRef()}}const o=(()=>{const r=e.extends?.id??\"Model\";if(\"Model\"==r)return c.Model;const t=n.resolver.get(r);if(null!=t)return t;throw new Error(`base model ${r} of ${e.name} is not defined`)})(),l=(r=class extends o{},s.__setFunctionName(r,\"model\"),r.__qualified__=e.name,r);function d(e){return void 0===e?e:n.decode(e)}for(const n of e.properties??[]){const e=t(n.kind);l.define({[n.name]:[e,d(n.default)]})}for(const n of e.overrides??[])l.override({[n.name]:d(n.default)});return n.resolver.register(l),l}},\n function _(e,t,s,n,a){var _,r,l,o,u,c,i,v,d,m,p,h,x,y,g,E,P,f,M,R,S,D,b,k,L,C,O,U,I;n();var w=this&&this.__decorate||function(e,t,s,n){var a,_=arguments.length,r=_<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,s):n;if(\"object\"==typeof Reflect&&\"function\"==typeof Reflect.decorate)r=Reflect.decorate(e,t,s,n);else for(var l=e.length-1;l>=0;l--)(a=e[l])&&(r=(_<3?a(r):_>3?a(t,s,r):a(t,s))||r);return _>3&&r&&Object.defineProperty(t,s,r),r};const T=e(51),B=e(8),j=e(12),z=e(32),G=e(47),V=e(26);function W(e){return t=>{t.prototype.event_name=e}}G.Deserializer.register(\"event\",((e,t)=>{const s=q.get(e.name);if(void 0!==s&&null!=s.from_values){const n=t.decode(e.values);return(0,j.assert)((0,B.isPlainObject)(n)),s.from_values(n)}t.error(`deserialization of '${e.name}' event is not supported`)}));const q=new Map;s.server_event=function(e){return t=>{if(q.has(e))throw new Error(`'${e}' event is already registered`);q.set(e,t),t.prototype.event_name=e,t.prototype.publish=!1}};class ${[z.serialize](e){const{event_name:t,event_values:s}=this;return{type:\"event\",name:t,values:e.encode(s)}}[V.equals](e,t){return this.event_name==e.event_name&&t.eq(this.event_values,e.event_values)}}s.BokehEvent=$,_=$,$.__name__=\"BokehEvent\",_.prototype.publish=!0;class A extends ${constructor(){super(...arguments),this.origin=null}get event_values(){return{model:this.origin}}}s.ModelEvent=A,A.__name__=\"ModelEvent\";class F extends A{constructor(e){super(),this.values=e}get event_values(){return{...super.event_values,...this.values}}static from_values(e){const t=(()=>{if(\"model\"in e){const{model:t}=e;return(0,j.assert)(null===t||t instanceof T.Model),delete e.model,t}return null})(),s=new this(e);return s.origin=t,s}}s.UserEvent=F,F.__name__=\"UserEvent\";class H extends ${}s.DocumentEvent=H,H.__name__=\"DocumentEvent\";let J=((r=class extends H{get event_values(){return{}}}).__name__=\"DocumentReady\",r);s.DocumentReady=J,s.DocumentReady=J=w([W(\"document_ready\")],J);class K extends H{}s.ConnectionEvent=K,K.__name__=\"ConnectionEvent\";class N extends K{constructor(){super(...arguments),this.timestamp=new Date}get event_values(){const{timestamp:e}=this;return{timestamp:e}}}s.ConnectionLost=N,l=N,N.__name__=\"ConnectionLost\",l.prototype.event_name=\"connection_lost\",l.prototype.publish=!1;let Q=((o=class extends A{}).__name__=\"ButtonClick\",o);s.ButtonClick=Q,s.ButtonClick=Q=w([W(\"button_click\")],Q);let X=((u=class extends A{constructor(e,t){super(),this.model=e,this.item=t}get event_values(){const{item:e}=this;return{...super.event_values,item:e}}}).__name__=\"LegendItemClick\",u);s.LegendItemClick=X,s.LegendItemClick=X=w([W(\"legend_item_click\")],X);let Y=((c=class extends A{constructor(e){super(),this.item=e}get event_values(){const{item:e}=this;return{...super.event_values,item:e}}}).__name__=\"MenuItemClick\",c);s.MenuItemClick=Y,s.MenuItemClick=Y=w([W(\"menu_item_click\")],Y);let Z=((i=class extends A{constructor(e){super(),this.value=e}get event_values(){const{value:e}=this;return{...super.event_values,value:e}}}).__name__=\"ValueSubmit\",i);s.ValueSubmit=Z,s.ValueSubmit=Z=w([W(\"value_submit\")],Z);class ee extends A{}s.UIEvent=ee,ee.__name__=\"UIEvent\";let te=((v=class extends ee{}).__name__=\"LODStart\",v);s.LODStart=te,s.LODStart=te=w([W(\"lodstart\")],te);let se=((d=class extends ee{}).__name__=\"LODEnd\",d);s.LODEnd=se,s.LODEnd=se=w([W(\"lodend\")],se);let ne=((m=class extends ee{constructor(e,t,s,n){super(),this.x0=e,this.x1=t,this.y0=s,this.y1=n}get event_values(){const{x0:e,x1:t,y0:s,y1:n}=this;return{...super.event_values,x0:e,x1:t,y0:s,y1:n}}}).__name__=\"RangesUpdate\",m);s.RangesUpdate=ne,s.RangesUpdate=ne=w([W(\"rangesupdate\")],ne);let ae=((p=class extends ee{constructor(e,t){super(),this.geometry=e,this.final=t}get event_values(){const{geometry:e,final:t}=this;return{...super.event_values,geometry:e,final:t}}}).__name__=\"SelectionGeometry\",p);s.SelectionGeometry=ae,s.SelectionGeometry=ae=w([W(\"selectiongeometry\")],ae);let _e=((h=class extends ee{}).__name__=\"Reset\",h);s.Reset=_e,s.Reset=_e=w([W(\"reset\")],_e);class re extends ee{constructor(e,t,s,n,a){super(),this.sx=e,this.sy=t,this.x=s,this.y=n,this.modifiers=a}get event_values(){const{sx:e,sy:t,x:s,y:n,modifiers:a}=this;return{...super.event_values,sx:e,sy:t,x:s,y:n,modifiers:a}}}s.PointEvent=re,re.__name__=\"PointEvent\";let le=((x=class extends re{constructor(e,t,s,n,a,_,r){super(e,t,s,n,r),this.delta_x=a,this.delta_y=_}get event_values(){const{delta_x:e,delta_y:t}=this;return{...super.event_values,delta_x:e,delta_y:t}}}).__name__=\"Pan\",x);s.Pan=le,s.Pan=le=w([W(\"pan\")],le);let oe=((y=class extends re{constructor(e,t,s,n,a,_){super(e,t,s,n,_),this.scale=a}get event_values(){const{scale:e}=this;return{...super.event_values,scale:e}}}).__name__=\"Pinch\",y);s.Pinch=oe,s.Pinch=oe=w([W(\"pinch\")],oe);let ue=((g=class extends re{constructor(e,t,s,n,a,_){super(e,t,s,n,_),this.rotation=a}get event_values(){const{rotation:e}=this;return{...super.event_values,rotation:e}}}).__name__=\"Rotate\",g);s.Rotate=ue,s.Rotate=ue=w([W(\"rotate\")],ue);let ce=((E=class extends re{constructor(e,t,s,n,a,_){super(e,t,s,n,_),this.delta=a}get event_values(){const{delta:e}=this;return{...super.event_values,delta:e}}}).__name__=\"MouseWheel\",E);s.MouseWheel=ce,s.MouseWheel=ce=w([W(\"wheel\")],ce);let ie=((P=class extends re{}).__name__=\"MouseMove\",P);s.MouseMove=ie,s.MouseMove=ie=w([W(\"mousemove\")],ie);let ve=((f=class extends re{}).__name__=\"MouseEnter\",f);s.MouseEnter=ve,s.MouseEnter=ve=w([W(\"mouseenter\")],ve);let de=((M=class extends re{}).__name__=\"MouseLeave\",M);s.MouseLeave=de,s.MouseLeave=de=w([W(\"mouseleave\")],de);let me=((R=class extends re{}).__name__=\"Tap\",R);s.Tap=me,s.Tap=me=w([W(\"tap\")],me);let pe=((S=class extends re{}).__name__=\"DoubleTap\",S);s.DoubleTap=pe,s.DoubleTap=pe=w([W(\"doubletap\")],pe);let he=((D=class extends re{}).__name__=\"Press\",D);s.Press=he,s.Press=he=w([W(\"press\")],he);let xe=((b=class extends re{}).__name__=\"PressUp\",b);s.PressUp=xe,s.PressUp=xe=w([W(\"pressup\")],xe);let ye=((k=class extends re{}).__name__=\"PanStart\",k);s.PanStart=ye,s.PanStart=ye=w([W(\"panstart\")],ye);let ge=((L=class extends re{}).__name__=\"PanEnd\",L);s.PanEnd=ge,s.PanEnd=ge=w([W(\"panend\")],ge);let Ee=((C=class extends re{}).__name__=\"PinchStart\",C);s.PinchStart=Ee,s.PinchStart=Ee=w([W(\"pinchstart\")],Ee);let Pe=((O=class extends re{}).__name__=\"PinchEnd\",O);s.PinchEnd=Pe,s.PinchEnd=Pe=w([W(\"pinchend\")],Pe);let fe=((U=class extends re{}).__name__=\"RotateStart\",U);s.RotateStart=fe,s.RotateStart=fe=w([W(\"rotatestart\")],fe);let Me=((I=class extends re{}).__name__=\"RotateEnd\",I);s.RotateEnd=Me,s.RotateEnd=Me=w([W(\"rotateend\")],Me)},\n function _(e,n,t,i,o){i();const r=e(5),a=e(55),s=e(57),c=e(8),d=e(12);t.index=new Proxy(new a.ViewManager,{get(e,n){if((0,c.isString)(n)){const t=e.get_by_id(n);if(null!=t)return t}return Reflect.get(e,n)},has(e,n){if((0,c.isString)(n)){if(null!=e.get_by_id(n))return!0}return Reflect.has(e,n)},ownKeys:e=>e.roots.map((e=>e.model.id)),getOwnPropertyDescriptor(e,n){if((0,c.isString)(n)){const t=e.get_by_id(n);if(null!=t)return{configurable:!0,enumerable:!0,writable:!1,value:t}}return Reflect.getOwnPropertyDescriptor(e,n)}}),t.add_document_standalone=async function(e,n,i=[],o=!1){(0,d.assert)(null==e.views_manager);const c=new a.ViewManager([],t.index);async function l(o){null!=o.default_view?await async function(o){const r=await c.build_view(o);if(r instanceof s.DOMView){const t=e.roots().indexOf(o),a=i[t]??n;r.build(a)}t.index.add(r)}(o):e.notify_idle(o)}e.views_manager=c;for(const n of e.roots())await l(n);return o&&(window.document.title=e.title()),e.on_change((e=>{e instanceof r.RootAddedEvent?l(e.model):e instanceof r.RootRemovedEvent?function(e){const n=c.get(e);n?.remove()}(e.model):o&&e instanceof r.TitleChangedEvent&&(window.document.title=e.title)})),c}},\n function _(e,t,i,r,n){r();const o=e(56);class s{*all_views(){yield*this.query((()=>!0))}*query(e){const t=new Set;function*i(r){if(!t.has(r)){t.add(r),e(r)&&(yield r);for(const e of r.children())yield*i(e)}}for(const e of this)yield*i(e)}query_one(e){for(const t of this.query(e))return t;return null}*find(e){yield*this.query((t=>t.model==e))}*find_by_id(e){yield*this.query((t=>t.model.id==e))}find_one(e){for(const t of this.find(e))return t;return null}find_one_by_id(e){for(const t of this.find_by_id(e))return t;return null}get_one(e){const t=this.find_one(e);if(null!=t)return t;throw new Error(`cannot find a view for ${e}`)}get_one_by_id(e){const t=this.find_one_by_id(e);if(null!=t)return t;throw new Error(`cannot find a view for a model with '${e}' identity`)}find_all(e){return[...this.find(e)]}find_all_by_id(e){return[...this.find_by_id(e)]}}s.__name__=\"AbstractViewQuery\";class d extends s{constructor(e){super(),this.view=e}*[Symbol.iterator](){yield this.view}toString(){return`ViewQuery(${this.view})`}}i.ViewQuery=d,d.__name__=\"ViewQuery\";class l extends s{constructor(e=[],t){super(),this.global=t,this._roots=new Set(e)}toString(){return`ViewManager(${[...this._roots].map((e=>`${e}`)).join(\", \")})`}async build_view(e,t=null){const i=await(0,o.build_view)(e,{owner:this,parent:t});return null==t&&this.add(i),i}get(e){for(const t of this._roots)if(t.model==e)return t;return null}get_by_id(e){for(const t of this._roots)if(t.model.id==e)return t;return null}add(e){this._roots.add(e),this.global?.add(e)}delete(e){this._roots.delete(e),this.global?.delete(e)}remove(e){this.delete(e)}clear(){for(const e of this)e.remove()}get roots(){return[...this._roots]}*[Symbol.iterator](){yield*this._roots}}i.ViewManager=l,l.__name__=\"ViewManager\"},\n function _(e,n,t,o,i){o();const s=e(10),c=e(12);async function a(e,n,t){(0,c.assert)(null!=e,\"model doesn't implement a view\");const o=new e({...t,model:n});return o.initialize(),await o.lazy_initialize(),o}t.build_view=async function(e,n={parent:null},t=(e=>e.default_view)){const o=await a(t(e),e,n);return o.connect_signals(),o},t.build_views=async function(e,n,t={parent:null},o=(e=>e.default_view)){const i=(0,s.difference)([...e.keys()],n),c=[];for(const n of i){const t=e.get(n);null!=t&&(e.delete(n),c.push(t),t.remove())}const r=[],l=n.filter((n=>!e.has(n)));for(const n of l){const i=await a(o(n),n,t);e.set(n,i),r.push(i)}for(const e of r)e.connect_signals();return{created:r,removed:c}},t.remove_views=function(e){for(const[n,t]of e)t.remove(),e.delete(n)},t.traverse_views=function(e,n){const t=new Set,o=[...e];for(;;){const e=o.shift();if(void 0===e)break;t.has(e)||(t.add(e),o.push(...e.children()),n(e))}}},\n function _(s,e,t,i,_){i();const l=s(1),a=s(58),r=s(63),h=s(8),n=s(12),c=l.__importDefault(s(66));class p extends a.View{constructor(){super(...arguments),this._was_built=!1}get bbox(){}serializable_state(){const s=super.serializable_state(),{bbox:e}=this;return null!=e?{...s,bbox:e.round()}:s}get children_el(){return this.shadow_el??this.el}initialize(){super.initialize(),this.el=this._create_element()}remove(){this.el.remove(),super.remove()}stylesheets(){return[]}css_classes(){return[]}render_to(s){this.render(),s.appendChild(this.el)}after_render(){this.reposition()}r_after_render(){for(const s of this.children())s instanceof p&&s.r_after_render();this.after_render(),this._was_built=!0}_create_element(){return(0,r.create_element)(this.constructor.tag_name,{})}reposition(s){}build(s){(0,n.assert)(this.is_root),this.render_to(s),this.r_after_render(),this.notify_finished()}rendering_target(){return null}}t.DOMView=p,p.__name__=\"DOMView\",p.tag_name=\"div\";class o extends p{initialize(){super.initialize(),this.class_list=new r.ClassList(this.el.classList)}}t.DOMElementView=o,o.__name__=\"DOMElementView\";class d extends o{constructor(){super(...arguments),this._applied_stylesheets=[],this._applied_css_classes=[]}initialize(){super.initialize(),this.shadow_el=this.el.attachShadow({mode:\"open\"})}stylesheets(){return[...super.stylesheets(),c.default]}empty(){(0,r.empty)(this.shadow_el),this.class_list.clear(),this._applied_css_classes=[],this._applied_stylesheets=[]}render(){this.empty(),this._update_stylesheets(),this._update_css_classes(),this._update_css_variables()}reposition(s){this._update_css_variables()}*_stylesheets(){for(const s of this.stylesheets())yield(0,h.isString)(s)?new r.InlineStyleSheet(s):s}*_css_classes(){yield`bk-${this.model.type.replace(/\\./g,\"-\")}`,yield*this.css_classes()}*_css_variables(){}_apply_stylesheets(s){this._applied_stylesheets.push(...s),s.forEach((s=>s.install(this.shadow_el)))}_apply_css_classes(s){this._applied_css_classes.push(...s),this.class_list.add(...s)}_update_stylesheets(){this._applied_stylesheets.forEach((s=>s.uninstall())),this._applied_stylesheets=[],this._apply_stylesheets([...this._stylesheets()])}_update_css_classes(){this.class_list.remove(this._applied_css_classes),this._applied_css_classes=[],this._apply_css_classes([...this._css_classes()])}_update_css_variables(){for(const[s,e]of this._css_variables()){const t=s.startsWith(\"--\")?s:`--${s}`;this.el.style.setProperty(t,e)}}}t.DOMComponentView=d,d.__name__=\"DOMComponentView\"},\n function _(e,t,s,i,n){i();const r=e(14),o=e(15),l=e(8),h=e(59),a=e(61),_=e(62),c=e(55),d=e(26);class u{get ready(){return this._ready}connect(e,t){let s=this._slots.get(t);return null==s&&(s=(e,s)=>{const i=Promise.resolve(t.call(this,e,s));this._ready=this._ready.then((()=>i)),this.root!=this&&(this.root._ready=this.root._ready.then((()=>this._ready)))},this._slots.set(t,s)),e.connect(s,this)}disconnect(e,t){return e.disconnect(t,this)}constructor(e){this.removed=new o.Signal0(this,\"removed\"),this.views=new c.ViewQuery(this),this._ready=Promise.resolve(void 0),this._slots=new WeakMap,this._destroyed=!1,this._has_finished=!1,this._idle_notified=!1;const{model:t,parent:s,owner:i}=e;this.model=t,this.parent=s,null==s?(this.root=this,this.owner=i??new c.ViewManager([this])):(this.root=s.root,this.owner=this.root.owner)}initialize(){}async lazy_initialize(){}remove(){this.disconnect_signals(),this.owner.remove(this),this.removed.emit(),this._destroyed=!0}get is_destroyed(){return this._destroyed}toString(){return`${this.model.type}View(${this.model.id})`}[d.equals](e,t){return Object.is(this,e)}*children(){}mark_finished(){this._has_finished=!0}force_finished(){this.mark_finished()}finish(){this.mark_finished(),this.notify_finished()}notify_finished(){if(this.is_root){if(!this._idle_notified&&this.has_finished()){const{document:e}=this.model;null!=e&&(this._idle_notified=!0,e.notify_idle(this.model))}}else this.root.notify_finished()}serializable_state(){const e=[...this.children()].filter((e=>e.model.is_syncable)).map((e=>e.serializable_state())).filter((e=>null!=e.bbox&&e.bbox.is_valid&&!e.bbox.is_empty));return{type:this.model.type,children:e}}get is_root(){return null==this.parent}has_finished(){return this._has_finished}get is_idle(){return this.has_finished()}connect_signals(){}disconnect_signals(){o.Signal.disconnect_receiver(this)}on_change(e,t){for(const s of(0,l.isArray)(e)?e:[e])this.connect(s.change,t)}on_transitive_change(e,t){const s=()=>{const t=e.is_unset?[]:e.get_value();return r.HasProps.references(t,{recursive:!1})},i=e=>{for(const s of e)this.connect(s.change,t)},n=e=>{for(const s of e)this.disconnect(s.change,t)};let o=s();i(o),this.on_change(e,(()=>{n(o),o=s(),i(o),t()}))}cursor(e,t){return null}resolve_frame(){return null}resolve_canvas(){return null}resolve_plot(){return null}resolve_target(e){if(!(0,l.isString)(e)){const t=[this.root];for(;;){const s=t.shift();if(null==s)break;if(s.model==e)return s;t.push(...s.children())}return null}{const t=e=>{let t=this;for(;null!=t;){const s=e(t);if(null!=s)return s;t=t.parent}return null};switch(e){case\"parent\":return this.parent;case\"frame\":return t((e=>e.resolve_frame()));case\"canvas\":return t((e=>e.resolve_canvas()));case\"plot\":return t((e=>e.resolve_plot()))}}}resolve_symbol(e){return{x:NaN,y:NaN}}resolve_node(e){const t=this.resolve_target(e.target);return null!=t?t.resolve_symbol(e):{x:NaN,y:NaN}}resolve_coordinate(e){if(e instanceof a.XY){let t=this;for(;null!=t&&null==t.resolve_xy;)t=t.parent;return t?.resolve_xy?.(e)??{x:NaN,y:NaN}}if(e instanceof _.Indexed){let t=this;for(;null!=t&&null==t.resolve_indexed;)t=t.parent;return t?.resolve_indexed?.(e)??{x:NaN,y:NaN}}return e instanceof h.Node?this.resolve_node(e):{x:NaN,y:NaN}}resolve_as_xy(e){const t=this.resolve_coordinate(e);return(0,l.isNumber)(t)?{x:NaN,y:NaN}:t}resolve_as_scalar(e,t){const s=this.resolve_coordinate(e);return(0,l.isNumber)(s)?s:s[t]}}s.View=u,u.__name__=\"View\"},\n function _(t,e,r,n,o){var s;n();const a=t(60),i=t(51),_=t(21);r.ImplicitTarget=(0,_.Enum)(\"canvas\",\"plot\",\"frame\",\"parent\"),r.NodeTarget=(0,_.Or)((0,_.Ref)(i.Model),r.ImplicitTarget);class h{constructor(t,e=!1){this._left=null,this._right=null,this._top=null,this._bottom=null,this.target=t,this.frozen=e}_node(t){const{target:e,frozen:r}=this,n=new l({target:e,symbol:t});return r&&(this[`_${t}`]=n),n}get left(){return this._left??this._node(\"left\")}get right(){return this._right??this._node(\"right\")}get top(){return this._top??this._node(\"top\")}get bottom(){return this._bottom??this._node(\"bottom\")}freeze(){return new h(this.target,!0)}}r.BoxNodes=h,h.__name__=\"BoxNodes\";class l extends a.Coordinate{constructor(t){super(t)}static get frame(){return this._frame_nodes}static get canvas(){return this._canvas_nodes}}r.Node=l,s=l,l.__name__=\"Node\",s.define((({Str:t,Int:e})=>({target:[r.NodeTarget],symbol:[t],offset:[e,0]}))),l._frame_nodes=new h(\"frame\"),l._canvas_nodes=new h(\"canvas\")},\n function _(o,n,e,t,s){t();const c=o(51);class r extends c.Model{constructor(o){super(o)}}e.Coordinate=r,r.__name__=\"Coordinate\"},\n function _(n,e,o,t,s){var a;t();const c=n(60);class r extends c.Coordinate{constructor(n){super(n)}}o.XY=r,a=r,r.__name__=\"XY\",a.define((({Float:n})=>({x:[n],y:[n]})))},\n function _(e,n,d,r,t){var o;r();const s=e(60);class c extends s.Coordinate{constructor(e){super(e)}}d.Indexed=c,o=c,c.__name__=\"Indexed\",o.define((({Int:e,AnyRef:n})=>({index:[e],renderer:[n()]})))},\n function _(t,e,n,o,i){o(),n.template=n.td=n.tbody=n.table=n.sup=n.summary=n.sub=n.style=n.strong=n.span=n.source=n.small=n.slot=n.select=n.section=n.search=n.script=n.samp=n.s=n.ruby=n.rt=n.rp=n.q=n.progress=n.pre=n.picture=n.p=n.output=n.option=n.optgroup=n.ol=n.object=n.noscript=n.nav=n.meter=n.meta=n.menu=n.mark=n.map=n.main=n.link=n.li=n.legend=n.label=n.kbd=n.ins=n.input=n.img=n.iframe=n.i=void 0,n.supports_adopted_stylesheets=n.px=n.dom_ready=n.GlobalImportedStyleSheet=n.ImportedStyleSheet=n.GlobalInlineStyleSheet=n.InlineStyleSheet=n.StyleSheet=n.MouseButton=n.toggle_attribute=n.classes=n.ClassList=n.position=n.box_size=n.bounding_box=n.content_size=n.outer_size=n.scroll_size=n.size=n.extents=n.parent=n.offset_bbox=n.hide=n.show=n.undisplay=n.display=n.contains=n.empty=n.prepend=n.nbsp=n.text=n.createSVGElement=n.wbr=n.video=n.ul=n.u=n.track=n.tr=n.title=n.time=n.thead=n.th=n.tfoot=n.textarea=void 0;const s=t(8),l=t(9),r=t(64),a=t(65),d=t(19),c=t=>(e={},...n)=>{const o=document.createElement(t);if((0,s.isPlainObject)(e)?e={...e}:(n=[e,...n],e={}),null!=e.class){const t=(0,s.isString)(e.class)?e.class.split(/\\s+/):e.class;for(const e of t)null!=e&&o.classList.add(e);delete e.class}if(null!=e.style&&((0,s.isString)(e.style)?o.setAttribute(\"style\",e.style):(0,a.apply_styles)(o.style,e.style),delete e.style),null!=e.data){for(const[t,n]of(0,l.entries)(e.data))null!=n&&(o.dataset[t]=n);delete e.data}for(const[t,n]of(0,l.entries)(e))null!=n&&((0,s.isBoolean)(n)?o.toggleAttribute(t,n):(0,s.isNumber)(n)?o.setAttribute(t,`${n}`):(0,s.isString)(n)?o.setAttribute(t,n):d.logger.warn(`unable to set attribute: ${t} = ${n}`));function i(t){if((0,s.isString)(t))o.append(document.createTextNode(t));else if(t instanceof Node)o.append(t);else if(t instanceof NodeList||t instanceof HTMLCollection)o.append(...t);else if(null!=t&&!1!==t)throw new Error(`expected a DOM element, string, false or null, got ${JSON.stringify(t)}`)}for(const t of n)if((0,s.isArray)(t))for(const e of t)i(e);else i(t);return o};function u(t){return document.createTextNode(t)}function h(t){const e=parseFloat(t);return isFinite(e)?e:0}function p(t){const e=getComputedStyle(t);return{border:{top:h(e.borderTopWidth),bottom:h(e.borderBottomWidth),left:h(e.borderLeftWidth),right:h(e.borderRightWidth)},margin:{top:h(e.marginTop),bottom:h(e.marginBottom),left:h(e.marginLeft),right:h(e.marginRight)},padding:{top:h(e.paddingTop),bottom:h(e.paddingBottom),left:h(e.paddingLeft),right:h(e.paddingRight)}}}function f(t){const e=t.getBoundingClientRect();return{width:Math.ceil(e.width),height:Math.ceil(e.height)}}n.create_element=function(t,e,...n){return c(t)(e,...n)},n.a=c(\"a\"),n.abbr=c(\"abbr\"),n.address=c(\"address\"),n.area=c(\"area\"),n.article=c(\"article\"),n.aside=c(\"aside\"),n.audio=c(\"audio\"),n.b=c(\"b\"),n.base=c(\"base\"),n.bdi=c(\"bdi\"),n.bdo=c(\"bdo\"),n.blockquote=c(\"blockquote\"),n.body=c(\"body\"),n.br=c(\"br\"),n.button=c(\"button\"),n.canvas=c(\"canvas\"),n.caption=c(\"caption\"),n.cite=c(\"cite\"),n.code=c(\"code\"),n.col=c(\"col\"),n.colgroup=c(\"colgroup\"),n.data=c(\"data\"),n.datalist=c(\"datalist\"),n.dd=c(\"dd\"),n.del=c(\"del\"),n.details=c(\"details\"),n.dfn=c(\"dfn\"),n.dialog=c(\"dialog\"),n.div=c(\"div\"),n.dl=c(\"dl\"),n.dt=c(\"dt\"),n.em=c(\"em\"),n.embed=c(\"embed\"),n.fieldset=c(\"fieldset\"),n.figcaption=c(\"figcaption\"),n.figure=c(\"figure\"),n.footer=c(\"footer\"),n.form=c(\"form\"),n.h1=c(\"h1\"),n.h2=c(\"h2\"),n.h3=c(\"h3\"),n.h4=c(\"h4\"),n.h5=c(\"h5\"),n.h6=c(\"h6\"),n.head=c(\"head\"),n.header=c(\"header\"),n.hgroup=c(\"hgroup\"),n.hr=c(\"hr\"),n.html=c(\"html\"),n.i=c(\"i\"),n.iframe=c(\"iframe\"),n.img=c(\"img\"),n.input=c(\"input\"),n.ins=c(\"ins\"),n.kbd=c(\"kbd\"),n.label=c(\"label\"),n.legend=c(\"legend\"),n.li=c(\"li\"),n.link=c(\"link\"),n.main=c(\"main\"),n.map=c(\"map\"),n.mark=c(\"mark\"),n.menu=c(\"menu\"),n.meta=c(\"meta\"),n.meter=c(\"meter\"),n.nav=c(\"nav\"),n.noscript=c(\"noscript\"),n.object=c(\"object\"),n.ol=c(\"ol\"),n.optgroup=c(\"optgroup\"),n.option=c(\"option\"),n.output=c(\"output\"),n.p=c(\"p\"),n.picture=c(\"picture\"),n.pre=c(\"pre\"),n.progress=c(\"progress\"),n.q=c(\"q\"),n.rp=c(\"rp\"),n.rt=c(\"rt\"),n.ruby=c(\"ruby\"),n.s=c(\"s\"),n.samp=c(\"samp\"),n.script=c(\"script\"),n.search=c(\"search\"),n.section=c(\"section\"),n.select=c(\"select\"),n.slot=c(\"slot\"),n.small=c(\"small\"),n.source=c(\"source\"),n.span=c(\"span\"),n.strong=c(\"strong\"),n.style=c(\"style\"),n.sub=c(\"sub\"),n.summary=c(\"summary\"),n.sup=c(\"sup\"),n.table=c(\"table\"),n.tbody=c(\"tbody\"),n.td=c(\"td\"),n.template=c(\"template\"),n.textarea=c(\"textarea\"),n.tfoot=c(\"tfoot\"),n.th=c(\"th\"),n.thead=c(\"thead\"),n.time=c(\"time\"),n.title=c(\"title\"),n.tr=c(\"tr\"),n.track=c(\"track\"),n.u=c(\"u\"),n.ul=c(\"ul\"),n.video=c(\"video\"),n.wbr=c(\"wbr\"),n.createSVGElement=function(t,e=null,...n){const o=document.createElementNS(\"http://www.w3.org/2000/svg\",t);for(const[t,n]of(0,l.entries)(e??{}))null!=n&&!1!==n&&o.setAttribute(t,n);function i(t){if((0,s.isString)(t))o.appendChild(document.createTextNode(t));else if(t instanceof Node)o.appendChild(t);else if(t instanceof NodeList||t instanceof HTMLCollection)for(const e of t)o.appendChild(e);else if(null!=t&&!1!==t)throw new Error(`expected a DOM element, string, false or null, got ${JSON.stringify(t)}`)}for(const t of n)if((0,s.isArray)(t))for(const e of t)i(e);else i(t);return o},n.text=u,n.nbsp=function(){return u(\"\\xa0\")},n.prepend=function(t,...e){const n=t.firstChild;for(const o of e)t.insertBefore(o,n)},n.empty=function(t,e=!1){let n;for(;null!=(n=t.firstChild);)t.removeChild(n);if(e&&t instanceof Element)for(const e of t.attributes)t.removeAttributeNode(e)},n.contains=function(t,e){let n=e;for(;null!=n.parentNode;){const e=n.parentNode;if(e==t)return!0;n=e instanceof ShadowRoot?e.host:e}return!1},n.display=function(t,e=!0){t.style.display=e?\"\":\"none\"},n.undisplay=function(t){t.style.display=\"none\"},n.show=function(t){t.style.visibility=\"\"},n.hide=function(t){t.style.visibility=\"hidden\"},n.offset_bbox=function(t){const{top:e,left:n,width:o,height:i}=t.getBoundingClientRect();return new r.BBox({left:n+scrollX-document.documentElement.clientLeft,top:e+scrollY-document.documentElement.clientTop,width:o,height:i})},n.parent=function(t,e){let n=t;for(;null!=(n=n.parentElement);)if(n.matches(e))return n;return null},n.extents=p,n.size=f,n.scroll_size=function(t){return{width:Math.ceil(t.scrollWidth),height:Math.ceil(t.scrollHeight)}},n.outer_size=function(t){const{margin:{left:e,right:n,top:o,bottom:i}}=p(t),{width:s,height:l}=f(t);return{width:Math.ceil(s+e+n),height:Math.ceil(l+o+i)}},n.content_size=function(t){const{left:e,top:n}=t.getBoundingClientRect(),{padding:o}=p(t);let i=0,s=0;for(const l of(t.shadowRoot??t).children){const t=l.getBoundingClientRect();i=Math.max(i,Math.ceil(t.left-e-o.left+t.width)),s=Math.max(s,Math.ceil(t.top-n-o.top+t.height))}return{width:i,height:s}},n.bounding_box=function(t){const{x:e,y:n,width:o,height:i}=t.getBoundingClientRect();return new r.BBox({x:e,y:n,width:o,height:i})},n.box_size=function(t){const{width:e,height:n}=t.getBoundingClientRect();return{width:e,height:n}},n.position=function(t,e,n){const{style:o}=t;if(o.left=`${e.x}px`,o.top=`${e.y}px`,o.width=`${e.width}px`,o.height=`${e.height}px`,null==n)o.margin=\"\";else{const{top:t,right:e,bottom:i,left:s}=n;o.margin=`${t}px ${e}px ${i}px ${s}px`}};class m{constructor(t){this.class_list=t}get values(){const t=[];for(let e=0;ethis.class_list.remove(t))):this.class_list.remove(e);return this}clear(){for(const t of this.values)this.class_list.remove(t);return this}toggle(t,e){return(null!=e?e:!this.has(t))?this.add(t):this.remove(t),this}}var g;n.ClassList=m,m.__name__=\"ClassList\",n.classes=function(t){return new m(t.classList)},n.toggle_attribute=function(t,e,n){null==n&&(n=!t.hasAttribute(e)),n?t.setAttribute(e,\"true\"):t.removeAttribute(e)},(g=n.MouseButton||(n.MouseButton={}))[g.None=0]=\"None\",g[g.Primary=1]=\"Primary\",g[g.Secondary=2]=\"Secondary\",g[g.Auxiliary=4]=\"Auxiliary\",g[g.Left=1]=\"Left\",g[g.Right=2]=\"Right\",g[g.Middle=4]=\"Middle\";class b{install(t){t.append(this.el)}uninstall(){this.el.remove()}}n.StyleSheet=b,b.__name__=\"StyleSheet\";class y extends b{constructor(t){super(),this.el=(0,n.style)(),(0,s.isString)(t)?this._update(t):null!=t&&this._update((0,a.compose_stylesheet)(t))}get css(){return this.el.textContent??\"\"}_update(t){this.el.textContent=t}clear(){this.replace(\"\")}_to_css(t,e){return null==e?t:(0,a.compose_stylesheet)({[t]:e})}replace(t,e){this._update(this._to_css(t,e))}prepend(t,e){this._update(`${this._to_css(t,e)}\\n${this.css}`)}append(t,e){this._update(`${this.css}\\n${this._to_css(t,e)}`)}remove(){this.el.remove()}}n.InlineStyleSheet=y,y.__name__=\"InlineStyleSheet\";class _ extends y{install(){this.el.isConnected||document.head.appendChild(this.el)}}n.GlobalInlineStyleSheet=_,_.__name__=\"GlobalInlineStyleSheet\";class S extends b{constructor(t){super(),this.el=(0,n.link)({rel:\"stylesheet\",href:t})}replace(t){this.el.href=t}remove(){this.el.remove()}}n.ImportedStyleSheet=S,S.__name__=\"ImportedStyleSheet\";class x extends S{install(){this.el.isConnected||document.head.appendChild(this.el)}}n.GlobalImportedStyleSheet=x,x.__name__=\"GlobalImportedStyleSheet\",n.dom_ready=async function(){if(\"loading\"==document.readyState)return new Promise(((t,e)=>{document.addEventListener(\"DOMContentLoaded\",(()=>t()),{once:!0})}))},n.px=function(t){return(0,s.isNumber)(t)?`${t}px`:t},n.supports_adopted_stylesheets=\"adoptedStyleSheets\"in ShadowRoot.prototype},\n function _(t,e,r,i,n){i();const s=t(24),h=t(26),o=t(13),c=t(8),{min:u,max:x,round:y}=Math;function g(t,e){return isNaN(t)?e:isNaN(e)?t:u(t,e)}function a(t,e){return isNaN(t)?e:isNaN(e)?t:x(t,e)}r.empty=function(){return{x0:1/0,y0:1/0,x1:-1/0,y1:-1/0}},r.positive_x=function(){return{x0:Number.MIN_VALUE,y0:-1/0,x1:1/0,y1:1/0}},r.positive_y=function(){return{x0:-1/0,y0:Number.MIN_VALUE,x1:1/0,y1:1/0}},r.union=function(t,e){return{x0:g(t.x0,e.x0),x1:a(t.x1,e.x1),y0:g(t.y0,e.y0),y1:a(t.y1,e.y1)}},r.isXY=function(t){return(0,c.isPlainObject)(t)&&\"x\"in t&&\"y\"in t};class _{constructor(t,e=!1){if(null==t)this.x0=0,this.y0=0,this.x1=0,this.y1=0;else if(\"x0\"in t){const{x0:e,y0:r,x1:i,y1:n}=t;if(isFinite(e+r+i+n)){if(!(e<=i&&r<=n))throw new Error(`invalid bbox {x0: ${e}, y0: ${r}, x1: ${i}, y1: ${n}}`);this.x0=e,this.y0=r,this.x1=i,this.y1=n}else this.x0=NaN,this.y0=NaN,this.x1=NaN,this.y1=NaN}else if(\"x\"in t){const{x:e,y:r,width:i,height:n,origin:s=\"top_left\"}=t;if(!(i>=0&&n>=0))throw new Error(`invalid bbox {x: ${e}, y: ${r}, width: ${i}, height: ${n}}`);const h=(()=>{switch(s){case\"left\":return\"center_left\";case\"right\":return\"center_right\";case\"top\":return\"top_center\";case\"bottom\":return\"bottom_center\";case\"center\":return\"center_center\";default:return s}})(),[o,c]=h.split(\"_\",2),u=(()=>{switch(o){case\"top\":return 0;case\"center\":return.5;case\"bottom\":return 1}})(),x=e-(()=>{switch(c){case\"left\":return 0;case\"center\":return.5;case\"right\":return 1}})()*i,y=r-u*n,g=x+i,a=y+n;this.x0=x,this.y0=y,this.x1=g,this.y1=a}else{let r,i,n,s;if(\"width\"in t)if(\"left\"in t)r=t.left,i=r+t.width;else if(\"right\"in t)i=t.right,r=i-t.width;else{const e=t.width/2;r=t.hcenter-e,i=t.hcenter+e}else r=t.left,i=t.right;if(\"height\"in t)if(\"top\"in t)n=t.top,s=n+t.height;else if(\"bottom\"in t)s=t.bottom,n=s-t.height;else{const e=t.height/2;n=t.vcenter-e,s=t.vcenter+e}else n=t.top,s=t.bottom;if(r>i||n>s){if(!e)throw new Error(`invalid bbox {left: ${r}, top: ${n}, right: ${i}, bottom: ${s}}`);r>i&&(r=i),n>s&&(n=s)}this.x0=r,this.y0=n,this.x1=i,this.y1=s}}static from_lrtb({left:t,right:e,top:r,bottom:i}){return new _({x0:u(t,e),y0:u(r,i),x1:x(t,e),y1:x(r,i)})}static from_rect({x0:t,y0:e,x1:r,y1:i}){return new _({x0:u(t,r),y0:u(e,i),x1:x(t,r),y1:x(e,i)})}static empty(){return new _({x0:0,y0:0,x1:0,y1:0})}static invalid(){return new _({x0:NaN,y0:NaN,x1:NaN,y1:NaN})}clone(){return new _(this)}equals(t){return this.x0==t.x0&&this.y0==t.y0&&this.x1==t.x1&&this.y1==t.y1}[h.equals](t,e){return e.eq(this.x0,t.x0)&&e.eq(this.y0,t.y0)&&e.eq(this.x1,t.x1)&&e.eq(this.y1,t.y1)}toString(){return`BBox({left: ${this.left}, top: ${this.top}, width: ${this.width}, height: ${this.height}})`}get is_valid(){const{x0:t,x1:e,y0:r,y1:i}=this;return isFinite(t+e+r+i)}get is_empty(){const{x0:t,x1:e,y0:r,y1:i}=this;return 0==t&&0==e&&0==r&&0==i}get left(){return this.x0}get top(){return this.y0}get right(){return this.x1}get bottom(){return this.y1}get p0(){return{x:this.x0,y:this.y0}}get p1(){return{x:this.x1,y:this.y1}}get x(){return this.x0}get y(){return this.y0}get width(){return this.x1-this.x0}get height(){return this.y1-this.y0}get size(){return{width:this.width,height:this.height}}get rect(){const{x0:t,y0:e,x1:r,y1:i}=this;return{p0:{x:t,y:e},p1:{x:r,y:e},p2:{x:r,y:i},p3:{x:t,y:i}}}get box(){const{x:t,y:e,width:r,height:i}=this;return{x:t,y:e,width:r,height:i}}get lrtb(){const{left:t,right:e,top:r,bottom:i}=this;return{left:t,right:e,top:r,bottom:i}}get x_range(){return{start:this.x0,end:this.x1}}get y_range(){return{start:this.y0,end:this.y1}}get h_range(){return this.x_range}get v_range(){return this.y_range}get ranges(){return[this.x_range,this.y_range]}get aspect(){return this.width/this.height}get x_center(){return(this.left+this.right)/2}get y_center(){return(this.top+this.bottom)/2}get hcenter(){return this.x_center}get vcenter(){return this.y_center}get area(){return this.width*this.height}resolve(t){switch(t){case\"top_left\":return this.top_left;case\"top_center\":return this.top_center;case\"top_right\":return this.top_right;case\"center_left\":return this.center_left;case\"center_center\":return this.center_center;case\"center_right\":return this.center_right;case\"bottom_left\":return this.bottom_left;case\"bottom_center\":return this.bottom_center;case\"bottom_right\":return this.bottom_right;case\"center\":return this.center;case\"top\":return this.top;case\"left\":return this.left;case\"right\":return this.right;case\"bottom\":return this.bottom;case\"width\":return this.width;case\"height\":return this.height;default:return{x:NaN,y:NaN}}}get top_left(){return{x:this.left,y:this.top}}get top_center(){return{x:this.hcenter,y:this.top}}get top_right(){return{x:this.right,y:this.top}}get center_left(){return{x:this.left,y:this.vcenter}}get center_center(){return{x:this.hcenter,y:this.vcenter}}get center_right(){return{x:this.right,y:this.vcenter}}get bottom_left(){return{x:this.left,y:this.bottom}}get bottom_center(){return{x:this.hcenter,y:this.bottom}}get bottom_right(){return{x:this.right,y:this.bottom}}get center(){return{x:this.hcenter,y:this.vcenter}}round(){return new _({x0:y(this.x0),x1:y(this.x1),y0:y(this.y0),y1:y(this.y1)})}relative(){const{width:t,height:e}=this;return new _({x:0,y:0,width:t,height:e})}translate(t,e){const{x:r,y:i,width:n,height:s}=this;return new _({x:t+r,y:e+i,width:n,height:s})}scale(t){return new _({x0:this.x0*t,x1:this.x1*t,y0:this.y0*t,y1:this.y1*t})}relativize(t,e){return[t-this.x,e-this.y]}contains(t,e){return this.x0<=t&&t<=this.x1&&this.y0<=e&&e<=this.y1}clip(t,e){return tthis.x1&&(t=this.x1),ethis.y1&&(e=this.y1),[t,e]}grow_by(t){return new _({left:this.left-t,right:this.right+t,top:this.top-t,bottom:this.bottom+t})}shrink_by(t){return new _({left:this.left+t,right:this.right-t,top:this.top+t,bottom:this.bottom-t},!0)}union(t){return new _({x0:u(this.x0,t.x0),y0:u(this.y0,t.y0),x1:x(this.x1,t.x1),y1:x(this.y1,t.y1)})}intersection(t){return this.intersects(t)?new _({x0:x(this.x0,t.x0),y0:x(this.y0,t.y0),x1:u(this.x1,t.x1),y1:u(this.y1,t.y1)}):null}intersects(t){return!(t.x1this.x1||t.y1this.y1)}get x_screen(){const t=this;return this._x_screen??(this._x_screen={compute:e=>t.left+e,invert:e=>e-t.left,v_compute(e){const{left:r}=t;return new s.ScreenArray((0,o.map)(e,(t=>r+t)))},v_invert(e){const{left:r}=t;return(0,o.map)(e,(t=>t-r))},get source_range(){return t.x_range},get target_range(){return t.x_range}})}get y_screen(){const t=this;return this._y_screen??(this._y_screen={compute:e=>t.top+e,invert:e=>e-t.top,v_compute(e){const{top:r}=t;return new s.ScreenArray((0,o.map)(e,(t=>r+t)))},v_invert(e){const{top:r}=t;return(0,o.map)(e,(t=>t-r))},get source_range(){return t.y_range},get target_range(){return t.y_range}})}get x_view(){const t=this;return this._x_view??(this._x_view={compute:e=>t.left+e,invert:e=>e-t.left,v_compute(e){const{left:r}=t;return new s.ScreenArray((0,o.map)(e,(t=>r+t)))},v_invert(e){const{left:r}=t;return(0,o.map)(e,(t=>t-r))},get source_range(){return t.x_range},get target_range(){return t.x_range}})}get y_view(){const t=this;return this._y_view??(this._y_view={compute:e=>t.bottom-e,invert:e=>t.bottom-e,v_compute(e){const{bottom:r}=t;return new s.ScreenArray((0,o.map)(e,(t=>r-t)))},v_invert(e){const{bottom:r}=t;return(0,o.map)(e,(t=>r-t))},get source_range(){return t.y_range},get target_range(){return{start:t.bottom,end:t.top}}})}get xview(){return this.x_view}get yview(){return this.y_view}}r.BBox=_,_.__name__=\"BBox\"},\n function _(n,t,e,o,r){o();const s=n(19),i=n(9),c=n(8),l=document.createElement(\"div\").style;function f(n){if(n.startsWith(\"--\"))return n;const t=n.replaceAll(/_/g,\"-\").replaceAll(/[A-Z]/g,(n=>`-${n.toLowerCase()}`));if(t in l)return t;const e=`-webkit-${t}`;if(e in l)return e;const o=`-moz-${t}`;return o in l?o:(s.logger.warn(`unknown CSS property '${n}'`),null)}function*u(n){if((0,c.isPlainObject)(n)||n instanceof Map)for(const[t,e]of(0,i.entries)(n)){const n=f(t);null!=n&&(yield[n,e])}else for(const t of n.own_properties())if(t.dirty){const n=f(t.attr);null!=n&&(yield[n,t.get_value()])}}e.apply_styles=function(n,t){for(const[e,o]of u(t))(0,c.isString)(o)?n.setProperty(e,o):n.removeProperty(e)},e.compose_stylesheet=function(n){const t=[];for(const[e,o]of(0,i.entries)(n)){t.push(`${e} {`);for(const[n,e]of u(o))(0,c.isString)(e)&&0!=e.length&&t.push(` ${n}: ${e};`);t.push(\"}\")}return t.join(\"\\n\")}},\n function _(e,o,i,n,t){n(),i.default=\":host{--base-font:var(--bokeh-base-font, Helvetica, Arial, sans-serif);--mono-font:var(--bokeh-mono-font, monospace);--font-size:var(--bokeh-font-size, 12px);--line-height:calc(20 / 14);--line-height-computed:calc(var(--font-size) * var(--line-height));--border-radius:4px;--padding-vertical:6px;--padding-horizontal:12px;--bokeh-top-level:10000;}:host{box-sizing:border-box;font-family:var(--base-font);font-size:var(--font-size);line-height:var(--line-height);}*,*:before,*:after{box-sizing:inherit;font-family:inherit;}pre,code{font-family:var(--mono-font);margin:0;}\"},\n function _(e,t,n,o,s){o();const a=e(68),r=e(19),l=e(54);n._get_ws_url=function(e,t){let n;void 0===t&&function(e){if(null===e)return!1;if(\"IFRAME\"===e.tagName.toUpperCase())return!0;return!1}(frameElement)&&void 0!==frameElement.dataset.absoluteUrl&&(t=frameElement.dataset.absoluteUrl),null!=t?(n=document.createElement(\"a\"),n.href=t):n=window.location;const o=\"https:\"==n.protocol?\"wss:\":\"ws:\";return null!=e?\"/\"==e&&(e=\"\"):e=n.pathname.replace(/\\/+$/,\"\"),`${o}//${n.host}${e}/ws`};const c=new Map;n.add_document_from_session=async function(e,t,n,o=[],s=!1){const i=window.location.search.substring(1);let u;try{u=await function(e,t,n){const o=(0,a.parse_token)(t).session_id;c.has(e)||c.set(e,new Map);const s=c.get(e);return s.has(o)||s.set(o,(0,a.pull_session)(e,t,n)),s.get(o)}(e,t,i)}catch(e){const n=(0,a.parse_token)(t).session_id;throw r.logger.error(`Failed to load Bokeh session ${n}: ${e}`),e}return(0,l.add_document_standalone)(u.document,n,o,s)}},\n function _(e,s,n,t,o){t();const r=e(19),i=e(5),l=e(69),c=e(70),_=e(71);n.DEFAULT_SERVER_WEBSOCKET_URL=\"ws://localhost:5006/ws\",n.DEFAULT_TOKEN=\"eyJzZXNzaW9uX2lkIjogImRlZmF1bHQifQ\";let h=0;function a(e){let s=e.split(\".\")[0];const n=s.length%4;return 0!=n&&(s+=\"=\".repeat(4-n)),JSON.parse(atob(s.replace(/_/g,\"/\").replace(/-/g,\"+\")))}n.parse_token=a;class d{constructor(e=n.DEFAULT_SERVER_WEBSOCKET_URL,s=n.DEFAULT_TOKEN,t=null){this._number=h++,this.socket=null,this.session=null,this.closed_permanently=!1,this._current_handler=null,this._pending_replies=new Map,this._pending_messages=[],this._receiver=new c.Receiver,this.url=e,this.token=s,this.args_string=t,this.id=a(s).session_id.split(\".\")[0],r.logger.debug(`Creating websocket ${this._number} to '${this.url}' session '${this.id}'`)}async connect(){if(this.closed_permanently)throw new Error(\"Cannot connect() a closed ClientConnection\");if(null!=this.socket)throw new Error(\"Already connected\");this._current_handler=null,this._pending_replies.clear(),this._pending_messages=[];try{let e=`${this.url}`;return null!=this.args_string&&this.args_string.length>0&&(e+=`?${this.args_string}`),this.socket=new WebSocket(e,[\"bokeh\",this.token]),new Promise(((e,s)=>{this.socket.binaryType=\"arraybuffer\",this.socket.onopen=()=>this._on_open(e,s),this.socket.onmessage=e=>this._on_message(e),this.socket.onclose=e=>this._on_close(e,s),this.socket.onerror=()=>this._on_error(s)}))}catch(e){throw r.logger.error(`websocket creation failed to url: ${this.url}`),r.logger.error(` - ${e}`),e}}close(){this.closed_permanently||(r.logger.debug(`Permanently closing websocket connection ${this._number}`),this.closed_permanently=!0,null!=this.socket&&this.socket.close(1e3,`close method called on ClientConnection ${this._number}`),this.session._connection_closed())}_schedule_reconnect(e){setTimeout((()=>{this.closed_permanently||(r.logger.info(`Websocket connection ${this._number} disconnected, will not attempt to reconnect`),this.session?.notify_connection_lost())}),e)}send(e){null!=this.socket?e.send(this.socket):r.logger.error(\"not connected so cannot send\",e)}async send_with_reply(e){const s=await new Promise(((s,n)=>{this._pending_replies.set(e.msgid(),{resolve:s,reject:n}),this.send(e)}));if(\"ERROR\"==s.msgtype())throw new Error(`Error reply ${s.content.text}`);return s}async _pull_doc_json(){const e=l.Message.create(\"PULL-DOC-REQ\",{},{}),s=await this.send_with_reply(e);if(!(\"doc\"in s.content))throw new Error(\"No 'doc' field in PULL-DOC-REPLY\");return s.content.doc}async _repull_session_doc(e,s){r.logger.debug(null!=this.session?\"Repulling session\":\"Pulling session for first time\");try{const n=await this._pull_doc_json();if(null==this.session)if(this.closed_permanently)r.logger.debug(\"Got new document after connection was already closed\"),s(new Error(\"The connection has been closed\"));else{const s=[],t=i.Document.from_json(n,s);this.session=new _.ClientSession(this,t);for(const e of s)t._trigger_on_change(e);for(const e of this._pending_messages)this.session.handle(e);this._pending_messages=[],r.logger.debug(\"Created a new session from new pulled doc\"),e(this.session)}else this.session.document.replace_with_json(n),r.logger.debug(\"Updated existing session with new pulled doc\")}catch(e){console.trace(e),r.logger.error(`Failed to repull session ${e}`),s(e instanceof Error?e:`${e}`)}}_on_open(e,s){r.logger.info(`Websocket connection ${this._number} is now open`),this._current_handler=n=>{this._awaiting_ack_handler(n,e,s)}}_on_message(e){null==this._current_handler&&r.logger.error(\"Got a message with no current handler set\");try{this._receiver.consume(e.data)}catch(e){this._close_bad_protocol(`${e}`)}const s=this._receiver.message;if(null!=s){const e=s.problem();null!=e&&this._close_bad_protocol(e),this._current_handler(s)}}_on_close(e,s){r.logger.info(`Lost websocket ${this._number} connection, ${e.code} (${e.reason})`),this.socket=null,this._pending_replies.forEach((e=>e.reject(\"Disconnected\"))),this._pending_replies.clear(),this.closed_permanently||this._schedule_reconnect(2e3),s(new Error(`Lost websocket connection, ${e.code} (${e.reason})`))}_on_error(e){r.logger.debug(`Websocket error on socket ${this._number}`);const s=\"Could not open websocket\";r.logger.error(`Failed to connect to Bokeh server: ${s}`),e(new Error(s))}_close_bad_protocol(e){r.logger.error(`Closing connection: ${e}`),null!=this.socket&&this.socket.close(1002,e)}_awaiting_ack_handler(e,s,n){\"ACK\"===e.msgtype()?(this._current_handler=e=>this._steady_state_handler(e),this._repull_session_doc(s,n)):this._close_bad_protocol(\"First message was not an ACK\")}_steady_state_handler(e){const s=e.reqid(),n=this._pending_replies.get(s);null!=n?(this._pending_replies.delete(s),n.resolve(e)):null!=this.session?this.session.handle(e):\"PATCH-DOC\"!=e.msgtype()&&this._pending_messages.push(e)}}n.ClientConnection=d,d.__name__=\"ClientConnection\",n.pull_session=function(e,s,n){return new d(e,s,n).connect()}},\n function _(e,s,t,r,n){r();const i=e(32),a=e(40),h=e(12);class f{get buffers(){return this._buffers}constructor(e,s,t){this._buffers=new Map,this.header=e,this.metadata=s,this.content=t}static assemble(e,s,t){const r=JSON.parse(e),n=JSON.parse(s),i=JSON.parse(t);return new f(r,n,i)}assemble_buffer(e,s){const t=this.header.num_buffers??0;if(t<=this._buffers.size)throw new Error(`too many buffers received, expecting ${t}`);const{id:r}=JSON.parse(e);this._buffers.set(r,s)}static create(e,s,t){const r=f.create_header(e);return new f(r,s,t)}static create_header(e){return{msgid:(0,a.unique_id)(),msgtype:e}}complete(){const{num_buffers:e}=this.header;return null==e||this._buffers.size==e}send(e){(0,h.assert)(null==this.header.num_buffers);const s=[],t=JSON.stringify(this.content,((e,t)=>{if(t instanceof i.Buffer){const e={id:`${s.length}`};return s.push([e,t.buffer]),e}return t})),r=s.length;r>0&&(this.header.num_buffers=r);const n=JSON.stringify(this.header),a=JSON.stringify(this.metadata);e.send(n),e.send(a),e.send(t);for(const[t,r]of s)e.send(JSON.stringify(t)),e.send(r)}msgid(){return this.header.msgid}msgtype(){return this.header.msgtype}reqid(){return this.header.reqid}problem(){return\"msgid\"in this.header?\"msgtype\"in this.header?null:\"No msgtype in header\":\"No msgid in header\"}}t.Message=f,f.__name__=\"Message\"},\n function _(t,e,s,_,r){_();const i=t(69),h=t(8),a=t(12);class n{constructor(){this.message=null,this._partial=null,this._fragments=[],this._buf_header=null,this._current_consumer=this._HEADER}consume(t){this._current_consumer(t)}_HEADER(t){this._assume_text(t),this.message=null,this._partial=null,this._fragments=[t],this._buf_header=null,this._current_consumer=this._METADATA}_METADATA(t){this._assume_text(t),this._fragments.push(t),this._current_consumer=this._CONTENT}_CONTENT(t){this._assume_text(t),this._fragments.push(t);const[e,s,_]=this._fragments;(0,a.assert)(null!=e&&null!=s&&null!=_),this._partial=i.Message.assemble(e,s,_),this._check_complete()}_BUFFER_HEADER(t){this._assume_text(t),this._buf_header=t,this._current_consumer=this._BUFFER_PAYLOAD}_BUFFER_PAYLOAD(t){this._assume_binary(t),(0,a.assert)(null!=this._partial&&null!=this._buf_header),this._partial.assemble_buffer(this._buf_header,t),this._check_complete()}_assume_text(t){if(!(0,h.isString)(t))throw new Error(\"Expected text fragment but received binary fragment\")}_assume_binary(t){if(!(t instanceof ArrayBuffer))throw new Error(\"Expected binary fragment but received text fragment\")}_check_complete(){this._partial.complete()?(this.message=this._partial,this._current_consumer=this._HEADER):this._current_consumer=this._BUFFER_HEADER}}s.Receiver=n,n.__name__=\"Receiver\"},\n function _(e,n,t,o,s){o();const c=e(5),i=e(53),_=e(69),r=e(19);class a{constructor(e,n){this._document_listener=e=>{this._document_changed(e)},this._connection=e,this.document=n,this.document.on_change(this._document_listener,!0)}get id(){return this._connection.id}handle(e){const n=e.msgtype();switch(n){case\"PATCH-DOC\":this._handle_patch(e);break;case\"OK\":this._handle_ok(e);break;case\"ERROR\":this._handle_error(e);break;default:r.logger.debug(`Doing nothing with message '${n}'`)}}notify_connection_lost(){this.document.event_manager.send_event(new i.ConnectionLost)}close(){this._connection.close()}_connection_closed(){this.document.remove_on_change(this._document_listener)}async request_server_info(){const e=_.Message.create(\"SERVER-INFO-REQ\",{},{});return(await this._connection.send_with_reply(e)).content}async force_roundtrip(){await this.request_server_info()}_document_changed(e){const n=(e instanceof c.DocumentEventBatch?e.sync?e.events:[]:[e]).filter((e=>e.sync));if(0==n.length)return;const t=this.document.create_json_patch(n),o=_.Message.create(\"PATCH-DOC\",{},t);this._connection.send(o)}_handle_patch(e){this.document.apply_json_patch(e.content,e.buffers)}_handle_ok(e){r.logger.trace(`Unhandled OK reply to ${e.reqid()}`)}_handle_error(e){r.logger.error(`Unhandled ERROR reply to ${e.reqid()}: ${e.content.text}`)}}t.ClientSession=a,a.__name__=\"ClientSession\"},\n function _(n,e,o,t,r){t();const i=n(63),l=n(8);function s(n){let e=(0,l.isString)(n)?document.getElementById(n):n;if(null==e)throw new Error(`Error rendering Bokeh model: could not find ${(0,l.isString)(n)?`#${n}`:n} HTML tag`);if(!(0,i.contains)(document.body,e))throw new Error(`Error rendering Bokeh model: element ${(0,l.isString)(n)?`#${n}`:n} must be under `);if(e instanceof HTMLElement&&\"SCRIPT\"==e.tagName){const n=(0,i.div)();e.replaceWith(n),e=n}return e}o._resolve_element=function(n){const{elementid:e}=n;return null!=e?s(e):document.body},o._resolve_root_elements=function(n){const e=[];if(null!=n.root_ids&&null!=n.roots)for(const o of n.root_ids)e.push(s(n.roots[o]));return e}},\n function _(e,o,t,n,r){n();const s=e(5),i=e(70),c=e(19),l=e(9),g=e(54),a=e(72);function f(e,o){o.buffers.length>0?e.consume(o.buffers[0].buffer):e.consume(o.content.data);const t=e.message;null!=t&&this.apply_json_patch(t.content,t.buffers)}function m(e,o){if(\"undefined\"!=typeof Jupyter&&null!=Jupyter.notebook.kernel){c.logger.info(`Registering Jupyter comms for target ${e}`);const t=Jupyter.notebook.kernel.comm_manager;try{t.register_target(e,(t=>{c.logger.info(`Registering Jupyter comms for target ${e}`);const n=new i.Receiver;t.on_msg(f.bind(o,n))}))}catch(e){c.logger.warn(`Jupyter comms failed to register. push_notebook() will not function. (exception reported: ${e})`)}}else if(o.roots()[0].id in t.kernels){c.logger.info(`Registering JupyterLab comms for target ${e}`);const n=t.kernels[o.roots()[0].id];try{n.registerCommTarget(e,(t=>{c.logger.info(`Registering JupyterLab comms for target ${e}`);const n=new i.Receiver;t.onMsg=f.bind(o,n)}))}catch(e){c.logger.warn(`Jupyter comms failed to register. push_notebook() will not function. (exception reported: ${e})`)}}else if(\"undefined\"!=typeof google&&null!=google.colab.kernel){c.logger.info(`Registering Google Colab comms for target ${e}`);const t=google.colab.kernel.comms;try{t.registerTarget(e,(async t=>{c.logger.info(`Registering Google Colab comms for target ${e}`);const n=new i.Receiver;for await(const e of t.messages){const t={data:e.data},r=[];for(const o of e.buffers??[])r.push(new DataView(o));const s={content:t,buffers:r};f.bind(o)(n,s)}}))}catch(e){c.logger.warn(`Google Colab comms failed to register. push_notebook() will not function. (exception reported: ${e})`)}}else console.warn(\"Jupyter notebooks comms not available. push_notebook() will not function. If running JupyterLab ensure the latest @bokeh/jupyter_bokeh extension is installed. In an exported notebook this warning is expected.\")}t.kernels={},t.embed_items_notebook=async function(e,o){if(1!=(0,l.size)(e))throw new Error(\"embed_items_notebook expects exactly one document in docs_json\");const t=s.Document.from_json((0,l.values)(e)[0]);for(const e of o){null!=e.notebook_comms_target&&m(e.notebook_comms_target,t);const o=(0,a._resolve_element)(e),n=(0,a._resolve_root_elements)(e);await(0,g.add_document_standalone)(t,o,n);for(const e of n)e instanceof HTMLElement&&e.removeAttribute(\"id\")}}},\n function _(t,_,o,r,n){r();const a=t(1);a.__exportStar(t(69),o),a.__exportStar(t(70),o)},\n function _(e,t,o,l,n){l(),o.safely=function(e,t=!1){try{return e()}catch(e){if(function(e){const t=document.createElement(\"div\");t.style.backgroundColor=\"#f2dede\",t.style.border=\"1px solid #a94442\",t.style.borderRadius=\"4px\",t.style.display=\"inline-block\",t.style.fontFamily=\"sans-serif\",t.style.marginTop=\"5px\",t.style.minWidth=\"200px\",t.style.padding=\"5px 5px 5px 10px\",t.classList.add(\"bokeh-error-box-into-flames\");const o=document.createElement(\"span\");o.style.backgroundColor=\"#a94442\",o.style.borderRadius=\"0px 4px 0px 0px\",o.style.color=\"white\",o.style.cursor=\"pointer\",o.style.cssFloat=\"right\",o.style.fontSize=\"0.8em\",o.style.margin=\"-6px -6px 0px 0px\",o.style.padding=\"2px 5px 4px 5px\",o.title=\"close\",o.setAttribute(\"aria-label\",\"close\"),o.appendChild(document.createTextNode(\"x\")),o.addEventListener(\"click\",(()=>s.removeChild(t)));const l=document.createElement(\"h3\");l.style.color=\"#a94442\",l.style.margin=\"8px 0px 0px 0px\",l.style.padding=\"0px\",l.appendChild(document.createTextNode(\"Bokeh Error\"));const n=document.createElement(\"pre\");n.style.whiteSpace=\"unset\",n.style.overflowX=\"auto\",n.appendChild(document.createTextNode(e)),t.appendChild(o),t.appendChild(l),t.appendChild(n);const s=document.getElementsByTagName(\"body\")[0];s.insertBefore(t,s.firstChild)}(e instanceof Error&&null!=e.stack?e.stack:`${e}`),t)return;throw e}}},\n function _(t,r,o,_,e){_();const s=t(1),i=t(7),m=s.__importStar(t(77));(0,i.register_models)(m);const n=s.__importStar(t(532));(0,i.register_models)(n)},\n function _(t,_,r,o,a){o();const e=t(1);e.__exportStar(t(78),r),e.__exportStar(t(265),r),e.__exportStar(t(313),r),e.__exportStar(t(323),r),e.__exportStar(t(328),r),e.__exportStar(t(329),r),e.__exportStar(t(338),r),e.__exportStar(t(241),r),e.__exportStar(t(347),r),e.__exportStar(t(389),r),e.__exportStar(t(390),r),e.__exportStar(t(394),r),e.__exportStar(t(396),r),e.__exportStar(t(270),r),e.__exportStar(t(419),r),e.__exportStar(t(421),r),e.__exportStar(t(422),r),e.__exportStar(t(431),r),e.__exportStar(t(447),r),e.__exportStar(t(448),r),e.__exportStar(t(255),r),e.__exportStar(t(450),r),e.__exportStar(t(253),r),e.__exportStar(t(453),r),e.__exportStar(t(454),r),e.__exportStar(t(459),r),e.__exportStar(t(200),r),e.__exportStar(t(464),r),e.__exportStar(t(473),r),e.__exportStar(t(477),r),e.__exportStar(t(487),r)},\n function _(e,a,o,n,r){n();const t=e(1);r(\"AreaVisuals\",e(79).AreaVisuals),r(\"Annotation\",e(81).Annotation),r(\"Arrow\",e(126).Arrow),r(\"ArrowHead\",e(168).ArrowHead),r(\"OpenHead\",e(168).OpenHead),r(\"NormalHead\",e(168).NormalHead),r(\"TeeHead\",e(168).TeeHead),r(\"VeeHead\",e(168).VeeHead),r(\"BaseColorBar\",e(170).BaseColorBar),r(\"Band\",e(260).Band);var l=e(262);r(\"BoxAnnotation\",l.BoxAnnotation),r(\"BoxInteractionHandles\",l.BoxInteractionHandles),r(\"ColorBar\",e(264).ColorBar),r(\"ContourColorBar\",e(280).ContourColorBar),r(\"Label\",e(281).Label),r(\"LabelSet\",e(282).LabelSet),r(\"Legend\",e(283).Legend),r(\"LegendItem\",e(284).LegendItem),r(\"PolyAnnotation\",e(285).PolyAnnotation),r(\"ScaleBar\",e(286).ScaleBar);var i=e(287);r(\"Metric\",i.Metric),r(\"ReciprocalMetric\",i.ReciprocalMetric),r(\"MetricLength\",i.MetricLength),r(\"ReciprocalMetricLength\",i.ReciprocalMetricLength),r(\"ImperialLength\",i.ImperialLength),r(\"Angular\",i.Angular),r(\"Slope\",e(288).Slope),r(\"Span\",e(289).Span),r(\"TextAnnotation\",e(172).TextAnnotation),r(\"Title\",e(171).Title),r(\"ToolbarPanel\",e(290).ToolbarPanel),r(\"Whisker\",e(307).Whisker),t.__exportStar(e(308),o)},\n function _(e,r,n,s,o){var a;s();const i=e(1),l=e(51),t=i.__importStar(e(80));class _ extends l.Model{constructor(e){super(e)}clone(e){return super.clone(e)}}n.AreaVisuals=_,a=_,_.__name__=\"AreaVisuals\",a.mixins([t.Line,t.Fill,t.Hatch,[\"hover_\",t.Line],[\"hover_\",t.Fill],[\"hover_\",t.Hatch]])},\n function _(l,e,a,t,o){t();const c=l(1),r=c.__importStar(l(18)),n=l(20),_=c.__importStar(l(21)),i=l(9),h=l(8);a.Line={line_color:[_.Nullable(_.Color),\"black\"],line_alpha:[_.Alpha,1],line_width:[_.Float,1],line_join:[n.LineJoin,\"bevel\"],line_cap:[n.LineCap,\"butt\"],line_dash:[_.Or(n.LineDash,_.Array(_.Float)),[]],line_dash_offset:[_.Float,0]},a.Fill={fill_color:[_.Nullable(_.Color),\"gray\"],fill_alpha:[_.Alpha,1]},a.Image={global_alpha:[_.Alpha,1]},a.Hatch={hatch_color:[_.Nullable(_.Color),\"black\"],hatch_alpha:[_.Alpha,1],hatch_scale:[_.Float,12],hatch_pattern:[_.Nullable(_.Or(n.HatchPatternType,_.Str)),null],hatch_weight:[_.Float,1],hatch_extra:[_.Dict(_.AnyRef()),{}]},a.Text={text_color:[_.Nullable(_.Color),\"#444444\"],text_outline_color:[_.Nullable(_.Color),null],text_alpha:[_.Alpha,1],text_font:[r.Font,\"helvetica\"],text_font_size:[_.FontSize,\"16px\"],text_font_style:[n.FontStyle,\"normal\"],text_align:[n.TextAlign,\"left\"],text_baseline:[n.TextBaseline,\"bottom\"],text_line_height:[_.Float,1.2]},a.LineScalar={line_color:[r.ColorScalar,\"black\"],line_alpha:[r.NumberScalar,1],line_width:[r.NumberScalar,1],line_join:[r.LineJoinScalar,\"bevel\"],line_cap:[r.LineCapScalar,\"butt\"],line_dash:[r.LineDashScalar,[]],line_dash_offset:[r.NumberScalar,0]},a.FillScalar={fill_color:[r.ColorScalar,\"gray\"],fill_alpha:[r.NumberScalar,1]},a.ImageScalar={global_alpha:[r.NumberScalar,1]},a.HatchScalar={hatch_color:[r.ColorScalar,\"black\"],hatch_alpha:[r.NumberScalar,1],hatch_scale:[r.NumberScalar,12],hatch_pattern:[r.NullStringScalar,null],hatch_weight:[r.NumberScalar,1],hatch_extra:[r.DictScalar,{}]},a.TextScalar={text_color:[r.ColorScalar,\"#444444\"],text_outline_color:[r.ColorScalar,null],text_alpha:[r.NumberScalar,1],text_font:[r.FontScalar,\"helvetica\"],text_font_size:[r.FontSizeScalar,\"16px\"],text_font_style:[r.FontStyleScalar,\"normal\"],text_align:[r.TextAlignScalar,\"left\"],text_baseline:[r.TextBaselineScalar,\"bottom\"],text_line_height:[r.NumberScalar,1.2]},a.LineVector={line_color:[r.ColorSpec,\"black\"],line_alpha:[r.NumberSpec,1],line_width:[r.NumberSpec,1],line_join:[r.LineJoinSpec,\"bevel\"],line_cap:[r.LineCapSpec,\"butt\"],line_dash:[r.LineDashSpec,[]],line_dash_offset:[r.NumberSpec,0]},a.FillVector={fill_color:[r.ColorSpec,\"gray\"],fill_alpha:[r.NumberSpec,1]},a.ImageVector={global_alpha:[r.NumberSpec,1]},a.HatchVector={hatch_color:[r.ColorSpec,\"black\"],hatch_alpha:[r.NumberSpec,1],hatch_scale:[r.NumberSpec,12],hatch_pattern:[r.NullStringSpec,null],hatch_weight:[r.NumberSpec,1],hatch_extra:[r.DictScalar,{}]},a.TextVector={text_color:[r.ColorSpec,\"#444444\"],text_outline_color:[r.ColorSpec,null],text_alpha:[r.NumberSpec,1],text_font:[r.FontSpec,\"helvetica\"],text_font_size:[r.FontSizeSpec,\"16px\"],text_font_style:[r.FontStyleSpec,\"normal\"],text_align:[r.TextAlignSpec,\"left\"],text_baseline:[r.TextBaselineSpec,\"bottom\"],text_line_height:[r.NumberSpec,1.2]},a.attrs_of=function(l,e,a,t=!1){const o={};for(const c of(0,i.keys)(a)){const a=`${e}${c}`,r=l[a];o[(0,h.isString)(t)?`${t}${c}`:t?a:c]=r}return o}},\n function _(t,e,n,i,o){var s;i();const r=t(82);class l extends r.CompositeRendererView{get bbox(){return super.bbox??this.layout?.bbox}get_size(){if(this.displayed){const{width:t,height:e}=this._get_size();return{width:Math.round(t),height:Math.round(e)}}return{width:0,height:0}}_get_size(){throw new Error(\"not implemented\")}connect_signals(){super.connect_signals();const t=this.model.properties;this.on_change(t.visible,(()=>{null!=this.layout&&(this.layout.visible=this.model.visible,this.plot_view.request_layout())}))}get needs_clip(){return null==this.layout}}n.AnnotationView=l,l.__name__=\"AnnotationView\";class h extends r.CompositeRenderer{constructor(t){super(t)}}n.Annotation=h,s=h,h.__name__=\"Annotation\",s.override({level:\"annotation\"})},\n function _(e,t,s,r,i){var n;r();const _=e(83),d=e(111),o=e(125),l=e(8),a=e(56),h=e(21),m=(0,h.Or)((0,h.Ref)(d.UIElement),(0,h.Ref)(o.DOMNode));class u extends _.RendererView{constructor(){super(...arguments),this._renderer_views=new Map,this._element_views=new Map,this._computed_renderers=[],this._computed_elements=[],this._has_rendered_elements=!1}get renderer_views(){return this.model.renderers.map((e=>this._renderer_views.get(e)))}get element_views(){return this.model.elements.map((e=>this._element_views.get(e)))}*children(){yield*super.children(),yield*this.renderer_views,yield*this.element_views}async lazy_initialize(){await super.lazy_initialize(),await this._build_renderers(),await this._build_elements()}get computed_renderers(){return[...this.model.renderers,...this._computed_renderers]}get computed_renderer_views(){return this.computed_renderers.map((e=>this._renderer_views.get(e))).filter(l.non_null)}async _build_renderers(){return await(0,a.build_views)(this._renderer_views,this.computed_renderers,{parent:this.plot_view})}get computed_elements(){return[...this.model.elements,...this._computed_elements]}get computed_element_views(){return this.computed_elements.map((e=>this._element_views.get(e))).filter(l.non_null)}async _build_elements(){return await(0,a.build_views)(this._element_views,this.computed_elements,{parent:this.plot_view})}async _update_renderers(){await this._build_renderers()}async _update_elements(){const{created:e}=await this._build_elements(),t=new Set(e);for(const e of this.element_views)e.el.remove();for(const e of this.element_views){const s=t.has(e),r=e.rendering_target()??this.shadow_el;s?e.render_to(r):r.append(e.el)}this.r_after_render()}remove(){(0,a.remove_views)(this._renderer_views),(0,a.remove_views)(this._element_views),super.remove()}connect_signals(){super.connect_signals();const{renderers:e,elements:t}=this.model.properties;this.on_change(e,(async()=>{await this._update_renderers()})),this.on_change(t,(async()=>{await this._update_elements()}))}paint(){if(!this._has_rendered_elements){for(const e of this.element_views){const t=e.rendering_target()??this.shadow_el;e.render_to(t)}this._has_rendered_elements=!0}if(super.paint(),this.displayed&&this.is_renderable)for(const e of this.computed_renderer_views)e.paint();const{displayed:e}=this;for(const t of this.element_views)t.reposition(e)}has_finished(){if(!super.has_finished())return!1;for(const e of this.renderer_views)if(!e.has_finished())return!1;for(const e of this.element_views)if(!e.has_finished())return!1;return!0}}s.CompositeRendererView=u,u.__name__=\"CompositeRendererView\";class c extends _.Renderer{constructor(e){super(e)}}s.CompositeRenderer=c,n=c,c.__name__=\"CompositeRenderer\",n.define((({List:e,Ref:t})=>({renderers:[e(t(_.Renderer)),[]],elements:[e(m),[]]})))},\n function _(e,t,i,n,s){var r;n();const o=e(1),l=e(84),a=e(56),_=o.__importStar(e(87)),u=e(20),h=e(8),p=e(12),c=e(95),d=e(110),m=e(124),v=e(63);class g extends l.StyledElementView{constructor(){super(...arguments),this.position=new v.InlineStyleSheet,this._context_menu=null,this._custom_coordinates=null}rendering_target(){return this.plot_view.canvas_view.underlays_el}get context_menu(){return this._context_menu}get coordinates(){const{_coordinates:e}=this;return null!=e?e:this._coordinates=this._initialize_coordinates()}set coordinates(e){this._custom_coordinates=e}stylesheets(){return[...super.stylesheets(),this.position]}initialize(){super.initialize(),this.visuals=new _.Visuals(this)}async lazy_initialize(){await super.lazy_initialize();const{context_menu:e}=this.model;null!=e&&(this._context_menu=await(0,a.build_view)(e,{parent:this.plot_view}))}remove(){this._context_menu?.remove(),super.remove()}connect_signals(){super.connect_signals();const{group:e}=this.model;null!=e&&this.on_change(e.properties.visible,(()=>{this.model.visible=e.visible}));const{x_range_name:t,y_range_name:i}=this.model.properties;this.on_change([t,i],(()=>delete this._coordinates)),this.connect(this.plot_view.frame.model.change,(()=>delete this._coordinates))}_initialize_coordinates(){if(null!=this._custom_coordinates)return this._custom_coordinates;const{coordinates:e}=this.model,{frame:t}=this.plot_view;if(null!=e)return e.get_transform(t);{const{x_range_name:e,y_range_name:i}=this.model,n=t.x_scales.get(e),s=t.y_scales.get(i);return(0,p.assert)(null!=n,`missing '${e}' range`),(0,p.assert)(null!=s,`missing '${i}' range`),new c.CoordinateTransform(n,s)}}get plot_view(){return this.parent}get plot_model(){return this.parent.model}get layer(){const{overlays:e,primary:t}=this.canvas;return\"overlay\"==this.model.level?e:t}get canvas(){return this.plot_view.canvas_view}request_paint(){this.plot_view.request_paint(this)}request_layout(){this.plot_view.request_layout()}notify_finished(){this.plot_view.notify_finished()}notify_finished_after_paint(){this.plot_view.notify_finished_after_paint()}get needs_clip(){return!1}get has_webgl(){return!1}get displayed(){return this.model.visible}get is_renderable(){return!0}paint(){this.update_geometry(),this.compute_geometry(),this.update_position(),this.displayed&&this.is_renderable&&this._paint(),this.mark_finished()}renderer_view(e){}update_geometry(){}compute_geometry(){}update_position(){const{bbox:e,position:t}=this;null!=e&&e.is_valid?t.replace(`\\n :host {\\n position: absolute;\\n left: ${e.left}px;\\n top: ${e.top}px;\\n width: ${e.width}px;\\n height: ${e.height}px;\\n }\\n `):t.replace(\"\\n :host {\\n display: none;\\n }\\n \")}resolve_frame(){return this.plot_view.frame}resolve_canvas(){return this.plot_view.canvas}resolve_plot(){return this.plot_view}resolve_symbol(e){const{bbox:t}=this;if(null==t)return{x:NaN,y:NaN};{const i=t.resolve(e.symbol),{offset:n}=e;if((0,h.isNumber)(i))return i+n;{const{x:e,y:t}=i;return{x:e+n,y:t+n}}}}get attribution(){return null}}i.RendererView=g,g.__name__=\"RendererView\";class y extends l.StyledElement{constructor(e){super(e)}}i.Renderer=y,r=y,y.__name__=\"Renderer\",r.define((({Bool:e,Str:t,Ref:i,Nullable:n})=>({group:[n(i(m.RendererGroup)),null],level:[u.RenderLevel,\"image\"],visible:[e,!0],x_range_name:[t,\"default\"],y_range_name:[t,\"default\"],coordinates:[n(i(c.CoordinateMapping)),null],propagate_hover:[e,!1],context_menu:[n(i(d.Menu)),null]})))},\n function _(e,s,t,l,i){var _;l();const n=e(51),y=e(59),a=e(85),c=e(86),r=e(57),h=e(65),o=e(63),d=e(9),S=e(8),p=e(21);t.StylesLike=(0,p.Or)((0,p.Dict)((0,p.Nullable)(p.Str)),(0,p.Ref)(a.Styles)),t.StyleSheets=(0,p.List)((0,p.Or)((0,p.Ref)(c.StyleSheet),p.Str,(0,p.Dict)(t.StylesLike))),t.CSSVariables=(0,p.Dict)((0,p.Ref)(y.Node));class u extends r.DOMComponentView{constructor(){super(...arguments),this.style=new o.InlineStyleSheet}connect_signals(){super.connect_signals();const{styles:e,css_classes:s,css_variables:t,stylesheets:l}=this.model.properties;this.on_change(e,(()=>this._update_styles())),this.on_change(s,(()=>this._update_css_classes())),this.on_transitive_change(t,(()=>this._update_css_variables())),this.on_change(l,(()=>this._update_stylesheets()))}render(){super.render(),this._apply_styles()}*_css_classes(){yield*super._css_classes(),yield*this.model.css_classes}*_css_variables(){yield*super._css_variables();for(const[e,s]of(0,d.entries)(this.model.css_variables)){const t=this.resolve_coordinate(s);(0,S.isNumber)(t)&&(yield[e,`${t}px`])}}*_stylesheets(){yield*super._stylesheets(),yield this.style,yield*this._computed_stylesheets()}*_computed_stylesheets(){for(const e of this.model.stylesheets)e instanceof c.StyleSheet?yield e.underlying():yield new o.InlineStyleSheet(e)}_apply_styles(){(0,h.apply_styles)(this.el.style,this.model.styles)}_update_styles(){this.el.removeAttribute(\"style\"),this._apply_styles()}}t.StyledElementView=u,u.__name__=\"StyledElementView\";class m extends n.Model{constructor(e){super(e)}}t.StyledElement=m,_=m,m.__name__=\"StyledElement\",_.define((({List:e,Str:s})=>({css_classes:[e(s),[]],css_variables:[t.CSSVariables,{}],styles:[t.StylesLike,{}],stylesheets:[t.StyleSheets,[]]})))},\n function _(l,n,u,_,e){var t;_();const o=l(51);class r extends o.Model{constructor(l){super(l)}}u.Styles=r,t=r,r.__name__=\"Styles\",t.define((({Str:l,Nullable:n})=>({align_content:[n(l),null],align_items:[n(l),null],align_self:[n(l),null],alignment_baseline:[n(l),null],all:[n(l),null],animation:[n(l),null],animation_delay:[n(l),null],animation_direction:[n(l),null],animation_duration:[n(l),null],animation_fill_mode:[n(l),null],animation_iteration_count:[n(l),null],animation_name:[n(l),null],animation_play_state:[n(l),null],animation_timing_function:[n(l),null],aspect_ratio:[n(l),null],backface_visibility:[n(l),null],background:[n(l),null],background_attachment:[n(l),null],background_clip:[n(l),null],background_color:[n(l),null],background_image:[n(l),null],background_origin:[n(l),null],background_position:[n(l),null],background_position_x:[n(l),null],background_position_y:[n(l),null],background_repeat:[n(l),null],background_size:[n(l),null],baseline_shift:[n(l),null],block_size:[n(l),null],border:[n(l),null],border_block_end:[n(l),null],border_block_end_color:[n(l),null],border_block_end_style:[n(l),null],border_block_end_width:[n(l),null],border_block_start:[n(l),null],border_block_start_color:[n(l),null],border_block_start_style:[n(l),null],border_block_start_width:[n(l),null],border_bottom:[n(l),null],border_bottom_color:[n(l),null],border_bottom_left_radius:[n(l),null],border_bottom_right_radius:[n(l),null],border_bottom_style:[n(l),null],border_bottom_width:[n(l),null],border_collapse:[n(l),null],border_color:[n(l),null],border_image:[n(l),null],border_image_outset:[n(l),null],border_image_repeat:[n(l),null],border_image_slice:[n(l),null],border_image_source:[n(l),null],border_image_width:[n(l),null],border_inline_end:[n(l),null],border_inline_end_color:[n(l),null],border_inline_end_style:[n(l),null],border_inline_end_width:[n(l),null],border_inline_start:[n(l),null],border_inline_start_color:[n(l),null],border_inline_start_style:[n(l),null],border_inline_start_width:[n(l),null],border_left:[n(l),null],border_left_color:[n(l),null],border_left_style:[n(l),null],border_left_width:[n(l),null],border_radius:[n(l),null],border_right:[n(l),null],border_right_color:[n(l),null],border_right_style:[n(l),null],border_right_width:[n(l),null],border_spacing:[n(l),null],border_style:[n(l),null],border_top:[n(l),null],border_top_color:[n(l),null],border_top_left_radius:[n(l),null],border_top_right_radius:[n(l),null],border_top_style:[n(l),null],border_top_width:[n(l),null],border_width:[n(l),null],bottom:[n(l),null],box_shadow:[n(l),null],box_sizing:[n(l),null],break_after:[n(l),null],break_before:[n(l),null],break_inside:[n(l),null],caption_side:[n(l),null],caret_color:[n(l),null],clear:[n(l),null],clip:[n(l),null],clip_path:[n(l),null],clip_rule:[n(l),null],color:[n(l),null],color_interpolation:[n(l),null],color_interpolation_filters:[n(l),null],column_count:[n(l),null],column_fill:[n(l),null],column_gap:[n(l),null],column_rule:[n(l),null],column_rule_color:[n(l),null],column_rule_style:[n(l),null],column_rule_width:[n(l),null],column_span:[n(l),null],column_width:[n(l),null],columns:[n(l),null],content:[n(l),null],counter_increment:[n(l),null],counter_reset:[n(l),null],cursor:[n(l),null],direction:[n(l),null],display:[n(l),null],dominant_baseline:[n(l),null],empty_cells:[n(l),null],fill:[n(l),null],fill_opacity:[n(l),null],fill_rule:[n(l),null],filter:[n(l),null],flex:[n(l),null],flex_basis:[n(l),null],flex_direction:[n(l),null],flex_flow:[n(l),null],flex_grow:[n(l),null],flex_shrink:[n(l),null],flex_wrap:[n(l),null],float:[n(l),null],flood_color:[n(l),null],flood_opacity:[n(l),null],font:[n(l),null],font_family:[n(l),null],font_feature_settings:[n(l),null],font_kerning:[n(l),null],font_size:[n(l),null],font_size_adjust:[n(l),null],font_stretch:[n(l),null],font_style:[n(l),null],font_synthesis:[n(l),null],font_variant:[n(l),null],font_variant_caps:[n(l),null],font_variant_east_asian:[n(l),null],font_variant_ligatures:[n(l),null],font_variant_numeric:[n(l),null],font_variant_position:[n(l),null],font_weight:[n(l),null],gap:[n(l),null],glyph_orientation_vertical:[n(l),null],grid:[n(l),null],grid_area:[n(l),null],grid_auto_columns:[n(l),null],grid_auto_flow:[n(l),null],grid_auto_rows:[n(l),null],grid_column:[n(l),null],grid_column_end:[n(l),null],grid_column_gap:[n(l),null],grid_column_start:[n(l),null],grid_gap:[n(l),null],grid_row:[n(l),null],grid_row_end:[n(l),null],grid_row_gap:[n(l),null],grid_row_start:[n(l),null],grid_template:[n(l),null],grid_template_areas:[n(l),null],grid_template_columns:[n(l),null],grid_template_rows:[n(l),null],height:[n(l),null],hyphens:[n(l),null],image_orientation:[n(l),null],image_rendering:[n(l),null],inline_size:[n(l),null],justify_content:[n(l),null],justify_items:[n(l),null],justify_self:[n(l),null],left:[n(l),null],letter_spacing:[n(l),null],lighting_color:[n(l),null],line_break:[n(l),null],line_height:[n(l),null],list_style:[n(l),null],list_style_image:[n(l),null],list_style_position:[n(l),null],list_style_type:[n(l),null],margin:[n(l),null],margin_block_end:[n(l),null],margin_block_start:[n(l),null],margin_bottom:[n(l),null],margin_inline_end:[n(l),null],margin_inline_start:[n(l),null],margin_left:[n(l),null],margin_right:[n(l),null],margin_top:[n(l),null],marker:[n(l),null],marker_end:[n(l),null],marker_mid:[n(l),null],marker_start:[n(l),null],mask:[n(l),null],mask_composite:[n(l),null],mask_image:[n(l),null],mask_position:[n(l),null],mask_repeat:[n(l),null],mask_size:[n(l),null],mask_type:[n(l),null],max_block_size:[n(l),null],max_height:[n(l),null],max_inline_size:[n(l),null],max_width:[n(l),null],min_block_size:[n(l),null],min_height:[n(l),null],min_inline_size:[n(l),null],min_width:[n(l),null],object_fit:[n(l),null],object_position:[n(l),null],opacity:[n(l),null],order:[n(l),null],orphans:[n(l),null],outline:[n(l),null],outline_color:[n(l),null],outline_offset:[n(l),null],outline_style:[n(l),null],outline_width:[n(l),null],overflow:[n(l),null],overflow_anchor:[n(l),null],overflow_wrap:[n(l),null],overflow_x:[n(l),null],overflow_y:[n(l),null],overscroll_behavior:[n(l),null],overscroll_behavior_block:[n(l),null],overscroll_behavior_inline:[n(l),null],overscroll_behavior_x:[n(l),null],overscroll_behavior_y:[n(l),null],padding:[n(l),null],padding_block_end:[n(l),null],padding_block_start:[n(l),null],padding_bottom:[n(l),null],padding_inline_end:[n(l),null],padding_inline_start:[n(l),null],padding_left:[n(l),null],padding_right:[n(l),null],padding_top:[n(l),null],page_break_after:[n(l),null],page_break_before:[n(l),null],page_break_inside:[n(l),null],paint_order:[n(l),null],perspective:[n(l),null],perspective_origin:[n(l),null],place_content:[n(l),null],place_items:[n(l),null],place_self:[n(l),null],pointer_events:[n(l),null],position:[n(l),null],quotes:[n(l),null],resize:[n(l),null],right:[n(l),null],rotate:[n(l),null],row_gap:[n(l),null],ruby_align:[n(l),null],ruby_position:[n(l),null],scale:[n(l),null],scroll_behavior:[n(l),null],shape_rendering:[n(l),null],stop_color:[n(l),null],stop_opacity:[n(l),null],stroke:[n(l),null],stroke_dasharray:[n(l),null],stroke_dashoffset:[n(l),null],stroke_linecap:[n(l),null],stroke_linejoin:[n(l),null],stroke_miterlimit:[n(l),null],stroke_opacity:[n(l),null],stroke_width:[n(l),null],tab_size:[n(l),null],table_layout:[n(l),null],text_align:[n(l),null],text_align_last:[n(l),null],text_anchor:[n(l),null],text_combine_upright:[n(l),null],text_decoration:[n(l),null],text_decoration_color:[n(l),null],text_decoration_line:[n(l),null],text_decoration_style:[n(l),null],text_emphasis:[n(l),null],text_emphasis_color:[n(l),null],text_emphasis_position:[n(l),null],text_emphasis_style:[n(l),null],text_indent:[n(l),null],text_justify:[n(l),null],text_orientation:[n(l),null],text_overflow:[n(l),null],text_rendering:[n(l),null],text_shadow:[n(l),null],text_transform:[n(l),null],text_underline_position:[n(l),null],top:[n(l),null],touch_action:[n(l),null],transform:[n(l),null],transform_box:[n(l),null],transform_origin:[n(l),null],transform_style:[n(l),null],transition:[n(l),null],transition_delay:[n(l),null],transition_duration:[n(l),null],transition_property:[n(l),null],transition_timing_function:[n(l),null],translate:[n(l),null],unicode_bidi:[n(l),null],user_select:[n(l),null],vertical_align:[n(l),null],visibility:[n(l),null],white_space:[n(l),null],widows:[n(l),null],width:[n(l),null],will_change:[n(l),null],word_break:[n(l),null],word_spacing:[n(l),null],word_wrap:[n(l),null],writing_mode:[n(l),null],z_index:[n(l),null]})))},\n function _(e,t,n,l,r){var s,i;l();const u=e(1),S=e(51),_=u.__importStar(e(63));class d extends S.Model{constructor(e){super(e)}}n.StyleSheet=d,d.__name__=\"StyleSheet\";class h extends d{constructor(e){super(e)}underlying(){return new _.InlineStyleSheet(this.css)}}n.InlineStyleSheet=h,s=h,h.__name__=\"InlineStyleSheet\",s.define((({Str:e})=>({css:[e]})));class o extends d{constructor(e){super(e)}underlying(){return new _.ImportedStyleSheet(this.url)}}n.ImportedStyleSheet=o,i=o,o.__name__=\"ImportedStyleSheet\",i.define((({Str:e})=>({url:[e]})));class y extends h{constructor(e){super(e),this._underlying=null}underlying(){return null==this._underlying&&(this._underlying=new _.GlobalInlineStyleSheet(this.css)),this._underlying}}n.GlobalInlineStyleSheet=y,y.__name__=\"GlobalInlineStyleSheet\";class c extends o{constructor(e){super(e),this._underlying=null}underlying(){return null==this._underlying&&(this._underlying=new _.GlobalImportedStyleSheet(this.url)),this._underlying}}n.GlobalImportedStyleSheet=c,c.__name__=\"GlobalImportedStyleSheet\"},\n function _(e,a,r,t,c){t();const n=e(1),l=e(88);c(\"Line\",l.Line),c(\"LineScalar\",l.LineScalar),c(\"LineVector\",l.LineVector);const s=e(90);c(\"Fill\",s.Fill),c(\"FillScalar\",s.FillScalar),c(\"FillVector\",s.FillVector);const i=e(91);c(\"Text\",i.Text),c(\"TextScalar\",i.TextScalar),c(\"TextVector\",i.TextVector);const o=e(92);c(\"Hatch\",o.Hatch),c(\"HatchScalar\",o.HatchScalar),c(\"HatchVector\",o.HatchVector);const u=e(94);c(\"Image\",u.Image),c(\"ImageScalar\",u.ImageScalar),c(\"ImageVector\",u.ImageVector);const V=n.__importStar(e(80)),S=e(89);c(\"VisualProperties\",S.VisualProperties),c(\"VisualUniforms\",S.VisualUniforms);class m{*[Symbol.iterator](){yield*this._visuals}constructor(e){this._visuals=[];for(const[a,r]of e.model._mixins){const t=(()=>{switch(r){case V.Line:return new l.Line(e,a);case V.LineScalar:return new l.LineScalar(e,a);case V.LineVector:return new l.LineVector(e,a);case V.Fill:return new s.Fill(e,a);case V.FillScalar:return new s.FillScalar(e,a);case V.FillVector:return new s.FillVector(e,a);case V.Text:return new i.Text(e,a);case V.TextScalar:return new i.TextScalar(e,a);case V.TextVector:return new i.TextVector(e,a);case V.Hatch:return new o.Hatch(e,a);case V.HatchScalar:return new o.HatchScalar(e,a);case V.HatchVector:return new o.HatchVector(e,a);case V.Image:return new u.Image(e,a);case V.ImageScalar:return new u.ImageScalar(e,a);case V.ImageVector:return new u.ImageVector(e,a);default:throw new Error(\"unknown visual\")}})();t instanceof S.VisualProperties&&t.update(),this._visuals.push(t),Object.defineProperty(this,a+t.type,{get:()=>t,configurable:!1,enumerable:!0})}}}r.Visuals=m,m.__name__=\"Visuals\"},\n function _(e,t,i,s,l){s();const n=e(1),a=e(89),_=n.__importStar(e(80)),h=e(20),o=e(22),r=e(8);function c(e){if((0,r.isArray)(e))return e;switch(e){case\"solid\":return[];case\"dashed\":return[6];case\"dotted\":return[2,4];case\"dotdash\":return[2,4,6,4];case\"dashdot\":return[6,4,2,4];default:return e.split(\" \").map(Number).filter(r.isInteger)}}i.resolve_line_dash=c;class u extends a.VisualProperties{get doit(){const e=this.get_line_color(),t=this.get_line_alpha(),i=this.get_line_width();return!(null==e||0==t||0==i)}apply(e){const{doit:t}=this;return t&&(this.set_value(e),e.stroke()),t}values(){return{color:this.get_line_color(),alpha:this.get_line_alpha(),width:this.get_line_width(),join:this.get_line_join(),cap:this.get_line_cap(),dash:this.get_line_dash(),offset:this.get_line_dash_offset()}}set_value(e){const t=this.get_line_color(),i=this.get_line_alpha();e.strokeStyle=(0,o.color2css)(t,i),e.lineWidth=this.get_line_width(),e.lineJoin=this.get_line_join(),e.lineCap=this.get_line_cap(),e.setLineDash(c(this.get_line_dash())),e.lineDashOffset=this.get_line_dash_offset()}get_line_color(){const e=this._get_css_value(\"line-color\");return\"\"!=e?e:this.line_color.get_value()}get_line_alpha(){const e=this._get_css_value(\"line-alpha\");if(\"\"!=e){const t=Number(e);if(isFinite(t))return t}return this.line_alpha.get_value()}get_line_width(){const e=this._get_css_value(\"line-width\");if(\"\"!=e){const t=Number(e);if(isFinite(t))return t}return this.line_width.get_value()}get_line_join(){const e=this._get_css_value(\"line-join\");return h.LineJoin.valid(e)?e:this.line_join.get_value()}get_line_cap(){const e=this._get_css_value(\"line-cap\");return h.LineCap.valid(e)?e:this.line_cap.get_value()}get_line_dash(){const e=this._get_css_value(\"line-dash\");return h.LineDash.valid(e)?e:this.line_dash.get_value()}get_line_dash_offset(){const e=this._get_css_value(\"line-dash-offset\");if(\"\"!=e){const t=Number(e);if(isFinite(t))return t}return this.line_dash_offset.get_value()}}i.Line=u,u.__name__=\"Line\";class d extends a.VisualUniforms{get doit(){const e=this.line_color.value,t=this.line_alpha.value,i=this.line_width.value;return!(0==e||0==t||0==i)}apply(e){const{doit:t}=this;return t&&(this.set_value(e),e.stroke()),t}values(){return{color:this.line_color.value,alpha:this.line_alpha.value,width:this.line_width.value,join:this.line_join.value,cap:this.line_cap.value,dash:this.line_dash.value,offset:this.line_dash_offset.value}}set_value(e){const t=this.line_color.value,i=this.line_alpha.value;e.strokeStyle=(0,o.color2css)(t,i),e.lineWidth=this.line_width.value,e.lineJoin=this.line_join.value,e.lineCap=this.line_cap.value,e.setLineDash(c(this.line_dash.value)),e.lineDashOffset=this.line_dash_offset.value}}i.LineScalar=d,d.__name__=\"LineScalar\";class g extends a.VisualUniforms{get doit(){const{line_color:e}=this;if(e.is_Scalar()&&0==e.value)return!1;const{line_alpha:t}=this;if(t.is_Scalar()&&0==t.value)return!1;const{line_width:i}=this;return!i.is_Scalar()||0!=i.value}v_doit(e){return 0!=this.line_color.get(e)&&(0!=this.line_alpha.get(e)&&0!=this.line_width.get(e))}apply(e,t){const i=this.v_doit(t);return i&&(this.set_vectorize(e,t),e.stroke()),i}values(e){return{color:this.line_color.get(e),alpha:this.line_alpha.get(e),width:this.line_width.get(e),join:this.line_join.get(e),cap:this.line_cap.get(e),dash:this.line_dash.get(e),offset:this.line_dash_offset.get(e)}}set_vectorize(e,t){const i=this.line_color.get(t),s=this.line_alpha.get(t),l=this.line_width.get(t),n=this.line_join.get(t),a=this.line_cap.get(t),_=this.line_dash.get(t),h=this.line_dash_offset.get(t);e.strokeStyle=(0,o.color2css)(i,s),e.lineWidth=l,e.lineJoin=n,e.lineCap=a,e.setLineDash(c(_)),e.lineDashOffset=h}}i.LineVector=g,g.__name__=\"LineVector\",u.prototype.type=\"line\",u.prototype.attrs=Object.keys(_.Line),d.prototype.type=\"line\",d.prototype.attrs=Object.keys(_.LineScalar),g.prototype.type=\"line\",g.prototype.attrs=Object.keys(_.LineVector)},\n function _(t,s,e,i,o){i();class r{*[Symbol.iterator](){yield*this._props}constructor(t,s=\"\"){this.obj=t,this.prefix=s,this.css_prefix=`--bk-${s.replaceAll(\"_\",\"-\")}`;const e=this;this._props=[];for(const i of this.attrs){const o=t.model.properties[s+i];o.change.connect((()=>this.update())),e[i]=o,this._props.push(o)}}update(){}_get_css_value(t){return getComputedStyle(this.obj.el).getPropertyValue(`${this.css_prefix}${t}`)}}e.VisualProperties=r,r.__name__=\"VisualProperties\";class p{*[Symbol.iterator](){for(const t of this.attrs)yield this.obj.model.properties[this.prefix+t]}constructor(t,s=\"\"){this.obj=t,this.prefix=s;for(const e of this.attrs)Object.defineProperty(this,e,{get:()=>t[s+e]})}update(){}}e.VisualUniforms=p,p.__name__=\"VisualUniforms\"},\n function _(l,t,i,e,s){e();const o=l(1),a=l(89),r=o.__importStar(l(80)),_=l(22);class c extends a.VisualProperties{get doit(){const l=this.get_fill_color(),t=this.get_fill_alpha();return!(null==l||0==t)}apply(l,t){const{doit:i}=this;return i&&(this.set_value(l),l.fill(t)),i}values(){return{color:this.get_fill_color(),alpha:this.get_fill_alpha()}}set_value(l){const t=this.get_fill_color(),i=this.get_fill_alpha();l.fillStyle=(0,_.color2css)(t,i)}get_fill_color(){const l=this._get_css_value(\"fill-color\");return\"\"!=l?l:this.fill_color.get_value()}get_fill_alpha(){const l=this._get_css_value(\"fill-alpha\");if(\"\"!=l){const t=Number(l);if(isFinite(t))return t}return this.fill_alpha.get_value()}}i.Fill=c,c.__name__=\"Fill\";class h extends a.VisualUniforms{get doit(){const l=this.fill_color.value,t=this.fill_alpha.value;return!(0==l||0==t)}apply(l,t){const{doit:i}=this;return i&&(this.set_value(l),l.fill(t)),i}values(){return{color:this.fill_color.value,alpha:this.fill_alpha.value}}set_value(l){const t=this.fill_color.value,i=this.fill_alpha.value;l.fillStyle=(0,_.color2css)(t,i)}}i.FillScalar=h,h.__name__=\"FillScalar\";class f extends a.VisualUniforms{get doit(){const{fill_color:l}=this;if(l.is_Scalar()&&0==l.value)return!1;const{fill_alpha:t}=this;return!t.is_Scalar()||0!=t.value}v_doit(l){return 0!=this.fill_color.get(l)&&0!=this.fill_alpha.get(l)}apply(l,t,i){const e=this.v_doit(t);return e&&(this.set_vectorize(l,t),l.fill(i)),e}values(l){return{color:this.fill_color.get(l),alpha:this.fill_alpha.get(l)}}set_vectorize(l,t){const i=this.fill_color.get(t),e=this.fill_alpha.get(t);l.fillStyle=(0,_.color2css)(i,e)}}i.FillVector=f,f.__name__=\"FillVector\",c.prototype.type=\"fill\",c.prototype.attrs=Object.keys(r.Fill),h.prototype.type=\"fill\",h.prototype.attrs=Object.keys(r.FillScalar),f.prototype.type=\"fill\",f.prototype.attrs=Object.keys(r.FillVector)},\n function _(t,e,s,_,i){_();const l=t(1),o=t(89),n=l.__importStar(t(80)),a=t(20),h=t(22),r=new Map;function x(t,e){const s=r.get(t);if(null==s){const s=new WeakSet([e]);r.set(t,s)}else{if(s.has(e))return;s.add(e)}const{fonts:_}=document;_.check(t)||_.load(t).then((()=>e.request_paint()))}class u extends o.VisualProperties{get doit(){const t=this.get_text_color(),e=this.get_text_alpha();return!(null==t||0==e)}update(){if(!this.doit)return;x(this.font_value(),this.obj)}values(){return{color:this.get_text_color(),outline_color:this.get_text_outline_color(),alpha:this.get_text_alpha(),font:this.get_text_font(),font_size:this.get_text_font_size(),font_style:this.get_text_font_style(),align:this.get_text_align(),baseline:this.get_text_baseline(),line_height:this.get_text_line_height()}}set_value(t){const e=this.get_text_color(),s=this.get_text_outline_color(),_=this.get_text_alpha();t.fillStyle=(0,h.color2css)(e,_),t.strokeStyle=(0,h.color2css)(s,_),t.font=this.font_value(),t.textAlign=this.get_text_align(),t.textBaseline=this.get_text_baseline()}font_value(){return`${this.get_text_font_style()} ${this.get_text_font_size()} ${this.get_text_font()}`}get_text_color(){const t=this._get_css_value(\"text-color\");return\"\"!=t?t:this.text_color.get_value()}get_text_outline_color(){const t=this._get_css_value(\"text-outline-color\");return\"\"!=t?t:this.text_outline_color.get_value()}get_text_alpha(){const t=this._get_css_value(\"text-alpha\");if(\"\"!=t){const e=Number(t);if(isFinite(e))return e}return this.text_alpha.get_value()}get_text_font(){const t=this._get_css_value(\"text-font\");return\"\"!=t?t:this.text_font.get_value()}get_text_font_size(){const t=this._get_css_value(\"text-font-size\");return\"\"!=t?t:this.text_font_size.get_value()}get_text_font_style(){const t=this._get_css_value(\"text-font-style\");return a.FontStyle.valid(t)?t:this.text_font_style.get_value()}get_text_align(){const t=this._get_css_value(\"text-align\");return a.TextAlign.valid(t)?t:this.text_align.get_value()}get_text_baseline(){const t=this._get_css_value(\"text-baseline\");return a.TextBaseline.valid(t)?t:this.text_baseline.get_value()}get_text_line_height(){const t=this._get_css_value(\"line-height\");if(\"\"!=t){const e=Number(t);if(isFinite(e))return e}return this.text_line_height.get_value()}}s.Text=u,u.__name__=\"Text\";class g extends o.VisualUniforms{get doit(){const t=this.text_color.value,e=this.text_alpha.value;return!(0==t||0==e)}update(){if(!this.doit)return;x(this.font_value(),this.obj)}values(){return{color:this.text_color.value,outline_color:this.text_outline_color.value,alpha:this.text_alpha.value,font:this.text_font.value,font_size:this.text_font_size.value,font_style:this.text_font_style.value,align:this.text_align.value,baseline:this.text_baseline.value,line_height:this.text_line_height.value}}set_value(t){const e=this.text_color.value,s=this.text_alpha.value,_=this.text_outline_color.value,i=this.font_value(),l=this.text_align.value,o=this.text_baseline.value;t.fillStyle=(0,h.color2css)(e,s),t.strokeStyle=(0,h.color2css)(_,s),t.font=i,t.textAlign=l,t.textBaseline=o}font_value(){return`${this.text_font_style.value} ${this.text_font_size.value} ${this.text_font.value}`}}s.TextScalar=g,g.__name__=\"TextScalar\";class c extends o.VisualUniforms{_assert_font(t){x(this.font_value(t),this.obj)}values(t){return this._assert_font(t),{color:this.text_color.get(t),outline_color:this.text_outline_color.get(t),alpha:this.text_alpha.get(t),font:this.text_font.get(t),font_size:this.text_font_size.get(t),font_style:this.text_font_style.get(t),align:this.text_align.get(t),baseline:this.text_baseline.get(t),line_height:this.text_line_height.get(t)}}get doit(){const{text_color:t}=this;if(t.is_Scalar()&&0==t.value)return!1;const{text_alpha:e}=this;return!e.is_Scalar()||0!=e.value}v_doit(t){return 0!=this.text_color.get(t)&&0!=this.text_alpha.get(t)}apply(t,e){const s=this.v_doit(e);return s&&this.set_vectorize(t,e),s}set_vectorize(t,e){this._assert_font(e);const s=this.text_color.get(e),_=this.text_outline_color.get(e),i=this.text_alpha.get(e),l=this.font_value(e),o=this.text_align.get(e),n=this.text_baseline.get(e);t.fillStyle=(0,h.color2css)(s,i),t.strokeStyle=(0,h.color2css)(_,i),t.font=l,t.textAlign=o,t.textBaseline=n}font_value(t){return`${this.text_font_style.get(t)} ${this.text_font_size.get(t)} ${this.text_font.get(t)}`}}s.TextVector=c,c.__name__=\"TextVector\",u.prototype.type=\"text\",u.prototype.attrs=Object.keys(n.Text),g.prototype.type=\"text\",g.prototype.attrs=Object.keys(n.TextScalar),c.prototype.type=\"text\",c.prototype.attrs=Object.keys(n.TextVector)},\n function _(t,e,a,h,i){h();const r=t(1),s=t(89),c=t(93),n=r.__importStar(t(18)),_=r.__importStar(t(80)),o=t(9);class l extends s.VisualProperties{constructor(){super(...arguments),this._update_iteration=0}update(){if(this._update_iteration++,this._hatch_image=null,!this.doit)return;const t=this.get_hatch_color(),e=this.get_hatch_alpha(),a=this.get_hatch_scale(),h=this.get_hatch_pattern(),i=this.get_hatch_weight(),r=t=>{this._hatch_image=t},s=(0,o.dict)(this.get_hatch_extra()).get(h);if(null!=s){const h=s.get_pattern(t,e,a,i);if(h instanceof Promise){const{_update_iteration:t}=this;h.then((e=>{this._update_iteration==t&&(r(e),this.obj.request_paint())}))}else r(h)}else{const s=this.obj.canvas.create_layer(),n=(0,c.get_pattern)(s,h,t,e,a,i);r(n)}}get doit(){const t=this.get_hatch_color(),e=this.get_hatch_alpha(),a=this.get_hatch_pattern();return!(null==t||0==e||\" \"==a||\"blank\"==a||null==a)}apply(t,e){const{doit:a}=this;return a&&(this.set_value(t),t.layer.undo_transform((()=>t.fill(e)))),a}set_value(t){const e=this.pattern(t);t.fillStyle=e??\"transparent\"}pattern(t){const e=this._hatch_image;return null==e?null:t.createPattern(e,this.repetition())}repetition(){const t=this.get_hatch_pattern(),e=(0,o.dict)(this.get_hatch_extra()).get(t);if(null==e)return\"repeat\";switch(e.repetition){case\"repeat\":return\"repeat\";case\"repeat_x\":return\"repeat-x\";case\"repeat_y\":return\"repeat-y\";case\"no_repeat\":return\"no-repeat\"}}get_hatch_color(){const t=this._get_css_value(\"hatch-color\");return\"\"!=t?t:this.hatch_color.get_value()}get_hatch_alpha(){const t=this._get_css_value(\"hatch-alpha\");if(\"\"!=t){const e=Number(t);if(isFinite(e))return e}return this.hatch_alpha.get_value()}get_hatch_scale(){const t=this._get_css_value(\"hatch-scale\");if(\"\"!=t){const e=Number(t);if(isFinite(e))return e}return this.hatch_scale.get_value()}get_hatch_pattern(){const t=this._get_css_value(\"hatch-pattern\");return\"\"!=t?\"none\"==t?null:t:this.hatch_pattern.get_value()}get_hatch_weight(){const t=this._get_css_value(\"hatch-weight\");if(\"\"!=t){const e=Number(t);if(isFinite(e))return e}return this.hatch_weight.get_value()}get_hatch_extra(){return this.hatch_extra.get_value()}}a.Hatch=l,l.__name__=\"Hatch\";class u extends s.VisualUniforms{constructor(){super(...arguments),this._static_doit=!1,this._update_iteration=0}_compute_static_doit(){const t=this.hatch_color.value,e=this.hatch_alpha.value,a=this.hatch_pattern.value;return!(0==t||0==e||\" \"==a||\"blank\"==a||null==a)}update(){this._update_iteration++;const t=this.hatch_color.length;if(this._hatch_image=new n.UniformScalar(null,t),this._static_doit=this._compute_static_doit(),!this._static_doit)return;const e=this.hatch_color.value,a=this.hatch_alpha.value,h=this.hatch_scale.value,i=this.hatch_pattern.value,r=this.hatch_weight.value,s=e=>{this._hatch_image=new n.UniformScalar(e,t)},_=(0,o.dict)(this.hatch_extra.value).get(i);if(null!=_){const t=_.get_pattern(e,a,h,r);if(t instanceof Promise){const{_update_iteration:e}=this;t.then((t=>{this._update_iteration==e&&(s(t),this.obj.request_paint())}))}else s(t)}else{const t=this.obj.canvas.create_layer(),n=(0,c.get_pattern)(t,i,e,a,h,r);s(n)}}get doit(){return this._static_doit}apply(t,e){const{doit:a}=this;return a&&(this.set_value(t),t.layer.undo_transform((()=>t.fill(e)))),a}set_value(t){t.fillStyle=this.pattern(t)??\"transparent\"}pattern(t){const e=this._hatch_image.value;return null==e?null:t.createPattern(e,this.repetition())}repetition(){const t=this.hatch_pattern.value;if(null!=t){const e=(0,o.dict)(this.hatch_extra.value).get(t);if(null!=e)switch(e.repetition){case\"repeat\":return\"repeat\";case\"repeat_x\":return\"repeat-x\";case\"repeat_y\":return\"repeat-y\";case\"no_repeat\":return\"no-repeat\"}}return\"repeat\"}}a.HatchScalar=u,u.__name__=\"HatchScalar\";class p extends s.VisualUniforms{constructor(){super(...arguments),this._static_doit=!1,this._update_iteration=0}_compute_static_doit(){const{hatch_color:t}=this;if(t.is_Scalar()&&0==t.value)return!1;const{hatch_alpha:e}=this;if(e.is_Scalar()&&0==e.value)return!1;const{hatch_pattern:a}=this;if(a.is_Scalar()){const t=a.value;if(\" \"==t||\"blank\"==t||null==t)return!1}return!0}update(){this._update_iteration++;const t=this.hatch_color.length;if(this._hatch_image=new n.UniformScalar(null,t),this._static_doit=this._compute_static_doit(),!this._static_doit)return;const e=(t,e,a,h,i,r)=>{const s=(0,o.dict)(this.hatch_extra.value).get(t);if(null!=s){const t=s.get_pattern(e,a,h,i);if(t instanceof Promise){const{_update_iteration:e}=this;t.then((t=>{this._update_iteration==e&&(r(t),this.obj.request_paint())}))}else r(t)}else{const s=this.obj.canvas.create_layer(),n=(0,c.get_pattern)(s,t,e,a,h,i);r(n)}};if(this.hatch_color.is_Scalar()&&this.hatch_alpha.is_Scalar()&&this.hatch_scale.is_Scalar()&&this.hatch_pattern.is_Scalar()&&this.hatch_weight.is_Scalar()){const a=this.hatch_color.value,h=this.hatch_alpha.value,i=this.hatch_scale.value;e(this.hatch_pattern.value,a,h,i,this.hatch_weight.value,(e=>{this._hatch_image=new n.UniformScalar(e,t)}))}else{const a=new Array(t);a.fill(null),this._hatch_image=new n.UniformVector(a);for(let h=0;h{a[h]=t}))}}}get doit(){return this._static_doit}v_doit(t){if(!this.doit)return!1;if(0==this.hatch_color.get(t))return!1;if(0==this.hatch_alpha.get(t))return!1;const e=this.hatch_pattern.get(t);return\" \"!=e&&\"blank\"!=e&&null!=e}apply(t,e,a){const h=this.v_doit(e);return h&&(this.set_vectorize(t,e),t.layer.undo_transform((()=>t.fill(a)))),h}set_vectorize(t,e){t.fillStyle=this.pattern(t,e)??\"transparent\"}pattern(t,e){const a=this._hatch_image.get(e);return null==a?null:t.createPattern(a,this.repetition(e))}repetition(t){const e=this.hatch_pattern.get(t);if(null!=e){const t=(0,o.dict)(this.hatch_extra.value).get(e);if(null!=t)switch(t.repetition){case\"repeat\":return\"repeat\";case\"repeat_x\":return\"repeat-x\";case\"repeat_y\":return\"repeat-y\";case\"no_repeat\":return\"no-repeat\"}}return\"repeat\"}}a.HatchVector=p,p.__name__=\"HatchVector\",l.prototype.type=\"hatch\",l.prototype.attrs=Object.keys(_.Hatch),u.prototype.type=\"hatch\",u.prototype.attrs=Object.keys(_.HatchScalar),p.prototype.type=\"hatch\",p.prototype.attrs=Object.keys(_.HatchVector)},\n function _(e,o,a,r,s){r();const i=e(19),n=e(22);function l(e,o,a){e.moveTo(0,a+.5),e.lineTo(o,a+.5),e.stroke()}function t(e,o,a){e.moveTo(a+.5,0),e.lineTo(a+.5,o),e.stroke()}function c(e,o){e.moveTo(0,o),e.lineTo(o,0),e.stroke(),e.moveTo(0,0),e.lineTo(o,o),e.stroke()}a.hatch_aliases={\" \":\"blank\",\".\":\"dot\",o:\"ring\",\"-\":\"horizontal_line\",\"|\":\"vertical_line\",\"+\":\"cross\",'\"':\"horizontal_dash\",\":\":\"vertical_dash\",\"@\":\"spiral\",\"/\":\"right_diagonal_line\",\"\\\\\":\"left_diagonal_line\",x:\"diagonal_cross\",\",\":\"right_diagonal_dash\",\"`\":\"left_diagonal_dash\",v:\"horizontal_wave\",\">\":\"vertical_wave\",\"*\":\"criss_cross\"},a.get_pattern=function(e,o,r,s,k,_){return e.resize(k,k),e.prepare(),function(e,o,r,s,k,_){const T=k,h=T/2,v=h/2,d=(0,n.color2css)(r,s);switch(e.strokeStyle=d,e.fillStyle=d,e.lineCap=\"square\",e.lineWidth=_,a.hatch_aliases[o]??o){case\"blank\":break;case\"dot\":e.arc(h,h,h/2,0,2*Math.PI,!0),e.fill();break;case\"ring\":e.arc(h,h,h/2,0,2*Math.PI,!0),e.stroke();break;case\"horizontal_line\":l(e,T,h);break;case\"vertical_line\":t(e,T,h);break;case\"cross\":l(e,T,h),t(e,T,h);break;case\"horizontal_dash\":l(e,h,h);break;case\"vertical_dash\":t(e,h,h);break;case\"spiral\":{const o=T/30;e.moveTo(h,h);for(let a=0;a<360;a++){const r=.1*a,s=h+o*r*Math.cos(r),i=h+o*r*Math.sin(r);e.lineTo(s,i)}e.stroke();break}case\"right_diagonal_line\":e.moveTo(.5-v,T),e.lineTo(v+.5,0),e.stroke(),e.moveTo(v+.5,T),e.lineTo(3*v+.5,0),e.stroke(),e.moveTo(3*v+.5,T),e.lineTo(5*v+.5,0),e.stroke();break;case\"left_diagonal_line\":e.moveTo(v+.5,T),e.lineTo(.5-v,0),e.stroke(),e.moveTo(3*v+.5,T),e.lineTo(v+.5,0),e.stroke(),e.moveTo(5*v+.5,T),e.lineTo(3*v+.5,0),e.stroke();break;case\"diagonal_cross\":c(e,T);break;case\"right_diagonal_dash\":e.moveTo(v+.5,3*v+.5),e.lineTo(3*v+.5,v+.5),e.stroke();break;case\"left_diagonal_dash\":e.moveTo(v+.5,v+.5),e.lineTo(3*v+.5,3*v+.5),e.stroke();break;case\"horizontal_wave\":e.moveTo(0,v),e.lineTo(h,3*v),e.lineTo(T,v),e.stroke();break;case\"vertical_wave\":e.moveTo(v,0),e.lineTo(3*v,h),e.lineTo(v,T),e.stroke();break;case\"criss_cross\":c(e,T),l(e,T,h),t(e,T,h);break;default:i.logger.warn(`unknown hatch pattern: ${o}`)}}(e.ctx,o,r,s,k,_),e.canvas}},\n function _(a,t,e,l,s){l();const o=a(1),r=a(89),p=o.__importStar(a(80));class _ extends r.VisualProperties{get doit(){return!(0==this.global_alpha.get_value())}apply(a){const{doit:t}=this;return t&&this.set_value(a),t}values(){return{global_alpha:this.global_alpha.get_value()}}set_value(a){const t=this.global_alpha.get_value();a.globalAlpha=t}}e.Image=_,_.__name__=\"Image\";class i extends r.VisualUniforms{get doit(){return!(0==this.global_alpha.value)}apply(a){const{doit:t}=this;return t&&this.set_value(a),t}values(){return{global_alpha:this.global_alpha.value}}set_value(a){const t=this.global_alpha.value;a.globalAlpha=t}}e.ImageScalar=i,i.__name__=\"ImageScalar\";class g extends r.VisualUniforms{get doit(){const{global_alpha:a}=this;return!a.is_Scalar()||0!=a.value}v_doit(a){return 0!=this.global_alpha.get(a)}apply(a,t){const e=this.v_doit(t);return e&&this.set_vectorize(a,t),e}values(a){return{alpha:this.global_alpha.get(a)}}set_vectorize(a,t){const e=this.global_alpha.get(t);a.globalAlpha=e}}e.ImageVector=g,g.__name__=\"ImageVector\",_.prototype.type=\"image\",_.prototype.attrs=Object.keys(p.Image),i.prototype.type=\"image\",i.prototype.attrs=Object.keys(p.ImageScalar),g.prototype.type=\"image\",g.prototype.attrs=Object.keys(p.ImageVector)},\n function _(e,a,s,t,r){var c;t();const n=e(51),_=e(96),o=e(101),i=e(103),g=e(104),l=e(105),u=e(98),h=e(106),y=e(109);class x{constructor(e,a){this.x_scale=e,this.y_scale=a,this.x_source=this.x_scale.source_range,this.y_source=this.y_scale.source_range,this.ranges=[this.x_source,this.y_source],this.scales=[this.x_scale,this.y_scale]}map_to_screen(e,a){return[this.x_scale.v_compute(e),this.y_scale.v_compute(a)]}map_from_screen(e,a){return[this.x_scale.v_invert(e),this.y_scale.v_invert(a)]}}s.CoordinateTransform=x,x.__name__=\"CoordinateTransform\";class p extends n.Model{constructor(e){super(e)}get x_ranges(){return new Map([[\"default\",this.x_source]])}get y_ranges(){return new Map([[\"default\",this.y_source]])}_get_scale(e,a,s){if(e instanceof y.FactorRange!=a instanceof g.CategoricalScale)throw new Error(`Range ${e.type} is incompatible is Scale ${a.type}`);a instanceof i.LogScale&&e instanceof h.DataRange1d&&(e.scale_hint=\"log\");const t=a.clone();return t.setv({source_range:e,target_range:s}),t}get_transform(e){const{x_source:a,x_scale:s,x_target:t}=this,r=this._get_scale(a,s,t),{y_source:c,y_scale:n,y_target:_}=this,o=this._get_scale(c,n,_),i=new l.CompositeScale({source_scale:r,source_range:r.source_range,target_scale:e.x_scale,target_range:e.x_target}),g=new l.CompositeScale({source_scale:o,source_range:o.source_range,target_scale:e.y_scale,target_range:e.y_target});return new x(i,g)}}s.CoordinateMapping=p,c=p,p.__name__=\"CoordinateMapping\",c.define((({Ref:e})=>({x_source:[e(u.Range),()=>new h.DataRange1d],y_source:[e(u.Range),()=>new h.DataRange1d],x_scale:[e(_.Scale),()=>new o.LinearScale],y_scale:[e(_.Scale),()=>new o.LinearScale],x_target:[e(u.Range)],y_target:[e(u.Range)]})))},\n function _(e,t,r,n,s){var _;n();const a=e(97),c=e(98),o=e(99),i=e(24);class u extends a.Transform{constructor(e){super(e)}compute(e){return this.s_compute(e)}v_compute(e){const t=new i.ScreenArray(e.length),{s_compute:r}=this;for(let n=0;n({source_range:[e(c.Range)],target_range:[e(o.Range1d)]})))},\n function _(n,s,o,r,c){r();const e=n(51);class t extends e.Model{constructor(n){super(n)}}o.Transform=t,t.__name__=\"Transform\"},\n function _(t,e,n,l,s){var a;l();const u=t(51),i=t(21),r=t(34),o=(0,i.Nullable)((0,i.Or)((0,i.Tuple)((0,i.Nullable)(i.Float),(0,i.Nullable)(i.Float)),i.Auto));class d extends u.Model{constructor(t){super(t),this.have_updated_interactively=!1,this.frames=new Set}get computed_bounds(){return this._computed_bounds}get is_reversed(){return this.start>this.end}get is_valid(){return isFinite(this.min)&&isFinite(this.max)}get span(){return Math.abs(this.end-this.start)}get linked_plots(){return new Set((0,r.map)(this.frames,(t=>t.parent)))}}n.Range=d,a=d,d.__name__=\"Range\",a.define((({Float:t,Nullable:e})=>({bounds:[o,null,{on_update(t,e){const[n,l]=\"auto\"==t||null==t?[null,null]:t;e._computed_bounds=[n??-1/0,l??1/0]}}],min_interval:[e(t),null],max_interval:[e(t),null]})))},\n function _(t,e,s,n,r){var a;n();const i=t(100);class _ extends i.NumericalRange{constructor(t){super(t)}_set_auto_bounds(){if(\"auto\"==this.bounds){const t=Math.min(this._reset_start,this._reset_end),e=Math.max(this._reset_start,this._reset_end);this._computed_bounds=[t,e]}}initialize(){super.initialize(),this._set_auto_bounds()}get min(){return Math.min(this.start,this.end)}get max(){return Math.max(this.start,this.end)}reset(){this._set_auto_bounds();const{_reset_start:t,_reset_end:e}=this;this.start!=t||this.end!=e?this.setv({start:t,end:e}):this.change.emit()}map(t){return new a({start:t(this.start),end:t(this.end)})}widen(t){let{start:e,end:s}=this;return this.is_reversed?(e+=t,s-=t):(e-=t,s+=t),new a({start:e,end:s})}}s.Range1d=_,a=_,_.__name__=\"Range1d\",a.define((({Float:t,Nullable:e})=>({reset_start:[e(t),null,{on_update(t,e){e._reset_start=t??e.start}}],reset_end:[e(t),null,{on_update(t,e){e._reset_end=t??e.end}}]}))),a.override({start:0,end:1})},\n function _(n,e,t,c,r){var o;c();const a=n(1),s=n(98),u=a.__importStar(n(18)),_=n(11);class m extends s.Range{constructor(n){super(n)}}t.NumericalRange=m,o=m,m.__name__=\"NumericalRange\",o.define((({Float:n})=>({start:[n,u.unset,{convert(n,e){const[t,c]=e.computed_bounds;return(0,_.clamp)(n,t,c)}}],end:[n,u.unset,{convert(n,e){const[t,c]=e.computed_bounds;return(0,_.clamp)(n,t,c)}}]})))},\n function _(t,e,n,r,s){r();const a=t(102);class _ extends a.ContinuousScale{constructor(t){super(t)}get s_compute(){const[t,e]=this._linear_compute_state();return n=>t*n+e}get s_invert(){const[t,e]=this._linear_compute_state();return n=>(n-e)/t}_linear_compute_state(){const t=this.source_range.start,e=this.source_range.end,n=this.target_range.start,r=(this.target_range.end-n)/(e-t);return[r,-r*t+n]}}n.LinearScale=_,_.__name__=\"LinearScale\"},\n function _(n,c,o,s,e){s();const t=n(96);class u extends t.Scale{constructor(n){super(n)}}o.ContinuousScale=u,u.__name__=\"ContinuousScale\"},\n function _(t,e,s,a,r){a();const o=t(102);class n extends o.ContinuousScale{constructor(t){super(t)}get s_compute(){const[t,e,s,a]=this._compute_state();return r=>{if(0==s)return 0;{const o=(Math.log(r)-a)/s;return isFinite(o)?o*t+e:NaN}}}get s_invert(){const[t,e,s,a]=this._compute_state();return r=>{const o=(r-e)/t;return Math.exp(s*o+a)}}_get_safe_factor(t,e){let s=t<0?0:t,a=e<0?0:e;if(s==a)if(0==s)[s,a]=[1,10];else{const t=Math.log10(s);s=10**Math.floor(t),a=Math.ceil(t)!=Math.floor(t)?10**Math.ceil(t):10**(Math.ceil(t)+1)}return[s,a]}_compute_state(){const t=this.source_range.start,e=this.source_range.end,s=this.target_range.start,a=this.target_range.end-s,[r,o]=this._get_safe_factor(t,e);let n,c;0==r?(n=Math.log(o),c=0):(n=Math.log(o/r),c=Math.log(r));return[a,s,n,c]}}s.LogScale=n,n.__name__=\"LogScale\"},\n function _(t,e,c,a,s){a();const n=t(96),r=t(101),{_linear_compute_state:o}=r.LinearScale.prototype;class l extends n.Scale{constructor(t){super(t)}get s_compute(){const[t,e]=o.call(this),c=this.source_range;return a=>t*c.synthetic(a)+e}get s_invert(){const[t,e]=o.call(this);return c=>(c-e)/t}}c.CategoricalScale=l,l.__name__=\"CategoricalScale\"},\n function _(e,t,s,c,r){var n;c();const _=e(96),o=e(13);class a extends _.Scale{constructor(e){super(e)}get s_compute(){const e=this.source_scale.s_compute,t=this.target_scale.s_compute;return s=>t(e(s))}get s_invert(){const e=this.source_scale.s_invert,t=this.target_scale.s_invert;return s=>e(t(s))}compute(e){return this.s_compute(e)}v_compute(e){const{s_compute:t}=this;return(0,o.map)(e,t)}invert(e){return this.s_invert(e)}v_invert(e){const{s_invert:t}=this;return(0,o.map)(e,t)}}s.CompositeScale=a,n=a,a.__name__=\"CompositeScale\",n.define((({Ref:e})=>({source_scale:[e(_.Scale)],target_scale:[e(_.Scale)]})))},\n function _(t,i,n,a,e){var s;a();const l=t(1),_=t(107),o=t(20),r=t(34),d=t(11),h=t(19),u=l.__importStar(t(64)),g=t(108);n.auto_ranged=Symbol(\"auto_ranged\"),n.is_auto_ranged=function(t){return n.auto_ranged in t};class p extends _.DataRange{constructor(t){super(t),this.have_updated_interactively=!1}initialize(){super.initialize(),this._initial_start=isNaN(this.start)?null:this.start,this._initial_end=isNaN(this.end)?null:this.end,this._initial_range_padding=this.range_padding,this._initial_range_padding_units=this.range_padding_units,this._initial_follow=this.follow,this._initial_follow_interval=this.follow_interval,this._initial_default_span=this.default_span,this._plot_bounds=new Map}get min(){return Math.min(this.start,this.end)}get max(){return Math.max(this.start,this.end)}computed_renderers(){const{renderers:t}=this,i=(0,r.flat_map)(this.linked_plots,(t=>t.auto_ranged_renderers.map((t=>t.model))));return(0,g.compute_renderers)(0==t.length?\"auto\":t,[...i])}_compute_plot_bounds(t,i){let n=u.empty();for(const a of t){const t=i.get(a);null==t||!a.visible&&this.only_visible||(n=u.union(n,t))}return n}adjust_bounds_for_aspect(t,i){const n=u.empty();let a=t.x1-t.x0;a<=0&&(a=1);let e=t.y1-t.y0;e<=0&&(e=1);const s=.5*(t.x1+t.x0),l=.5*(t.y1+t.y0);return ao&&(\"start\"==this.follow?l=s+_*o:\"end\"==this.follow&&(s=l-_*o)),[s,l]}update(t,i,n,a){if(this.have_updated_interactively)return;const e=this.computed_renderers();let s=this._compute_plot_bounds(e,t);null!=a&&(s=this.adjust_bounds_for_aspect(s,a)),this._plot_bounds.set(n,s);const[l,_]=this._compute_min_max(this._plot_bounds.entries(),i);let[o,r]=this._compute_range(l,_);null!=this._initial_start&&(\"log\"==this.scale_hint?this._initial_start>0&&(o=this._initial_start):o=this._initial_start),null!=this._initial_end&&(\"log\"==this.scale_hint?this._initial_end>0&&(r=this._initial_end):r=this._initial_end);let d=!1;\"auto\"==this.bounds&&(this._computed_bounds=[o,r],d=!0);const[h,u]=[this.start,this.end];if(o!=h||r!=u){const t={};o!=h&&(t.start=o),r!=u&&(t.end=r),this.setv(t),d=!1}d&&this.change.emit()}reset(){this.have_updated_interactively=!1,this.setv({range_padding:this._initial_range_padding,range_padding_units:this._initial_range_padding_units,follow:this._initial_follow,follow_interval:this._initial_follow_interval,default_span:this._initial_default_span},{silent:!0}),this.change.emit()}}n.DataRange1d=p,s=p,p.__name__=\"DataRange1d\",s.define((({Bool:t,Float:i,Nullable:n})=>({range_padding:[i,.1],range_padding_units:[o.PaddingUnits,\"percent\"],flipped:[t,!1],follow:[n(o.StartEnd),null],follow_interval:[n(i),null],default_span:[i,2],only_visible:[t,!1]}))),s.internal((({Enum:t})=>({scale_hint:[t(\"log\",\"auto\"),\"auto\"]})))},\n function _(e,a,n,r,t){var s;r();const c=e(100);class o extends c.NumericalRange{constructor(e){super(e)}}n.DataRange=o,s=o,o.__name__=\"DataRange\",s.define((({List:e,AnyRef:a,Or:n,Auto:r})=>({renderers:[n(e(a()),r),[]]}))),s.override({start:NaN,end:NaN})},\n function _(n,e,r,t,u){t(),r.compute_renderers=function(n,e){return\"auto\"==n?e:n??[]}},\n function _(t,n,e,s,i){var r;s();const a=t(1),o=t(98),p=t(20),c=a.__importStar(t(18)),g=t(21),l=t(24),h=t(15),u=t(10),d=t(8),_=t(12);function f(t,n,e=0){const s=new Map;for(let i=0;ip.get(t).value)));i.set(t,{value:g/r,mapping:p}),a+=r+n+c}return[i,(r.size-1)*n+o]}function v(t,n,e,s,i=0){const r=new Map,a=new Map;for(const[n,e,s]of t){const t=a.get(n)??[];a.set(n,[...t,[e,s]])}let o=i,p=0;for(const[t,i]of a){const a=i.length,[c,g]=m(i,e,s,o);p+=g;const l=(0,u.sum)(i.map((([t])=>c.get(t).value)));r.set(t,{value:l/a,mapping:c}),o+=a+n+g}return[r,(a.size-1)*n+p]}e.Factor=(0,g.Or)(g.Str,(0,g.Tuple)(g.Str,g.Str),(0,g.Tuple)(g.Str,g.Str,g.Str)),e.FactorSeq=(0,g.Or)((0,g.List)(g.Str),(0,g.List)((0,g.Tuple)(g.Str,g.Str)),(0,g.List)((0,g.Tuple)(g.Str,g.Str,g.Str))),e.map_one_level=f,e.map_two_levels=m,e.map_three_levels=v;class S extends o.Range{constructor(t){super(t),this.invalidate_synthetic=new h.Signal0(this,\"invalidate_synthetic\")}get min(){return this.start}get max(){return this.end}initialize(){super.initialize(),this._init()}connect_signals(){super.connect_signals(),this.connect(this.properties.factors.change,(()=>this.reset())),this.connect(this.properties.factor_padding.change,(()=>this.reset())),this.connect(this.properties.group_padding.change,(()=>this.reset())),this.connect(this.properties.subgroup_padding.change,(()=>this.reset())),this.connect(this.properties.range_padding.change,(()=>this.reset())),this.connect(this.properties.range_padding_units.change,(()=>this.reset()))}reset(){this._init(),this.invalidate_synthetic.emit()}_lookup(t){switch(t.length){case 1:{const[n]=t,e=this._mapping.get(n);return null!=e?e.value:NaN}case 2:{const[n,e]=t,s=this._mapping.get(n);if(null!=s){const t=s.mapping.get(e);if(null!=t)return t.value}return NaN}case 3:{const[n,e,s]=t,i=this._mapping.get(n);if(null!=i){const t=i.mapping.get(e);if(null!=t){const n=t.mapping.get(s);if(null!=n)return n.value}}return NaN}}}synthetic(t){if((0,d.isNumber)(t))return t;if((0,d.isString)(t))return this._lookup([t]);let n=0;const e=t[t.length-1];return(0,d.isNumber)(e)&&(n=e,t=t.slice(0,-1)),this._lookup(t)+n}v_synthetic(t){const n=t.length,e=new l.ScreenArray(n);for(let s=0;s{if((0,u.every)(this.factors,d.isString)){const t=this.factors,[n,e]=f(t,this.factor_padding);return{levels:1,mapping:n,tops:null,mids:null,inside_padding:e}}if((0,u.every)(this.factors,(t=>(0,d.isArray)(t)&&2==t.length&&(0,d.isString)(t[0])&&(0,d.isString)(t[1])))){const t=this.factors,[n,e]=m(t,this.group_padding,this.factor_padding),s=[...n.keys()];return{levels:2,mapping:n,tops:s,mids:null,inside_padding:e}}if((0,u.every)(this.factors,(t=>(0,d.isArray)(t)&&3==t.length&&(0,d.isString)(t[0])&&(0,d.isString)(t[1])&&(0,d.isString)(t[2])))){const t=this.factors,[n,e]=v(t,this.group_padding,this.subgroup_padding,this.factor_padding),s=[...n.keys()],i=[];for(const[t,e]of n)for(const n of e.mapping.keys())i.push([t,n]);return{levels:3,mapping:n,tops:s,mids:i,inside_padding:e}}(0,_.unreachable)()})();this._mapping=n,this.tops=e,this.mids=s;let r=0,a=this.factors.length+i;if(\"percent\"==this.range_padding_units){const t=(a-r)*this.range_padding/2;r-=t,a+=t}else r-=this.range_padding,a+=this.range_padding;this.setv({start:r,end:a,levels:t},{silent:!0}),\"auto\"==this.bounds&&(this._computed_bounds=[r,a])}}e.FactorRange=S,r=S,S.__name__=\"FactorRange\",r.define((({Float:t})=>({factors:[e.FactorSeq,[]],factor_padding:[t,0],subgroup_padding:[t,.8],group_padding:[t,1.4],range_padding:[t,0],range_padding_units:[p.PaddingUnits,\"percent\"],start:[t,c.unset,{readonly:!0}],end:[t,c.unset,{readonly:!0}]}))),r.internal((({Float:t,Str:n,List:e,Tuple:s,Nullable:i})=>({levels:[t],mids:[i(e(s(n,n))),null],tops:[i(e(n)),null]})))},\n function _(e,t,s,i,n){var o;i();const l=e(1),d=e(111),r=e(117),h=e(118),a=e(120),c=e(121),_=e(63),u=e(20),m=e(56),v=e(10),p=e(8),w=e(50),f=l.__importStar(e(122)),y=f,b=l.__importDefault(e(123));class k extends d.UIElementView{constructor(){super(...arguments),this._menu_views=new Map,this._open=!1,this._item_click=e=>{if(!e.disabled){const{action:t}=e;null!=t&&(0,w.execute)(t,this.model,{item:e}),this.hide()}},this._on_mousedown=e=>{e.composedPath().includes(this.el)||this.prevent_hide?.(e)||this.hide()},this._on_keydown=e=>{\"Escape\"==e.key&&this.hide()},this._on_blur=()=>{this.hide()}}*children(){yield*super.children(),yield*this._menu_views.values()}async lazy_initialize(){await super.lazy_initialize();const e=this.model.items.map((e=>e instanceof h.ActionItem?e.menu:null)).filter(p.isNotNull);await(0,m.build_views)(this._menu_views,e,{parent:this})}get is_open(){return this._open}remove(){this._unlisten(),(0,m.remove_views)(this._menu_views),super.remove()}_listen(){document.addEventListener(\"mousedown\",this._on_mousedown),document.addEventListener(\"keydown\",this._on_keydown),window.addEventListener(\"blur\",this._on_blur)}_unlisten(){document.removeEventListener(\"mousedown\",this._on_mousedown),document.removeEventListener(\"keydown\",this._on_keydown),window.removeEventListener(\"blur\",this._on_blur)}stylesheets(){return[...super.stylesheets(),f.default,b.default]}render(){super.render();const e=(()=>{const{reversed:e,items:t}=this.model;return e?(0,v.reversed)(t):t})();for(const t of e)if(t instanceof c.DividerItem){const e=(0,_.div)({class:y.divider});this.shadow_el.append(e)}else if(t instanceof h.ActionItem){const e=(0,_.div)({class:y.check}),s=(0,_.div)({class:y.icon}),i=(0,_.div)({class:y.label},t.label),n=(0,_.div)({class:y.shortcut},t.shortcut),o=(0,_.div)({class:y.chevron}),{icon:l}=t;if(null!=l)if(l.startsWith(\"data:image\")){const e=`url(\"${encodeURI(l)}\")`;s.style.backgroundImage=e}else if(l.startsWith(\"--\"))s.style.backgroundImage=`var(${l})`;else if(l.startsWith(\".\")){const e=l.substring(1);s.classList.add(e)}else if(u.ToolIcon.valid(l)){const e=`bk-tool-icon-${l.replace(/_/g,\"-\")}`;s.classList.add(e)}const d=(0,_.div)({class:y.item,title:t.tooltip,tabIndex:0},e,s,i,n,o);d.classList.toggle(y.menu,null!=t.menu),d.classList.toggle(y.disabled,t.disabled),t instanceof a.CheckableItem&&(d.classList.add(y.checkable),d.classList.toggle(y.checked,t.checked)),d.addEventListener(\"click\",(()=>{this._item_click(t)})),d.addEventListener(\"keydown\",(e=>{\"Enter\"==e.key&&this._item_click(t)}));const{menu:r}=t;null!=r&&(d.addEventListener(\"pointerenter\",(()=>{this._menu_views.get(r)._show_submenu(d)})),d.addEventListener(\"pointerleave\",(()=>{this._menu_views.get(r).hide()}))),this.shadow_el.append(d)}}_show_submenu(e){if(0==this.model.items.length)return void this.hide();this.render(),e.append(this.el);const{style:t}=this.el;t.left=\"100%\",t.top=\"0\",this._listen(),this._open=!0}show(e){if(0==this.model.items.length)return void this.hide();const{parent:t}=this;if(null==t)return void this.hide();this.render();(t.el.shadowRoot??t.el).append(this.el);const{style:s}=this.el;s.left=(0,_.px)(e.x),s.top=(0,_.px)(e.y),this._listen(),this._open=!0}hide(){this._open&&(this._open=!1,this._unlisten(),this.el.remove())}}s.MenuView=k,k.__name__=\"MenuView\";class g extends d.UIElement{constructor(e){super(e)}}s.Menu=g,o=g,g.__name__=\"Menu\",o.prototype.default_view=k,o.define((({Bool:e,List:t,Ref:s})=>({items:[t(s(r.MenuItem)),[]],reversed:[e,!1]})))},\n function _(e,t,i,s,n){var r;s();const l=e(1),o=e(84),_=e(56),h=e(63),a=e(112),u=e(64),d=e(8),p=e(16),c=l.__importDefault(e(116)),{round:b,floor:y}=Math;class x extends o.StyledElementView{constructor(){super(...arguments),this._display=new h.InlineStyleSheet,this._bbox=new u.BBox,this._context_menu=null,this._resized=!1,this._is_displayed=!1}*_stylesheets(){yield*super._stylesheets(),yield this._display}stylesheets(){return[...super.stylesheets(),c.default]}update_style(){this.style.clear()}box_sizing(){return{width_policy:\"auto\",height_policy:\"auto\",width:null,height:null,aspect_ratio:null}}get bbox(){return this._bbox}update_bbox(){return this._update_bbox()}_update_bbox(){const e=(()=>{if(this.el.isConnected){if(null!=this.el.offsetParent)return!0;{const{position:e,display:t}=getComputedStyle(this.el);return\"fixed\"==e&&\"none\"!=t}}return!1})(),t=e?(()=>{const e=this.el.getBoundingClientRect(),{left:t,top:i}=(()=>{if(null!=this.parent){const t=this.parent.el.getBoundingClientRect();return{left:e.left-t.left,top:e.top-t.top}}return{left:0,top:0}})();return new u.BBox({left:b(t),top:b(i),width:y(e.width),height:y(e.height)})})():new u.BBox,i=!this._bbox.equals(t);return this._bbox=t,this._is_displayed=e,i}initialize(){super.initialize(),this._resize_observer=new ResizeObserver((e=>this.after_resize())),this._resize_observer.observe(this.el,{box:\"border-box\"})}async lazy_initialize(){await super.lazy_initialize();const{context_menu:e}=this.model;null!=e&&(this._context_menu=await(0,_.build_view)(e,{parent:this}))}connect_signals(){super.connect_signals();const{visible:e}=this.model.properties;this.on_change(e,(()=>this._update_visible())),this.el.addEventListener(\"contextmenu\",(e=>this.show_context_menu(e)))}get_context_menu(e){return this._context_menu}show_context_menu(e){if(!e.shiftKey){const t=this.el.getBoundingClientRect(),i=e.x-t.x,s=e.y-t.y,n=this.get_context_menu({x:i,y:s});null!=n&&(e.stopPropagation(),e.preventDefault(),n.show({x:i,y:s}))}}remove(){this._resize_observer.disconnect(),this._context_menu?.remove(),super.remove()}_after_resize(){}after_resize(){this._resized=!0,this.update_bbox()&&this._after_resize(),this.finish()}render(){super.render(),this._apply_visible()}_after_render(){this.update_style(),this.update_bbox()}after_render(){super.after_render(),this._after_render(),this._has_finished||(this.is_displayed?(0,p.defer)().then((()=>{this._resized||this.finish()})):this.force_finished())}get is_displayed(){return this._is_displayed}_apply_visible(){this.model.visible?this._display.clear():this._display.replace(\":host { display: none !important; }\")}_update_visible(){this._apply_visible()}export(e=\"auto\",t=!0){const i=\"auto\"==e||\"png\"==e?\"canvas\":\"svg\",s=new a.CanvasLayer(i,t),{width:n,height:r}=this.bbox;return s.resize(n,r),s}resolve_symbol(e){const t=this.bbox.resolve(e.symbol),{offset:i}=e;if((0,d.isNumber)(t))return t+i;{const{x:e,y:s}=t;return{x:e+i,y:s+i}}}}i.UIElementView=x,x.__name__=\"UIElementView\";class f extends o.StyledElement{constructor(e){super(e)}}i.UIElement=f,r=f,f.__name__=\"UIElement\",r.define((({Bool:e,AnyRef:t,Nullable:i})=>({visible:[e,!0],context_menu:[i(t()),null]})))},\n function _(t,e,s,i,a){i();const n=t(113),r=t(64),h=t(63);class o{get canvas(){return this._canvas}get ctx(){return this._ctx}get el(){return this._el}constructor(t,e){switch(this.pixel_ratio=1,this.bbox=new r.BBox,this.backend=t,this.hidpi=e,t){case\"webgl\":case\"canvas\":{this._el=this._canvas=(0,h.canvas)({class:\"bk-layer\"});const t=this.canvas.getContext(\"2d\");if(null==t)throw new Error(\"unable to obtain 2D rendering context\");this._ctx=t,e&&(this.pixel_ratio=devicePixelRatio);break}case\"svg\":{const t=new n.SVGRenderingContext2D;this._ctx=t,this._canvas=t.get_svg(),this._el=(0,h.div)({class:\"bk-layer\"});this._el.attachShadow({mode:\"open\"}).appendChild(this._canvas);break}}this._ctx.layer=this}resize(t,e){if(this.bbox.width==t&&this.bbox.height==e)return;this.bbox=new r.BBox({left:0,top:0,width:t,height:e});const{target:s}=this;s.width=t*this.pixel_ratio,s.height=e*this.pixel_ratio}get target(){return this._ctx instanceof n.SVGRenderingContext2D?this._ctx:this.canvas}undo_transform(t){const{ctx:e}=this,s=e.getTransform();e.resetTransform();try{t(e)}finally{e.setTransform(s)}}prepare(){const{ctx:t,hidpi:e,pixel_ratio:s}=this;t.save(),e&&(t.scale(s,s),t.translate(.5,.5)),this.clear()}clear(){const{x:t,y:e,width:s,height:i}=this.bbox;this.ctx.clearRect(t,e,s,i)}finish(){this.ctx.restore()}to_blob(){const{_canvas:t}=this;if(t instanceof HTMLCanvasElement)return new Promise(((e,s)=>{t.toBlob((t=>null!=t?e(t):s()),\"image/png\")}));{const t=this._ctx.get_serialized_svg(!0),e=new Blob([t],{type:\"image/svg+xml\"});return Promise.resolve(e)}}}s.CanvasLayer=o,o.__name__=\"CanvasLayer\"},\n function _(t,e,i,s,r){s();const n=t(114),a=t(8),o=t(9),l=t(115),h=t(11),_=t(63);function c(t){const e={left:\"start\",right:\"end\",center:\"middle\",start:\"start\",end:\"end\"};return t in e?e[t]:e.start}function p(t){const e={alphabetic:\"alphabetic\",hanging:\"hanging\",top:\"text-before-edge\",bottom:\"text-after-edge\",middle:\"central\"};return t in e?e[t]:e.alphabetic}const u=function(t,e){const i=new Map,s=t.split(\",\");e=e??10;for(let t=0;t=0?Math.acos(e):-Math.acos(e)}const A=v(g),w=v(b);this.lineTo(m+g[0]*r,f+g[1]*r),this.arc(m,f,r,A,w)}stroke(){\"path\"===this.__currentElement.nodeName&&this.__currentElement.setAttribute(\"paint-order\",\"fill\"),this.__applyCurrentDefaultPath(),this.__applyStyleToCurrentElement(\"stroke\"),null!=this._clip_path&&this.__currentElement.setAttribute(\"clip-path\",this._clip_path)}fill(t,e){let i=null;if(t instanceof Path2D)i=t;else{if(null!=e)throw new Error(\"invalid arguments\");e=t}if(null!=i)throw new Error(\"not implemented\");\"none\"!=this.__currentElement.getAttribute(\"fill\")&&this.__init_element(),\"path\"===this.__currentElement.nodeName&&this.__currentElement.setAttribute(\"paint-order\",\"stroke\"),this.__applyCurrentDefaultPath(),this.__applyStyleToCurrentElement(\"fill\"),null!=e&&this.__currentElement.setAttribute(\"fill-rule\",e),null!=this._clip_path&&this.__currentElement.setAttribute(\"clip-path\",this._clip_path)}rect(t,e,i,s){isFinite(t+e+i+s)&&(this.moveTo(t,e),this.lineTo(t+i,e),this.lineTo(t+i,e+s),this.lineTo(t,e+s),this.lineTo(t,e),this.closePath())}fillRect(t,e,i,s){isFinite(t+e+i+s)&&(this.beginPath(),this.rect(t,e,i,s),this.fill())}strokeRect(t,e,i,s){isFinite(t+e+i+s)&&(this.beginPath(),this.rect(t,e,i,s),this.stroke())}__clearCanvas(){(0,_.empty)(this.__defs),(0,_.empty)(this.__root),this.__root.appendChild(this.__defs),this.__currentElement=this.__root}clearRect(t,e,i,s){if(!isFinite(t+e+i+s))return;if(0===t&&0===e&&i===this.width&&s===this.height)return void this.__clearCanvas();const r=this.__createElement(\"rect\",{x:t,y:e,width:i,height:s,fill:\"#FFFFFF\"},!0);this._apply_transform(r),this.__root.appendChild(r)}roundRect(t,e,i,s,r){throw new Error(\"not implemented\")}createLinearGradient(t,e,i,s){if(!isFinite(t+e+i+s))throw new Error(\"The provided double value is non-finite\");const[r,n]=this._transform.apply(t,e),[a,o]=this._transform.apply(i,s),l=this.__createElement(\"linearGradient\",{id:this._random_string(),x1:`${r}px`,x2:`${a}px`,y1:`${n}px`,y2:`${o}px`,gradientUnits:\"userSpaceOnUse\"},!1);return this.__defs.appendChild(l),new f(l,this)}createRadialGradient(t,e,i,s,r,n){if(!isFinite(t+e+i+s+r+n))throw new Error(\"The provided double value is non-finite\");const[a,o]=this._transform.apply(t,e),[l,h]=this._transform.apply(s,r),_=this.__createElement(\"radialGradient\",{id:this._random_string(),cx:`${l}px`,cy:`${h}px`,r:`${n}px`,r0:`${i}px`,fx:`${a}px`,fy:`${o}px`,gradientUnits:\"userSpaceOnUse\"},!1);return this.__defs.appendChild(_),new f(_,this)}createConicGradient(t,e,i){throw Error(\"not implemented\")}__parseFont(){const[,t,e,i,s,,r]=/^\\s*(?=(?:(?:[-a-z]+\\s*){0,2}(italic|oblique))?)(?=(?:(?:[-a-z]+\\s*){0,2}(small-caps))?)(?=(?:(?:[-a-z]+\\s*){0,2}(bold(?:er)?|lighter|[1-9]00))?)(?:(?:normal|\\1|\\2|\\3)\\s*){0,3}((?:xx?-)?(?:small|large)|medium|smaller|larger|[.\\d]+(?:\\%|in|[cem]m|ex|p[ctx]))(?:\\s*\\/\\s*(normal|[.\\d]+(?:\\%|in|[cem]m|ex|p[ctx])))?\\s*([-,\\'\\\"\\sa-z0-9]+?)\\s*$/i.exec(this.font);return{style:t??\"normal\",size:s??\"10px\",family:r??\"sans-serif\",weight:i??\"normal\",decoration:e??\"normal\"}}__applyText(t,e,i,s){const r=this.__parseFont(),n=this.__createElement(\"text\",{\"font-family\":r.family,\"font-size\":r.size,\"font-style\":r.style,\"font-weight\":r.weight,\"text-decoration\":r.decoration,x:e,y:i,\"text-anchor\":c(this.textAlign),\"dominant-baseline\":p(this.textBaseline)},!0);n.appendChild(this.__document.createTextNode(t)),this._apply_transform(n),this.__currentElement=n,this.__applyStyleToCurrentElement(s);const a=(()=>{if(null!=this._clip_path){const t=this.__createElement(\"g\");return t.setAttribute(\"clip-path\",this._clip_path),t.appendChild(n),t}return n})();this.__root.appendChild(a)}fillText(t,e,i){isFinite(e+i)&&this.__applyText(t,e,i,\"fill\")}strokeText(t,e,i){isFinite(e+i)&&this.__applyText(t,e,i,\"stroke\")}measureText(t){return this.__ctx.font=this.font,this.__ctx.measureText(t)}arc(t,e,i,s,r,n=!1){this.ellipse(t,e,i,i,0,s,r,n)}ellipse(t,e,i,s,r,a,o,l=!1){if(!isFinite(t+e+i+s+r+a+o))return;if(i<0||s<0)throw new DOMException(\"IndexSizeError, radius can't be negative\");const _=l?o-a:a-o;a%=2*Math.PI,o%=2*Math.PI;const c=(new n.AffineTransform).translate(t,e).rotate(r),p=i*Math.cos(a),u=s*Math.sin(a),[d,m]=c.apply(p,u);this.lineTo(d,m);const f=180*r/Math.PI,g=l?0:1;if(Math.abs(a-o)<2*h.float32_epsilon&&!(Math.abs(_)<2*h.float32_epsilon&&_<0)){const[t,e]=this._transform.apply(d,m),r=i*Math.cos(a+Math.PI),n=s*Math.sin(a+Math.PI),[o,l]=c.apply(r,n),[h,_]=this._transform.apply(o,l);this.__addPathCommand(t,e,`A ${i} ${s} ${f} 0 ${g} ${h} ${_} A ${i} ${s} ${f} 0 ${g} ${t} ${e}`)}else{const t=i*Math.cos(o),e=s*Math.sin(o),[r,n]=c.apply(t,e);let h=o-a;h<0&&(h+=2*Math.PI);const _=l!==h>Math.PI?1:0,[p,u]=this._transform.apply(r,n);this.__addPathCommand(p,u,`A ${i} ${s} ${f} ${_} ${g} ${p} ${u}`)}}clip(t,e){let i=null;if(t instanceof Path2D)i=t;else{if(null!=e)throw new Error(\"invalid arguments\");e=t}if(null!=i)throw new Error(\"not implemented\");const s=this.__createElement(\"clipPath\"),r=this._random_string();this.__applyCurrentDefaultPath(),null!=e&&this.__currentElement.setAttribute(\"clip-rule\",e),s.setAttribute(\"id\",r),s.appendChild(this.__currentElement),this.__defs.appendChild(s),this._clip_path=`url(#${r})`}drawImage(t,...e){let i,s,r,n,a,o,l,h;if(2==e.length){if([i,s]=e,!isFinite(i+s))return;a=0,o=0;const[_,c]=m(t);r=l=_,n=h=c}else if(4==e.length){if([i,s,r,n]=e,!isFinite(i+s+r+n))return;a=0,o=0;const[_,c]=m(t);l=_,h=c}else{if(8!==e.length)throw new Error(`Inavlid number of arguments passed to drawImage: ${arguments.length}`);if([a,o,l,h,i,s,r,n]=e,!isFinite(a+o+l+h+i+s+r+n))return}const _=this.__root,c=this._transform.clone().translate(i,s);if(t instanceof b||t instanceof SVGSVGElement){const e=(t instanceof SVGSVGElement?t:t.get_svg()).cloneNode(!0);let i;c.is_identity&&1==this.globalAlpha&&null==this._clip_path?i=_:(i=this.__createElement(\"g\"),c.is_identity||this._apply_transform(i,c),1!=this.globalAlpha&&i.setAttribute(\"opacity\",`${this.globalAlpha}`),null!=this._clip_path&&i.setAttribute(\"clip-path\",this._clip_path),_.appendChild(i));for(const t of[...e.childNodes])if(t instanceof SVGDefsElement){for(const e of[...t.childNodes])if(e instanceof Element){const t=e.getAttribute(\"id\");this.__ids.add(t),this.__defs.appendChild(e.cloneNode(!0))}}else i.appendChild(t.cloneNode(!0))}else if(t instanceof HTMLImageElement||t instanceof SVGImageElement){const e=this.__createElement(\"image\");if(e.setAttribute(\"width\",`${r}`),e.setAttribute(\"height\",`${n}`),e.setAttribute(\"preserveAspectRatio\",\"none\"),1!=this.globalAlpha&&e.setAttribute(\"opacity\",`${this.globalAlpha}`),0!=a||0!=o||l!==t.width||h!==t.height){const e=this.__document.createElement(\"canvas\");e.width=r,e.height=n;e.getContext(\"2d\").drawImage(t,a,o,l,h,0,0,r,n),t=e}this._apply_transform(e,c);const i=t instanceof HTMLCanvasElement?t.toDataURL():t.getAttribute(\"src\");if(e.setAttribute(\"href\",i),null!=this._clip_path){const t=this.__createElement(\"g\");t.setAttribute(\"clip-path\",this._clip_path),t.appendChild(e),_.appendChild(t)}else _.appendChild(e)}else if(t instanceof HTMLCanvasElement){const e=this.__createElement(\"image\");e.setAttribute(\"width\",`${r}`),e.setAttribute(\"height\",`${n}`),e.setAttribute(\"preserveAspectRatio\",\"none\"),1!=this.globalAlpha&&e.setAttribute(\"opacity\",`${this.globalAlpha}`);const i=this.__document.createElement(\"canvas\");i.width=r,i.height=n;const s=i.getContext(\"2d\");if(s.imageSmoothingEnabled=!1,s.drawImage(t,a,o,l,h,0,0,r,n),t=i,this._apply_transform(e,c),e.setAttribute(\"href\",t.toDataURL()),null!=this._clip_path){const t=this.__createElement(\"g\");t.setAttribute(\"clip-path\",this._clip_path),t.appendChild(e),_.appendChild(t)}else _.appendChild(e)}}createPattern(t,e){const i=this.__document.createElementNS(\"http://www.w3.org/2000/svg\",\"pattern\"),s=this._random_string(),[r,n]=m(t);if(i.setAttribute(\"id\",s),i.setAttribute(\"width\",`${r}`),i.setAttribute(\"height\",`${n}`),i.setAttribute(\"patternUnits\",\"userSpaceOnUse\"),t instanceof HTMLCanvasElement||t instanceof HTMLImageElement||t instanceof SVGImageElement){const e=this.__document.createElementNS(\"http://www.w3.org/2000/svg\",\"image\"),s=t instanceof HTMLCanvasElement?t.toDataURL():t.getAttribute(\"src\");e.setAttribute(\"href\",s),i.appendChild(e),this.__defs.appendChild(i)}else if(t instanceof b){for(const e of[...t.__root.childNodes])e instanceof SVGDefsElement||i.appendChild(e.cloneNode(!0));this.__defs.appendChild(i)}else{if(!(t instanceof SVGSVGElement))throw new Error(\"unsupported\");for(const e of[...t.childNodes])e instanceof SVGDefsElement||i.appendChild(e.cloneNode(!0));this.__defs.appendChild(i)}return new g(i,this)}getLineDash(){const{lineDash:t}=this;return(0,a.isString)(t)?t.split(\",\").map((t=>parseInt(t))):t??[]}setLineDash(t){t.length>0?this.lineDash=t.join(\",\"):this.lineDash=null}getTransform(){return this._transform.to_DOMMatrix()}setTransform(...t){let e;e=(0,a.isNumber)(t[0])?new DOMMatrix(t):t[0]instanceof DOMMatrix?t[0]:new DOMMatrix(Object.values(null==t[0])),this._transform=n.AffineTransform.from_DOMMatrix(e)}resetTransform(){this._transform=new n.AffineTransform}isPointInPath(...t){throw new Error(\"not implemented\")}isPointInStroke(...t){throw new Error(\"not implemented\")}createImageData(...t){throw new Error(\"not implemented\")}getImageData(t,e,i,s){throw new Error(\"not implemented\")}putImageData(...t){throw new Error(\"not implemented\")}drawFocusIfNeeded(...t){throw new Error(\"not implemented\")}scrollPathIntoView(...t){throw new Error(\"not implemented\")}}i.SVGRenderingContext2D=b,b.__name__=\"SVGRenderingContext2D\",b.__random=l.random},\n function _(t,r,s,e,i){e();const n=t(26),{sin:a,cos:h}=Math;class o{constructor(t=1,r=0,s=0,e=1,i=0,n=0){this.a=t,this.b=r,this.c=s,this.d=e,this.e=i,this.f=n}toString(){const{a:t,b:r,c:s,d:e,e:i,f:n}=this;return`matrix(${t}, ${r}, ${s}, ${e}, ${i}, ${n})`}static from_DOMMatrix(t){const{a:r,b:s,c:e,d:i,e:n,f:a}=t;return new o(r,s,e,i,n,a)}to_DOMMatrix(){const{a:t,b:r,c:s,d:e,e:i,f:n}=this;return new DOMMatrix([t,r,s,e,i,n])}clone(){const{a:t,b:r,c:s,d:e,e:i,f:n}=this;return new o(t,r,s,e,i,n)}[n.equals](t,r){return r.eq(this.a,t.a)&&r.eq(this.b,t.b)&&r.eq(this.c,t.c)&&r.eq(this.d,t.d)&&r.eq(this.e,t.e)&&r.eq(this.f,t.f)}reset(){this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0}get is_identity(){const{a:t,b:r,c:s,d:e,e:i,f:n}=this;return 1==t&&0==r&&0==s&&1==e&&0==i&&0==n}apply_point(t){const[r,s]=this.apply(t.x,t.y);return{x:r,y:s}}apply_rect(t){return{p0:this.apply_point(t.p0),p1:this.apply_point(t.p1),p2:this.apply_point(t.p2),p3:this.apply_point(t.p3)}}apply(t,r){const{a:s,b:e,c:i,d:n,e:a,f:h}=this;return[s*t+i*r+a,e*t+n*r+h]}iv_apply(t,r){const{a:s,b:e,c:i,d:n,e:a,f:h}=this,o=t.length;for(let c=0;cthis.uniform(t,e)))}normal(t,e){return this.normals(t,e,1)[0]}normals(t,e,n){const[s,r]=[t,e],l=new Float64Array(n);for(let t=0;t({icon:[n(s),null],label:[l],tooltip:[n(l),null],shortcut:[n(l),null],menu:[n(t()),null],disabled:[e,!1],action:[n((0,r.Or)(o(a.Callback),c())),null]})))},\n function _(c,a,l,n,s){n();const e=c(51);class o extends e.Model{constructor(c){super(c)}}l.Callback=o,o.__name__=\"Callback\"},\n function _(e,c,t,n,o){var s;n();const a=e(118);class _ extends a.ActionItem{constructor(e){super(e)}}t.CheckableItem=_,s=_,_.__name__=\"CheckableItem\",s.define((({Bool:e})=>({checked:[e,!1]})))},\n function _(e,t,n,s,c){s();const i=e(117);class r extends i.MenuItem{constructor(e){super(e)}}n.DividerItem=r,r.__name__=\"DividerItem\"},\n function _(e,o,r,i,t){i(),r.item=\"bk-item\",r.divider=\"bk-divider\",r.check=\"bk-check\",r.icon=\"bk-icon\",r.label=\"bk-label\",r.shortcut=\"bk-shortcut\",r.chevron=\"bk-chevron\",r.disabled=\"bk-disabled\",r.menu=\"bk-menu\",r.checkable=\"bk-checkable\",r.checked=\"bk-checked\",r.default=\":host{position:absolute;display:inline-grid;grid-template-columns:repeat(5, max-content);grid-template-rows:auto;user-select:none;-webkit-user-select:none;cursor:pointer;width:auto;height:auto;z-index:var(--bokeh-top-level);font-size:var(--font-size);background-color:#fff;border:1px solid #ccc;border-radius:var(--border-radius);box-shadow:2px 4px 8px rgba(0, 0, 0, 0.175);}.bk-item,.bk-divider{position:relative;display:grid;grid-template-columns:subgrid;grid-column:span 5;align-items:center;}.bk-check{grid-column:1;}.bk-icon{grid-column:2;}.bk-label{grid-column:3;}.bk-shortcut{grid-column:4;}.bk-chevron{grid-column:5;}.bk-divider{cursor:default;overflow:hidden;background-color:#e5e5e5;height:1px;margin:1px 5px;}.bk-item{padding:0.5em;}.bk-item:hover{background-color:#f9f9f9;}.bk-item:focus,.bk-item:focus-visible{outline:1px dotted var(--active-tool-highlight);outline-offset:-1px;}.bk-item::-moz-focus-inner{border:0;}.bk-item.bk-disabled{color:gray;}.bk-item.bk-disabled .bk-icon{color:var(--bokeh-icon-color-disabled);}.bk-item:first-of-type{border-top-left-radius:var(--border-radius);border-top-right-radius:var(--border-radius);}.bk-item:last-of-type{border-bottom-left-radius:var(--border-radius);border-bottom-right-radius:var(--border-radius);}.bk-icon{position:relative;width:16px;height:16px;mask-size:100% 100%;-webkit-mask-size:100% 100%;mask-position:center center;-webkit-mask-position:center center;mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;background-size:100% 100%;background-origin:border-box;background-position:center center;background-repeat:no-repeat;}.bk-label{padding:0 0.5em;}.bk-shortcut{text-align:right;font-size:90%;color:gray;}.bk-chevron{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-caret-right);-webkit-mask-image:var(--bokeh-icon-caret-right);mask-size:100% 100%;-webkit-mask-size:100% 100%;mask-position:center center;-webkit-mask-position:center center;mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;width:12px;height:12px;}.bk-item:not(.bk-menu) .bk-chevron{display:none;}.bk-item.bk-checkable .bk-check{width:16px;height:16px;}.bk-item.bk-checkable.bk-checked .bk-check{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-check);-webkit-mask-image:var(--bokeh-icon-check);mask-size:100% 100%;-webkit-mask-size:100% 100%;mask-position:center center;-webkit-mask-position:center center;mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;}\"},\n function _(o,A,g,e,t){e(),g.tool_icon_square_check=g.tool_icon_square=g.tool_icon_check=g.tool_icon_text_align_right=g.tool_icon_text_align_center=g.tool_icon_text_align_left=g.tool_icon_italic=g.tool_icon_bold=g.tool_icon_delete=g.tool_icon_pointer=g.tool_icon_unpin=g.tool_icon_pin=g.tool_icon_maximize=g.tool_icon_minimize=g.tool_icon_arrow_up_from_bar=g.tool_icon_arrow_down_to_bar=g.tool_icon_y_grip=g.tool_icon_x_grip=g.tool_icon_see_off=g.tool_icon_see_on=g.tool_icon_caret_right=void 0,g.tool_icon_copy=\"bk-tool-icon-copy\",g.tool_icon_replace_mode=\"bk-tool-icon-replace-mode\",g.tool_icon_toggle_mode=\"bk-tool-icon-toggle-mode\",g.tool_icon_append_mode=\"bk-tool-icon-append-mode\",g.tool_icon_intersect_mode=\"bk-tool-icon-intersect-mode\",g.tool_icon_subtract_mode=\"bk-tool-icon-subtract-mode\",g.tool_icon_xor_mode=\"bk-tool-icon-xor-mode\",g.tool_icon_invert_selection=\"bk-tool-icon-invert-selection\",g.tool_icon_clear_selection=\"bk-tool-icon-clear-selection\",g.tool_icon_box_select=\"bk-tool-icon-box-select\",g.tool_icon_x_box_select=\"bk-tool-icon-x-box-select\",g.tool_icon_y_box_select=\"bk-tool-icon-y-box-select\",g.tool_icon_box_zoom=\"bk-tool-icon-box-zoom\",g.tool_icon_x_box_zoom=\"bk-tool-icon-x-box-zoom\",g.tool_icon_y_box_zoom=\"bk-tool-icon-y-box-zoom\",g.tool_icon_auto_box_zoom=\"bk-tool-icon-auto-box-zoom\",g.tool_icon_zoom_in=\"bk-tool-icon-zoom-in\",g.tool_icon_zoom_out=\"bk-tool-icon-zoom-out\",g.tool_icon_help=\"bk-tool-icon-help\",g.tool_icon_hover=\"bk-tool-icon-hover\",g.tool_icon_crosshair=\"bk-tool-icon-crosshair\",g.tool_icon_lasso_select=\"bk-tool-icon-lasso-select\",g.tool_icon_pan=\"bk-tool-icon-pan\",g.tool_icon_x_pan=\"bk-tool-icon-x-pan\",g.tool_icon_y_pan=\"bk-tool-icon-y-pan\",g.tool_icon_range=\"bk-tool-icon-range\",g.tool_icon_polygon_select=\"bk-tool-icon-polygon-select\",g.tool_icon_redo=\"bk-tool-icon-redo\",g.tool_icon_reset=\"bk-tool-icon-reset\",g.tool_icon_save=\"bk-tool-icon-save\",g.tool_icon_tap_select=\"bk-tool-icon-tap-select\",g.tool_icon_undo=\"bk-tool-icon-undo\",g.tool_icon_wheel_pan=\"bk-tool-icon-wheel-pan\",g.tool_icon_wheel_zoom=\"bk-tool-icon-wheel-zoom\",g.tool_icon_box_edit=\"bk-tool-icon-box-edit\",g.tool_icon_freehand_draw=\"bk-tool-icon-freehand-draw\",g.tool_icon_poly_draw=\"bk-tool-icon-poly-draw\",g.tool_icon_point_draw=\"bk-tool-icon-point-draw\",g.tool_icon_poly_edit=\"bk-tool-icon-poly-edit\",g.tool_icon_line_edit=\"bk-tool-icon-line-edit\",g.tool_icon_settings=\"bk-tool-icon-settings\",g.tool_icon_unknown=\"bk-tool-icon-unknown\",g.tool_icon_fullscreen=\"bk-tool-icon-fullscreen\",g.tool_icon_chevron_up=\"bk-tool-icon-chevron-up\",g.tool_icon_chevron_down=\"bk-tool-icon-chevron-down\",g.tool_icon_chevron_left=\"bk-tool-icon-chevron-left\",g.tool_icon_chevron_right=\"bk-tool-icon-chevron-right\",g.tool_icon_caret_up=\"bk-tool-icon-caret-up\",g.tool_icon_caret_down=\"bk-tool-icon-caret-down\",g.tool_icon_caret_left=\"bk-tool-icon-caret-left\",g.tool_icon_caret_right=\"bk-tool-icon-caret-right\",g.tool_icon_see_on=\"bk-tool-icon-see-on\",g.tool_icon_see_off=\"bk-tool-icon-see-off\",g.tool_icon_x_grip=\"bk-tool-icon-x-grip\",g.tool_icon_y_grip=\"bk-tool-icon-y-grip\",g.tool_icon_arrow_down_to_bar=\"bk-tool-icon-arrow-down-to-bar\",g.tool_icon_arrow_up_from_bar=\"bk-tool-icon-arrow-up-from-bar\",g.tool_icon_minimize=\"bk-tool-icon-minimize\",g.tool_icon_maximize=\"bk-tool-icon-maximize\",g.tool_icon_pin=\"bk-tool-icon-pin\",g.tool_icon_unpin=\"bk-tool-icon-unpin\",g.tool_icon_pointer=\"bk-tool-icon-pointer\",g.tool_icon_delete=\"bk-tool-icon-delete\",g.tool_icon_bold=\"bk-tool-icon-bold\",g.tool_icon_italic=\"bk-tool-icon-italic\",g.tool_icon_text_align_left=\"bk-tool-icon-text-align-left\",g.tool_icon_text_align_center=\"bk-tool-icon-text-align-center\",g.tool_icon_text_align_right=\"bk-tool-icon-text-align-right\",g.tool_icon_check=\"bk-tool-icon-check\",g.tool_icon_square=\"bk-tool-icon-square\",g.tool_icon_square_check=\"bk-tool-icon-square-check\",g.default=':host{--bokeh-icon-color:#a1a6a9;--bokeh-icon-color-disabled:#d4d9db;}.bk-tool-icon-copy{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-copy);-webkit-mask-image:var(--bokeh-icon-copy);}.bk-tool-icon-replace-mode{background-image:var(--bokeh-icon-replace-mode, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AUFFxokK3gniQAAAHpJREFUWMNjXLhsJcNAAiaGAQajDhhwB7DgEP+PxmeksvjgDwFcLmYkUh2hkBj8IcBIZXsYh1w2/I8v3sgAOM0bLYhGc8GgrwuICgldfQO88pcvXvg/aOuCUQeM5oLRuoCFCJcTbOMh5XOiW0JDNhdQS3y0IBp1ABwAAF8KGrhC1Eg6AAAAAElFTkSuQmCC\"));}.bk-tool-icon-toggle-mode{background-image:var(--bokeh-icon-toggle-mode, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADP3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHja7ZZbsiMnDIbfWUWWgCRAYjlcq2YHWX5+6HYf2+OpiieepxyoRrRQC6EPsN34+8d0f6GwZ3YhqqWckkcJOWQu6Jg/Stkt+bDbXfgcwvuD3l0DDJVAyvFq6bS/6elycIiCXrxzZO0cqI8DOZz+7cnROZGsiFYU/XSUT0fCxwCdDsqxLJ+y6f0S6jhkv63EjsetRvRY+s3J83tQZK9HKIV5CIlHK3IGIOsJTgo6abfCh7rAVNEGuUWChLzK01UyIpor1PDS6IHK1aPXevdMK/BpIk9JTpd8qXcUX1PZqb+bOdjZ40d9tsOV80/ZX8+c3eZeM1ZRQkKq07mo21J2D3YVU6ypzcFf8oonwoXumlENu7qBWvfNV9RGmRi4JgXqVGjS2LJRQ4iBh2NdJ4Uby1aaKGduYEeLmgSarJKli4Fo29iD8BUL7Wmzb27PZpi5E0yZ4IzwydvVvfvBnOsoEHm7coW4cPoXB1pplNXCDERonkmNO8G3+lwWVwHBuLK8jkhGYuvhokb6uglkgxYYRsjjuJD20wFShKkjgiEBAVAjiZTIK7MSIZEGQAWh4wBxBQGKkTuC5CCSwMZ4TY1PlLYpR4baQY/LDCQiTpyCTZYCWCFE7B8Nhj1UosQQY0xRo8UcS5IUUkwpaVqXYlHR4DRqUlXTrMXEgkVLpmaWrWTOgksz5pQ1W865FMxZ4Lng6wKDUipXqaFGV1PVajXX0rB9WmixpabNWm6lc5eO+6Onrt167mXQwFYaYcSRhg4beZSJrTbFzTDjTFOnzTzLRe3E+lN9gxqd1HiTWoZ6UYNW9eaC1nUSFzMAw68IgbguBNjQvJh5oxB4kVvMfGacisgIMi5mnRYxEAyDOE66sXN8EF3k/hM3p+GBG/8uObfQvUnuZ26vqPX1M9Q2seMUrqR6wekbOVlhQ0T4sVsdfy/ds+J35bejb0ffjv5vjkYtqzdcSF8vL/o10x+NqJZRMNcSp9aVxL7srv+Spw0E/kL9O+/uI+EgGveRcCDdR8JBFO4j4dh29H44e3+sbXLXd78YWPvnrTW7D5yOb0fvOsL/kOz+AVNjw+bixfXWAAABhGlDQ1BJQ0MgcHJvZmlsZQAAeJx9kT1Iw0AcxV9TtSIVRTuIOGSoThZERRy1CkWoEGqFVh1MLv0QmjQkKS6OgmvBwY/FqoOLs64OroIg+AHi7OCk6CIl/i8ptIjx4Lgf7+497t4BQq3ENKttDNB020wl4mImuyKGXtGBIPoQRq/MLGNWkpLwHV/3CPD1Lsaz/M/9ObrVnMWAgEg8wwzTJl4nntq0Dc77xBFWlFXic+JRky5I/Mh1xeM3zgWXBZ4ZMdOpOeIIsVhoYaWFWdHUiCeJo6qmU76Q8VjlvMVZK1VY4578heGcvrzEdZpDSGABi5AgQkEFGyjBRoxWnRQLKdqP+/gHXb9ELoVcG2DkmEcZGmTXD/4Hv7u18hPjXlI4DrS/OM7HMBDaBepVx/k+dpz6CRB8Bq70pr9cA6Y/Sa82tegR0LMNXFw3NWUPuNwBBp4M2ZRdKUhTyOeB9zP6pizQfwt0rXq9NfZx+gCkqavkDXBwCIwUKHvN592drb39e6bR3w/9qXJ4zBnVPwAADRppVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgIHhtbG5zOkdJTVA9Imh0dHA6Ly93d3cuZ2ltcC5vcmcveG1wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICB4bXBNTTpEb2N1bWVudElEPSJnaW1wOmRvY2lkOmdpbXA6MjFjNTkwNzMtOTc1Zi00ZjAxLTljMDktYzY2NWY3NTBhMDA2IgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjdhODY1NGM1LTZiYTktNDkwMi1iZWNmLTAyMGE3Y2I1MTI5NCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmNmNDY3MmE2LTRjN2ItNDAwYi04N2U4LWExMjFjY2ZlZjI1YSIKICAgZGM6Rm9ybWF0PSJpbWFnZS9wbmciCiAgIEdJTVA6QVBJPSIyLjAiCiAgIEdJTVA6UGxhdGZvcm09IkxpbnV4IgogICBHSU1QOlRpbWVTdGFtcD0iMTcxNzQzODY2NDU5OTYwMiIKICAgR0lNUDpWZXJzaW9uPSIyLjEwLjMwIgogICB0aWZmOk9yaWVudGF0aW9uPSIxIgogICB4bXA6Q3JlYXRvclRvb2w9IkdJTVAgMi4xMCI+CiAgIDx4bXBNTTpIaXN0b3J5PgogICAgPHJkZjpTZXE+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyOTk4NWE4LThlMjItNGI2NS1hNTQwLTE3MWI2NjU3YTYxOCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iR2ltcCAyLjEwIChMaW51eCkiCiAgICAgIHN0RXZ0OndoZW49IjIwMjQtMDYtMDNUMjA6MTc6NDQrMDI6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+rdIyIAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+gGAxIRLNWVUewAAAB9SURBVFjDY2AYBaNggAEjNsGFy1b+R+bHR4UzUlMcGTANymBZuGzlf3TX08o8ptFUOJoGBtrxTNS2HFd+p7kDyLF80KYZkiyhieXEWkas5VTLBciVDLXinOREiGwhNRIcCzmaBiSlj9YFw7Y9wEJK8Uppm3C0RTQKRgE2AAByVIRiIg+g0AAAAABJRU5ErkJggg==\"));}.bk-tool-icon-append-mode{background-image:var(--bokeh-icon-append-mode, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AUFFxkZWD04WwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAoUlEQVRYw+1WQQ6AIAwrhO8Y/bIXEz9jIMSDr8ETCUEPQzA4pMeFLKNbu4l5WR0CDOMEALBGIzMuQIBEZQjPgP9JLjwTfBjY9sO9lZsFA9IafZng3BlIyVefgd8XQFZBAWe8jfNxwsDhir6rzoCiPiy1K+J8/FRQemv2XfAdFcQ9znU4Viqg9ta1qYJ+D1BnAIBrkgGVOrXNqUA9rbyZm/AEzFh4jEeY/soAAAAASUVORK5CYII=\"));}.bk-tool-icon-intersect-mode{background-image:var(--bokeh-icon-intersect-mode, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AUFFxkrkOpp2wAAAPhJREFUWMPtV1EKwjAMTUavI3oawR/vtn5srJdREfzwMvHHQlcT2mpdMzFfWxiP5r2+JMN+mAiCOB72CABgR1cln4oOGocJnuMTSxWk8jMm7OggYkYXA9gPE3uyd8NXHONJ+eYMdE/NqCJmEZ5ZqlJJ4sUksKN7cYSaPoCZFWR1QI+Xm1fBACU63Cw22x0AAJxudwrffVwvZ+JmQdAHZkw0d4EpAMCw8k87pMdbnwtizQumJYv3nwV6XOA1qbUT/oQLUJgFRbsiNwFVucBIlyR3p0tdMp+XmFjfLKi1LatyAXtCRjPWBdL3Ke3VuACJKFfDr/xFN2fgAR/Go0qaLlmEAAAAAElFTkSuQmCC\"));}.bk-tool-icon-subtract-mode{background-image:var(--bokeh-icon-subtract-mode, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AUFFxgsF5XNOQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABFUlEQVRYw9VWUQqDMAxNpWfxQxD1MoP97G7zQ5mH2RTZYLtM9lWoMbXtxLXNX4OG9r28l4hrd0PQoqxqAACYpxH25C/nkwCHyCBwSPoS09k1T5Fo+4EiExcC4v584xGFmyIXHBLRISAVZyZufUPVa4rcrwmPDgr93ylo+2GliLRUYHK6th/o/6r7nfLpqaCsagEA8Hh9FmcNKeRmgeYDC+SCq0B6FFi8/BcV6BdR9cL3gCv3ijPKOacsn3rBEcjmaVxpfGcg4wHxzgJJnc6241Hn23DERFRAu1bNcWa3Q0uXi62XR6sCaWoSejbtdLYmU3kTEunNgj0bUbQqYG/IcMaqwPS9jftoVCAQ0ZVDJwf0zQdH4AsyW6fpQu4YegAAAABJRU5ErkJggg==\"));}.bk-tool-icon-xor-mode{background-image:var(--bokeh-icon-xor-mode, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAmJLR0QA/4ePzL8AAACXSURBVEjH3VXBCcAwCDxLxgl0oM7nQEL3sa9ASLQIqS3UT4SIOfXOECsAADsAwb1/ECbbsGilvdKfli9OgmUEdGo8uKb0QIZ6PUvrwfcJzCn0zGtM9fx1BKzzFMTgvRf3jBZGs1TnxeVNwUIxxsqrWmBNYmJptewBBuZMwas3uiP+sA+8i8i2rpSlRg7+FaxZCHpFpu/EC1y3NtihGuwKAAAAAElFTkSuQmCC\"));}.bk-tool-icon-invert-selection{background-image:var(--bokeh-icon-invert-selection, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAAmJLR0QA/4ePzL8AAAC2SURBVEjH7VVJEoQwCGysPMeqPCjv40FU+R/moCbMmEWNenFygUug6aYTmhRdZwBeX8ClVJboCRDN5SN4zc8gkBaCtaONKZ+vB0JEcT0HtmvtBALS+rnatAEAF/KcCroThXKZRAP0J3IUT25bpG33m1TwBCCqQOY9IKtCDsVkiOxQQdR6we2dlR+ws7FMYQObKsgJFUyB8Wtev2W+hYB1dtoxFa4mMaJQqb6PDRmPeIH+X1t/gQ8PhzjuVbw+wwAAAABJRU5ErkJggg==\"));}.bk-tool-icon-clear-selection{background-image:var(--bokeh-icon-clear-selection, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AUGEhcuan3d3wAAAoRJREFUWMPtlzFP3EAQhd+b3TNSzg0N5TWXLkJQUUaKhIQ4fgP/g5ArrriE/I3opEgRrZtIVJR0FJQ010SioUmEZHtnUpwN9gWHGA5BJCy58MraffvmfZ41v3z9hqe8BE98vQh4cgG+Ydzmnrng8efvQJNi/uN7dznx/B3ggtfhf4ehNdUttRzBDIm/2VTiiWCG1HK0nc+3UWtq8BQIiEEakEQOADBIA4QCQmBqoHBhFNR27ikQSmGdYCdTqCpEHMDZmEKRWUBEv1gBDg5SzRJnpopILWICgWuRYflLamuzxB2BmtYqSRIka5VWU8QduXO+1hRc5YZu5GAwmP2ZJzND0IBu5HCV2+NQcAhAVRsnC2IbPzPdSjzd6to6VtfWkXi6YLaVWr7xoAwkfpb8MnC3SH7rKSMBe4M0jA/OTicFIbtCGRIyNbURhcf3ErCd6YwA1m0HgAxhw1NGQnlXBHG4kylVlSJuH0RfIP2CkL2I/qS1gIAAQiBl1QwFggIHtyxgrxK5PgyfC0JWKoT0HLh8LwoietB4TYKaIl7yeNURxB05UtMxDOcVQlZIrlRKdK6m47gjR/fuBRQihyLArtNeJD50Izcx2Eczu7iFkIug4VM3cpOr3MKDekFED0fWUHv9Zq0kpLnridjhY3XDg7NTN0jDrhO3X7O9Wg7wwyANu4mnayNg3gmbu0tCNoUyBNGv2l4rB9EXynA7082FOxAQLhU6rQVO9T2AvWowFToNCJcPORGxIRcnpjZSKATSU9NxvOQnAPArDSaQoUKnNI4iufkGtD4P3EHIcWZhz4HLceSOyrR3Izf5memPAL2cX3yhAkonysZVaWLBkd9dw1Ivv2a/AYPkK+ty1U1DAAAAAElFTkSuQmCC\"));}.bk-tool-icon-box-select{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-box-select);-webkit-mask-image:var(--bokeh-icon-box-select);}.bk-tool-icon-x-box-select{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-x-box-select);-webkit-mask-image:var(--bokeh-icon-x-box-select);}.bk-tool-icon-y-box-select{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-y-box-select);-webkit-mask-image:var(--bokeh-icon-y-box-select);}.bk-tool-icon-box-zoom{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-box-zoom);-webkit-mask-image:var(--bokeh-icon-box-zoom);}.bk-tool-icon-x-box-zoom{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-x-box-zoom);-webkit-mask-image:var(--bokeh-icon-x-box-zoom);}.bk-tool-icon-y-box-zoom{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-y-box-zoom);-webkit-mask-image:var(--bokeh-icon-y-box-zoom);}.bk-tool-icon-auto-box-zoom{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-auto-box-zoom);-webkit-mask-image:var(--bokeh-icon-auto-box-zoom);}.bk-tool-icon-zoom-in{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-zoom-in);-webkit-mask-image:var(--bokeh-icon-zoom-in);}.bk-tool-icon-zoom-out{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-zoom-out);-webkit-mask-image:var(--bokeh-icon-zoom-out);}.bk-tool-icon-help{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-help);-webkit-mask-image:var(--bokeh-icon-help);}.bk-tool-icon-hover{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-hover);-webkit-mask-image:var(--bokeh-icon-hover);}.bk-tool-icon-crosshair{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-crosshair);-webkit-mask-image:var(--bokeh-icon-crosshair);}.bk-tool-icon-lasso-select{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-lasso-select);-webkit-mask-image:var(--bokeh-icon-lasso-select);}.bk-tool-icon-pan{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-pan);-webkit-mask-image:var(--bokeh-icon-pan);}.bk-tool-icon-x-pan{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-x-pan);-webkit-mask-image:var(--bokeh-icon-x-pan);}.bk-tool-icon-y-pan{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-y-pan);-webkit-mask-image:var(--bokeh-icon-y-pan);}.bk-tool-icon-range{background-image:var(--bokeh-icon-range, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAABCJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjMyPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4zMjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxkYzpzdWJqZWN0PgogICAgICAgICAgICA8cmRmOkJhZy8+CiAgICAgICAgIDwvZGM6c3ViamVjdD4KICAgICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMTgtMDQtMjhUMTQ6MDQ6NDk8L3htcDpNb2RpZnlEYXRlPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPlBpeGVsbWF0b3IgMy43PC94bXA6Q3JlYXRvclRvb2w+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgrsrWBhAAAD60lEQVRYCcVWv2scRxSemZ097SHbSeWkcYwwclDhzr1Q5T6QE1LghP6BGNIYJGRWNlaZItiFK1mr+JAu4HQu0kjpU8sgF3ITAsaFg0hOvt2Zyfvmdsa7a610Unx44Zgf773vvfneezPHNzrbhn3CT3xC3wPXYOC8LDzqdi8YY/gwh4BeknS/2th6dr2kf94AOp3OFyWgMyziOPbMDxV9FTtJnl1ut795Xd0/YQ0/vtYQwMT1KXWCfr2IjOWwtNehwN4xL9ykTrm6Pzl58yLn3J+mKh9mXbT3uRjGEDph+O8/TjfP5dBp7Ha7AX7O3o5nZeD/0E/OGyXntDgzA0X6qmCnrVutVlrUWV9f/3xo+pwhGDhvEPHOjoxnZjJggXmMHzBQ7NGNp9vxk61fr0HR7e/u7pZzCGHlc7qwBYYTT7tJYSx1AQzppyFPft5apta9w7SKcn0b7P7+/jCsDQ5mbc0dCmIJGDN0ehdcjsmkm6A6KUeKFOTE11PLxrC7Ukqh3ylL2fT0NAP9q6ur6rRCJJYsbKB0JsbCKMuy+xREePDyxQPCz+Crlw062QcA5wBOOt1l6vIl2WiI9F1fN6Q+BBqit6hEC4Hk08GQJMn4myjSP7RavVxgdaVUh/3U6HCMsPr9pYnJKRziHtWQ+un58+hGs6nsjQSjpuTyKGN3CX+FBwHXSiEVgjP+O8X6N12kIePES+GzTKAkGbNp8yJsGUMVzz8jPKReiyAQRimy5/cjye5RpF8utFp/+nwmT7d/NMzcFkS7yjJNGDaPURQxIQThEQy0SyF4l5WJYYhBa816vZ6dU7A6CAhbZVow/pDe0O9hVOoCi13r4BgBAvJHqMSQL2vE/iH6IAXEwgrRVUmBoRRwnwJQT98xEeVeSUyB4dJ5nwJBKdCFFGRmUCcu7rwIYypCTblaChuNBhWODrman5ub+4v0rMNBt8z6Ezh7GksJQpCbm79cMQE7QBFm/X6f0rjWnv8WRYg/QdbUpwDAEBy8vPyA8rNGzg3a8MiElwiM7dAtRqNoNptjGPM1laVxP9umWEMGLOKhKUOJDtBwDmzsw9fC/CzHr9SGuCTi2LbbKvVtmqXpCjMihBFa79Wrt5fGx9PDzc3fmu32Lf8qFliwU9emKhBSp+kRKn/hu9k1COEDbFdt/BoKWOAkuEbdVYyoIXv8+I/QK9dMHEb1Knb7MHOv8LFFOsjzCVHWOD7Ltn+MXCRF4729vWMDK+p8rLkvwjLg4N4v741m5YuwCI9CvHp1Ha8gFdBoPnQAkGsYYGxxcfEI7QQlFCTGUXwjAz4tWF+EpymOWu7fglE7qsOvrYE6g4+9/x/vhRbMdLOCFgAAAABJRU5ErkJggg==\"));}.bk-tool-icon-polygon-select{background-image:var(--bokeh-icon-polygon-select, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEMEjc1OfiVKAAAAe1JREFUWMPt1r9rU1EUB/DPK0XbqphFHETo4OCiFhwF0V1KHbRSROLqon+AUMVRRFBwEbRFMBiV+mMW/wIxi5OD1kERRVKRJHUwLvfBTZrU5OWBGXLgQu7Jfe98z/ec7z0vKa88b2q1BDtRHdAPBaylm1NzsxsOjPnPNt6WSWprbft+/c3I3zOAjhT1Y4+fvcjEQJIXnVECSa+AhqIHqlHH5lWCZoe+Gk4GRgDG86j9SAUdlDBSQaZhlOkuHyoVdJmsw98D1S5fM4NYM1LCpqM+Lwa240oLgmZzpVZvzKT75VLZcqksSZKWlQeAy/iORVwIvh31xvotvK7VG3Px4aWHj3Jl4C2uYSvq+Bn8v6LLbaVWb9zsBiKLCvbiNG7gLm7jAYqbPHMJMziZ9lsKoh8GtqCEVVzHftwJn+TFHp4/hg8BSCYVfMOZoPEv2NZGdy9WCGUr9toDR3E2/H4V6nwRe/BmgN65H1ZhvMuB3XiKIyFoGefwO6ysVkUlrNUNsyAK/jli533Q+Y8cJFvAeXyMS1CI/jiMr/gUtD2LQwMGr4R3p7bY3oQHQ5b38CT4D2AXXg6YcQXHpyYnlqKsi5iOAVSwL9zd7zJ09r+Cpwq72omFMazjT9Dnibym0dTkRDUKrrgwH7MwXVyYB38BstaGDfLUTsgAAAAASUVORK5CYII=\"));}.bk-tool-icon-redo{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-redo);-webkit-mask-image:var(--bokeh-icon-redo);}.bk-tool-icon-reset{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-reset);-webkit-mask-image:var(--bokeh-icon-reset);}.bk-tool-icon-save{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-save);-webkit-mask-image:var(--bokeh-icon-save);}.bk-tool-icon-tap-select{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-tap-select);-webkit-mask-image:var(--bokeh-icon-tap-select);}.bk-tool-icon-undo{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-undo);-webkit-mask-image:var(--bokeh-icon-undo);}.bk-tool-icon-wheel-pan{background-image:var(--bokeh-icon-wheel-pan, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEMEgswOmEYWAAABddJREFUWMO9l09oXNcVxn/n3vc0fzRjj2RHyIZ6ERuy6CarxJtS0pQSCsXNpqGFWK5tTHAwyqIGN7VdEts1LV04BEoxdlJnUbfNogtDCYWQRZOSxtAUCoFiJY0pWJVUjeTKM9LMe+9+Xcyb8ZMychuofeHCffeee7/vnXvOuefYlV/+mv932//tb91z/Y2rvxmMHQ+4FcEfOIGN4A+UwDDwoQScc7vM7AIwB8yZ2QXn3K77Ab6OgJnVgeOSbkqaBiaACUnTkm4Cx3OZzwf+qzcRQup1zNZ9RwDe+0YI4YKZTUn6zCGSMLOfAF/03r+QZdnyfwO+ePEiI6N1nPMgMDMkETLRbd2mXG8gCbd9YiIKIUxLKoLfBN7I+80+CUlTIYTp7RMT0b3Af37p8kh5y9gZcy4Fzt+5szqSaxkzUR7dwtrKMmaGW242d0t6vrD/He/90865o865o977p4F3Ctp4frnZ3L0Z+OryUrVSrZ0z8ZxhHjhcq1XPrS43q/0flDlK9XpPA2ma7gMeyvfPx3H8TJZlH4YQWiGEVpZlH8Zx/Awwn8s8lKbpvmq1ahvB641SXNk6dhLskNA2MIBtwKHK1vGTW8bKMRbAMgyPqWeETxUM8VSSJAv52JmZA0iSZMHMThWwnipXKp8hsLLcSaIR92oU8xjSayCQXotiHotG3Ku3m+0EOQwPQCDggMf7BzQajSs5eAk4B5zLx4O1vD2eJMmAQKliscgASJMw21pansFs1swQ/DNLmUmTMNuXX+taXHTDaj5OW612R1JZ0nFJJ/J+XFJ5aWmpA6S5bHV8fHsPHFU6q3pJCjtFxtrKMuXRLUUXXxdrRLazFOtUolZlsGhmACsgnHPTwJnCnjP5HMBKLotzxsTE9rgDL0t6LoriKsDIaB31ZEK+JxQJRHFUBR2NqLw8OTkZR0OC0ntm9k1JWU7OA4vD/mZ+YfElsANmNEKi75vztzB5M8uAr+bx48me88g757PQ1U5zNg52YH7hX8l6f+4Fi3c3BqHNmkI4YQOV2MGCNu9qHPYCewfzbrC+XSGcWEcgTRKA3wFfyzdDz5d+D3x9CIcfA4eBbQS9LscskgfLnHNPAnslvS/pbZDHLLPADpx9N9fqpSIBH8cxWZY9m6bpb4Ev5fN/iKLo2TRNgdx/eo8Wk5O7Ts/N/SOSdMjHdj4kmgkIEJLJzPZKetvMTkIvFLsR25Ml2gfuF5M7vnA66sdooJYkCSGERe/9VAjhzRxoKk3Tvg3U8nulVqvx8cyNpER2umM+SdOkbc5B8JhpqBdIgTRR24h+lpKen731aRIN7thscH9Zlv0d2F8YD2TIX7F2uw3A7ZWV1a0TYz9ca8cJZHRbuRuaDfUCw9/qJHamPOKToAwHtHN6lMvlSkH2o7wDMDo6WuGuQbbn5+YAKNcb3J5fSvrhtTY+vsOPuD1IOyRhMOkj9kSx29HfXB5RUnS964NT2+3vbGbxG9auO2cDNuV6A8NTb5TitBuOpQkfYD2vwOxgmvBB2g3Hto5X42EJyVsFlztbKpXGNgqVSqUxSWcLU2+tdToa9hasLjfPYlwGa+bTi8Dl1dvNsyvNtQQL9MO2w+HM7BqwlAtPdrvdq9773WAVsIr3fne3270KTOYyS2Z2bbXdHhogKmPj7YWF+VOSXs/v/9KdO+0fVBrjbRkgB/KIDBnYu9f/7D+ZmfmRxPd6qwB8YmZXcq1MAQ/nJhTM+OnDe/a8+PGNG9lm19V/D1Qw7HXZlcRa69+U6w38l5/4ipxzf5X0CPBILjcGPJH34pVcc8692FxcXLlXRnTwwH7+9P4f8aWe3fY59LIqo1NMyQBCCHNmdgx4BegUWefjDvCKmR0LIcz9L8nokSNH+PRvH4HC3YQ098pSbevg24qlmZmNmtmjkg4D3+j/tZldkvQXSa3PW5ptlpL3ZaIN99OS9F7+IgKUgSyEkNyv2nHT7DZX0dr9rpjua2l2r4rogRAYVqZvnPsPqVnpEXjEaB4AAAAASUVORK5CYII=\"));}.bk-tool-icon-wheel-zoom{background-image:var(--bokeh-icon-wheel-zoom, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEMEgskILvMJQAABTtJREFUWMPdl1+MXVUVxn/fPvf2zrSFmUKnoBCUdjRoVaIxEpO2JhilMYBCtBQS2hejpg1Uo2NUrIFAoyGmtiE+GHwQGtvQJhqDmKYRBv+URFsFDNCSptH60DJTO3dKnX/33rM/H7rvsDu9M20fDMaVnGTvtb69z7fWXmvtc/TEzqd4OyXwNsv/FwFJQVI/sA14SZKRLOlPkr5TrVYXHz70quYkEEK4TtI2YAgYkrQthHDdhV5uuw+43/ZrwCbgRttgY/tjtrc0m83X3/f+D6ydnJhYcB4BSZcBA7aP2d4ELAGW2N5k+xgwkDB0IH19CGGH7R8B1aQeAf4KvAw0ku4K2zu7uru3ApdPEyiKohd4TNKjtjt5h6RHgccSNrddbvuHtm9Jqoak7xVF8WFgdavV+pSk5cCObNmXgK++85prCj3z28HKqZMnH7D9YAY4BvwujT8BvCuL1INX9vVt+dfwcCvNb7f9q2RuSfrGvWu/sL2Nf3LX7pzvj4ENSGBPVarVd4fRkZFltjdmoMGiKO4IIWwIIWwoiuIOYDDzeOPoyMiyFLkum7WJCMDztrcrTTrIRuAQZ6NcK1utL4dWq/VZoC8BhqvV6l1lWb4YYxyLMY6VZflitVq9CxhOmL60hhCKeYiV7WMKIXw9jT1HpXw3c+bOAKzOjJubzebJrKQCQLPZPClpc7bP6rMYKtjXth2OMf7tIkr11Wz8oQDc1Fb09vY+kQw1YAuwJY2nbUluAnCWpKkaFl6IQIzxivaR2SYA89sJVK/Xp2x32R6w/a30DNjuqtfrU0ArYecDCEqgLqm94T0dEm9mBG7PxkdDlkBnkhebgIezNQ8nHcCZPL9ijE1Jf/bZZoPtzbavmqNZLbf9tSxq+yoduuJ+SZ+zXSZyBXCqU+d8fvC5yRUrV+0G2j3g2hDCLyXd/+Su3QdnvP/zCuH72LWsgf2k0oHlH2c2odlkxcpVEdgr6aDtjyb8x20/J+mA7T9I6rL9SWA5dne2/GdXLl58qNJh398An85yTMA+4DOz8Dgu6Zu2dwJXJ91ltm8Gbp7Fgb+EEB4aHhpq5CEtACqVyr3AC0AlPS8k3TSmQ2YPhhBuS/1/LpmS9JTtNTHGfwBU2uUALARotVqniqJYH2Pck85pfavVaufAwnQvnHc0McaDKVptebN94QAnJB0EdtjekydyZXqjs/0ZgLIs/w6sy8bnYGYJ63pgERKC05JutT1kOwITwL9tvzlzUQUYB+Zjs2DBgu6xsbGJZHstByZbezregcBXeCsEz1bnzXt5anLyzLq71zDLxTRdVgemdx0fv2e2w5thO5DbiqL4oKT3ZKpnpyYnz+SY2ZpTAPZmJfdIrVZbNBNUq9UW2X4kU+2dcf53Aj1pj2PA7y/6m1DS00A9za9uNBq7iqJYBuoGdRdFsazRaOzKSqye1rTbaa/tlbYrqXQP2X4FIA9/J1l39xrC0v7+w5IeB8XkwS1lWe6TGJAYKMty31tfO4qSHl/a3384I3CDpI+kzC4lnRfrue6GytEjR8oQwlY73gC0L4qlth/q0M1/LYWtR48cKQF6enrC6dOnVwGLEpnxnp7en4+O1i/tszzGOCTpPmB7ahb57QUwBWyXdF+McWg6MScmuoA8OX8xOlpvXGz422XYTsB/SnpA0h7bX5R0WzI9HUL4qe2XbI+dk3xl+V7gxoztD5jRI+YK/zkEEokx2/uB/RdzIfUtueqVN04cXwF8G3iHY3z9Urw/j8ClyhsnjrcS2Vv/J/8NLxT+/zqBTkcxU/cfEkyEAu3kmjAAAAAASUVORK5CYII=\"));}.bk-tool-icon-box-edit{background-image:var(--bokeh-icon-box-edit, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEMEg4QfHjM1QAAAGRJREFUWMNjXLhsJcNAAiaGAQYsDAwM/+lsJ+OgCwGsLqMB+D8o08CoA0YdMOqAUQewDFQdMBoFIyoN/B/U7YFRB7DQIc7xyo9GwbBMA4xDqhxgISH1klXbDYk0QOseEeOgDgEAIS0JQleje6IAAAAASUVORK5CYII=\"));}.bk-tool-icon-freehand-draw{background-image:var(--bokeh-icon-freehand-draw, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADTElEQVRYCeWWTWwMYRjH/88721X1lZJIGxJxcEE4OOiBgzjXWh8TJKR76kWacOBGxdEJIdk4VChZI/phidRBHMRRIr7DSUiaSCRFRM3u88gz+o7Z6bBTdjmYZPf9eJ55fv/5zzvvDPC/H9QsA66Olo9Ga+/MdR+Ljm2/KQIULsz9FqItGdOfJKLhApLgVkiSCGODjWit7QpKWy+TNrFeXvzKVUT8NiTVaIgDcbiCFJ7GiT8WkARXAdYBK0Lbhi/CenArRNskuM7/tgNp4ArQ42dwjf3WY5gWTqC7O/NbNn2Xkfw/YwdSw/We14HP2IEZwX+y9cZ9SH0LmgFP7UCz4KkENBNeV0Cz4b8U8DfgKiDxMWwUXETqLvJpCQpXZfawbzS7t9v5pL19cHBwfja7YA0y/lyCM0+E5hv5+piZXwKYcF23as+37bTXsQVqgkL0p/34fHR7DcBtbetFsBmGDwMOJCggYG55yw7dMlk6DuC1Bdu2RsCU9TYWQq2IoGbsreZ5NzvEqfSBsIsIy8OTbcdgiRHeh4o8AFAEwDakbY2AaCCpH7V9aGhoUUUy3UyVbkPYFuYLDlUZH8XBpwxkK0Dbgxg5HcVi0ent7a0RULMIozaHBSMfF9b2SzdutFcFB2FkwMIJOG6qfteXOa1nHZ48tyefuwyfT9s6wtzZ3t7eZse2DR2I228TtHXzuWCx9g8MtK5cuHCZTH4tiHEOa4xFngvTyS8f35d6enomiCi4/foEXBkZaQuukChL4FYA2Whd7YcC4gEdW3CpdL3LtGAVCVYJywEyTpAuJKeMOKXZs/Bw947C50KhUFOG4cwz35cjWNBlHGeD53n3xsfHP/T19U1qciggar8Fa4I3PHobIotBWBtc2hSiChyZxVzM53Pv7FVH6Tp3uVy+g0r1ImD2GjIrQGYIxjnfuXTZGICS5k/bBwJoubwEFX4TLah9EXomJGMA3za+f9913Yl4TnzsDQ+vE6YTZOjHh4ngibstt1pzQwd04F0bPStEBpXqRoBeQ/AKghfBnOEKgS+Q7z91Xfdz/HGKg8Ox7z8iYD9z6wqTkZFgnvhMGP9VZ2or1XVkPM9z0mytSfVsHa1RLBZbLoyNzUnK+ydz3wC6I9x+lwbngwAAAABJRU5ErkJggg==\"));}.bk-tool-icon-poly-draw{background-image:var(--bokeh-icon-poly-draw, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEMEjglo9eZgwAAAc5JREFUWMPt1zFrU1EUB/DfS4OmVTGDIChCP4BgnQXRxVHqIJUupp9AB8VBQcRBQUXIB9DWQoMRiXZzcnQSA34A7aAuHSJKkgo2LvfBrU3aJnlYkBy4vHcP557zP/9z3r33JdXa647N0kHSZd5Nn0rSxc8G3cXp85sMcnZZ8vge3osZ+l3vB8CWFA0iL14t79h210swAjACMAIwAjACkB90D/8/GchI9ve4nPwTBh5E9ws7OepzGWb9EddSn51Op9ZstadSg4VK1UKlKkmSDSMLALewiuNh/hVJq71Wxttmqz0dG88vPc+MgWP4grvYG3SLOBrZFFFrttqPe4HIDxh4GSei+98iSlusuYopXEAjBtEPA3tQwUpwluAbDm4TPJUz+BTW9l2Ce6G7L0X/Bw8D3T/7SKKIDzHg7QCcxjvcQAEtXAnrrg/RP0/DKPbqgcN4iVOR7gcO4dcQgRuoh7HSqwlP4n20m63jJu5n8MkWMYfP3UowhzdR8FU8w9iQwevBdyq3/27CMRzAE5yLuvsRLg+ZcR1nJ8YL81HWJUzGAPaFZwe/Q5MdyYDyNHgjzO90YyGHtVDncuiJchaHw8R4oREFV5qdiVmYLM3OgD9k5209/atmIAAAAABJRU5ErkJggg==\"));}.bk-tool-icon-point-draw{background-image:var(--bokeh-icon-point-draw, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEMEiERGWPELgAAA4RJREFUWMO1lr1uG1cQhb9ztdRSP7AF1QxgwKlcuZSqRC9gWUUUINWqTh5AnaFOnVPEteQmRuhCURqWsSqqc9IolREXdEvQBElxtdw7KURSFEVKu4w8wAKLxdw9Z+bMnRmZGXfZ29//II8th4WwGVNyIoQLYB5vxA9Caq04iUd9A+7ZlsNC2I7TdSd2hZXMJKlnTqp9jtl/GBaqoyQ0noFKpUIzBicYYc+DEFpxkglc4oVJa5gvDn8v1xV2irG3FM4NSVwjUKlUaMcpJhCGmSEJQ6QGD8M5WnHCd8+f3QCXpPLx8WNwv0j6Bm9FMK7FJ3WBE+R/2t7c/GBmFvSBrzRTCsyTDjXrxUgEMtpxynJYmJoBJ4VAybwVARgvL7Oik0okCodnKpVKX7P0leiVMb0VvbJT+upznK4vh0GIeQwwQStJkHQD3MwsCALTJRG7Qrdrj5m/djgYaIa0hlkRdJk26XEgC9txurccBtVW3IudBImmZuACUP+ZlIDBt9FKcubYNTcAH/X0RYM1E7utJPlqe+uZzPxUcEkiSS4sTT95n15Mud0xWC0o2PAWOCdK3KYZlFxfM+tHOcnMzNr1es18ug+cgsVjP4yBU/Ppfrter1m/+l0+zYygML1xRVHU7TSb1cSzBzoBzszsH+AMdJJ49jrNZjWKou6wBnwOzcyndBpNbuueURR1Dw8Pq35p9cc5p/Dy9Dypt7jXrtdGwQECS9NPhr6Gq6txUzNigE6zydLK6lTw12/KT4FGFEUfJX2YJNONq5tVs4ODA7sD/DnwJ/BoADZuE3tHFs12dna6d4C/BI6AlbyzI8ii2TTw12/KK33gb2cdXsNZoAntbZC2SeO4c9592k/5eNQbiwvFd1kJuFGwLJr1wSPg/SwpvyFBHufOeXcFeAlE97U/uCxOY+P3b+Bn4B3Q+L8EdJfD4a+/AbC4UBzPxiPg3wlHZquB28Cn2IuR9x3gr3uV4DbwfvSDOvi4uFA8BDZmIRHkjHpS9Ht9iRqd8+5G3g05mAGcQbsdiX5QJ428G7Kygo8XYdb1/K4NWVmjzkNge2sz84bs+ELmpDDLtqWsNZBXgvmw8CTtpWVMT7x5YWBjLARnwZfKQNYN2U2LPvrh+5nBt7c2M2/It9bArCTKR8eZN+SJ13AScPnoODeRdqNenH+wul5w2gUr2WUjMFAt8bZ/0axX/wNnv4H8vTFb1QAAAABJRU5ErkJggg==\"));}.bk-tool-icon-poly-edit{background-image:var(--bokeh-icon-poly-edit, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gELFi46qJmxxAAABV9JREFUWMOdl19vFFUYxn9n9u9sCyylUIzWUoMQBAWCMdEEIt6xIRQSLIEKtvHe6AcA4yeQb7CAUNJy0daLeomJN8SEULAC2kBBapBKoLvbmdl/c14vdmY7u91tF95kknPOnHmf95znPc97Ro2OTeBbdjFDT3c32ZxVHUOE9kSMB0/m6ExuoJn1H+ur6Y+OTfD50SMN5168OgrAlyf7CfuD+z7+iDs3p8hkLUQ0iFQ/yFl5Nm/qonfHVva+s32Zw9GxCYILsZ08tpNfBhbs+1YN4OH9+7huGdECSBVfqUosbsllfmauBqiR+cCNwOr7AEo8pPHJnymXykhg5fUWjoQpl0vVvhZhbSzGoUOHqgBlt6B6uruj2Zy1E9jo0fhfeyL2x4Mnc8VErK0KUEOB64JSyptfG4RSytsJjUJVxw2lsFy3urL9nx1Qd25ObctkrVMi+jQivd7U2ZyV/3Hzpq7h3h1b/7p9Y0o8v8rwAbTWrGpSocN/FGDlbAI0Rl23PCBan0Ok158H9Ipwzi25A/Mzc9Gl/BYx/E4kYqC1NKRARNAaDCNUM27Z+Zr+ouXs0q4+LSLBHPYCFkTkC6uU39kwCdsS7WRKmaYUiAhdnZ3MPX2K4+QjQI+C94A93rMzm8ltMwyDeDzWjMZeEb2pYQDdW3vITU2jtUZ5QThOPgm8C7wP7J15OPsBsB3oWpGnVWisCeDS1VHj4vBI92+/3tgB7Ab2AruAXiDBK5oIOkhtkEYRNRuJhObrd8Dl9ewf4D5wG7hVLpen29vb5wzD+BrkbBMaL3d1dk5nsrnlFDTTFWAWmAZueWD3gCemGde2k2fw1Al1YXhEvjozoO49eczdqekrWmsc2zlrmvEKOGoW1GUjFLqSk2KpJrCLwyMCPAP+BO54QL8DM6YZX/ClsP9YnwKkXnIBP4jdIpJRpdJTCYdMwwi98KU0Hjc/dDILNyUcwTCWdOSMJ0TRmBktGRhLugu0xyLk7CIqVNm+0bGJptl1YXikD0grpY4Rjc4a8Fbgdab/6OGbAJeCUuyJnnHmZH9pbSyGuBXV8NUwlUpR1EWyixmSyTWEwqGlJ2Swbo2JXbAAfgDGgGQA9I1A9t1tlq0AxrXxn0ilUpw4fhQqYkH/sT41OTnJJwf2s6FjI5mshdYa7bqVR2uezr9MJmJt14FvGrh/O9D+e6UkM/xyCuCqEKCYnJyUTKFQrZDHjxzGshwWLQcRsOz8Hi85P23id0ug/XilAMLBmm4tPGdoaKjSH5+oAGrhwvBI9SjZTn4QSK9yenoD7dlrExPoJlXW8G8ytpNHxRKk02lGxsdRKFwXLNvx5yY94HQLGhGk4LFCYQSqaE0AwWM1eOoEbR0dKBSW7bC4mKuffxs4D/wCLKwQQPAUzIkslfp6cVomROWSolh0GjldAM4nzDi2k9/i5UAzC9aKfwNJ3zgJg9YEvN6+C7SHgKm69+sD7RfNnKTTaZRPQfAut4oFV//IS7gkcB34VlVo8kGzphlfB+DU+TfNGBpZtRastvrvARJmfMF28ge9sc2B9/PNnCilMIDwK6y8/ow/Ai4kvILTljAXvDvEvrqKSUs60KolzPjBxspavQD2tKqCAGF/Ba+xE/Wbilu54wZV8NEKF5fXzQHl/bh4hUsE0WAXSlDMYcQSrQXgCmsTseXHsJkNnjqBFGwKJaHsKlxtUHYVhbLCzr1kaOA4bcn1y1Swmb+iLpJKpVrfgdpfsiVVCYcgluwgnU7jEgJ4s5UkLFtWYyHyEg0/N1q1tmQH+YXnAMFr97Nmv3p+0QsHQRsF8qpBOE5+rb9Nkaj50tVQKjqh4OU3GNL/1/So3vuUgbAAAAAASUVORK5CYII=\"));}.bk-tool-icon-line-edit{background-image:var(--bokeh-icon-line-edit, url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAG/3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjarVdpknSpDfzPKXwEJBDLccQW4Rv4+E4BtXR198znCdeLLijgQUoppWg3//Pv5f6FDwefXJRcUk3J4xNrrKzoFH8+pyUf9/f+8J3C7y/j7jnBGApow/mZ5l2vGJfXCzne8fZ13OV+9yl3ozvx2DDYyXbauCDvRoHPON3frl5Imt7MuX8hH0seiz9/xwxnDMFgYMczUPD7m89J4fwp/iK+OVRbiMf6gm8K4bv/3NN1Pzjw2fvwn+93PLzccTZ6mJU+/HTHSX723/bSOyLi58n8jmiqz/798+a/tUZZax7rNCKOakzXqIcpu4eFDe483kh4Mv4E/byfiqd49R2OHzC1Od/woxLD44siDVJaNHfbqQNi5MkZLXPnsMdKyFy5gwwCHXhocXahhhEK+OhgLmCYn1hon1vtPBxWcPIgrGTCZrR5fHvc58A/fb5stJaFOZEvT18BF1t8AYYxZ99YBUJoXZ/K9i+50/jPjxEbwKBsNxcYqL6dLZrQK7bC5jl4cVga/Ql5yuNuABfhbAEYCmDAJwpCiXxmzkTwYwE/CuQcIjcwQOKEB1ByDCGBnMJ2Nt7JtNey8BmGvIAICSlkUFODgqwYJSbkW0EIqZMgUUSSZClSRVNIMUlKKSfTKc0hxyw55ZxLrllLKLFISSWXUmrRyjVAxsTVVHMttVZVHKpRsZdivWKgcQstNmmp5VZabdoRPj126annXnrtOniEAQlwI408yqhDJ02E0oxTZpp5llmnLsTaCisuWWnlVVZd+mTtsvqVtU/m/po1uqzxJsrW5RdrGM75sQWZnIhxBsY4EhjPxgACmo0zXyhGNuaMM185uBCEgVKMnEHGGBiMk1gWPbl7Mfcrbw7e/V9545+Yc0bd/4M5Z9S9Mfedtx9YG7rlNmyCLAvhUyhkQPrNhvO5AJFnrZIR0plaLL5liQYdDi5TubaIokFDkmoFEB8CzxZVxemssDqthPhUblPgW1iQU5g6XwNwyVI7bUFRm035iNziMkgWvEso2SXnsJfveR0Y4SlVF8YWC1pVQhJiQa8JwDvlMNIxAfq3F7GDObHU1LlhzlZaWwNp6BvACxAgInGXlllMGZCpEnZHrGA6GM2718xuFcz7YdUQxzEEfjdWz4GlkcwaonT0pgA6mB25grPILtnSMhuCpsGhmMU6uJbixJs4lbKHqh+wos1jW2rchyGRCIvN9MXu+KAmMSfAlIKVvi/tybhCPJZCu2Ow9pLdyo427+X2ovMBmKNu8PA0zgl3fS0PB1DWWkVYB47bkyiJHhkFPzTzCjzn4Dq1mqoIWzCmcDGsHQmQAQdEHsixK1IXESd5rLU7THVJNV8obHS8sZeN0G5Jdt5pQTVKCCbgK1hItTS8o92iEZpuWJ/oC2r/0+zTmhvFXoaMVKRe27altDtid6OvG1hENVwBnC61KKugNoemOiPCCNb3GoHAZOFuDxxPsD+07nbSPcr/o1Zmc4jARhotrA5F5ZcjP9rPk90vR8A+k028A+8+5wKlHVID542sMzMCuXktkRzUCpE+xCBZywjNcJITx0II9x5948CekBl4XaC5OCX2nCyObdwN3HwQh5DWL/BBEkhDYHn/vpXNgZkVTZs8rj+HO8JFC6qvDVhgAEQSYCDyC86rMhG1WPzAVB9ZldDWG6EzDcFiqJBDvFS8mXDv3SK2LPoguVB2kwUx7UL5KqZWiEzocsbvSjNnaYDNtcYJuA5cDcsrvHd6yCxGjqvl9+wh3Qh8Kc9py8sNW8ncU8qwxdPj1qIGfrPqlXeoS4/JLa/LwRLTCtxuSoZUT+2Su6kXW3QNacYQbId6NUKVbROpviybFSPQQL9lhB2MamEnFyB9Y+hrG1+xBg+L0QG2TZdTdlcsBdq9oHdt9Bu5/IM9+Nfh1AwrSqlboTA6Bgq568A7UfbaMrZjoQZhQphofvNw93+bN+5X7FYKBgLmRid+tSdV6c02A4R0cHwKobmoMt5+6WI9XNISFIywpf6RMd5/a91vE78FzVHIFmxud4woyJx76OMTCa4yhgN3iJO2VfRPFMv9sYTxFzU+1eWeYS52pwOoSJldZY6koib4P1O427rbeUrNZfu44hWjz5ZSuu/vKPpimoXbLkfxWSPetvxDWG5jQSaZCxA3ad+p6rlttDhK+YwwK1LHVe0drDtorc5vnQ1247g58vewDtU7L3DRwrG4dhCUDRKKOtYr2dXHtpt+33d1WZmfkAHdl7Q8ENF+CNgB+nOw29n5F7SeNo/ckbu4laLTCdqJLHjmhJbKzmrCEX7zULrhefuHmu0V/1nbP1pnb6FaT7sOxn4pvWkfrYhYtCeJ4Xv+kOXrroIs1eHWXN1/AfzaY94ms5vaAAABg2lDQ1BJQ0MgcHJvZmlsZQAAeJx9kT1Iw0AcxV/TSkUqDnYQUchQnSyIijhqFYpQIdQKrTqYXPoFTRqSFBdHwbXg4Mdi1cHFWVcHV0EQ/ABxcnRSdJES/5cUWsR4cNyPd/ced+8AoVFhmhUaBzTdNtPJhJjNrYrhV4QwjAgGIMrMMuYkKQXf8XWPAF/v4jzL/9yfo1fNWwwIiMSzzDBt4g3i6U3b4LxPHGUlWSU+Jx4z6YLEj1xXPH7jXHRZ4JlRM5OeJ44Si8UOVjqYlUyNeIo4pmo65QtZj1XOW5y1So217slfGMnrK8tcpzmEJBaxBAkiFNRQRgU24rTqpFhI037Cxz/o+iVyKeQqg5FjAVVokF0/+B/87tYqTE54SZEE0PXiOB8jQHgXaNYd5/vYcZonQPAZuNLb/moDmPkkvd7WYkdA3zZwcd3WlD3gcgcYeDJkU3alIE2hUADez+ibckD/LdCz5vXW2sfpA5ChrlI3wMEhMFqk7HWfd3d39vbvmVZ/P2aecqIM1FFZAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AQdDBkQmV+argAABM5JREFUWMOtl9trHFUcxz9n9jYzm7Tb9JIWGtqUllLwVgRBQWl90S6lTaGmF6E2/4H+A4r+A0offdlWodL4kEZw9bG+iC9iKqLF0os0EBq02dtcdmdnfj7szGZ2M5vulv5g4JwzZ873+7ufUfMLi0RSa1TZNzVFrW511xBhzMxx79EyOwrbGSSzZ073zOcXFnlv5lTi3mvfzAPwwYVZ0tHiq6+/xu+/LlGtWYgEINL9oG657N41yfSRgxw9cHjDgfMLi8QVsR0X23E3gMXnkXQJ3L9zB99vI4EA0sVXqsPF93xW7y73ACVJBJwE1j8HUBIi3Sz/QNtrIzHN+yWdSdNue915IMKWXI4TJ050Adp+U+2bmkrV6tZeYAXwEJExMyf3Hi0rM5fvAvS4wPdBKRW6vZeEUiq0RIBCddddpymu0+rRbPvEzkPVmmWLBA1EdGAbYNctt7V712QwfeSgd/uXJQnPVVoEEAQBTxXpuEMELNtNNFW1WrsrQdBCRImQEeE/wBUh53v+7tW7y5n1+BZRIoJSioXvy3itdgclURSZTBrP87AdV57G1TT0d4GPgC+Bw8Ca7bifATsTgzBvjlH1qgNdICJM7tjB8soKw4jtuD+Gw3c229e1wF+P/uHPpT86rhBBRHActwAcAl4EjgIvAYcFJnlOoq5dv6EBU8AR4OUQ6AVgGjATwuC5YUdZ4A+z+1mBTUM/AKwqpZSIpPfu2VP7+/6DYEMMPE9N83lzq23ZWwxDd4GaQnmgUloqperSCpKC8HGCXz8G7NANU8CWUKPzsUDbyLPVyjYC39e0VMZx3Ccoha4b4lQqbUlnsBqNWCXpEMgKfA38DNSBcdPQr4zlMtTtFiqlulmQmJv9ks2idUZGZMjZmZMAfBUvxWHR0y5dmPV2FcbPG9ncFdPQS3nTuAJQLBZpBS1qjSqFwjipdGr9SWlsHTewm9ZmnngMKAaV9nBd+/bmdxSLRc6dnemm3+yZ06pcLvPGW2+yfWIn1ZpFEAQEvt95goCV1TXMXH4zAt4woaRF7RTAVylAUS6Xpdpsdjvk2VMnsSyHhuVEZTh+xgywBhwLfZIdKRfj7dWqPGFubq7T428ukslkaHttLNsZ9P3nwIfh+DhwS4EO9DA0zByBCE2n1fPxpQuznSCaX1js9nFp2pjbtqGhobQ0jUY9CbgALERah3IM+El1rNqTaqaph5W1uYGAFrfA5YvnyE9MoFBYtjMI/BXgQR/4pqVDZL3V9/cYrX+x7SnsXh/H5TLwW2iBQbVLNgn65CDsrSPOIJOXwmdQ4fRHrZilUqmXwNXrNzbbfxv4ArgFVBLeJ95oDEMHwHHcvvUcRqEwuBf0SSUEB9gfxsAgAkO1kcj/WvwKPaR8EhvPAUvRtdIMtR1FtBH37w8DEeChaehXw/xfAnzHcVOjEkhHrIe0Qlz7T8PuWLEd9+2w9KphgUUgQJ7JAgAPDT13NTrJyOYqIilrlEwQv/NPMTSByxfPIU37eCqtq2zWmPYDjbavaLYVdn2NuffPjqRJK2hRLBaHzoK+X7L1QE+nIFeYoFQqkTVMaTn2UOe1LWtwEJqGzqgRnS9M4Fb+3XBJGfSrFzW9dBw0icioJBzHzUXdMJM18APwWo6Kmy1O6X+V8UHDotBqogAAAABJRU5ErkJggg==\"));}.bk-tool-icon-settings{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-settings);-webkit-mask-image:var(--bokeh-icon-settings);}.bk-tool-icon-unknown{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-unknown);-webkit-mask-image:var(--bokeh-icon-unknown);}.bk-tool-icon-fullscreen{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-fullscreen);-webkit-mask-image:var(--bokeh-icon-fullscreen);}.bk-tool-icon-chevron-up{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-chevron-up);-webkit-mask-image:var(--bokeh-icon-chevron-up);}.bk-tool-icon-chevron-down{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-chevron-down);-webkit-mask-image:var(--bokeh-icon-chevron-down);}.bk-tool-icon-chevron-left{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-chevron-left);-webkit-mask-image:var(--bokeh-icon-chevron-left);}.bk-tool-icon-chevron-right{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-chevron-right);-webkit-mask-image:var(--bokeh-icon-chevron-right);}.bk-tool-icon-caret-up{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-caret-up);-webkit-mask-image:var(--bokeh-icon-caret-up);}.bk-tool-icon-caret-down{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-caret-down);-webkit-mask-image:var(--bokeh-icon-caret-down);}.bk-tool-icon-caret-left{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-caret-left);-webkit-mask-image:var(--bokeh-icon-caret-left);}.bk-tool-icon-caret-right{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-caret-right);-webkit-mask-image:var(--bokeh-icon-caret-right);}.bk-tool-icon-see-on{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-see-on);-webkit-mask-image:var(--bokeh-icon-see-on);}.bk-tool-icon-see-off{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-see-off);-webkit-mask-image:var(--bokeh-icon-see-off);}.bk-tool-icon-x-grip{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-y-grip);-webkit-mask-image:var(--bokeh-icon-y-grip);}.bk-tool-icon-y-grip{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-y-grip);-webkit-mask-image:var(--bokeh-icon-y-grip);}.bk-tool-icon-arrow-down-to-bar{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-arrow-down-to-bar);-webkit-mask-image:var(--bokeh-icon-arrow-down-to-bar);}.bk-tool-icon-arrow-up-from-bar{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-arrow-up-from-bar);-webkit-mask-image:var(--bokeh-icon-arrow-up-from-bar);}.bk-tool-icon-minimize{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-minimize);-webkit-mask-image:var(--bokeh-icon-minimize);}.bk-tool-icon-maximize{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-maximize);-webkit-mask-image:var(--bokeh-icon-maximize);}.bk-tool-icon-pin{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-pin);-webkit-mask-image:var(--bokeh-icon-pin);}.bk-tool-icon-unpin{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-unpin);-webkit-mask-image:var(--bokeh-icon-unpin);}.bk-tool-icon-pointer{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-hand-pointing);-webkit-mask-image:var(--bokeh-icon-hand-pointing);}.bk-tool-icon-delete{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-trash);-webkit-mask-image:var(--bokeh-icon-trash);}.bk-tool-icon-bold{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-bold);-webkit-mask-image:var(--bokeh-icon-bold);}.bk-tool-icon-italic{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-italic);-webkit-mask-image:var(--bokeh-icon-italic);}.bk-tool-icon-text-align-left{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-text-align-left);-webkit-mask-image:var(--bokeh-icon-text-align-left);}.bk-tool-icon-text-align-center{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-text-align-center);-webkit-mask-image:var(--bokeh-icon-text-align-center);}.bk-tool-icon-text-align-right{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-text-align-right);-webkit-mask-image:var(--bokeh-icon-text-align-right);}.bk-tool-icon-check{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-check);-webkit-mask-image:var(--bokeh-icon-check);}.bk-tool-icon-square{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-square);-webkit-mask-image:var(--bokeh-icon-square);}.bk-tool-icon-square-check{background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-square-check);-webkit-mask-image:var(--bokeh-icon-square-check);}:host{--bokeh-icon-question-mark:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M8%208a3.5%203%200%200%201%203.5%20-3h1a3.5%203%200%200%201%203.5%203a3%203%200%200%201%20-2%203a3%204%200%200%200%20-2%204%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%2212%22%20y1%3D%2219%22%20x2%3D%2212%22%20y2%3D%2219.01%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-help:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%229%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%2212%22%20y1%3D%2217%22%20x2%3D%2212%22%20y2%3D%2217.01%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%2013.5a1.5%201.5%200%200%201%201%20-1.5a2.6%202.6%200%201%200%20-3%20-4%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-x:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M18%206l-12%2012%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M6%206l12%2012%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-settings:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M10.325%204.317c.426%20-1.756%202.924%20-1.756%203.35%200a1.724%201.724%200%200%200%202.573%201.066c1.543%20-.94%203.31%20.826%202.37%202.37a1.724%201.724%200%200%200%201.065%202.572c1.756%20.426%201.756%202.924%200%203.35a1.724%201.724%200%200%200%20-1.066%202.573c.94%201.543%20-.826%203.31%20-2.37%202.37a1.724%201.724%200%200%200%20-2.572%201.065c-.426%201.756%20-2.924%201.756%20-3.35%200a1.724%201.724%200%200%200%20-2.573%20-1.066c-1.543%20.94%20-3.31%20-.826%20-2.37%20-2.37a1.724%201.724%200%200%200%20-1.065%20-2.572c-1.756%20-.426%20-1.756%20-2.924%200%20-3.35a1.724%201.724%200%200%200%201.066%20-2.573c-.94%20-1.543%20.826%20-3.31%202.37%20-2.37c1%20.608%202.296%20.07%202.572%20-1.065z%22%3E%3C%2Fpath%3E%0A%20%20%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%223%22%3E%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-unknown:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M14%203v4a1%201%200%200%200%201%201h4%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M17%2021h-10a2%202%200%200%201%20-2%20-2v-14a2%202%200%200%201%202%20-2h7l5%205v11a2%202%200%200%201%20-2%202z%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%2017v.01%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%2014a1.5%201.5%200%201%200%20-1.14%20-2.474%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-fullscreen:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Crect%20x%3D%223%22%20y%3D%2216%22%20width%3D%225%22%20height%3D%225%22%20rx%3D%221%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M4%2012v-6a2%202%200%200%201%202%20-2h12a2%202%200%200%201%202%202v12a2%202%200%200%201%20-2%202h-6%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%208h4v4%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M16%208l-5%205%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-save:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M4%2017v2a2%202%200%200%200%202%202h12a2%202%200%200%200%202%20-2v-2%22%20%2F%3E%0A%20%20%3Cpolyline%20points%3D%227%2011%2012%2016%2017%2011%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%2212%22%20y1%3D%224%22%20x2%3D%2212%22%20y2%3D%2216%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-copy:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Crect%20x%3D%228%22%20y%3D%228%22%20width%3D%2212%22%20height%3D%2212%22%20rx%3D%222%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M16%208v-2a2%202%200%200%200%20-2%20-2h-8a2%202%200%200%200%20-2%202v8a2%202%200%200%200%202%202h2%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-tap-select:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cline%20x1%3D%223%22%20y1%3D%2212%22%20x2%3D%226%22%20y2%3D%2212%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%2212%22%20y1%3D%223%22%20x2%3D%2212%22%20y2%3D%226%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%227.8%22%20y1%3D%227.8%22%20x2%3D%225.6%22%20y2%3D%225.6%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%2216.2%22%20y1%3D%227.8%22%20x2%3D%2218.4%22%20y2%3D%225.6%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%227.8%22%20y1%3D%2216.2%22%20x2%3D%225.6%22%20y2%3D%2218.4%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%2012l9%203l-4%202l-2%204l-3%20-9%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-lasso-select:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M4.028%2013.252c-.657%20-.972%20-1.028%20-2.078%20-1.028%20-3.252c0%20-3.866%204.03%20-7%209%20-7s9%203.134%209%207s-4.03%207%20-9%207c-1.913%200%20-3.686%20-.464%20-5.144%20-1.255%22%20%2F%3E%0A%20%20%3Ccircle%20cx%3D%225%22%20cy%3D%2215%22%20r%3D%222%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M5%2017c0%201.42%20.316%202.805%201%204%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-pan:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M18%209l3%203l-3%203%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M6%209l-3%203l3%203%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M9%2018l3%203l3%20-3%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M15%206l-3%20-3l-3%203%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%2012h9%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%2012h9%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%2012v9%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%203v9%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-x-pan:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M18%209l3%203l-3%203%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M6%209l-3%203l3%203%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%2012h9%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%2012h9%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-y-pan:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M9%2018l3%203l3%20-3%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M15%206l-3%20-3l-3%203%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%2012v9%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%203v9%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-box-select:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M4%206v-1a1%201%200%200%201%201%20-1h1m5%200h2m5%200h1a1%201%200%200%201%201%201v1m0%205v2m0%205v1a1%201%200%200%201%20-1%201h-1m-5%200h-2m-5%200h-1a1%201%200%200%201%20-1%20-1v-1m0%20-5v-2%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-x-box-select:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M17%2013l-4%204%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M13%2013l4%204%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M4%206v-1a1%201%200%200%201%201%20-1h1m5%200h2m5%200h1a1%201%200%200%201%201%201v1m0%205v2m0%205v1a1%201%200%200%201%20-1%201h-1m-5%200h-2m-5%200h-1a1%201%200%200%201%20-1%20-1v-1m0%20-5v-2%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-y-box-select:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M13%2013l2%202l2%20-2%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M15%2015v2.5%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M4%206v-1a1%201%200%200%201%201%20-1h1m5%200h2m5%200h1a1%201%200%200%201%201%201v1m0%205v2m0%205v1a1%201%200%200%201%20-1%201h-1m-5%200h-2m-5%200h-1a1%201%200%200%201%20-1%20-1v-1m0%20-5v-2%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-box-zoom:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Ccircle%20cx%3D%2215%22%20cy%3D%2215%22%20r%3D%225%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M22%2022l-3%20-3%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M6%2018h-1a2%202%200%200%201%20-2%20-2v-1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%2011v-1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%206v-1a2%202%200%200%201%202%20-2h1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M10%203h1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M15%203h1a2%202%200%200%201%202%202v1%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-x-box-zoom:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M17%2013l-4%204%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M13%2013l4%204%22%20%2F%3E%0A%20%20%3Ccircle%20cx%3D%2215%22%20cy%3D%2215%22%20r%3D%225%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M22%2022l-3%20-3%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M6%2018h-1a2%202%200%200%201%20-2%20-2v-1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%2011v-1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%206v-1a2%202%200%200%201%202%20-2h1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M10%203h1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M15%203h1a2%202%200%200%201%202%202v1%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-y-box-zoom:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M13%2013l2%202l2%20-2%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M15%2015v2.5%22%20%2F%3E%0A%20%20%3Ccircle%20cx%3D%2215%22%20cy%3D%2215%22%20r%3D%225%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M22%2022l-3%20-3%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M6%2018h-1a2%202%200%200%201%20-2%20-2v-1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%2011v-1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%206v-1a2%202%200%200%201%202%20-2h1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M10%203h1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M15%203h1a2%202%200%200%201%202%202v1%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-auto-box-zoom:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Ccircle%20cx%3D%2215%22%20cy%3D%2215%22%20r%3D%225%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M22%2022l-3%20-3%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M6%2018h-1a2%202%200%200%201%20-2%20-2v-1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%2011v-1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M3%206v-1a2%202%200%200%201%202%20-2h1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M10%203h1%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M15%203h1a2%202%200%200%201%202%202v1%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-zoom-in:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Ccircle%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%227%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%227%22%20y1%3D%2210%22%20x2%3D%2213%22%20y2%3D%2210%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%2210%22%20y1%3D%227%22%20x2%3D%2210%22%20y2%3D%2213%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%2221%22%20y1%3D%2221%22%20x2%3D%2215%22%20y2%3D%2215%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-zoom-out:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Ccircle%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%227%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%227%22%20y1%3D%2210%22%20x2%3D%2213%22%20y2%3D%2210%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%2221%22%20y1%3D%2221%22%20x2%3D%2215%22%20y2%3D%2215%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-undo:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M9%2013l-4%20-4l4%20-4m-4%204h11a4%204%200%200%201%200%208h-1%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-redo:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M15%2013l4%20-4l-4%20-4m4%204h-11a4%204%200%200%200%200%208h1%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-reset:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M20%2011a8.1%208.1%200%200%200%20-15.5%20-2m-.5%20-4v4h4%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M4%2013a8.1%208.1%200%200%200%2015.5%202m.5%204v-4h-4%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-hover:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M12%2020l-3%20-3h-2a3%203%200%200%201%20-3%20-3v-6a3%203%200%200%201%203%20-3h10a3%203%200%200%201%203%203v6a3%203%200%200%201%20-3%203h-2l-3%203%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%228%22%20y1%3D%229%22%20x2%3D%2216%22%20y2%3D%229%22%20%2F%3E%0A%20%20%3Cline%20x1%3D%228%22%20y1%3D%2213%22%20x2%3D%2214%22%20y2%3D%2213%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-crosshair:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%229%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M20%2012h-4%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M4%2012h4%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%2020v-4%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M12%204v4%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-chevron-up:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpolyline%20points%3D%226%2015%2012%209%2018%2015%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-chevron-down:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpolyline%20points%3D%226%209%2012%2015%2018%209%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-chevron-left:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpolyline%20points%3D%2215%206%209%2012%2015%2018%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-chevron-right:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpolyline%20points%3D%229%206%2015%2012%209%2018%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-caret-up:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22currentColor%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M18%2015l-6%20-6l-6%206h12%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-caret-down:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22currentColor%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M18%2015l-6%20-6l-6%206h12%22%20transform%3D%22rotate(180%2012%2012)%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-caret-left:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22currentColor%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M18%2015l-6%20-6l-6%206h12%22%20transform%3D%22rotate(270%2012%2012)%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-caret-right:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22currentColor%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M18%2015l-6%20-6l-6%206h12%22%20transform%3D%22rotate(90%2012%2012)%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-see-on:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%222%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M22%2012c-2.667%204.667%20-6%207%20-10%207s-7.333%20-2.333%20-10%20-7c2.667%20-4.667%206%20-7%2010%20-7s7.333%202.333%2010%207%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-see-off:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cline%20x1%3D%223%22%20y1%3D%223%22%20x2%3D%2221%22%20y2%3D%2221%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M10.584%2010.587a2%202%200%200%200%202.828%202.83%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M9.363%205.365a9.466%209.466%200%200%201%202.637%20-.365c4%200%207.333%202.333%2010%207c-.778%201.361%20-1.612%202.524%20-2.503%203.488m-2.14%201.861c-1.631%201.1%20-3.415%201.651%20-5.357%201.651c-4%200%20-7.333%20-2.333%20-10%20-7c1.369%20-2.395%202.913%20-4.175%204.632%20-5.341%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-cursor-rotate:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%20%20%3Cg%20stroke-width%3D%224%22%20stroke%3D%22white%22%3E%0A%20%20%20%20%20%20%3Cpath%20d%3D%22M9%204.55a8%208%200%200%201%206%2014.9m0%20-4.45v5h5%22%20%2F%3E%0A%20%20%20%20%3C%2Fg%3E%0A%20%20%20%20%3Cg%20stroke-width%3D%222%22%20stroke%3D%22black%22%3E%0A%20%20%20%20%20%20%3Cpath%20d%3D%22M9%204.55a8%208%200%200%201%206%2014.9m0%20-4.45v5h5%22%20%2F%3E%0A%20%20%20%20%3C%2Fg%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A\") 12 12, auto;--bokeh-icon-x-grip:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M5%209m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M5%2015m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M12%209m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M12%2015m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M19%209m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M19%2015m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-y-grip:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M9%205m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M9%2012m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M9%2019m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M15%205m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M15%2012m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M15%2019m-1%200a1%201%200%201%200%202%200a1%201%200%201%200%20-2%200%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-arrow-down-to-bar:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M4%2020l16%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M12%2014l0%20-10%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M12%2014l4%20-4%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M12%2014l-4%20-4%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-arrow-up-from-bar:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M12%204l0%2010%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M12%204l4%204%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M12%204l-4%204%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M4%2020l16%200%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-minimize:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M15%2019v-2a2%202%200%200%201%202%20-2h2%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M15%205v2a2%202%200%200%200%202%202h2%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M5%2015h2a2%202%200%200%201%202%202v2%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M5%209h2a2%202%200%200%200%202%20-2v-2%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-maximize:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M4%208v-2a2%202%200%200%201%202%20-2h2%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M4%2016v2a2%202%200%200%200%202%202h2%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M16%204h2a2%202%200%200%201%202%202v2%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M16%2020h2a2%202%200%200%200%202%20-2v-2%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-pin:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M15%204.5l-4%204l-4%201.5l-1.5%201.5l7%207l1.5%20-1.5l1.5%20-4l4%20-4%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M9%2015l-4.5%204.5%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M14.5%204l5.5%205.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-unpin:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M3%203l18%2018%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M15%204.5l-3.249%203.249m-2.57%201.433l-2.181%20.818l-1.5%201.5l7%207l1.5%20-1.5l.82%20-2.186m1.43%20-2.563l3.25%20-3.251%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M9%2015l-4.5%204.5%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M14.5%204l5.5%205.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-hand-pointing:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M8%2013v-8.5a1.5%201.5%200%200%201%203%200v7.5%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M11%2011.5v-2a1.5%201.5%200%201%201%203%200v2.5%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M14%2010.5a1.5%201.5%200%200%201%203%200v1.5%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M17%2011.5a1.5%201.5%200%200%201%203%200v4.5a6%206%200%200%201%20-6%206h-2h.208a6%206%200%200%201%20-5.012%20-2.7a69.74%2069.74%200%200%201%20-.196%20-.3c-.312%20-.479%20-1.407%20-2.388%20-3.286%20-5.728a1.5%201.5%200%200%201%20.536%20-2.022a1.867%201.867%200%200%201%202.28%20.28l1.47%201.47%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-trash:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M4%207l16%200%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M10%2011l0%206%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M14%2011l0%206%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M5%207l1%2012a2%202%200%200%200%202%202h8a2%202%200%200%200%202%20-2l1%20-12%22%3E%3C%2Fpath%3E%0A%20%20%3Cpath%20d%3D%22M9%207v-3a1%201%200%200%201%201%20-1h4a1%201%200%200%201%201%201v3%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-bold:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M7%205h6a3.5%203.5%200%200%201%200%207h-6z%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M13%2012h1a3.5%203.5%200%200%201%200%207h-7v-7%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-italic:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M11%205l6%200%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M7%2019l6%200%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M14%205l-4%2014%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-text-align-left:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M4%206l16%200%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M4%2012l10%200%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M4%2018l14%200%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-text-align-center:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M4%206l16%200%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M8%2012l8%200%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M6%2018l12%200%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-text-align-right:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M4%206l16%200%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M10%2012l10%200%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M6%2018l14%200%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-check:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M5%2012l5%205l10%20-10%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-square:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M3%203m0%202a2%202%200%200%201%202%20-2h14a2%202%200%200%201%202%202v14a2%202%200%200%201%20-2%202h-14a2%202%200%200%201%20-2%20-2z%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");--bokeh-icon-square-check:url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%222%22%20stroke%3D%22currentColor%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%3Cpath%20d%3D%22M3%203m0%202a2%202%200%200%201%202%20-2h14a2%202%200%200%201%202%202v14a2%202%200%200%201%20-2%202h-14a2%202%200%200%201%20-2%20-2z%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22M9%2012l2%202l4%20-4%22%20%2F%3E%0A%3C%2Fsvg%3E%0A\");}'},\n function _(e,r,n,o,s){var c;o();const d=e(51);class t extends d.Model{constructor(e){super(e)}}n.RendererGroup=t,c=t,t.__name__=\"RendererGroup\",c.define((({Bool:e})=>({visible:[e,!0]})))},\n function _(e,o,_,d,s){d();const n=e(57),c=e(51);class t extends n.DOMView{}_.DOMNodeView=t,t.__name__=\"DOMNodeView\";class M extends c.Model{constructor(e){super(e)}}_.DOMNode=M,M.__name__=\"DOMNode\",M.__module__=\"bokeh.models.dom\"},\n function _(t,e,s,a,n){var r;a();const i=t(1),_=t(127),o=t(168),l=t(80),d=t(20),c=t(24),h=t(56),u=t(24),p=i.__importStar(t(18)),v=t(11);class y extends _.DataAnnotationView{*children(){yield*super.children();const{start:t,end:e}=this;null!=t&&(yield t),null!=e&&(yield e)}async lazy_initialize(){await super.lazy_initialize();const{start:t,end:e}=this.model;null!=t&&(this.start=await(0,h.build_view)(t,{parent:this})),null!=e&&(this.end=await(0,h.build_view)(e,{parent:this}))}set_data(t){super.set_data(t);const e=u.Indices.all_set(this._x_start.length);this.start?.set_data(t,e),this.end?.set_data(t,e)}remove(){this.start?.remove(),this.end?.remove(),super.remove()}map_data(){const{frame:t}=this.plot_view,[e,s]=(()=>{switch(this.model.start_units){case\"canvas\":return[new c.ScreenArray(this._x_start),new c.ScreenArray(this._y_start)];case\"screen\":return[t.bbox.xview.v_compute(this._x_start),t.bbox.yview.v_compute(this._y_start)];case\"data\":return[this.coordinates.x_scale.v_compute(this._x_start),this.coordinates.y_scale.v_compute(this._y_start)]}})(),[a,n]=(()=>{switch(this.model.end_units){case\"canvas\":return[new c.ScreenArray(this._x_end),new c.ScreenArray(this._y_end)];case\"screen\":return[t.bbox.xview.v_compute(this._x_end),t.bbox.yview.v_compute(this._y_end)];case\"data\":return[this.coordinates.x_scale.v_compute(this._x_end),this.coordinates.y_scale.v_compute(this._y_end)]}})();this._sx_start=e,this._sy_start=s,this._sx_end=a,this._sy_end=n;const r=e.length,i=this._angles=new c.ScreenArray(r);for(let t=0;t({x_start:[p.XCoordinateSpec,{field:\"x_start\"}],y_start:[p.YCoordinateSpec,{field:\"y_start\"}],start_units:[d.CoordinateUnits,\"data\"],start:[e(t(o.ArrowHead)),null],x_end:[p.XCoordinateSpec,{field:\"x_end\"}],y_end:[p.YCoordinateSpec,{field:\"y_end\"}],end_units:[d.CoordinateUnits,\"data\"],end:[e(t(o.ArrowHead)),()=>new o.OpenHead]})))},\n function _(t,n,e,s,a){var o;s();const i=t(1),c=t(81),_=t(128),r=t(133),l=t(134),h=i.__importStar(t(18));class d extends c.AnnotationView{constructor(){super(...arguments),this._initial_set_data=!1}connect_signals(){super.connect_signals();const t=()=>{this.set_data(this.model.source),this._rerender()};this.connect(this.model.change,t),this.connect(this.model.source.streaming,t),this.connect(this.model.source.patching,t),this.connect(this.model.source.change,t)}_rerender(){this.request_paint()}set_data(t){const n=this;for(const e of this.model)if(e instanceof h.VectorSpec||e instanceof h.ScalarSpec)if(e instanceof h.BaseCoordinateSpec){const s=e.array(t);n[`_${e.attr}`]=s}else{const s=e.uniform(t);n[`${e.attr}`]=s}this.plot_model.use_map&&(null!=n._x&&l.inplace.project_xy(n._x,n._y),null!=n._xs&&l.inplace.project_xsys(n._xs,n._ys));for(const t of this.visuals)t.update()}_paint(){this._initial_set_data||(this.set_data(this.model.source),this._initial_set_data=!0),this.map_data(),this._paint_data(this.layer.ctx)}}e.DataAnnotationView=d,d.__name__=\"DataAnnotationView\";class u extends c.Annotation{constructor(t){super(t)}}e.DataAnnotation=u,o=u,u.__name__=\"DataAnnotation\",o.define((({Ref:t})=>({source:[t(_.ColumnarDataSource),()=>new r.ColumnDataSource]})))},\n function _(t,e,n,s,r){var i;s();const a=t(19),c=t(129),o=t(15),l=t(12),u=t(10),h=t(30),d=t(9),f=t(8),g=t(131),_=t(130),p=t(132);class y extends p.DataSource{get_array(t){const e=(0,d.dict)(this.data);let n=e.get(t);return null==n?e.set(t,n=[]):(0,f.isArray)(n)||e.set(t,n=Array.from(n)),n}constructor(t){super(t),this.selection_manager=new c.SelectionManager(this)}initialize(){super.initialize(),this._select=new o.Signal0(this,\"select\"),this.inspect=new o.Signal(this,\"inspect\")}get inferred_defaults(){const t=new Map;for(const[e,n]of(0,d.entries)(this.data)){const s=(()=>{if((0,h.is_NDArray)(n))switch(n.dtype){case\"bool\":return!1;case\"uint8\":case\"int8\":case\"uint16\":case\"int16\":case\"uint32\":case\"int32\":case\"float32\":case\"float64\":return 0;case\"object\":return null}else if((0,f.isArray)(n)&&0!=n.length){const[t]=n;if(null===t)return null;if((0,f.isBoolean)(t))return!1;if((0,f.isNumber)(t))return 0;if((0,f.isString)(t))return\"\";if((0,f.isArray)(t))return[]}})();void 0!==s&&t.set(e,s)}return t}get(t){const e=this.get_column(t);return(0,l.assert)(null!=e,`unknown column '${t}' in ${this}`),e}set(t,e){(0,d.dict)(this.data).set(t,e)}get_column(t){return(0,d.dict)(this.data).get(t)??null}columns(){return(0,d.keys)(this.data)}get_length(t=!0){const e=(0,u.uniq)((0,d.values)(this.data).map((t=>(0,h.is_NDArray)(t)?t.shape[0]:t.length)));switch(e.length){case 0:return null;case 1:return e[0];default:{const n=\"data source has columns of inconsistent lengths\";if(t)return a.logger.warn(n),e.sort()[0];throw new Error(n)}}}get length(){return this.get_length()??0}clear(){const t=(0,d.clone)(this.data),e=(0,d.dict)(t);for(const[t,n]of e){const s=new n.constructor(0);e.set(t,s)}this.data=t}stream(t,e,{sync:n}={}){this.stream_to(this.properties.data,t,e,{sync:n})}patch(t,{sync:e}={}){this.patch_to(this.properties.data,t,{sync:e})}}n.ColumnarDataSource=y,i=y,y.__name__=\"ColumnarDataSource\",i.define((({Ref:t,Dict:e,Unknown:n})=>({default_values:[e(n),{}],selection_policy:[t(g.SelectionPolicy),()=>new g.UnionRenderers]}))),i.internal((({AnyRef:t})=>({inspected:[t(),()=>new _.Selection]})))},\n function _(e,t,s,o,c){o();const n=e(130);function i(e){return\"GlyphRenderer\"==e.model.type}function r(e){return\"GraphRenderer\"==e.model.type}class l{constructor(e){this.inspectors=new Map,this.source=e}select(e,t,s,o=\"replace\"){const c=[],n=[];for(const t of e)i(t)?c.push(t):r(t)&&n.push(t);let l=!1;for(const e of n){const c=e.model.selection_policy.hit_test(t,e);l=l||e.model.selection_policy.do_selection(c,e.model,s,o)}if(c.length>0){const e=this.source.selection_policy.hit_test(t,c);l=l||this.source.selection_policy.do_selection(e,this.source,s,o)}return l}inspect(e,t){let s=!1;if(i(e)){const o=e.hit_test(t);if(null!=o){s=!o.is_empty();const c=this.get_or_create_inspector(e.model);c.update(o,!0,\"replace\"),this.source.setv({inspected:c},{silent:!0}),this.source.inspect.emit([e.model,{geometry:t}])}}else if(r(e)){const o=e.model.inspection_policy.hit_test(t,e);s=e.model.inspection_policy.do_inspection(o,t,e,!1,\"replace\")}return s}invert(e){const t=this.source.get_length();null!=t&&(this.source.selected.invert(t),null!=e&&this.get_or_create_inspector(e.model).invert(t))}clear(e){this.source.selected.clear(),null!=e&&this.get_or_create_inspector(e.model).clear()}get_or_create_inspector(e){let t=this.inspectors.get(e);return null==t&&(t=new n.Selection,this.inspectors.set(e,t)),t}}s.SelectionManager=l,l.__name__=\"SelectionManager\"},\n function _(i,e,s,n,t){var c;n();const d=i(51),h=i(10),l=i(9),_=i(21),u=i(13);s.OpaqueIndices=(0,_.Arrayable)(_.Int),s.MultiIndices=(0,_.Mapping)(_.Int,s.OpaqueIndices);class a extends d.Model{constructor(i){super(i)}get_view(){return this.view}get selected_glyph(){return this.selected_glyphs.length>0?this.selected_glyphs[0]:null}add_to_selected_glyphs(i){this.selected_glyphs.push(i)}update(i,e=!0,s=\"replace\"){switch(s){case\"replace\":this.update_through_replacement(i);break;case\"toggle\":this.update_through_toggle(i);break;case\"append\":this.update_through_union(i);break;case\"intersect\":this.update_through_intersection(i);break;case\"subtract\":this.update_through_subtraction(i);break;case\"xor\":this.update_through_symmetric_difference(i)}}invert(i){const e=new Set(this.indices),s=[];for(let n=0;n[i(e),s]))),image_indices:this.image_indices.map((e=>({...e,index:i(e.index)})))})}is_empty(){return 0==this.indices.length&&0==this.line_indices.length&&0==this.image_indices.length}_union_image_indices(...i){const e=new Map,s=new Map,n=[];for(const t of i)for(const i of t){const{index:t,i:c,j:d}=i,h=e.get(t),l=s.get(t);null!=h&&null!=l?h.has(c)&&l.has(d)||(n.push(i),h.add(c),l.add(d)):(n.push(i),e.set(t,new Set([c])),s.set(t,new Set([d])))}return n}update_through_replacement(i){this.indices=i.indices,this.line_indices=i.line_indices,this.multiline_indices=i.multiline_indices,this.image_indices=i.image_indices,this.view=i.view,this.selected_glyphs=i.selected_glyphs}update_through_toggle(i){this.indices=(0,h.difference)(i.indices,this.indices),this.selected_glyphs=(0,h.union)(i.selected_glyphs,this.selected_glyphs),this.line_indices=(0,h.union)(i.line_indices,this.line_indices),this.image_indices=this._union_image_indices(this.image_indices,i.image_indices),this.view=i.view,this.multiline_indices=(0,l.merge)(i.multiline_indices,this.multiline_indices)}update_through_union(i){this.indices=(0,h.union)(this.indices,i.indices),this.selected_glyphs=(0,h.union)(i.selected_glyphs,this.selected_glyphs),this.line_indices=(0,h.union)(i.line_indices,this.line_indices),this.image_indices=this._union_image_indices(this.image_indices,i.image_indices),this.view=i.view,this.multiline_indices=(0,l.merge)(i.multiline_indices,this.multiline_indices)}update_through_intersection(i){this.indices=(0,h.intersection)(this.indices,i.indices),this.selected_glyphs=(0,h.union)(i.selected_glyphs,this.selected_glyphs),this.line_indices=(0,h.union)(i.line_indices,this.line_indices),this.image_indices=this._union_image_indices(this.image_indices,i.image_indices),this.view=i.view,this.multiline_indices=(0,l.merge)(i.multiline_indices,this.multiline_indices)}update_through_subtraction(i){this.indices=(0,h.difference)(this.indices,i.indices),this.selected_glyphs=(0,h.union)(i.selected_glyphs,this.selected_glyphs),this.line_indices=(0,h.union)(i.line_indices,this.line_indices),this.image_indices=this._union_image_indices(this.image_indices,i.image_indices),this.view=i.view,this.multiline_indices=(0,l.merge)(i.multiline_indices,this.multiline_indices)}update_through_symmetric_difference(i){this.indices=(0,h.symmetric_difference)(this.indices,i.indices),this.selected_glyphs=(0,h.union)(i.selected_glyphs,this.selected_glyphs),this.line_indices=(0,h.union)(i.line_indices,this.line_indices),this.image_indices=this._union_image_indices(this.image_indices,i.image_indices),this.view=i.view,this.multiline_indices=(0,l.merge)(i.multiline_indices,this.multiline_indices)}}s.Selection=a,c=a,a.__name__=\"Selection\",c.define((({Int:i,List:e,Struct:n})=>({indices:[s.OpaqueIndices,[]],line_indices:[s.OpaqueIndices,[]],multiline_indices:[s.MultiIndices,new Map],image_indices:[e(n({index:i,i,j:i,flat_index:i})),[]]}))),c.internal((({List:i,AnyRef:e,Nullable:s})=>({selected_glyphs:[i(e()),[]],view:[s(e()),null]})))},\n function _(e,t,n,s,o){s();const r=e(51);class c extends r.Model{do_selection(e,t,n,s){return null!=e&&(t.selected.update(e,n,s),t._select.emit(),!t.selected.is_empty())}}n.SelectionPolicy=c,c.__name__=\"SelectionPolicy\";class l extends c{hit_test(e,t){const n=[];for(const s of t){const t=s.hit_test(e);null!=t&&n.push(t)}if(n.length>0){const e=n[0];for(const t of n)e.update_through_intersection(t);return e}return null}}n.IntersectRenderers=l,l.__name__=\"IntersectRenderers\";class _ extends c{hit_test(e,t){const n=[];for(const s of t){const t=s.hit_test(e);null!=t&&n.push(t)}if(n.length>0){const e=n[0];for(const t of n)e.update_through_union(t);return e}return null}}n.UnionRenderers=_,_.__name__=\"UnionRenderers\"},\n function _(e,n,c,o,t){var a;o();const r=e(51),s=e(130);class l extends r.Model{constructor(e){super(e)}}c.DataSource=l,a=l,l.__name__=\"DataSource\",a.define((({Ref:e})=>({selected:[e(s.Selection),()=>new s.Selection,{readonly:!0}]})))},\n function _(a,n,o,e,r){var t;e();const c=a(128);class u extends c.ColumnarDataSource{constructor(a){super(a)}}o.ColumnDataSource=u,t=u,u.__name__=\"ColumnDataSource\",t.define((({Unknown:a,Dict:n,Arrayable:o})=>({data:[n(o(a)),{}]})))},\n function _(n,t,e,o,r){o();const c=n(1),i=c.__importDefault(n(135)),l=c.__importDefault(n(136)),a=n(24),s=new l.default(\"GOOGLE\"),u=new l.default(\"WGS84\"),f=(0,i.default)(u,s);e.wgs84_mercator={compute:(n,t)=>isFinite(n)&&isFinite(t)?f.forward([n,t]):[NaN,NaN],invert:(n,t)=>isFinite(n)&&isFinite(t)?f.inverse([n,t]):[NaN,NaN]};const _={lon:[-20026376.39,20026376.39],lat:[-20048966.1,20048966.1]},p={lon:[-180,180],lat:[-85.06,85.06]},{min:g,max:h}=Math;function m(n,t){const o=g(n.length,t.length),r=(0,a.infer_type)(n,t),c=new r(o),i=new r(o);return e.inplace.project_xy(n,t,c,i),[c,i]}e.clip_mercator=function(n,t,e){const[o,r]=_[e];return[h(n,o),g(t,r)]},e.in_bounds=function(n,t){const[e,o]=p[t];return e2?void 0!==e.name&&\"geocent\"===e.name||void 0!==n.name&&\"geocent\"===n.name?\"number\"==typeof o.z?[o.x,o.y,o.z].concat(t.splice(3)):[o.x,o.y,t[2]].concat(t.splice(3)):[o.x,o.y].concat(t.splice(2)):[o.x,o.y]):(a=(0,c.default)(e,n,t,r),2===(i=Object.keys(t)).length||i.forEach((function(r){if(void 0!==e.name&&\"geocent\"===e.name||void 0!==n.name&&\"geocent\"===n.name){if(\"x\"===r||\"y\"===r||\"z\"===r)return}else if(\"x\"===r||\"y\"===r)return;a[r]=t[r]})),a)}function l(e){return e instanceof i.default?e:e.oProj?e.oProj:(0,i.default)(e)}t.default=function(e,n,t){e=l(e);var r,o=!1;return void 0===n?(n=e,e=u,o=!0):(void 0!==n.x||Array.isArray(n))&&(t=n,n=e,e=u,o=!0),n=l(n),t?f(e,n,t):(r={forward:function(t,r){return f(e,n,t,r)},inverse:function(t,r){return f(n,e,t,r)}},o&&(r.oProj=n),r)}},\n function _(t,e,a,s,i){s();const l=t(1),u=l.__importDefault(t(137)),r=l.__importDefault(t(148)),d=l.__importDefault(t(149)),o=t(157),f=l.__importDefault(t(159)),p=l.__importDefault(t(160)),m=l.__importDefault(t(144)),n=t(161);function h(t,e){if(!(this instanceof h))return new h(t);e=e||function(t){if(t)throw t};var a=(0,u.default)(t);if(\"object\"==typeof a){var s=h.projections.get(a.projName);if(s){if(a.datumCode&&\"none\"!==a.datumCode){var i=(0,m.default)(f.default,a.datumCode);i&&(a.datum_params=a.datum_params||(i.towgs84?i.towgs84.split(\",\"):null),a.ellps=i.ellipse,a.datumName=i.datumName?i.datumName:a.datumCode)}a.k0=a.k0||1,a.axis=a.axis||\"enu\",a.ellps=a.ellps||\"wgs84\",a.lat1=a.lat1||a.lat0;var l=(0,o.sphere)(a.a,a.b,a.rf,a.ellps,a.sphere),d=(0,o.eccentricity)(l.a,l.b,l.rf,a.R_A),_=(0,n.getNadgrids)(a.nadgrids),c=a.datum||(0,p.default)(a.datumCode,a.datum_params,l.a,l.b,d.es,d.ep2,_);(0,r.default)(this,a),(0,r.default)(this,s),this.a=l.a,this.b=l.b,this.rf=l.rf,this.sphere=l.sphere,this.es=d.es,this.e=d.e,this.ep2=d.ep2,this.datum=c,this.init(),e(null,this)}else e(t)}else e(t)}h.projections=d.default,h.projections.start(),a.default=h},\n function _(t,r,n,u,e){u();const f=t(1),i=f.__importDefault(t(138)),a=f.__importDefault(t(145)),o=f.__importDefault(t(140)),l=f.__importDefault(t(144));var C=[\"PROJECTEDCRS\",\"PROJCRS\",\"GEOGCS\",\"GEOCCS\",\"PROJCS\",\"LOCAL_CS\",\"GEODCRS\",\"GEODETICCRS\",\"GEODETICDATUM\",\"ENGCRS\",\"ENGINEERINGCRS\"];var d=[\"3857\",\"900913\",\"3785\",\"102113\"];n.default=function(t){if(!function(t){return\"string\"==typeof t}(t))return t;if(function(t){return t in i.default}(t))return i.default[t];if(function(t){return C.some((function(r){return t.indexOf(r)>-1}))}(t)){var r=(0,a.default)(t);if(function(t){var r=(0,l.default)(t,\"authority\");if(r){var n=(0,l.default)(r,\"epsg\");return n&&d.indexOf(n)>-1}}(r))return i.default[\"EPSG:3857\"];var n=function(t){var r=(0,l.default)(t,\"extension\");if(r)return(0,l.default)(r,\"proj4\")}(r);return n?(0,o.default)(n):r}return function(t){return\"+\"===t[0]}(t)?(0,o.default)(t):void 0}},\n function _(t,r,i,e,n){e();const f=t(1),a=f.__importDefault(t(139)),l=f.__importDefault(t(140)),u=f.__importDefault(t(145));function o(t){var r=this;if(2===arguments.length){var i=arguments[1];\"string\"==typeof i?\"+\"===i.charAt(0)?o[t]=(0,l.default)(arguments[1]):o[t]=(0,u.default)(arguments[1]):o[t]=i}else if(1===arguments.length){if(Array.isArray(t))return t.map((function(t){Array.isArray(t)?o.apply(r,t):o(t)}));if(\"string\"==typeof t){if(t in o)return o[t]}else\"EPSG\"in t?o[\"EPSG:\"+t.EPSG]=t:\"ESRI\"in t?o[\"ESRI:\"+t.ESRI]=t:\"IAU2000\"in t?o[\"IAU2000:\"+t.IAU2000]=t:console.log(t);return}}(0,a.default)(o),i.default=o},\n function _(t,l,G,S,e){S(),G.default=function(t){t(\"EPSG:4326\",\"+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees\"),t(\"EPSG:4269\",\"+title=NAD83 (long/lat) +proj=longlat +a=6378137.0 +b=6356752.31414036 +ellps=GRS80 +datum=NAD83 +units=degrees\"),t(\"EPSG:3857\",\"+title=WGS 84 / Pseudo-Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs\"),t.WGS84=t[\"EPSG:4326\"],t[\"EPSG:3785\"]=t[\"EPSG:3857\"],t.GOOGLE=t[\"EPSG:3857\"],t[\"EPSG:900913\"]=t[\"EPSG:3857\"],t[\"EPSG:102113\"]=t[\"EPSG:3857\"]}},\n function _(t,n,o,a,u){a();const e=t(1),r=t(141),i=e.__importDefault(t(142)),f=e.__importDefault(t(143)),l=e.__importDefault(t(144));o.default=function(t){var n,o,a,u={},e=t.split(\"+\").map((function(t){return t.trim()})).filter((function(t){return t})).reduce((function(t,n){var o=n.split(\"=\");return o.push(!0),t[o[0].toLowerCase()]=o[1],t}),{}),c={proj:\"projName\",datum:\"datumCode\",rf:function(t){u.rf=parseFloat(t)},lat_0:function(t){u.lat0=t*r.D2R},lat_1:function(t){u.lat1=t*r.D2R},lat_2:function(t){u.lat2=t*r.D2R},lat_ts:function(t){u.lat_ts=t*r.D2R},lon_0:function(t){u.long0=t*r.D2R},lon_1:function(t){u.long1=t*r.D2R},lon_2:function(t){u.long2=t*r.D2R},alpha:function(t){u.alpha=parseFloat(t)*r.D2R},gamma:function(t){u.rectified_grid_angle=parseFloat(t)},lonc:function(t){u.longc=t*r.D2R},x_0:function(t){u.x0=parseFloat(t)},y_0:function(t){u.y0=parseFloat(t)},k_0:function(t){u.k0=parseFloat(t)},k:function(t){u.k0=parseFloat(t)},a:function(t){u.a=parseFloat(t)},b:function(t){u.b=parseFloat(t)},r:function(t){u.a=u.b=parseFloat(t)},r_a:function(){u.R_A=!0},zone:function(t){u.zone=parseInt(t,10)},south:function(){u.utmSouth=!0},towgs84:function(t){u.datum_params=t.split(\",\").map((function(t){return parseFloat(t)}))},to_meter:function(t){u.to_meter=parseFloat(t)},units:function(t){u.units=t;var n=(0,l.default)(f.default,t);n&&(u.to_meter=n.to_meter)},from_greenwich:function(t){u.from_greenwich=t*r.D2R},pm:function(t){var n=(0,l.default)(i.default,t);u.from_greenwich=(n||parseFloat(t))*r.D2R},nadgrids:function(t){\"@null\"===t?u.datumCode=\"none\":u.nadgrids=t},axis:function(t){var n=\"ewnsud\";3===t.length&&-1!==n.indexOf(t.substr(0,1))&&-1!==n.indexOf(t.substr(1,1))&&-1!==n.indexOf(t.substr(2,1))&&(u.axis=t)},approx:function(){u.approx=!0}};for(n in e)o=e[n],n in c?\"function\"==typeof(a=c[n])?a(o):u[a]=o:u[n]=o;return\"string\"==typeof u.datumCode&&\"WGS84\"!==u.datumCode&&(u.datumCode=u.datumCode.toLowerCase()),u}},\n function _(S,_,P,R,I){R(),P.PJD_3PARAM=1,P.PJD_7PARAM=2,P.PJD_GRIDSHIFT=3,P.PJD_WGS84=4,P.PJD_NODATUM=5,P.SRS_WGS84_SEMIMAJOR=6378137,P.SRS_WGS84_SEMIMINOR=6356752.314,P.SRS_WGS84_ESQUARED=.0066943799901413165,P.SEC_TO_RAD=484813681109536e-20,P.HALF_PI=Math.PI/2,P.SIXTH=.16666666666666666,P.RA4=.04722222222222222,P.RA6=.022156084656084655,P.EPSLN=1e-10,P.D2R=.017453292519943295,P.R2D=57.29577951308232,P.FORTPI=Math.PI/4,P.TWO_PI=2*Math.PI,P.SPI=3.14159265359},\n function _(o,r,a,e,s){e();var n={};a.default=n,n.greenwich=0,n.lisbon=-9.131906111111,n.paris=2.337229166667,n.bogota=-74.080916666667,n.madrid=-3.687938888889,n.rome=12.452333333333,n.bern=7.439583333333,n.jakarta=106.807719444444,n.ferro=-17.666666666667,n.brussels=4.367975,n.stockholm=18.058277777778,n.athens=23.7163375,n.oslo=10.722916666667},\n function _(t,e,f,o,u){o(),f.default={ft:{to_meter:.3048},\"us-ft\":{to_meter:1200/3937}}},\n function _(e,r,t,a,n){a();var o=/[\\s_\\-\\/\\(\\)]/g;t.default=function(e,r){if(e[r])return e[r];for(var t,a=Object.keys(e),n=r.toLowerCase().replace(o,\"\"),f=-1;++f0?90:-90)):(e.lat0=_(e.lat1>0?90:-90),e.lat_ts=e.lat1)}(n),n}},\n function _(t,e,r,i,s){i(),r.default=function(t){return new c(t).output()};var h=1,o=/\\s/,n=/[A-Za-z]/,a=/[A-Za-z84_]/,u=/[,\\]]/,d=/[\\d\\.E\\-\\+]/;function c(t){if(\"string\"!=typeof t)throw new Error(\"not a string\");this.text=t.trim(),this.level=0,this.place=0,this.root=null,this.stack=[],this.currentObject=null,this.state=h}c.prototype.readCharicter=function(){var t=this.text[this.place++];if(4!==this.state)for(;o.test(t);){if(this.place>=this.text.length)return;t=this.text[this.place++]}switch(this.state){case h:return this.neutral(t);case 2:return this.keyword(t);case 4:return this.quoted(t);case 5:return this.afterquote(t);case 3:return this.number(t);case-1:return}},c.prototype.afterquote=function(t){if('\"'===t)return this.word+='\"',void(this.state=4);if(u.test(t))return this.word=this.word.trim(),void this.afterItem(t);throw new Error(\"havn't handled \\\"\"+t+'\" in afterquote yet, index '+this.place)},c.prototype.afterItem=function(t){return\",\"===t?(null!==this.word&&this.currentObject.push(this.word),this.word=null,void(this.state=h)):\"]\"===t?(this.level--,null!==this.word&&(this.currentObject.push(this.word),this.word=null),this.state=h,this.currentObject=this.stack.pop(),void(this.currentObject||(this.state=-1))):void 0},c.prototype.number=function(t){if(!d.test(t)){if(u.test(t))return this.word=parseFloat(this.word),void this.afterItem(t);throw new Error(\"havn't handled \\\"\"+t+'\" in number yet, index '+this.place)}this.word+=t},c.prototype.quoted=function(t){'\"'!==t?this.word+=t:this.state=5},c.prototype.keyword=function(t){if(a.test(t))this.word+=t;else{if(\"[\"===t){var e=[];return e.push(this.word),this.level++,null===this.root?this.root=e:this.currentObject.push(e),this.stack.push(this.currentObject),this.currentObject=e,void(this.state=h)}if(!u.test(t))throw new Error(\"havn't handled \\\"\"+t+'\" in keyword yet, index '+this.place);this.afterItem(t)}},c.prototype.neutral=function(t){if(n.test(t))return this.word=t,void(this.state=2);if('\"'===t)return this.word=\"\",void(this.state=4);if(d.test(t))return this.word=t,void(this.state=3);if(!u.test(t))throw new Error(\"havn't handled \\\"\"+t+'\" in neutral yet, index '+this.place);this.afterItem(t)},c.prototype.output=function(){for(;this.place90&&a*o.R2D<-90&&h*o.R2D>180&&h*o.R2D<-180)return null;if(Math.abs(Math.abs(a)-o.HALF_PI)<=o.EPSLN)return null;if(this.sphere)i=this.x0+this.a*this.k0*(0,n.default)(h-this.long0),s=this.y0+this.a*this.k0*Math.log(Math.tan(o.FORTPI+.5*a));else{var e=Math.sin(a),r=(0,l.default)(this.e,a,e);i=this.x0+this.a*this.k0*(0,n.default)(h-this.long0),s=this.y0-this.a*this.k0*Math.log(r)}return t.x=i,t.y=s,t}function M(t){var i,s,h=t.x-this.x0,a=t.y-this.y0;if(this.sphere)s=o.HALF_PI-2*Math.atan(Math.exp(-a/(this.a*this.k0)));else{var e=Math.exp(-a/(this.a*this.k0));if(-9999===(s=(0,u.default)(this.e,e)))return null}return i=(0,n.default)(this.long0+h/(this.a*this.k0)),t.x=i,t.y=s,t}s.init=f,s.forward=_,s.inverse=M,s.names=[\"Mercator\",\"Popular Visualisation Pseudo Mercator\",\"Mercator_1SP\",\"Mercator_Auxiliary_Sphere\",\"merc\"],s.default={init:f,forward:_,inverse:M,names:s.names}},\n function _(t,n,r,u,a){u(),r.default=function(t,n,r){var u=t*n;return r/Math.sqrt(1-u*u)}},\n function _(t,n,u,a,f){a();const e=t(1),o=t(141),_=e.__importDefault(t(153));u.default=function(t){return Math.abs(t)<=o.SPI?t:t-(0,_.default)(t)*o.TWO_PI}},\n function _(n,t,u,f,c){f(),u.default=function(n){return n<0?-1:1}},\n function _(t,n,a,o,u){o();const c=t(141);a.default=function(t,n,a){var o=t*a,u=.5*t;return o=Math.pow((1-o)/(1+o),u),Math.tan(.5*(c.HALF_PI-n))/o}},\n function _(t,a,n,r,f){r();const h=t(141);n.default=function(t,a){for(var n,r,f=.5*t,o=h.HALF_PI-2*Math.atan(a),u=0;u<=15;u++)if(n=t*Math.sin(o),o+=r=h.HALF_PI-2*Math.atan(a*Math.pow((1-n)/(1+n),f))-o,Math.abs(r)<=1e-10)return o;return-9999}},\n function _(n,i,e,t,r){function a(){}function f(n){return n}t(),e.init=a,e.forward=f,e.inverse=f,e.names=[\"longlat\",\"identity\"],e.default={init:a,forward:f,inverse:f,names:e.names}},\n function _(t,r,e,a,n){a();const f=t(1),i=t(141),u=f.__importStar(t(158)),c=f.__importDefault(t(144));e.eccentricity=function(t,r,e,a){var n=t*t,f=r*r,u=(n-f)/n,c=0;return a?(n=(t*=1-u*(i.SIXTH+u*(i.RA4+u*i.RA6)))*t,u=0):c=Math.sqrt(u),{es:u,e:c,ep2:(n-f)/f}},e.sphere=function(t,r,e,a,n){if(!t){var f=(0,c.default)(u.default,a);f||(f=u.WGS84),t=f.a,r=f.b,e=f.rf}return e&&!r&&(r=(1-1/e)*t),(0===e||Math.abs(t-r)3&&(0===s.datum_params[3]&&0===s.datum_params[4]&&0===s.datum_params[5]&&0===s.datum_params[6]||(s.datum_type=d.PJD_7PARAM,s.datum_params[3]*=d.SEC_TO_RAD,s.datum_params[4]*=d.SEC_TO_RAD,s.datum_params[5]*=d.SEC_TO_RAD,s.datum_params[6]=s.datum_params[6]/1e6+1))),r&&(s.datum_type=d.PJD_GRIDSHIFT,s.grids=r),s.a=_,s.b=t,s.es=u,s.ep2=p,s}},\n function _(t,e,n,r,i){r();var u={};function o(t){if(0===t.length)return null;var e=\"@\"===t[0];return e&&(t=t.slice(1)),\"null\"===t?{name:\"null\",mandatory:!e,grid:null,isNull:!0}:{name:t,mandatory:!e,grid:u[t]||null,isNull:!1}}function l(t){return t/3600*Math.PI/180}function a(t,e,n){return String.fromCharCode.apply(null,new Uint8Array(t.buffer.slice(e,n)))}function d(t){return t.map((function(t){return[l(t.longitudeShift),l(t.latitudeShift)]}))}function g(t,e,n){return{name:a(t,e+8,e+16).trim(),parent:a(t,e+24,e+24+8).trim(),lowerLatitude:t.getFloat64(e+72,n),upperLatitude:t.getFloat64(e+88,n),lowerLongitude:t.getFloat64(e+104,n),upperLongitude:t.getFloat64(e+120,n),latitudeInterval:t.getFloat64(e+136,n),longitudeInterval:t.getFloat64(e+152,n),gridNodeCount:t.getInt32(e+168,n)}}function f(t,e,n,r){for(var i=e+176,u=[],o=0;oa.y||N>a.x||g1e-12&&Math.abs(n.y)>1e-12);if(d<0)return console.log(\"Inverse grid shift iterator failed to converge.\"),a;a.x=(0,u.default)(l.x+t.ll[0]),a.y=l.y+t.ll[1]}else isNaN(l.x)||(a.x=r.x+l.x,a.y=r.y+l.y);return a}function f(r,e){var t,a={x:r.x/e.del[0],y:r.y/e.del[1]},i=Math.floor(a.x),l=Math.floor(a.y),n=a.x-1*i,o=a.y-1*l,u={x:Number.NaN,y:Number.NaN};if(i<0||i>=e.lim[0])return u;if(l<0||l>=e.lim[1])return u;t=l*e.lim[0]+i;var d=e.cvs[t][0],s=e.cvs[t][1];t++;var y=e.cvs[t][0],f=e.cvs[t][1];t+=e.lim[0];var x=e.cvs[t][0],m=e.cvs[t][1];t--;var N=e.cvs[t][0],c=e.cvs[t][1],_=n*o,g=n*(1-o),v=(1-n)*(1-o),S=(1-n)*o;return u.x=v*d+g*y+S*N+_*x,u.y=v*s+g*f+S*c+_*m,u}t.default=function(r,e,t){if((0,o.compareDatums)(r,e))return t;if(r.datum_type===n.PJD_NODATUM||e.datum_type===n.PJD_NODATUM)return t;var a=r.a,i=r.es;if(r.datum_type===n.PJD_GRIDSHIFT){if(0!==s(r,!1,t))return;a=n.SRS_WGS84_SEMIMAJOR,i=n.SRS_WGS84_ESQUARED}var l=e.a,u=e.b,y=e.es;if(e.datum_type===n.PJD_GRIDSHIFT&&(l=n.SRS_WGS84_SEMIMAJOR,u=n.SRS_WGS84_SEMIMINOR,y=n.SRS_WGS84_ESQUARED),i===y&&a===l&&!d(r.datum_type)&&!d(e.datum_type))return t;if(t=(0,o.geodeticToGeocentric)(t,i,a),d(r.datum_type)&&(t=(0,o.geocentricToWgs84)(t,r.datum_type,r.datum_params)),d(e.datum_type)&&(t=(0,o.geocentricFromWgs84)(t,e.datum_type,e.datum_params)),t=(0,o.geocentricToGeodetic)(t,y,l,u),e.datum_type===n.PJD_GRIDSHIFT&&0!==s(e,!0,t))return;return t},t.applyGridShift=s},\n function _(a,t,r,m,s){m();const u=a(141);r.compareDatums=function(a,t){return a.datum_type===t.datum_type&&(!(a.a!==t.a||Math.abs(a.es-t.es)>5e-11)&&(a.datum_type===u.PJD_3PARAM?a.datum_params[0]===t.datum_params[0]&&a.datum_params[1]===t.datum_params[1]&&a.datum_params[2]===t.datum_params[2]:a.datum_type!==u.PJD_7PARAM||a.datum_params[0]===t.datum_params[0]&&a.datum_params[1]===t.datum_params[1]&&a.datum_params[2]===t.datum_params[2]&&a.datum_params[3]===t.datum_params[3]&&a.datum_params[4]===t.datum_params[4]&&a.datum_params[5]===t.datum_params[5]&&a.datum_params[6]===t.datum_params[6]))},r.geodeticToGeocentric=function(a,t,r){var m,s,_,e,n=a.x,d=a.y,i=a.z?a.z:0;if(d<-u.HALF_PI&&d>-1.001*u.HALF_PI)d=-u.HALF_PI;else if(d>u.HALF_PI&&d<1.001*u.HALF_PI)d=u.HALF_PI;else{if(d<-u.HALF_PI)return{x:-1/0,y:-1/0,z:a.z};if(d>u.HALF_PI)return{x:1/0,y:1/0,z:a.z}}return n>Math.PI&&(n-=2*Math.PI),s=Math.sin(d),e=Math.cos(d),_=s*s,{x:((m=r/Math.sqrt(1-t*_))+i)*e*Math.cos(n),y:(m+i)*e*Math.sin(n),z:(m*(1-t)+i)*s}},r.geocentricToGeodetic=function(a,t,r,m){var s,_,e,n,d,i,p,P,y,z,M,o,A,c,x,h=1e-12,f=a.x,I=a.y,F=a.z?a.z:0;if(s=Math.sqrt(f*f+I*I),_=Math.sqrt(f*f+I*I+F*F),s/r1e-24&&A<30);return{x:c,y:Math.atan(M/Math.abs(z)),z:x}},r.geocentricToWgs84=function(a,t,r){if(t===u.PJD_3PARAM)return{x:a.x+r[0],y:a.y+r[1],z:a.z+r[2]};if(t===u.PJD_7PARAM){var m=r[0],s=r[1],_=r[2],e=r[3],n=r[4],d=r[5],i=r[6];return{x:i*(a.x-d*a.y+n*a.z)+m,y:i*(d*a.x+a.y-e*a.z)+s,z:i*(-n*a.x+e*a.y+a.z)+_}}},r.geocentricFromWgs84=function(a,t,r){if(t===u.PJD_3PARAM)return{x:a.x-r[0],y:a.y-r[1],z:a.z-r[2]};if(t===u.PJD_7PARAM){var m=r[0],s=r[1],_=r[2],e=r[3],n=r[4],d=r[5],i=r[6],p=(a.x-m)/i,P=(a.y-s)/i,y=(a.z-_)/i;return{x:p+d*P-n*y,y:-d*p+P+e*y,z:n*p-e*P+y}}}},\n function _(e,a,i,s,n){s(),i.default=function(e,a,i){var s,n,r,c=i.x,d=i.y,f=i.z||0,u={};for(r=0;r<3;r++)if(!a||2!==r||void 0!==i.z)switch(0===r?(s=c,n=-1!==\"ew\".indexOf(e.axis[r])?\"x\":\"y\"):1===r?(s=d,n=-1!==\"ns\".indexOf(e.axis[r])?\"y\":\"x\"):(s=f,n=\"z\"),e.axis[r]){case\"e\":case\"n\":u[n]=s;break;case\"w\":case\"s\":u[n]=-s;break;case\"u\":void 0!==i[n]&&(u.z=s);break;case\"d\":void 0!==i[n]&&(u.z=-s);break;default:return null}return u}},\n function _(n,t,e,u,f){u(),e.default=function(n){var t={x:n[0],y:n[1]};return n.length>2&&(t.z=n[2]),n.length>3&&(t.m=n[3]),t}},\n function _(e,i,n,t,r){function o(e){if(\"function\"==typeof Number.isFinite){if(Number.isFinite(e))return;throw new TypeError(\"coordinates must be finite numbers\")}if(\"number\"!=typeof e||e!=e||!isFinite(e))throw new TypeError(\"coordinates must be finite numbers\")}t(),n.default=function(e){o(e.x),o(e.y)}},\n function _(e,i,s,o,t){var n,l,a,r,_;o();const c=e(1),p=e(169),d=e(80),T=c.__importStar(e(18));class m extends p.MarkingView{}s.ArrowHeadView=m,m.__name__=\"ArrowHeadView\";class v extends p.Marking{constructor(e){super(e)}}s.ArrowHead=v,n=v,v.__name__=\"ArrowHead\",n.define((()=>({size:[T.NumberSpec,25]})));class u extends m{clip(e,i){this.visuals.line.set_vectorize(e,i);const s=this.size.get(i);e.moveTo(.5*s,s),e.lineTo(.5*s,-2),e.lineTo(-.5*s,-2),e.lineTo(-.5*s,s),e.lineTo(0,0),e.lineTo(.5*s,s)}paint(e,i){const s=this.size.get(i);e.beginPath(),e.moveTo(.5*s,s),e.lineTo(0,0),e.lineTo(-.5*s,s),this.visuals.line.apply(e,i)}}s.OpenHeadView=u,u.__name__=\"OpenHeadView\";class h extends v{constructor(e){super(e)}}s.OpenHead=h,l=h,h.__name__=\"OpenHead\",l.prototype.default_view=u,l.mixins(d.LineVector);class V extends m{clip(e,i){this.visuals.line.set_vectorize(e,i);const s=this.size.get(i);e.moveTo(.5*s,s),e.lineTo(.5*s,-2),e.lineTo(-.5*s,-2),e.lineTo(-.5*s,s),e.lineTo(.5*s,s)}paint(e,i){const s=this.size.get(i);e.beginPath(),e.moveTo(.5*s,s),e.lineTo(0,0),e.lineTo(-.5*s,s),e.closePath(),this.visuals.fill.apply(e,i),this.visuals.line.apply(e,i)}}s.NormalHeadView=V,V.__name__=\"NormalHeadView\";class H extends v{constructor(e){super(e)}}s.NormalHead=H,a=H,H.__name__=\"NormalHead\",a.prototype.default_view=V,a.mixins([d.LineVector,d.FillVector]),a.override({fill_color:\"black\"});class w extends m{clip(e,i){this.visuals.line.set_vectorize(e,i);const s=this.size.get(i);e.moveTo(.5*s,s),e.lineTo(.5*s,-2),e.lineTo(-.5*s,-2),e.lineTo(-.5*s,s),e.lineTo(0,.5*s),e.lineTo(.5*s,s)}paint(e,i){const s=this.size.get(i);e.beginPath(),e.moveTo(.5*s,s),e.lineTo(0,0),e.lineTo(-.5*s,s),e.lineTo(0,.5*s),e.closePath(),this.visuals.fill.apply(e,i),this.visuals.line.apply(e,i)}}s.VeeHeadView=w,w.__name__=\"VeeHeadView\";class x extends v{constructor(e){super(e)}}s.VeeHead=x,r=x,x.__name__=\"VeeHead\",r.prototype.default_view=w,r.mixins([d.LineVector,d.FillVector]),r.override({fill_color:\"black\"});class g extends m{paint(e,i){const s=this.size.get(i);e.beginPath(),e.moveTo(.5*s,0),e.lineTo(-.5*s,0),this.visuals.line.apply(e,i)}clip(e,i){}}s.TeeHeadView=g,g.__name__=\"TeeHeadView\";class z extends v{constructor(e){super(e)}}s.TeeHead=z,_=z,z.__name__=\"TeeHead\",_.prototype.default_view=g,_.mixins(d.LineVector)},\n function _(t,e,n,i,s){var a;i();const r=t(1),o=t(51),c=t(57),_=r.__importStar(t(87)),p=r.__importStar(t(18));class u extends c.DOMComponentView{initialize(){super.initialize(),this.visuals=new _.Visuals(this)}request_paint(){this.parent.request_paint()}get canvas(){return this.parent.canvas}set_data(t,e){const n=this;for(const i of this.model){if(!(i instanceof p.VectorSpec||i instanceof p.ScalarSpec))continue;const s=i.uniform(t).select(e);n[`${i.attr}`]=s}}}n.MarkingView=u,u.__name__=\"MarkingView\";class l extends o.Model{constructor(t){super(t)}}n.Marking=l,a=l,l.__name__=\"Marking\",a.define((({})=>({})))},\n function _(t,e,i,o,a){var r;o();const n=t(1),_=t(81),s=t(171),l=t(188),h=t(189),c=t(194),u=t(191),p=t(200),d=t(192),m=t(241),f=t(193),g=t(253),w=t(255),b=t(176),y=t(20),x=n.__importStar(t(80)),v=t(256),k=t(257),j=t(259),z=t(173),B=t(56),S=t(64),L=t(8);class T extends _.AnnotationView{get orientation(){return this._orientation}*children(){yield*super.children(),yield this._axis_view,yield this._title_view}initialize(){super.initialize();const{ticker:t,formatter:e}=this.model;this._ticker=\"auto\"!=t?t:this._create_ticker(),this._formatter=\"auto\"!=e?e:this._create_formatter(),this._major_range=this._create_major_range(),this._major_scale=this._create_major_scale(),this._minor_range=new w.Range1d({start:0,end:1}),this._minor_scale=new g.LinearScale,this._frame=new l.CartesianFrame({x_scale:this._major_scale,y_scale:this._minor_scale,x_range:this._major_range,y_range:this._minor_range}),this._axis=this._create_axis(),this._apply_axis_properties(),this._title=new s.Title,this._apply_title_properties()}async lazy_initialize(){await super.lazy_initialize();const t=this,e={get parent(){return t.parent},get root(){return t.root},get frame(){return t._frame_view},get frame_view(){return t._frame_view},get canvas_view(){return t.parent.canvas_view},request_layout(){t.layout.dirty=!0,t.parent.request_layout()},request_paint(){t.parent.request_paint(t)},notify_finished_after_paint(){t.parent.notify_finished_after_paint()}};this._frame_view=await(0,B.build_view)(this._frame,{parent:e}),this._axis_view=await(0,B.build_view)(this._axis,{parent:e}),this._title_view=await(0,B.build_view)(this._title,{parent:e})}remove(){this._title_view.remove(),this._axis_view.remove(),super.remove()}_apply_axis_properties(){const t={ticker:this._ticker,formatter:this._formatter,major_label_standoff:this.model.label_standoff,axis_line_color:null,major_tick_in:this.model.major_tick_in,major_tick_out:this.model.major_tick_out,minor_tick_in:this.model.minor_tick_in,minor_tick_out:this.model.minor_tick_out,major_label_overrides:this.model.major_label_overrides,major_label_policy:this.model.major_label_policy,...x.attrs_of(this.model,\"major_label_\",x.Text,!0),...x.attrs_of(this.model,\"major_tick_\",x.Line,!0),...x.attrs_of(this.model,\"minor_tick_\",x.Line,!0)};this._axis.setv(t)}_apply_title_properties(){const t={text:this.model.title??\"\",standoff:this.model.title_standoff,...x.attrs_of(this.model,\"title_\",x.Text,!1)};this._title.setv(t)}connect_signals(){super.connect_signals(),this.connect(this.model.change,(()=>{this._apply_title_properties(),this._apply_axis_properties()})),this.connect(this._ticker.change,(()=>this.request_paint())),this.connect(this._formatter.change,(()=>this.request_paint()))}_update_frame(){const[t,e,i,o]=(()=>\"horizontal\"==this.orientation?[this._major_scale,this._minor_scale,this._major_range,this._minor_range]:[this._minor_scale,this._major_scale,this._minor_range,this._major_range])();this._frame.setv({x_scale:t,y_scale:e,x_range:i,y_range:o})}update_layout(){const{location:t,width:e,height:i,padding:o,margin:a}=this.model,[r,n]=(()=>{if(!(0,L.isString)(t))return[\"end\",\"start\"];switch(t){case\"top_left\":return[\"start\",\"start\"];case\"top\":case\"top_center\":return[\"start\",\"center\"];case\"top_right\":return[\"start\",\"end\"];case\"bottom_left\":return[\"end\",\"start\"];case\"bottom\":case\"bottom_center\":return[\"end\",\"center\"];case\"bottom_right\":return[\"end\",\"end\"];case\"left\":case\"center_left\":return[\"center\",\"start\"];case\"center\":case\"center_center\":return[\"center\",\"center\"];case\"right\":case\"center_right\":return[\"center\",\"end\"]}})(),_=this._orientation=(()=>{const{orientation:t}=this.model;return\"auto\"==t?null!=this.panel?this.panel.is_horizontal?\"horizontal\":\"vertical\":\"start\"==n||\"end\"==n||\"center\"==r?\"vertical\":\"horizontal\":t})();this._update_frame();const s=new k.NodeLayout,l=new k.VStack,h=new k.VStack,c=new k.HStack,u=new k.HStack;s.absolute=!0,l.absolute=!0,h.absolute=!0,c.absolute=!0,u.absolute=!0,s.on_resize((t=>this._frame_view.set_geometry(t)));const p=new j.BorderLayout;this._inner_layout=p,p.absolute=!0,p.center_panel=s,p.top_panel=l,p.bottom_panel=h,p.left_panel=c,p.right_panel=u;const d={left:o,right:o,top:o,bottom:o},m=(()=>{if(null==this.panel){if((0,L.isString)(t))return{left:a,right:a,top:a,bottom:a};{const[e,i]=t;return{left:e,right:a,top:a,bottom:i}}}if(!(0,L.isString)(t)){const[e,i]=t;return p.fixup_geometry=(t,o)=>{const a=t,r=this.layout.bbox,{width:n,height:_}=t;if(t=new S.BBox({left:r.left+e,bottom:r.bottom-i,width:n,height:_}),null!=o){const e=t.left-a.left,i=t.top-a.top,{left:r,top:n,width:_,height:s}=o;o=new S.BBox({left:r+e,top:n+i,width:_,height:s})}return[t,o]},{left:e,right:0,top:0,bottom:i}}p.fixup_geometry=(t,e)=>{const i=t;if(\"horizontal\"==_){const{top:e,width:i,height:o}=t;if(\"end\"==n){const{right:a}=this.layout.bbox;t=new S.BBox({right:a,top:e,width:i,height:o})}else if(\"center\"==n){const{hcenter:a}=this.layout.bbox;t=new S.BBox({hcenter:Math.round(a),top:e,width:i,height:o})}}else{const{left:e,width:i,height:o}=t;if(\"end\"==r){const{bottom:a}=this.layout.bbox;t=new S.BBox({left:e,bottom:a,width:i,height:o})}else if(\"center\"==r){const{vcenter:a}=this.layout.bbox;t=new S.BBox({left:e,vcenter:Math.round(a),width:i,height:o})}}if(null!=e){const o=t.left-i.left,a=t.top-i.top,{left:r,top:n,width:_,height:s}=e;e=new S.BBox({left:r+o,top:n+a,width:_,height:s})}return[t,e]}})();let f,g,w,b;if(p.padding=d,null!=this.panel)f=\"max\",g=void 0,w=void 0,b=void 0;else if(\"auto\"==(\"horizontal\"==_?e:i)){f=\"fixed\";const t=this._get_major_size_factor();null!=t&&(g=25*t),w={percent:.3},b={percent:.8}}else f=\"fit\",g=void 0;if(\"horizontal\"==_){const t=\"auto\"==e?void 0:e,o=\"auto\"==i?25:i;p.set_sizing({width_policy:f,height_policy:\"min\",width:g,min_width:w,max_width:b,halign:n,valign:r,margin:m}),p.center_panel.set_sizing({width_policy:\"auto\"==e?\"fit\":\"fixed\",height_policy:\"fixed\",width:t,height:o})}else{const t=\"auto\"==e?25:e,o=\"auto\"==i?void 0:i;p.set_sizing({width_policy:\"min\",height_policy:f,height:g,min_height:w,max_height:b,halign:n,valign:r,margin:m}),p.center_panel.set_sizing({width_policy:\"fixed\",height_policy:\"auto\"==i?\"fit\":\"fixed\",width:t,height:o})}l.set_sizing({width_policy:\"fit\",height_policy:\"min\"}),h.set_sizing({width_policy:\"fit\",height_policy:\"min\"}),c.set_sizing({width_policy:\"min\",height_policy:\"fit\"}),u.set_sizing({width_policy:\"min\",height_policy:\"fit\"});const{_title_view:y}=this;\"horizontal\"==_?(y.panel=new z.SidePanel(\"above\"),y.update_layout(),l.children.push(y.layout)):(y.panel=new z.SidePanel(\"left\"),y.update_layout(),c.children.push(y.layout));const{panel:x}=this,B=null!=x&&_==x.orientation?x.side:\"horizontal\"==_?\"below\":\"right\",T=(()=>{switch(B){case\"above\":return l;case\"below\":return h;case\"left\":return c;case\"right\":return u}})(),{_axis_view:q}=this;if(q.panel=new z.SidePanel(B),q.update_layout(),null!=q.layout&&T.children.push(q.layout),null!=this.panel){const t=new v.Grid([{layout:p,row:0,col:0}]);t.absolute=!0,\"horizontal\"==_?t.set_sizing({width_policy:\"max\",height_policy:\"min\"}):t.set_sizing({width_policy:\"min\",height_policy:\"max\"}),this.layout=t}else this.layout=this._inner_layout;const{visible:A}=this.model;this.layout.sizing.visible=A}_create_axis(){return new c.LinearAxis}_create_formatter(){return new m.BasicTickFormatter}_create_major_range(){return new w.Range1d({start:0,end:1})}_create_major_scale(){return new g.LinearScale}_create_ticker(){return new p.BasicTicker}_get_major_size_factor(){return null}_paint(){const{ctx:t}=this.layer;t.save(),this._paint_bbox(t,this._inner_layout.bbox),this._paint_colors(t,this._inner_layout.center_panel.bbox),this._title_view.paint(),this._axis_view.paint(),t.restore()}_paint_bbox(t,e){const{x:i,y:o}=e;let{width:a,height:r}=e;i+a>=this.parent.canvas_view.bbox.width&&(a-=1),o+r>=this.parent.canvas_view.bbox.height&&(r-=1),t.save(),this.visuals.background_fill.doit&&(this.visuals.background_fill.set_value(t),t.fillRect(i,o,a,r)),this.visuals.border_line.doit&&(this.visuals.border_line.set_value(t),t.strokeRect(i,o,a,r)),t.restore()}}i.BaseColorBarView=T,T.__name__=\"BaseColorBarView\";class q extends _.Annotation{constructor(t){super(t)}}i.BaseColorBar=q,r=q,q.__name__=\"BaseColorBar\",r.mixins([[\"major_label_\",x.Text],[\"title_\",x.Text],[\"major_tick_\",x.Line],[\"minor_tick_\",x.Line],[\"border_\",x.Line],[\"bar_\",x.Line],[\"background_\",x.Fill]]),r.define((({Alpha:t,Float:e,Str:i,Tuple:o,Or:a,Ref:r,Auto:n,Nullable:_})=>({location:[a(y.Anchor,o(e,e)),\"top_right\"],orientation:[a(y.Orientation,n),\"auto\"],title:[_(a(i,r(b.BaseText))),null],title_standoff:[e,2],width:[a(e,n),\"auto\"],height:[a(e,n),\"auto\"],scale_alpha:[t,1],ticker:[a(r(u.Ticker),n),\"auto\"],formatter:[a(r(d.TickFormatter),n),\"auto\"],major_label_overrides:[h.LabelOverrides,new Map],major_label_policy:[r(f.LabelingPolicy),()=>new f.NoOverlap],label_standoff:[e,5],margin:[e,30],padding:[e,10],major_tick_in:[e,5],major_tick_out:[e,0],minor_tick_in:[e,0],minor_tick_out:[e,0]}))),r.override({background_fill_color:\"#ffffff\",background_fill_alpha:.95,bar_line_color:null,border_line_color:null,major_label_text_font_size:\"11px\",major_tick_line_color:\"#ffffff\",minor_tick_line_color:null,title_text_font_size:\"13px\",title_text_font_style:\"italic\"})},\n function _(e,t,i,a,s){var o;a();const r=e(1),n=e(172),c=e(20),l=r.__importStar(e(186));class h extends n.TextAnnotationView{_get_position(){const e=this.model.offset,t=this.model.standoff/2,{align:i,vertical_align:a}=this.model;let s,o;const{bbox:r}=this.layout;switch(this.panel.side){case\"above\":case\"below\":switch(a){case\"top\":o=r.top+t;break;case\"middle\":o=r.vcenter;break;case\"bottom\":o=r.bottom-t}switch(i){case\"left\":s=r.left+e;break;case\"center\":s=r.hcenter;break;case\"right\":s=r.right-e}break;case\"left\":switch(a){case\"top\":s=r.left+t;break;case\"middle\":s=r.hcenter;break;case\"bottom\":s=r.right-t}switch(i){case\"left\":o=r.bottom-e;break;case\"center\":o=r.vcenter;break;case\"right\":o=r.top+e}break;case\"right\":switch(a){case\"top\":s=r.right-t;break;case\"middle\":s=r.hcenter;break;case\"bottom\":s=r.left+t}switch(i){case\"left\":o=r.top+e;break;case\"center\":o=r.vcenter;break;case\"right\":o=r.bottom-e}}return{sx:s,sy:o}}get anchor(){const{align:e,vertical_align:t}=this.model;return l.text_anchor(\"auto\",e,t)}get origin(){return this._get_position()}get angle(){return this.panel.get_label_angle_heuristic(\"parallel\")}_get_size(){const e=e=>0==e?0:2+e+this.model.standoff,{width:t,height:i}=super._get_size();return this.panel.is_horizontal?{width:t,height:e(i)}:{width:e(t),height:i}}}i.TitleView=h,h.__name__=\"TitleView\";class _ extends n.TextAnnotation{constructor(e){super(e)}}i.Title=_,o=_,_.__name__=\"Title\",o.prototype.default_view=h,o.define((({Float:e})=>({vertical_align:[c.VerticalAlign,\"bottom\"],align:[c.TextAlign,\"left\"],offset:[e,0],standoff:[e,10]}))),o.override({text_font_size:\"13px\",text_font_style:\"bold\",text_line_height:1})},\n function _(t,i,e,s,n){var a;s();const r=t(1),o=t(81),d=t(173),h=t(176),_=t(56),l=t(8),c=t(177),u=t(185),g=r.__importStar(t(186)),p=t(64),x=t(187),y=r.__importStar(t(80));class w extends o.AnnotationView{*children(){yield*super.children(),yield this._text_view}async lazy_initialize(){await super.lazy_initialize(),await this._init_text()}async _init_text(){const{text:t}=this.model,i=(0,l.isString)(t)?(0,c.parse_delimited_string)(t):t;this._text_view=await(0,_.build_view)(i,{parent:this})}update_layout(){const{panel:t}=this;this.layout=null!=t?new d.SideLayout(t,(()=>this.get_size()),!1):void 0}connect_signals(){super.connect_signals();const{text:t}=this.model.properties;this.on_change(t,(async()=>{this._text_view.remove(),await this._init_text()})),this.connect(this.model.change,(()=>this.request_paint()))}remove(){this._text_view.remove(),super.remove()}has_finished(){return!!super.has_finished()&&!!this._text_view.has_finished()}get displayed(){return super.displayed&&\"\"!=this._text_view.model.text&&this.visuals.text.doit}get padding(){return g.padding(this.model.padding)}get border_radius(){return g.border_radius(this.model.border_radius)}_get_size(){if(!this.displayed)return{width:0,height:0};const t=this._text_view.graphics();t.angle=this.angle,t.align=\"auto\",t.visuals=this.visuals.text.values();const i=t.size(),{padding:e}=this;return{width:i.width+e.left+e.right,height:i.height+e.top+e.bottom}}compute_geometry(){super.compute_geometry();const t=this._text_view.graphics();t.position={sx:0,sy:0,x_anchor:\"left\",y_anchor:\"top\"},t.angle=0,t.align=\"auto\",t.visuals=this.visuals.text.values();const i=t.size(),{sx:e,sy:s}=this.origin,{anchor:n,padding:a,border_radius:r,angle:o}=this,d=i.width+a.left+a.right,h=i.height+a.top+a.bottom;this._text_box=t,this._rect={sx:e,sy:s,width:d,height:h,angle:o,anchor:n,padding:a,border_radius:r}}_paint(){const{ctx:t}=this.layer,{sx:i,sy:e,width:s,height:n,angle:a,anchor:r,padding:o,border_radius:d}=this._rect,h=this._text_box,_=r.x*s,l=r.y*n;t.translate(i,e),t.rotate(a),t.translate(-_,-l);const{background_fill:c,background_hatch:u,border_line:g,text:y}=this.visuals;if(c.doit||u.doit||g.doit){t.beginPath();const i=new p.BBox({x:0,y:0,width:s,height:n});(0,x.round_rect)(t,i,d),c.apply(t),u.apply(t),g.apply(t)}if(y.doit){const{left:i,top:e}=o;t.translate(i,e),h.paint(t),t.translate(-i,-e)}t.translate(_,l),t.rotate(-a),t.translate(-i,-e)}}e.TextAnnotationView=w,w.__name__=\"TextAnnotationView\";class v extends o.Annotation{constructor(t){super(t)}}e.TextAnnotation=v,a=v,v.__name__=\"TextAnnotation\",a.mixins([y.Text,[\"border_\",y.Line],[\"background_\",y.Fill],[\"background_\",y.Hatch]]),a.define((({Str:t,Or:i,Ref:e})=>({text:[i(t,e(h.BaseText)),\"\"],padding:[u.Padding,0],border_radius:[u.BorderRadius,0]}))),a.override({border_line_color:null,background_fill_color:null,background_hatch_color:null})},\n function _(t,e,i,l,a){l();const r=t(174),o=t(175),n=t(8),s=Math.PI/2,h={above:{parallel:0,normal:-s,horizontal:0,vertical:-s},below:{parallel:0,normal:s,horizontal:0,vertical:s},left:{parallel:-s,normal:0,horizontal:0,vertical:-s},right:{parallel:s,normal:0,horizontal:0,vertical:s}},c={above:{parallel:\"bottom\",normal:\"center\",horizontal:\"bottom\",vertical:\"center\"},below:{parallel:\"top\",normal:\"center\",horizontal:\"top\",vertical:\"center\"},left:{parallel:\"bottom\",normal:\"center\",horizontal:\"center\",vertical:\"bottom\"},right:{parallel:\"bottom\",normal:\"center\",horizontal:\"center\",vertical:\"bottom\"}},_={above:{parallel:\"center\",normal:\"left\",horizontal:\"center\",vertical:\"left\"},below:{parallel:\"center\",normal:\"left\",horizontal:\"center\",vertical:\"left\"},left:{parallel:\"center\",normal:\"right\",horizontal:\"right\",vertical:\"center\"},right:{parallel:\"center\",normal:\"left\",horizontal:\"left\",vertical:\"center\"}},g={above:\"right\",below:\"left\",left:\"right\",right:\"left\"},b={above:\"left\",below:\"right\",left:\"right\",right:\"left\"};class d{constructor(t,e){this.side=t,this.face=(()=>{if(null!=e&&\"auto\"!=e)return e;switch(this.side){case\"left\":case\"above\":return\"front\";case\"right\":case\"below\":return\"back\"}})(),this.dimension=\"above\"==this.side||\"below\"==this.side?0:1,this.orientation=0==this.dimension?\"horizontal\":\"vertical\",this.is_horizontal=0==this.dimension,this.is_vertical=1==this.dimension,this.normals=(()=>{const t=\"front\"==this.face?-1:1;switch(this.side){case\"left\":case\"right\":return[t,0];case\"above\":case\"below\":return[0,t]}})()}get face_adjusted_side(){const{side:t,face:e}=this;switch(t){case\"left\":case\"right\":return\"front\"==e?\"left\":\"right\";case\"above\":case\"below\":return\"front\"==e?\"above\":\"below\"}}get_label_text_heuristics(t){const e=this.face_adjusted_side;return(0,n.isString)(t)?{vertical_align:c[e][t],align:_[e][t]}:{vertical_align:\"center\",align:(t<0?g:b)[e]}}get_label_angle_heuristic(t){if((0,n.isString)(t)){const e=this.face_adjusted_side;return h[e][t]}return-t}}i.SidePanel=d,d.__name__=\"SidePanel\";class f extends o.ContentLayoutable{constructor(t,e,i=!1){super(),this.panel=t,this.get_size=e,this.rotate=i,this.panel.is_horizontal?this.set_sizing({width_policy:\"max\",height_policy:\"fixed\"}):this.set_sizing({width_policy:\"fixed\",height_policy:\"max\"})}_content_size(){const{width:t,height:e}=this.get_size();return!this.rotate||this.panel.is_horizontal?new r.Sizeable({width:t,height:e}):new r.Sizeable({width:e,height:t})}has_size_changed(){const{width:t,height:e}=this._content_size();return this.panel.is_horizontal?this.bbox.height!=e:this.bbox.width!=t}}i.SideLayout=f,f.__name__=\"SideLayout\"},\n function _(h,t,i,e,w){e();const n=h(21),{min:d,max:s}=Math;class g{constructor(h={}){this.width=null!=h.width?h.width:0,this.height=null!=h.height?h.height:0}bounded_to({width:h,height:t}){return new g({width:this.width==1/0&&null!=h?h:this.width,height:this.height==1/0&&null!=t?t:this.height})}expanded_to({width:h,height:t}){return new g({width:h!=1/0?s(this.width,h):this.width,height:t!=1/0?s(this.height,t):this.height})}expand_to({width:h,height:t}){this.width=s(this.width,h),this.height=s(this.height,t)}narrowed_to({width:h,height:t}){return new g({width:d(this.width,h),height:d(this.height,t)})}narrow_to({width:h,height:t}){this.width=d(this.width,h),this.height=d(this.height,t)}grow_by({left:h,right:t,top:i,bottom:e}){const w=this.width+h+t,n=this.height+i+e;return new g({width:w,height:n})}shrink_by({left:h,right:t,top:i,bottom:e}){const w=s(this.width-h-t,0),n=s(this.height-i-e,0);return new g({width:w,height:n})}map(h,t){return new g({width:h(this.width),height:(null!=t?t:h)(this.height)})}}i.Sizeable=g,g.__name__=\"Sizeable\",i.SizingPolicy=(0,n.Enum)(\"fixed\",\"fit\",\"min\",\"max\")},\n function _(i,t,e,h,s){h();const n=i(174),r=i(64),_=i(8),g=i(12),{abs:o,min:l,max:a,round:d}=Math;class u{constructor(){this.absolute=!1,this.position={left:0,top:0},this._bbox=new r.BBox,this._inner_bbox=new r.BBox,this._sizing=null,this._dirty=!1,this._handlers=[]}*[Symbol.iterator](){}get bbox(){return this._bbox}get inner_bbox(){return this._inner_bbox}get sizing(){return(0,g.assert)(null!=this._sizing),this._sizing}set dirty(i){this._dirty=i}get dirty(){return this._dirty}get visible(){return this.sizing.visible}set visible(i){this.sizing.visible!=i&&(this.sizing.visible=i,this._dirty=!0)}set_sizing(i={}){const t=i.width_policy??\"fit\",e=i.width,h=i.min_width,s=i.max_width,n=i.height_policy??\"fit\",r=i.height,_=i.min_height,g=i.max_height,o=i.aspect,l=i.margin??{top:0,right:0,bottom:0,left:0},a=i.visible??!0,d=i.halign??\"start\",u=i.valign??\"start\";this._sizing={width_policy:t,min_width:h,width:e,max_width:s,height_policy:n,min_height:_,height:r,max_height:g,aspect:o,margin:l,visible:a,halign:d,valign:u,size:{width:e,height:r}},this._init()}_init(){}_set_geometry(i,t){this._bbox=i,this._inner_bbox=t}set_geometry(i,t){const{fixup_geometry:e}=this;null!=e&&([i,t]=e(i,t)),this._set_geometry(i,t??i);for(const i of this._handlers)i(this._bbox,this._inner_bbox)}on_resize(i){this._handlers.push(i)}is_width_expanding(){return\"max\"==this.sizing.width_policy}is_height_expanding(){return\"max\"==this.sizing.height_policy}apply_aspect(i,{width:t,height:e}){const{aspect:h}=this.sizing;if(null!=h){const{width_policy:s,height_policy:n}=this.sizing,r=(i,t)=>{const e={max:4,fit:3,min:2,fixed:1};return e[i]>e[t]};if(\"fixed\"!=s&&\"fixed\"!=n)if(s==n){const s=t,n=d(t/h),r=d(e*h),_=e;o(i.width-s)+o(i.height-n)<=o(i.width-r)+o(i.height-_)?(t=s,e=n):(t=r,e=_)}else r(s,n)?e=d(t/h):t=d(e*h);else\"fixed\"==s?e=d(t/h):\"fixed\"==n&&(t=d(e*h))}return{width:t,height:e}}measure(i){if(null==this._sizing&&this.set_sizing(),!this.sizing.visible)return{width:0,height:0};const t=i=>\"fixed\"==this.sizing.width_policy&&null!=this.sizing.width?this.sizing.width:i,e=i=>\"fixed\"==this.sizing.height_policy&&null!=this.sizing.height?this.sizing.height:i,h=new n.Sizeable(i).shrink_by(this.sizing.margin).map(t,e),s=this._measure(h),r=this.clip_size(s,h),_=t(r.width),g=e(r.height),o=this.apply_aspect(h,{width:_,height:g});return{...s,...o}}compute(i={}){const t=this.measure({width:null!=i.width&&this.is_width_expanding()?i.width:1/0,height:null!=i.height&&this.is_height_expanding()?i.height:1/0}),{width:e,height:h}=t,{left:s,top:n}=this.position,_=new r.BBox({left:s,top:n,width:e,height:h});let g;if(null!=t.inner){const{left:i,top:s,right:n,bottom:_}=t.inner;g=new r.BBox({left:i,top:s,right:e-n,bottom:h-_})}this.set_geometry(_,g)}get xview(){return this.bbox.xview}get yview(){return this.bbox.yview}clip_size(i,t){function e(i,t,e,h){return null==e?e=0:(0,_.isNumber)(e)||(e=d(e.percent*t)),null==h?h=1/0:(0,_.isNumber)(h)||(h=d(h.percent*t)),a(e,l(i,h))}return{width:e(i.width,t.width,this.sizing.min_width,this.sizing.max_width),height:e(i.height,t.height,this.sizing.min_height,this.sizing.max_height)}}has_size_changed(){const{_dirty:i}=this;return this._dirty=!1,i}}e.Layoutable=u,u.__name__=\"Layoutable\";class c extends u{_measure(i){const t=this._content_size(),e=i.bounded_to(this.sizing.size).bounded_to(t);return{width:(()=>{switch(this.sizing.width_policy){case\"fixed\":return null!=this.sizing.width?this.sizing.width:t.width;case\"min\":return t.width;case\"fit\":return e.width;case\"max\":return a(t.width,e.width)}})(),height:(()=>{switch(this.sizing.height_policy){case\"fixed\":return null!=this.sizing.height?this.sizing.height:t.height;case\"min\":return t.height;case\"fit\":return e.height;case\"max\":return a(t.height,e.height)}})()}}}e.ContentLayoutable=c,c.__name__=\"ContentLayoutable\";class w extends c{constructor(i){super(),this.text=i}_content_size(){return new n.Sizeable(this.text.size())}}e.TextLayout=w,w.__name__=\"TextLayout\";class x extends c{constructor(i={}){super(),this.size=i}_content_size(){return new n.Sizeable(this.size)}}e.FixedLayout=x,x.__name__=\"FixedLayout\"},\n function _(e,s,t,a,n){var _;a();const x=e(51),c=e(58);class i extends c.View{}t.BaseTextView=i,i.__name__=\"BaseTextView\";class o extends x.Model{constructor(e){super(e)}}t.BaseText=o,_=o,o.__name__=\"BaseText\",_.define((({Str:e})=>({text:[e]})))},\n function _(n,e,t,i,r){i();const o=n(178),l=n(184),s=n(10),c=[{start:\"$$\",end:\"$$\",inline:!1},{start:\"\\\\[\",end:\"\\\\]\",inline:!1},{start:\"\\\\(\",end:\"\\\\)\",inline:!0}];function d(n){return n.split(\"\").map((n=>`\\\\${n}`)).join(\"\")}t.parse_delimited_string=function(n){const e=[];for(const t of c){const i=d(t.start),r=d(t.end),o=new RegExp(`${i}([^]*?)${r}`,\"gm\");for(const i of n.matchAll(o)){const{index:n}=i,[r,o]=i;e.push({index:n,outer:r,inner:o,delim:t})}}if(e.length>0){if(1==e.length){const[t]=e,i=t.index,r=t.outer.length;if(0==i&&r==n.length){const n=t.inner;return new o.TeX({text:n,inline:t.delim.inline})}}let t=\"\",i=0;for(const r of(0,s.sort_by)(e,(n=>n.index))){const e=r.index;i<=e&&(t+=0!=e?`\\\\text{${n.slice(i,e)}}${r.inner}`:`${r.inner}`,i=e+r.outer.length)}return t+=ithis.load_image()))}set visuals(t){const e=t.color,s=t.alpha,i=t.font_style;let n=t.font_size;const h=t.font,{font_size_scale:o,_base_font_size:r}=this,a=(0,d.parse_css_font_size)(n);if(null!=a){let{value:t,unit:e}=a;t*=o,\"em\"==e&&0!=r&&(t*=r,e=\"px\"),n=`${t}${e}`}const _=`${i} ${n} ${h}`;this.font=_,this.color=(0,c.color2css)(e,s);const l=t.align;this._x_anchor=l;const u=t.baseline;this._y_anchor=(()=>{switch(u){case\"top\":return\"top\";case\"middle\":return\"center\";case\"bottom\":return\"bottom\";default:return\"baseline\"}})()}_computed_position(){const{width:t,height:e}=this._size(),{sx:s,sy:i,x_anchor:n=this._x_anchor,y_anchor:h=this._y_anchor}=this.position,o=(0,d.font_metrics)(this.font);return{x:s-(()=>{if((0,_.isNumber)(n))return n*t;switch(n){case\"left\":return 0;case\"center\":return.5*t;case\"right\":return t}})(),y:i-(()=>{if((0,_.isNumber)(h))return h*e;switch(h){case\"top\":return o.height>e?e-(-this.valign-o.descent)-o.height:0;case\"center\":case\"baseline\":return.5*e;case\"bottom\":return o.height>e?e+o.descent+this.valign:e}})()}}size(){const{width:t,height:e}=this._size(),{angle:s}=this;if(null==s||0==s)return{width:t,height:e};{const i=Math.cos(Math.abs(s)),n=Math.sin(Math.abs(s));return{width:Math.abs(t*i+e*n),height:Math.abs(t*n+e*i)}}}get_image_dimensions(){const t=(0,d.font_metrics)(this.font),e=this.svg_element.getAttribute(\"style\")?.split(\";\");if(null!=e){const s=new Map;e.forEach((t=>{const[e,i]=t.split(\":\");\"\"!=e.trim()&&s.set(e.trim(),i.trim())}));const i=(0,d.parse_css_length)(s.get(\"vertical-align\"));\"ex\"==i?.unit?this.valign=i.value*t.x_height:\"px\"==i?.unit&&(this.valign=i.value)}const s=(()=>{const t=this.svg_element.getAttribute(\"width\"),e=this.svg_element.getAttribute(\"height\");return{width:null!=t&&t.endsWith(\"ex\")?parseFloat(t):1,height:null!=e&&e.endsWith(\"ex\")?parseFloat(e):1}})();return{width:t.x_height*s.width,height:t.x_height*s.height}}get truncated_text(){return this.model.text.length>6?`${this.model.text.substring(0,6)}...`:this.model.text}_size(){if(null==this.svg_image)return\"failed\"==this.provider.status||\"not_started\"==this.provider.status?{width:(0,u.text_width)(this.truncated_text,this.font),height:(0,d.font_metrics)(this.font).height}:{width:this._base_font_size,height:this._base_font_size};const t=(0,d.font_metrics)(this.font);let{width:e,height:s}=this.get_image_dimensions();s=Math.max(s,t.height);return{width:e*(\"%\"==this.width?.unit?this.width.value:1),height:s*(\"%\"==this.height?.unit?this.height.value:1)}}bbox(){const{p0:t,p1:e,p2:s,p3:i}=this.rect(),n=Math.min(t.x,e.x,s.x,i.x),h=Math.min(t.y,e.y,s.y,i.y),o=Math.max(t.x,e.x,s.x,i.x),r=Math.max(t.y,e.y,s.y,i.y);return new p.BBox({left:n,right:o,top:h,bottom:r})}rect(){const t=this._rect(),{angle:e}=this;if(null==e||0==e)return t;{const{sx:s,sy:i}=this.position,n=new x.AffineTransform;return n.translate(s,i),n.rotate(e),n.translate(-s,-i),n.apply_rect(t)}}paint_rect(t){const{p0:e,p1:s,p2:i,p3:n}=this.rect();t.save(),t.strokeStyle=\"red\",t.lineWidth=1,t.beginPath();const{round:h}=Math;t.moveTo(h(e.x),h(e.y)),t.lineTo(h(s.x),h(s.y)),t.lineTo(h(i.x),h(i.y)),t.lineTo(h(n.x),h(n.y)),t.closePath(),t.stroke(),t.restore()}paint_bbox(t){const{x:e,y:s,width:i,height:n}=this.bbox();t.save(),t.strokeStyle=\"blue\",t.lineWidth=1,t.beginPath();const{round:h}=Math;t.moveTo(h(e),h(s)),t.lineTo(h(e),h(s+n)),t.lineTo(h(e+i),h(s+n)),t.lineTo(h(e+i),h(s)),t.closePath(),t.stroke(),t.restore()}async request_image(){if(null==this.provider.MathJax)return;const t=this._process_text();if(null==t)return void(this._has_finished=!0);const e=t.children[0];this.svg_element=e,e.setAttribute(\"font\",this.font),e.setAttribute(\"stroke\",this.color);const s=e.outerHTML,i=`data:image/svg+xml;utf-8,${encodeURIComponent(s)}`;this.svg_image=await(0,l.load_image)(i)}async load_image(){await this.request_image(),this.parent.request_layout()}paint(t){null==this.svg_image&&(\"not_started\"!=this.provider.status&&\"loading\"!=this.provider.status||this.provider.ready.connect((()=>this.load_image())),\"loaded\"==this.provider.status&&this.load_image()),t.save();const{sx:e,sy:s}=this.position,{angle:i}=this;null!=i&&0!=i&&(t.translate(e,s),t.rotate(i),t.translate(-e,-s));const{x:n,y:h}=this._computed_position();if(null!=this.svg_image){const{width:e,height:s}=this.get_image_dimensions();t.drawImage(this.svg_image,n,h,e,s)}else\"failed\"!=this.provider.status&&\"not_started\"!=this.provider.status||(t.fillStyle=this.color,t.font=this.font,t.textAlign=\"left\",t.textBaseline=\"alphabetic\",t.fillText(this.truncated_text,n,h+(0,d.font_metrics)(this.font).ascent));t.restore(),this._has_finished||\"failed\"!=this.provider.status&&null==this.svg_image||(this._has_finished=!0,this.parent.notify_finished_after_paint())}}s.MathTextView=y,y.__name__=\"MathTextView\";class v extends f.BaseText{constructor(t){super(t)}}s.MathText=v,v.__name__=\"MathText\";class b extends y{get styled_text(){return this.text}_process_text(){}_size(){return{width:(0,u.text_width)(this.text,this.font),height:(0,d.font_metrics)(this.font).height}}paint(t){t.save();const{sx:e,sy:s}=this.position,{angle:i}=this;null!=i&&0!=i&&(t.translate(e,s),t.rotate(i),t.translate(-e,-s));const{x:n,y:h}=this._computed_position();t.fillStyle=this.color,t.font=this.font,t.textAlign=\"left\",t.textBaseline=\"alphabetic\",t.fillText(this.text,n,h+(0,d.font_metrics)(this.font).ascent),t.restore(),this._has_finished=!0,this.parent.notify_finished_after_paint()}}s.AsciiView=b,b.__name__=\"AsciiView\";class w extends v{constructor(t){super(t)}}s.Ascii=w,h=w,w.__name__=\"Ascii\",h.prototype.default_view=b;class M extends y{get styled_text(){let t=this.text.trim(),e=t.match(//s);return null==e?this.text.trim():(t=(0,g.insert_text_on_position)(t,t.indexOf(e[0])+e[0].length,``),e=t.match(/<\\/[^>]*?math.*?>/s),null==e?this.text.trim():(0,g.insert_text_on_position)(t,t.indexOf(e[0]),\"\"))}_process_text(){const t=(0,d.font_metrics)(this.font);return this.provider.MathJax?.mathml2svg(this.styled_text,{em:this.base_font_size,ex:t.x_height})}}s.MathMLView=M,M.__name__=\"MathMLView\";class z extends v{constructor(t){super(t)}}s.MathML=z,o=z,z.__name__=\"MathML\",o.prototype.default_view=M;class T extends y{get styled_text(){const[t,e,s]=(0,c.color2rgba)(this.color);return`\\\\color[RGB]{${t}, ${e}, ${s}} ${this.font.includes(\"bold\")?`\\\\pmb{${this.text}}`:this.text}`}_process_text(){const t=(0,d.font_metrics)(this.font);return this.provider.MathJax?.tex2svg(this.styled_text,{display:!this.model.inline,em:this.base_font_size,ex:t.x_height},(0,a.to_object)(this.model.macros))}}s.TeXView=T,T.__name__=\"TeXView\";class $ extends v{constructor(t){super(t)}}s.TeX=$,r=$,$.__name__=\"TeX\",r.prototype.default_view=T,r.define((({Bool:t,Float:e,Str:s,Dict:i,Tuple:n,Or:h})=>({macros:[i(h(s,n(s,e))),{}],inline:[t,!1]})))},\n function _(i,e,t,s,n){s();const a=i(19);t.load_image=async function(i,e){return new o(i,e).promise};class o{constructor(i,e={}){this.image=new Image,this._finished=!1;const{attempts:t=1,timeout:s=1}=e,n=(()=>{if(i instanceof ArrayBuffer){const e=new Blob([i],{type:\"image/png\"});return URL.createObjectURL(e)}return i})();this.promise=new Promise(((i,o)=>{this.image.crossOrigin=\"anonymous\";let r=0;this.image.onerror=()=>{if(++r==t){const i=`unable to load ${n} image after ${t} attempts`;if(a.logger.warn(i),null==this.image.crossOrigin)return void e.failed?.();a.logger.warn(`attempting to load ${n} without a cross origin policy`),this.image.crossOrigin=null,r=0}setTimeout((()=>this.image.src=n),s)},this.image.onload=()=>{this._finished=!0,e.loaded?.(this.image),i(this.image)},this.image.src=n}))}get finished(){return this._finished}}t.ImageLoader=o,o.__name__=\"ImageLoader\"},\n function _(t,e,s,i,n){i();const h=t(64),o=t(181),a=t(10),r=t(8),c=t(114),_=t(22);s.text_width=(()=>{const t=document.createElement(\"canvas\").getContext(\"2d\");let e=\"\";return(s,i)=>(i!=e&&(e=i,t.font=i),t.measureText(s).width)})();class l{constructor(){this._position={sx:0,sy:0},this.font_size_scale=1,this.align=\"left\",this._base_font_size=13,this._x_anchor=\"left\",this._y_anchor=\"center\"}set base_font_size(t){null!=t&&(this._base_font_size=t)}get base_font_size(){return this._base_font_size}set position(t){this._position=t}get position(){return this._position}infer_text_height(){return\"ascent_descent\"}bbox(){const{p0:t,p1:e,p2:s,p3:i}=this.rect(),n=Math.min(t.x,e.x,s.x,i.x),o=Math.min(t.y,e.y,s.y,i.y),a=Math.max(t.x,e.x,s.x,i.x),r=Math.max(t.y,e.y,s.y,i.y);return new h.BBox({left:n,right:a,top:o,bottom:r})}size(){const{width:t,height:e}=this._size(),{angle:s}=this;if(null==s||0==s)return{width:t,height:e};{const i=Math.cos(Math.abs(s)),n=Math.sin(Math.abs(s));return{width:Math.abs(t*i+e*n),height:Math.abs(t*n+e*i)}}}rect(){const t=this._rect(),{angle:e}=this;if(null==e||0==e)return t;{const{sx:s,sy:i}=this.position,n=new c.AffineTransform;return n.translate(s,i),n.rotate(e),n.translate(-s,-i),n.apply_rect(t)}}paint_rect(t){const{p0:e,p1:s,p2:i,p3:n}=this.rect();t.save(),t.strokeStyle=\"red\",t.lineWidth=1,t.beginPath();const{round:h}=Math;t.moveTo(h(e.x),h(e.y)),t.lineTo(h(s.x),h(s.y)),t.lineTo(h(i.x),h(i.y)),t.lineTo(h(n.x),h(n.y)),t.closePath(),t.stroke(),t.restore()}paint_bbox(t){const{x:e,y:s,width:i,height:n}=this.bbox();t.save(),t.strokeStyle=\"blue\",t.lineWidth=1,t.beginPath();const{round:h}=Math;t.moveTo(h(e),h(s)),t.lineTo(h(e),h(s+n)),t.lineTo(h(e+i),h(s+n)),t.lineTo(h(e+i),h(s)),t.closePath(),t.stroke(),t.restore()}}s.GraphicsBox=l,l.__name__=\"GraphicsBox\";class x extends l{set visuals(t){const e=t.color,s=t.alpha,i=t.outline_color,n=t.font_style;let h=t.font_size;const a=t.font,{font_size_scale:r,base_font_size:c}=this,l=(0,o.parse_css_font_size)(h);if(null!=l){let{value:t,unit:e}=l;t*=r,\"em\"==e&&0!=c&&(t*=c,e=\"px\"),h=`${t}${e}`}const x=`${n} ${h} ${a}`;this.font=x,this.color=(0,_.color2css)(e,s),this.outline_color=(0,_.color2css)(i,s),this.line_height=t.line_height;const u=t.align;this._visual_align=u,this._x_anchor=u;const p=t.baseline;this._y_anchor=(()=>{switch(p){case\"top\":return\"top\";case\"middle\":return\"center\";case\"bottom\":return\"bottom\";default:return\"baseline\"}})()}constructor({text:t}){super(),this._visual_align=\"left\",this.text=t}infer_text_height(){if(this.text.includes(\"\\n\"))return\"ascent_descent\";{function t(t){for(const e of new Set(t))if(!(\"0\"<=e&&e<=\"9\"))switch(e){case\",\":case\".\":case\"+\":case\"-\":case\"\\u2212\":case\"e\":continue;default:return!1}return!0}return t(this.text)?\"cap\":\"ascent_descent\"}}_text_line(t){const e=this.text_height_metric??this.infer_text_height(),s=(()=>{switch(e){case\"x\":case\"x_descent\":return t.x_height;case\"cap\":case\"cap_descent\":return t.cap_height;case\"ascent\":case\"ascent_descent\":return t.ascent}})(),i=(()=>{switch(e){case\"x\":case\"cap\":case\"ascent\":return 0;case\"x_descent\":case\"cap_descent\":case\"ascent_descent\":return t.descent}})();return{height:s+i,ascent:s,descent:i}}get nlines(){return this.text.split(\"\\n\").length}_size(){const{font:t}=this,e=(0,o.font_metrics)(t),i=(this.line_height-1)*e.height,n=\"\"==this.text,h=this.text.split(\"\\n\"),r=h.length,c=h.map((e=>(0,s.text_width)(e,t))),_=this._text_line(e).height*r,l=\"%\"==this.width?.unit?this.width.value:1,x=\"%\"==this.height?.unit?this.height.value:1;return{width:(0,a.max)(c)*l,height:n?0:(_+i*(r-1))*x,metrics:e}}_computed_position(t,e,s){const{width:i,height:n}=t,{sx:h,sy:o,x_anchor:a=this._x_anchor,y_anchor:c=this._y_anchor}=this.position;return{x:h-(()=>{if((0,r.isNumber)(a))return a*i;switch(a){case\"left\":return 0;case\"center\":return.5*i;case\"right\":return i}})(),y:o-(()=>{if((0,r.isNumber)(c))return c*n;switch(c){case\"top\":return 0;case\"center\":return.5*n;case\"bottom\":return n;case\"baseline\":if(1!=s)return.5*n;switch(this.text_height_metric??this.infer_text_height()){case\"x\":case\"x_descent\":return e.x_height;case\"cap\":case\"cap_descent\":return e.cap_height;case\"ascent\":case\"ascent_descent\":return e.ascent}}})()}}_rect(){const{width:t,height:e,metrics:s}=this._size(),i=this.text.split(\"\\n\").length,{x:n,y:o}=this._computed_position({width:t,height:e},s,i);return new h.BBox({x:n,y:o,width:t,height:e}).rect}paint(t){const{font:e}=this,i=(0,o.font_metrics)(e),n=(this.line_height-1)*i.height,h=this.text.split(\"\\n\"),r=h.length,c=h.map((t=>(0,s.text_width)(t,e))),_=this._text_line(i),l=_.height*r,x=\"%\"==this.width?.unit?this.width.value:1,u=\"%\"==this.height?.unit?this.height.value:1,p=(0,a.max)(c)*x,f=(l+n*(r-1))*u;t.save(),t.fillStyle=this.color,t.strokeStyle=this.outline_color,t.font=this.font,t.textAlign=\"left\",t.textBaseline=\"alphabetic\";const{sx:g,sy:d}=this.position,{align:b}=this,{angle:m}=this;null!=m&&0!=m&&(t.translate(g,d),t.rotate(m),t.translate(-g,-d));let{x:y,y:w}=this._computed_position({width:p,height:f},i,r);if(\"justify\"==b)for(let i=0;i(0,s.text_width)(t,e))),x=(p-(0,a.sum)(l))/(c-1);for(let e=0;e{switch(\"auto\"==b?this._visual_align:b){case\"left\":return 0;case\"center\":return.5*(p-c[e]);case\"right\":return p-c[e]}})(),i=h[e],o=w+_.ascent;t.fillText(i,s,o),t.strokeText(i,s,o),w+=_.height+n}t.restore()}}s.TextBox=x,x.__name__=\"TextBox\";class u extends l{constructor(t,e){super(),this.base=t,this.expo=e}get children(){return[this.base,this.expo]}set base_font_size(t){super.base_font_size=t,this.base.base_font_size=t,this.expo.base_font_size=t}set position(t){this._position=t;const e=this.base.size(),s=this.expo.size(),i=this._shift_scale()*e.height,n=Math.max(e.height,i+s.height);this.base.position={sx:0,x_anchor:\"left\",sy:n,y_anchor:\"bottom\"},this.expo.position={sx:e.width,x_anchor:\"left\",sy:i,y_anchor:\"bottom\"}}get position(){return this._position}set visuals(t){this.expo.font_size_scale=.7,this.base.visuals=t,this.expo.visuals=t}_shift_scale(){if(this.base instanceof x&&1==this.base.nlines){const{x_height:t,cap_height:e}=(0,o.font_metrics)(this.base.font);return t/e}return 2/3}infer_text_height(){return this.base.infer_text_height()}_rect(){const t=this.base.bbox(),e=this.expo.bbox(),s=t.union(e),{x:i,y:n}=this._computed_position();return s.translate(i,n).rect}_size(){const t=this.base.size(),e=this.expo.size();return{width:t.width+e.width,height:Math.max(t.height,this._shift_scale()*t.height+e.height)}}paint(t){t.save();const{angle:e}=this;if(null!=e&&0!=e){const{sx:s,sy:i}=this.position;t.translate(s,i),t.rotate(e),t.translate(-s,-i)}const{x:s,y:i}=this._computed_position();t.translate(s,i),this.base.paint(t),this.expo.paint(t),t.restore()}paint_bbox(t){super.paint_bbox(t);const{x:e,y:s}=this._computed_position();t.save(),t.translate(e,s);for(const e of this.children)e.paint_bbox(t);t.restore()}_computed_position(){const{width:t,height:e}=this._size(),{sx:s,sy:i,x_anchor:n=this._x_anchor,y_anchor:h=this._y_anchor}=this.position;return{x:s-(()=>{if((0,r.isNumber)(n))return n*t;switch(n){case\"left\":return 0;case\"center\":return.5*t;case\"right\":return t}})(),y:i-(()=>{if((0,r.isNumber)(h))return h*e;switch(h){case\"top\":return 0;case\"center\":case\"baseline\":return.5*e;case\"bottom\":return e}})()}}}s.BaseExpo=u,u.__name__=\"BaseExpo\";class p{constructor(t){this.items=t}set base_font_size(t){for(const e of this.items)e.base_font_size=t}get length(){return this.items.length}set visuals(t){for(const e of this.items)e.visuals=t;const e={x:0,cap:1,ascent:2,x_descent:3,cap_descent:4,ascent_descent:5},s=(0,a.max_by)(this.items.map((t=>t.infer_text_height())),(t=>e[t]));for(const t of this.items)t.text_height_metric=s}set angle(t){for(const e of this.items)e.angle=t}max_size(){let t=0,e=0;for(const s of this.items){const i=s.size();t=Math.max(t,i.width),e=Math.max(e,i.height)}return{width:t,height:e}}}s.GraphicsBoxes=p,p.__name__=\"GraphicsBoxes\"},\n function _(n,t,e,o,s){o();const c=n(12),u=n(63),i=(()=>{const n=(\"undefined\"!=typeof OffscreenCanvas?new OffscreenCanvas(0,0):(0,u.canvas)({width:0,height:0})).getContext(\"2d\");return(0,c.assert)(null!=n,\"can't obtain 2d context\"),n})();function a(n){const t=i;t.font=n;const e=t.measureText(\"M\"),o=t.measureText(\"x\"),s=t.measureText(\"\\xc5\\u015ag|\"),c=void 0!==s.fontBoundingBoxAscent?s.fontBoundingBoxAscent:s.actualBoundingBoxAscent,u=void 0!==s.fontBoundingBoxDescent?s.fontBoundingBoxDescent:s.actualBoundingBoxDescent;return{height:c+u,ascent:c,descent:u,cap_height:e.actualBoundingBoxAscent,x_height:o.actualBoundingBoxAscent}}const f=new Map;e.font_metrics=function(n){let t=f.get(n);if(null==t){const e=document.fonts.check(n);t={font:a(n)},e&&f.set(n,t)}return t.font},e.parse_css_font_size=function(n){const t=n.match(/^\\s*(\\d+(\\.\\d+)?)(\\w+)\\s*$/);if(null!=t){const[,n,,e]=t,o=Number(n);if(isFinite(o))return{value:o,unit:e}}return null},e.parse_css_length=function(n){const t=n.match(/^\\s*(-?\\d+(\\.\\d+)?)(\\w+)\\s*$/);if(null!=t){const[,n,,e]=t,o=Number(n);if(isFinite(o))return{value:o,unit:e}}return null}},\n function _(t,e,a,r,n){var s=this&&this.__createBinding||(Object.create?function(t,e,a,r){void 0===r&&(r=a);var n=Object.getOwnPropertyDescriptor(e,a);n&&!(\"get\"in n?!e.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return e[a]}}),Object.defineProperty(t,r,n)}:function(t,e,a,r){void 0===r&&(r=a),t[r]=e[a]}),i=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,\"default\",{enumerable:!0,value:e})}:function(t,e){t.default=e}),o=this&&this.__importStar||function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)\"default\"!==a&&Object.prototype.hasOwnProperty.call(t,a)&&s(e,t,a);return i(e,t),e};r();const d=t(15),l=t(183);class u{constructor(){this.ready=new d.Signal0(this,\"ready\"),this.status=\"not_started\"}}a.MathJaxProvider=u,u.__name__=\"MathJaxProvider\";class c extends u{get MathJax(){return null}async fetch(){this.status=\"failed\"}}a.NoProvider=c,c.__name__=\"NoProvider\";class h extends u{get MathJax(){return\"undefined\"!=typeof MathJax?MathJax:null}async fetch(){const t=document.createElement(\"script\");t.src=\"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js\",t.onload=()=>{this.status=\"loaded\",this.ready.emit()},t.onerror=()=>{this.status=\"failed\"},this.status=\"loading\",document.head.appendChild(t)}}a.CDNProvider=h,h.__name__=\"CDNProvider\";class _ extends u{get MathJax(){return this._mathjax}async fetch(){this.status=\"loading\";try{const e=await(0,l.load_module)(Promise.resolve().then((()=>o(t(704)))));this.status=null==e?\"failed\":\"loaded\",this._mathjax=e,this.ready.emit()}catch(t){this.status=\"failed\"}}}a.BundleProvider=_,_.__name__=\"BundleProvider\",a.default_provider=new _},\n function _(n,r,o,t,c){t(),o.load_module=async function(n){try{return await n}catch(n){if((r=n)instanceof Error&&\"code\"in r&&\"MODULE_NOT_FOUND\"===n.code)return null;throw n}var r}},\n function _(e,t,i,n,s){var a;n();const x=e(176),_=e(180);class l extends x.BaseTextView{initialize(){super.initialize(),this._has_finished=!0}graphics(){return new _.TextBox({text:this.model.text})}}i.PlainTextView=l,l.__name__=\"PlainTextView\";class r extends x.BaseText{constructor(e){super(e)}}i.PlainText=r,a=r,r.__name__=\"PlainText\",a.prototype.default_view=l},\n function _(t,n,e,i,r){i();const g=t(1),a=t(21),h=g.__importStar(t(20));e.Length=(0,a.NonNegative)(a.Int);var L;e.Anchor=(0,a.Or)(h.Anchor,(0,a.Tuple)((0,a.Or)(h.Align,h.HAlign,a.Percent),(0,a.Or)(h.Align,h.VAlign,a.Percent))),e.TextAnchor=(0,a.Or)(e.Anchor,a.Auto),e.Padding=(0,a.Or)(e.Length,(0,a.Tuple)(e.Length,e.Length),(L=e.Length,(0,a.PartialStruct)({x:L,y:L})),(0,a.Tuple)(e.Length,e.Length,e.Length,e.Length),(t=>(0,a.PartialStruct)({left:t,right:t,top:t,bottom:t}))(e.Length)),e.BorderRadius=(0,a.Or)(e.Length,(0,a.Tuple)(e.Length,e.Length,e.Length,e.Length),(0,a.PartialStruct)({top_left:e.Length,top_right:e.Length,bottom_right:e.Length,bottom_left:e.Length})),e.Index=(0,a.NonNegative)(a.Int),e.Span=(0,a.NonNegative)(a.Int);e.GridChild=t=>(0,a.Tuple)((0,a.Ref)(t),e.Index,e.Index,(0,a.Opt)(e.Span),(0,a.Opt)(e.Span)),e.GridSpacing=(0,a.Or)(e.Length,(0,a.Tuple)(e.Length,e.Length)),e.TrackAlign=(0,a.Enum)(\"start\",\"center\",\"end\",\"auto\"),e.TrackSize=a.Str,e.TrackSizing=(0,a.PartialStruct)({size:e.TrackSize,align:e.TrackAlign}),e.TrackSizingLike=(0,a.Or)(e.TrackSize,e.TrackSizing),e.TracksSizing=(0,a.Or)(e.TrackSizingLike,(0,a.List)(e.TrackSizingLike),(0,a.Mapping)(a.Int,e.TrackSizingLike))},\n function _(t,e,r,n,o){n();const c=t(8),i=t(12);function s(t){if(!(0,c.isString)(t)){return{x:(()=>{const[e]=t;switch(e){case\"start\":case\"left\":return 0;case\"center\":return.5;case\"end\":case\"right\":return 1;default:return e}})(),y:(()=>{const[,e]=t;switch(e){case\"start\":case\"top\":return 0;case\"center\":return.5;case\"end\":case\"bottom\":return 1;default:return e}})()}}switch(t){case\"top_left\":return{x:0,y:0};case\"top\":case\"top_center\":return{x:.5,y:0};case\"top_right\":return{x:1,y:0};case\"right\":case\"center_right\":return{x:1,y:.5};case\"bottom_right\":return{x:1,y:1};case\"bottom\":case\"bottom_center\":return{x:.5,y:1};case\"bottom_left\":return{x:0,y:1};case\"left\":case\"center_left\":return{x:0,y:.5};case\"center\":case\"center_center\":return{x:.5,y:.5}}}r.anchor=s,r.text_anchor=function(t,e,r){return s(\"auto\"!=t?t:[(()=>{switch(e){case\"left\":return\"start\";case\"center\":return\"center\";case\"right\":return\"end\"}})(),(()=>{switch(r){case\"alphabetic\":case\"ideographic\":case\"hanging\":case\"middle\":return\"center\";case\"top\":return\"start\";case\"bottom\":return\"end\"}})()])},r.padding=function(t){if((0,c.isNumber)(t))return{left:t,right:t,top:t,bottom:t};if(!(0,c.isPlainObject)(t)){if(2==t.length){const[e=0,r=0]=t;return{left:e,right:e,top:r,bottom:r}}{const[e=0,r=0,n=0,o=0]=t;return{left:e,right:r,top:n,bottom:o}}}if(\"x\"in t||\"y\"in t){const{x:e=0,y:r=0}=t;return{left:e,right:e,top:r,bottom:r}}if(\"left\"in t||\"right\"in t||\"top\"in t||\"bottom\"in t){const{left:e=0,right:r=0,top:n=0,bottom:o=0}=t;return{left:e,right:r,top:n,bottom:o}}(0,i.unreachable)()},r.border_radius=function(t){if((0,c.isNumber)(t))return{top_left:t,top_right:t,bottom_right:t,bottom_left:t};if((0,c.isPlainObject)(t))return{top_left:t.top_left??0,top_right:t.top_right??0,bottom_right:t.bottom_right??0,bottom_left:t.bottom_left??0};{const[e=0,r=0,n=0,o=0]=t;return{top_left:e,top_right:r,bottom_right:n,bottom_left:o}}}},\n function _(t,o,e,i,n){i(),e.round_rect=function(t,o,e){let{top_left:i,top_right:n,bottom_right:c,bottom_left:h}=e;if(0!=i||0!=n||0!=c||0!=h){const{left:e,right:l,top:r,bottom:T,width:f,height:a}=o,_=Math.min(f/(i+n),a/(n+c),f/(c+h),a/(i+h));_<1&&(i*=_,n*=_,c*=_,h*=_),t.moveTo(e+i,r),t.lineTo(l-n,r),0!=n&&t.arcTo(l,r,l,r+n,n),t.lineTo(l,T-c),0!=c&&t.arcTo(l,T,l-c,T,c),t.lineTo(e+h,T),0!=h&&t.arcTo(e,T,e,T-h,h),t.lineTo(e,r+i),0!=i&&t.arcTo(e,r,e+i,r,i),t.closePath()}else{const{left:e,top:i,width:n,height:c}=o;t.rect(e,i,n,c)}}},\n function _(e,s,t,a,r){var _;a();const n=e(84),i=e(104),g=e(103),o=e(96),l=e(101),c=e(98),h=e(99),x=e(106),u=e(109),p=e(64),y=e(9),f=e(12),d=e(8),m=e(63);class w extends n.StyledElementView{constructor(){super(...arguments),this._bbox=new p.BBox,this._x_ranges=new Map,this._y_ranges=new Map,this._x_scales=new Map,this._y_scales=new Map,this.position=new m.InlineStyleSheet}get bbox(){return this._bbox}initialize(){super.initialize(),this._configure_scales()}remove(){this._unregister_frame(),super.remove()}connect_signals(){super.connect_signals();const{x_range:e,y_range:s,x_scale:t,y_scale:a,extra_x_ranges:r,extra_y_ranges:_,extra_x_scales:n,extra_y_scales:i}=this.model.properties;this.on_change([e,s,t,a,r,_,n,i],(()=>{this._configure_scales()}))}_get_ranges(e,s){return new Map([...(0,y.entries)(s),[\"default\",e]])}_get_scales(e,s,t,a){const r=new Map([...(0,y.entries)(s),[\"default\",e]]),_=new Map;for(const[s,n]of t){if(n instanceof u.FactorRange!=e instanceof i.CategoricalScale)throw new Error(`'${n.type}' is incompatible '${e.type}'`);e instanceof g.LogScale&&n instanceof x.DataRange1d&&(n.scale_hint=\"log\");const t=(r.get(s)??e).clone();t.setv({source_range:n,target_range:a}),_.set(s,t)}return _}_configure_ranges(){const{bbox:e}=this;this._x_target=new h.Range1d({start:e.left,end:e.right}),this._y_target=new h.Range1d({start:e.bottom,end:e.top})}_configure_scales(){const{x_range:e,y_range:s,extra_x_ranges:t,extra_y_ranges:a}=this.model,{x_scale:r,y_scale:_,extra_x_scales:n,extra_y_scales:i}=this.model;(0,f.assert)(r.properties.source_range.is_unset&&r.properties.target_range.is_unset),(0,f.assert)(_.properties.source_range.is_unset&&_.properties.target_range.is_unset),this._configure_ranges(),this._unregister_frame(),this._x_ranges=this._get_ranges(e,t),this._y_ranges=this._get_ranges(s,a),this._register_frame(),this._x_scales=this._get_scales(r,n,this._x_ranges,this._x_target),this._y_scales=this._get_scales(_,i,this._y_ranges,this._y_target),this._x_scale=this._x_scales.get(\"default\"),this._y_scale=this._y_scales.get(\"default\"),this.mark_finished()}_update_scales(){this._configure_ranges();for(const[,e]of this._x_scales)e.target_range=this._x_target;for(const[,e]of this._y_scales)e.target_range=this._y_target}_register_frame(){for(const e of this.ranges.values())e.frames.add(this)}_unregister_frame(){for(const e of this.ranges.values())e.frames.delete(this)}set_geometry(e){this._bbox=e,this._update_scales(),this._update_position()}get x_range(){return this.model.x_range}get y_range(){return this.model.y_range}get x_target(){return this._x_target}get y_target(){return this._y_target}get x_ranges(){return this._x_ranges}get y_ranges(){return this._y_ranges}get ranges(){return new Set([...this.x_ranges.values(),...this.y_ranges.values()])}get x_scales(){return this._x_scales}get y_scales(){return this._y_scales}get scales(){return new Set([...this.x_scales.values(),...this.y_scales.values()])}get x_scale(){return this._x_scale}get y_scale(){return this._y_scale}resolve_symbol(e){const s=this.bbox.resolve(e.symbol),{offset:t}=e;if((0,d.isNumber)(s))return s+t;{const{x:e,y:a}=s;return{x:e+t,y:a+t}}}stylesheets(){return[...super.stylesheets(),this.position]}rendering_target(){return this.parent.canvas_view.underlays_el}_update_position(){const{bbox:e,position:s}=this;e.is_valid?s.replace(`\\n :host {\\n position: absolute;\\n left: ${e.left}px;\\n top: ${e.top}px;\\n width: ${e.width}px;\\n height: ${e.height}px;\\n }\\n `):s.replace(\"\\n :host {\\n display: none;\\n }\\n \")}}t.CartesianFrameView=w,w.__name__=\"CartesianFrameView\";class b extends n.StyledElement{constructor(e){super(e)}}t.CartesianFrame=b,_=b,b.__name__=\"CartesianFrame\",_.prototype.default_view=w,_.define((({Bool:e,Float:s,Dict:t,Ref:a})=>({x_range:[a(c.Range),()=>new x.DataRange1d],y_range:[a(c.Range),()=>new x.DataRange1d],x_scale:[a(o.Scale),()=>new l.LinearScale],y_scale:[a(o.Scale),()=>new l.LinearScale],extra_x_ranges:[t(a(c.Range)),{}],extra_y_ranges:[t(a(c.Range)),{}],extra_x_scales:[t(a(o.Scale)),{}],extra_y_scales:[t(a(o.Scale)),{}],match_aspect:[e,!1],aspect_scale:[s,1]})))},\n function _(t,e,i,s,a){var o;s();const n=t(1),l=t(190),r=t(191),_=t(192),h=t(193),c=n.__importStar(t(80)),d=t(20),u=t(24),m=t(173),b=t(10),f=t(9),x=t(8),g=t(180),p=t(109),v=t(176),w=t(56),k=t(12),j=t(8),y=t(64),z=t(177),M=t(21);i.LabelOverrides=(0,M.Or)((0,M.Dict)((0,M.Or)(M.Str,(0,M.Ref)(v.BaseText))),(0,M.Mapping)((0,M.Or)(M.Str,M.Float),(0,M.Or)(M.Str,(0,M.Ref)(v.BaseText))));const{abs:T}=Math;class A extends l.GuideRendererView{constructor(){super(...arguments),this._axis_label_view=null,this._major_label_views=new Map}get panel(){return this._panel}set panel(t){this._panel=new m.SidePanel(t.side,this.model.face)}get bbox(){if(null!=this.layout&&null==this.model.fixed_location)return this.layout.bbox;if(this.is_renderable){const{extents:t}=this,e=Math.round(t.tick+t.tick_label+t.axis_label);let{sx0:i,sy0:s,sx1:a,sy1:o}=this.rule_scoords;const{dimension:n,face:l}=this;return 0==n?\"front\"==l?s-=e:o+=e:\"front\"==l?i-=e:a+=e,y.BBox.from_lrtb({left:i,top:s,right:a,bottom:o})}return new y.BBox}*children(){yield*super.children(),null!=this._axis_label_view&&(yield this._axis_label_view),yield*this._major_label_views.values()}async lazy_initialize(){await super.lazy_initialize(),await this._init_axis_label(),await this._init_major_labels()}async _init_axis_label(){const{axis_label:t}=this.model;if(null!=t){const e=(0,j.isString)(t)?(0,z.parse_delimited_string)(t):t;this._axis_label_view=await(0,w.build_view)(e,{parent:this})}else this._axis_label_view=null}async _init_major_labels(){for(const[t,e]of(0,f.dict)(this.model.major_label_overrides)){const i=(0,j.isString)(e)?(0,z.parse_delimited_string)(e):e;this._major_label_views.set(t,await(0,w.build_view)(i,{parent:this}))}}update_layout(){this.layout=new m.SideLayout(this.panel,(()=>this.get_size()),!0),this.layout.on_resize((()=>{this._coordinates=void 0}))}get_size(){const{visible:t,fixed_location:e}=this.model;if(t&&null==e&&this.is_renderable){const{extents:t}=this;return{width:0,height:Math.round(t.tick+t.tick_label+t.axis_label)}}return{width:0,height:0}}get is_renderable(){const[t,e]=this.ranges;return super.is_renderable&&t.is_valid&&e.is_valid&&t.span>0&&e.span>0}_paint(){const{tick_coords:t,extents:e}=this,i=this.layer.ctx;this._draw_background(i,e),this._draw_rule(i,e),this._draw_major_ticks(i,e,t),this._draw_minor_ticks(i,e,t),this._draw_major_labels(i,e,t),this._draw_axis_label(i,e,t)}connect_signals(){super.connect_signals();const{axis_label:t,major_label_overrides:e}=this.model.properties;this.on_change(t,(async()=>{this._axis_label_view?.remove(),await this._init_axis_label()})),this.on_change(e,(async()=>{for(const t of this._major_label_views.values())t.remove();await this._init_major_labels()})),this.connect(this.model.change,(()=>this.plot_view.request_layout()))}get needs_clip(){return null!=this.model.fixed_location}_draw_background(t,e){if(!this.visuals.background_fill.doit)return;t.beginPath();const{x:i,y:s,width:a,height:o}=this.bbox;t.rect(i,s,a,o),this.visuals.background_fill.apply(t)}_draw_rule(t,e){if(!this.visuals.axis_line.doit)return;const{sx0:i,sy0:s,sx1:a,sy1:o}=this.rule_scoords;t.beginPath(),t.moveTo(i,s),t.lineTo(a,o),this.visuals.axis_line.apply(t)}_draw_major_ticks(t,e,i){const s=this.model.major_tick_in,a=this.model.major_tick_out,o=this.visuals.major_tick_line;this._draw_ticks(t,i.major,s,a,o)}_draw_minor_ticks(t,e,i){const s=this.model.minor_tick_in,a=this.model.minor_tick_out,o=this.visuals.minor_tick_line;this._draw_ticks(t,i.minor,s,a,o)}_draw_major_labels(t,e,i){const s=i.major,a=this.compute_labels(s[this.dimension]),o=this.model.major_label_orientation,n=e.tick+this.model.major_label_standoff,l=this.visuals.major_label_text;this._draw_oriented_labels(t,a,s,o,n,l)}_axis_label_extent(){if(null==this._axis_label_view)return 0;const t=this._axis_label_view.graphics(),e=this.model.axis_label_orientation;t.visuals=this.visuals.axis_label_text.values(),t.angle=this.panel.get_label_angle_heuristic(e),t.base_font_size=this.plot_view.base_font_size;const i=t.size(),s=0==this.dimension?i.height:i.width,a=this.model.axis_label_standoff;return s>0?a+s+3:0}_draw_axis_label(t,e,i){if(null==this._axis_label_view)return;const[s,a]=(()=>{const{bbox:t}=this,{side:e,face:i}=this.panel,[s]=this.ranges,{axis_label_align:a}=this.model;switch(e){case\"above\":case\"below\":{const[e,o]=(()=>{switch(a){case\"start\":return s.is_reversed?[t.right,\"right\"]:[t.left,\"left\"];case\"center\":return[t.hcenter,\"center\"];case\"end\":return s.is_reversed?[t.left,\"left\"]:[t.right,\"right\"]}})(),[n,l]=\"front\"==i?[t.bottom,\"bottom\"]:[t.top,\"top\"];return[e,n,o,l]}case\"left\":case\"right\":{const[e,o]=(()=>{switch(a){case\"start\":return s.is_reversed?[t.top,\"top\"]:[t.bottom,\"bottom\"];case\"center\":return[t.vcenter,\"center\"];case\"end\":return s.is_reversed?[t.bottom,\"bottom\"]:[t.top,\"top\"]}})(),[n,l]=\"front\"==i?[t.right,\"right\"]:[t.left,\"left\"];return[n,e,l,o]}}})(),[o,n]=this.normals,l=this.model.axis_label_orientation,r=e.tick+e.tick_label+this.model.axis_label_standoff,{vertical_align:_,align:h}=this.panel.get_label_text_heuristics(l),c={sx:s+o*r,sy:a+n*r,x_anchor:h,y_anchor:_},d=this._axis_label_view.graphics();d.visuals=this.visuals.axis_label_text.values(),d.angle=this.panel.get_label_angle_heuristic(l),d.base_font_size=this.plot_view.base_font_size,d.position=c,d.align=h,d.paint(t)}_draw_ticks(t,e,i,s,a){if(!a.doit)return;const[o,n]=this.scoords(e),[l,r]=this.normals,[_,h]=this.offsets,[c,d]=[l*(_-i),r*(h-i)],[u,m]=[l*(_+s),r*(h+s)];a.set_value(t),t.beginPath();for(let e=0;et.bbox())),j=(()=>{const[t]=this.ranges;return t.is_reversed?0==this.dimension?(t,e)=>k[t].left-k[e].right:(t,e)=>k[e].top-k[t].bottom:0==this.dimension?(t,e)=>k[e].left-k[t].right:(t,e)=>k[t].top-k[e].bottom})(),{major_label_policy:y}=this.model,z=y.filter(v,k,j),M=[...z.ones()];if(0!=M.length){const t=this.canvas.bbox,e=e=>{const i=k[e];if(i.left<0){const t=-i.left,{position:s}=w[e];w[e].position={...s,sx:s.sx+t}}else if(i.right>t.width){const s=i.right-t.width,{position:a}=w[e];w[e].position={...a,sx:a.sx-s}}},i=e=>{const i=k[e];if(i.top<0){const t=-i.top,{position:s}=w[e];w[e].position={...s,sy:s.sy+t}}else if(i.bottom>t.height){const s=i.bottom-t.height,{position:a}=w[e];w[e].position={...a,sy:a.sy-s}}},s=M[0],a=M[M.length-1];0==this.dimension?(e(s),e(a)):(i(s),i(a))}for(const e of z){w[e].paint(t)}}_tick_extent(){const{major:t,minor:e}=this.tick_coords,i=this.dimension;return Math.max(0==t[i].length?0:this.model.major_tick_out,0==e[i].length?0:this.model.minor_tick_out)}_tick_label_extents(){const t=this.tick_coords.major,e=this.compute_labels(t[this.dimension]),i=this.model.major_label_orientation,s=this.model.major_label_standoff,a=this.visuals.major_label_text;return[this._oriented_labels_extent(e,i,s,a)]}get extents(){const t=this._tick_label_extents();return{tick:this._tick_extent(),tick_labels:t,tick_label:(0,b.sum)(t),axis_label:this._axis_label_extent()}}_oriented_labels_extent(t,e,i,s){if(0==t.length||!s.doit)return 0;const a=this.panel.get_label_angle_heuristic(e);t.visuals=s.values(),t.angle=a,t.base_font_size=this.plot_view.base_font_size;const o=t.max_size(),n=0==this.dimension?o.height:o.width;return n>0?i+n+3:0}get normals(){return this.panel.normals}get dimension(){return this.panel.dimension}compute_labels(t){const e=this.model.formatter.format_graphics(t,this),{_major_label_views:i}=this,s=new Set;for(let a=0;aT(n-l)?(t=_(r(a,o),n),s=r(_(a,o),l)):(t=r(a,o),s=_(a,o)),[t,s]}}get rule_coords(){const t=this.dimension,e=1-t,[i]=this.ranges,[s,a]=this.computed_bounds,o=[new Array(2),new Array(2)];return o[t][0]=Math.max(s,i.min),o[t][1]=Math.min(a,i.max),o[t][0]>o[t][1]&&(o[t][0]=o[t][1]=NaN),o[e][0]=this.loc,o[e][1]=this.loc,o}get rule_scoords(){const[[t,e],[i,s]]=this.scoords(this.rule_coords);return{sx0:Math.round(t),sy0:Math.round(i),sx1:Math.round(e),sy1:Math.round(s)}}get tick_coords(){const t=this.dimension,e=1-t,[i]=this.ranges,[s,a]=this.computed_bounds,o=this.model.ticker.get_ticks(s,a,i,this.loc),n=o.major,l=o.minor,r=[[],[]],_=[[],[]],[h,c]=[i.min,i.max];for(let i=0;ic||(r[t].push(n[i]),r[e].push(this.loc));for(let i=0;ic||(_[t].push(l[i]),_[e].push(this.loc));return{major:r,minor:_}}get loc(){const{fixed_location:t}=this.model;if(null!=t){if((0,x.isNumber)(t))return t;const[,e]=this.ranges;if(e instanceof p.FactorRange)return e.synthetic(t);(0,k.unreachable)()}const[,e]=this.ranges;switch(this.panel.side){case\"left\":case\"below\":return e.start;case\"right\":case\"above\":return e.end}}get face(){return this.panel.face}remove(){this._axis_label_view?.remove();for(const t of this._major_label_views.values())t.remove();super.remove()}has_finished(){if(!super.has_finished())return!1;if(null!=this._axis_label_view&&!this._axis_label_view.has_finished())return!1;for(const t of this._major_label_views.values())if(!t.has_finished())return!1;return!0}}i.AxisView=A,A.__name__=\"AxisView\";class L extends l.GuideRenderer{constructor(t){super(t)}}i.Axis=L,o=L,L.__name__=\"Axis\",o.prototype.default_view=A,o.mixins([[\"axis_\",c.Line],[\"major_tick_\",c.Line],[\"minor_tick_\",c.Line],[\"major_label_\",c.Text],[\"axis_label_\",c.Text],[\"background_\",c.Fill]]),o.define((({Any:t,Int:e,Float:s,Str:a,Ref:o,Tuple:n,Or:l,Nullable:c,Auto:u,Enum:m})=>({dimension:[l(m(0,1),u),\"auto\"],face:[l(d.Face,u),\"auto\"],bounds:[l(n(s,s),u),\"auto\"],ticker:[o(r.Ticker)],formatter:[o(_.TickFormatter)],axis_label:[c(l(a,o(v.BaseText))),null],axis_label_standoff:[e,5],axis_label_orientation:[l(d.LabelOrientation,s),\"parallel\"],axis_label_align:[d.Align,\"center\"],major_label_standoff:[e,5],major_label_orientation:[l(d.LabelOrientation,s),\"horizontal\"],major_label_overrides:[i.LabelOverrides,new Map],major_label_policy:[o(h.LabelingPolicy),()=>new h.AllLabels],major_tick_in:[s,2],major_tick_out:[s,6],minor_tick_in:[s,0],minor_tick_out:[s,4],fixed_location:[c(l(s,t)),null]}))),o.override({axis_line_color:\"black\",major_tick_line_color:\"black\",minor_tick_line_color:\"black\",major_label_text_font_size:\"11px\",major_label_text_align:\"center\",major_label_text_baseline:\"alphabetic\",axis_label_text_font_size:\"13px\",axis_label_text_font_style:\"italic\",background_fill_color:null})},\n function _(e,r,d,n,i){var s;n();const _=e(83);class u extends _.RendererView{}d.GuideRendererView=u,u.__name__=\"GuideRendererView\";class c extends _.Renderer{constructor(e){super(e)}}d.GuideRenderer=c,s=c,c.__name__=\"GuideRenderer\",s.override({level:\"guide\"})},\n function _(c,e,n,s,o){s();const r=c(51);class t extends r.Model{constructor(c){super(c)}}n.Ticker=t,t.__name__=\"Ticker\"},\n function _(t,o,r,e,c){e();const a=t(51),m=t(180);class n extends a.Model{constructor(t){super(t)}format_graphics(t,o){return this.doFormat(t,o).map((t=>new m.TextBox({text:t})))}compute(t,o){return this.doFormat([t],o??{loc:0})[0]}v_compute(t,o){return this.doFormat(t,o??{loc:0})}}r.TickFormatter=n,n.__name__=\"TickFormatter\"},\n function _(e,n,s,t,i){var c,l;t();const r=e(51),o=e(9),a=e(40),u=e(8),d=e(24);class _ extends r.Model{constructor(e){super(e)}}s.LabelingPolicy=_,_.__name__=\"LabelingPolicy\";class f extends _{constructor(e){super(e)}filter(e,n,s){return e}}s.AllLabels=f,f.__name__=\"AllLabels\";class m extends _{constructor(e){super(e)}filter(e,n,s){const{min_distance:t}=this;let i=null;for(const n of e)null!=i&&s(i,n)({min_distance:[e,5]})));class v extends _{constructor(e){super(e)}get names(){return(0,o.keys)(this.args)}get values(){return(0,o.values)(this.args)}get func(){const e=(0,a.use_strict)(this.code);return new d.GeneratorFunction(\"indices\",\"bboxes\",\"distance\",...this.names,e)}filter(e,n,s){const t=Object.create(null),i=this.func.call(t,e,n,s,...this.values);let c=i.next();if(c.done&&void 0!==c.value){const{value:n}=c;return n instanceof d.Indices?n:void 0===n?e:(0,u.isIterable)(n)?d.Indices.from_indices(e.size,n):d.Indices.all_unset(e.size)}{const n=[];do{n.push(c.value),c=i.next()}while(!c.done);return d.Indices.from_indices(e.size,n)}}}s.CustomLabelingPolicy=v,l=v,v.__name__=\"CustomLabelingPolicy\",l.define((({Unknown:e,Str:n,Dict:s})=>({args:[s(e),{}],code:[n,\"\"]})))},\n function _(e,i,s,n,r){var t;n();const a=e(195),o=e(196),c=e(197);class _ extends a.ContinuousAxisView{}s.LinearAxisView=_,_.__name__=\"LinearAxisView\";class u extends a.ContinuousAxis{constructor(e){super(e)}}s.LinearAxis=u,t=u,u.__name__=\"LinearAxis\",t.prototype.default_view=_,t.override({ticker:()=>new c.BasicTicker,formatter:()=>new o.BasicTickFormatter})},\n function _(s,n,i,o,u){o();const e=s(189);class t extends e.AxisView{}i.ContinuousAxisView=t,t.__name__=\"ContinuousAxisView\";class _ extends e.Axis{constructor(s){super(s)}}i.ContinuousAxis=_,_.__name__=\"ContinuousAxis\"},\n function _(i,t,e,o,n){var r;o();const s=i(192),c=i(40);function _(i){let t=\"\";for(const e of i)t+=\"-\"==e?\"\\u2212\":e;return t}e.unicode_replace=_;class a extends s.TickFormatter{constructor(i){super(i),this.last_precision=3}get scientific_limit_low(){return 10**this.power_limit_low}get scientific_limit_high(){return 10**this.power_limit_high}_need_sci(i){if(!this.use_scientific)return!1;const{scientific_limit_high:t}=this,{scientific_limit_low:e}=this,o=i.length<2?0:Math.abs(i[1]-i[0])/1e4;for(const n of i){const i=Math.abs(n);if(!(i<=o)&&(i>=t||i<=e))return!0}return!1}_format_with_precision(i,t,e){return t?i.map((i=>_(i.toExponential(e)))):i.map((i=>_((0,c.to_fixed)(i,e))))}_auto_precision(i,t){const e=new Array(i.length),o=this.last_precision<=15;i:for(let n=this.last_precision;o?n<=15:n>=1;o?n++:n--){if(t){e[0]=i[0].toExponential(n);for(let t=1;t({precision:[o(t,e),\"auto\"],use_scientific:[i,!0],power_limit_high:[t,5],power_limit_low:[t,-3]})))},\n function _(c,e,s,i,n){i();const r=c(198);class t extends r.AdaptiveTicker{constructor(c){super(c)}}s.BasicTicker=t,t.__name__=\"BasicTicker\"},\n function _(t,e,s,a,i){var n;a();const r=t(199),_=t(10),l=t(11);class h extends r.ContinuousTicker{constructor(t){super(t)}get_min_interval(){return this.min_interval}get_max_interval(){return this.max_interval??1/0}get extended_mantissas(){const t=(0,_.nth)(this.mantissas,-1)/this.base,e=(0,_.nth)(this.mantissas,0)*this.base;return[t,...this.mantissas,e]}get base_factor(){return 0==this.get_min_interval()?1:this.get_min_interval()}get_interval(t,e,s){const a=e-t,i=this.get_ideal_interval(t,e,s),n=Math.floor((0,l.log)(i/this.base_factor,this.base)),r=this.base**n*this.base_factor,h=this.extended_mantissas,m=h.map((t=>Math.abs(s-a/(t*r)))),o=h[(0,_.argmin)(m)]*r;return(0,l.clamp)(o,this.get_min_interval(),this.get_max_interval())}}s.AdaptiveTicker=h,n=h,h.__name__=\"AdaptiveTicker\",n.define((({Float:t,List:e,Nullable:s})=>({base:[t,10],mantissas:[e(t),[1,2,5]],min_interval:[t,0],max_interval:[s(t),null]})))},\n function _(t,n,i,s,e){var o;s();const r=t(191),c=t(10);class _ extends r.Ticker{constructor(t){super(t)}get_ticks(t,n,i,s){return this.get_ticks_no_defaults(t,n,s,this.desired_num_ticks)}get_ticks_no_defaults(t,n,i,s){const e=this.get_interval(t,n,s),o=Math.floor(t/e),r=Math.ceil(n/e);let _;_=isFinite(o)&&isFinite(r)?(0,c.range)(o,r+1):[];const u=_.map((t=>t*e)).filter((i=>t<=i&&i<=n)),a=this.num_minor_ticks,f=[];if(a>0&&u.length>0){const i=e/a,s=(0,c.range)(0,a).map((t=>t*i));for(const i of s.slice(1)){const s=u[0]-i;t<=s&&s<=n&&f.push(s)}for(const i of u)for(const e of s){const s=i+e;t<=s&&s<=n&&f.push(s)}}return{major:u,minor:f}}get_ideal_interval(t,n,i){return(n-t)/i}}i.ContinuousTicker=_,o=_,_.__name__=\"ContinuousTicker\",o.define((({Int:t})=>({num_minor_ticks:[t,5],desired_num_ticks:[t,6]})))},\n function _(e,i,r,c,k){c(),k(\"AdaptiveTicker\",e(198).AdaptiveTicker),k(\"BasicTicker\",e(197).BasicTicker),k(\"CategoricalTicker\",e(201).CategoricalTicker),k(\"CompositeTicker\",e(202).CompositeTicker),k(\"ContinuousTicker\",e(199).ContinuousTicker),k(\"DatetimeTicker\",e(203).DatetimeTicker),k(\"DaysTicker\",e(204).DaysTicker),k(\"FixedTicker\",e(209).FixedTicker),k(\"LogTicker\",e(210).LogTicker),k(\"MercatorTicker\",e(211).MercatorTicker),k(\"MonthsTicker\",e(207).MonthsTicker),k(\"SingleIntervalTicker\",e(205).SingleIntervalTicker),k(\"Ticker\",e(191).Ticker),k(\"YearsTicker\",e(208).YearsTicker),k(\"BinnedTicker\",e(212).BinnedTicker)},\n function _(t,c,o,s,e){s();const r=t(191);class i extends r.Ticker{constructor(t){super(t)}get_ticks(t,c,o,s){return{major:this._collect(o.factors,o,t,c),minor:[],tops:this._collect(o.tops??[],o,t,c),mids:this._collect(o.mids??[],o,t,c)}}_collect(t,c,o,s){const e=[];for(const r of t){const t=c.synthetic(r);t>o&&tt.get_min_interval()))}get max_intervals(){return this.tickers.map((t=>t.get_max_interval()))}get_min_interval(){return this.min_intervals[0]}get_max_interval(){return this.max_intervals[0]}get_best_ticker(t,e,i){const s=e-t;if(0==s)return this.tickers[0];const r=this.get_ideal_interval(t,e,i),n=[(0,a.sorted_index)(this.min_intervals,r)-1,(0,a.sorted_index)(this.max_intervals,r)],_=[this.min_intervals[n[0]],this.max_intervals[n[1]]].map((t=>Math.abs(i-s/t)));let c;if((0,a.is_empty)(_.filter((t=>!isNaN(t)))))c=this.tickers[0];else{const t=n[(0,a.argmin)(_)];c=this.tickers[t]}return c}get_interval(t,e,i){return this.get_best_ticker(t,e,i).get_interval(t,e,i)}get_ticks_no_defaults(t,e,i,s){return this.get_best_ticker(t,e,s).get_ticks_no_defaults(t,e,i,s)}}i.CompositeTicker=c,n=c,c.__name__=\"CompositeTicker\",n.define((({NonEmptyList:t,Ref:e})=>({tickers:[t(e(_.ContinuousTicker))]})))},\n function _(e,n,i,a,s){var r;a();const t=e(10),c=e(198),m=e(202),_=e(204),k=e(207),o=e(208),T=e(206);class w extends m.CompositeTicker{constructor(e){super(e)}}i.DatetimeTicker=w,r=w,w.__name__=\"DatetimeTicker\",r.override({num_minor_ticks:0,tickers:()=>[new c.AdaptiveTicker({mantissas:[1,2,5],base:10,min_interval:0,max_interval:500*T.ONE_MILLI,num_minor_ticks:0}),new c.AdaptiveTicker({mantissas:[1,2,5,10,15,20,30],base:60,min_interval:T.ONE_SECOND,max_interval:30*T.ONE_MINUTE,num_minor_ticks:0}),new c.AdaptiveTicker({mantissas:[1,2,4,6,8,12],base:24,min_interval:T.ONE_HOUR,max_interval:12*T.ONE_HOUR,num_minor_ticks:0}),new _.DaysTicker({days:(0,t.range)(1,32)}),new _.DaysTicker({days:(0,t.range)(1,31,3)}),new _.DaysTicker({days:[1,8,15,22]}),new _.DaysTicker({days:[1,15]}),new k.MonthsTicker({months:(0,t.range)(0,12,1)}),new k.MonthsTicker({months:(0,t.range)(0,12,2)}),new k.MonthsTicker({months:(0,t.range)(0,12,4)}),new k.MonthsTicker({months:(0,t.range)(0,12,6)}),new o.YearsTicker({})]})},\n function _(t,e,n,s,o){var a;s();const i=t(205),r=t(206),c=t(10);class _ extends i.BaseSingleIntervalTicker{constructor(t){super(t)}initialize(){super.initialize();const t=this.days;t.length>1?this.interval=(t[1]-t[0])*r.ONE_DAY:this.interval=31*r.ONE_DAY}get_ticks_no_defaults(t,e,n,s){const o=function(t,e){const n=(0,r.last_month_no_later_than)(new Date(t)),s=(0,r.last_month_no_later_than)(new Date(e));s.setUTCMonth(s.getUTCMonth()+1);const o=[],a=n;for(;o.push((0,r.copy_date)(a)),a.setUTCMonth(a.getUTCMonth()+1),!(a>s););return o}(t,e),a=this.days;return{major:(0,c.concat)(o.map((t=>((t,e)=>{const n=t.getUTCMonth(),s=[];for(const o of a){const a=(0,r.copy_date)(t);a.setUTCDate(o),new Date(a.getTime()+e/2).getUTCMonth()==n&&s.push(a)}return s})(t,this.interval)))).map((t=>t.getTime())).filter((n=>t<=n&&n<=e)),minor:[]}}}n.DaysTicker=_,a=_,_.__name__=\"DaysTicker\",a.define((({Int:t,List:e})=>({days:[e(t),[]]}))),a.override({num_minor_ticks:0})},\n function _(e,n,t,r,i){var a;r();const l=e(199);class s extends l.ContinuousTicker{constructor(e){super(e)}get_interval(e,n,t){return this.interval}get_min_interval(){return this.interval}get_max_interval(){return this.interval}}t.BaseSingleIntervalTicker=s,s.__name__=\"BaseSingleIntervalTicker\";class _ extends s{constructor(e){super(e)}}t.SingleIntervalTicker=_,a=_,_.__name__=\"SingleIntervalTicker\",a.define((({Float:e})=>({interval:[e]})))},\n function _(t,n,e,_,E){function N(t){return new Date(t.getTime())}function O(t){const n=N(t);return n.setUTCDate(1),n.setUTCHours(0),n.setUTCMinutes(0),n.setUTCSeconds(0),n.setUTCMilliseconds(0),n}_(),e.ONE_MILLI=1,e.ONE_SECOND=1e3,e.ONE_MINUTE=60*e.ONE_SECOND,e.ONE_HOUR=60*e.ONE_MINUTE,e.ONE_DAY=24*e.ONE_HOUR,e.ONE_MONTH=30*e.ONE_DAY,e.ONE_YEAR=365*e.ONE_DAY,e.copy_date=N,e.last_month_no_later_than=O,e.last_year_no_later_than=function(t){const n=O(t);return n.setUTCMonth(0),n}},\n function _(t,e,n,a,s){var r;a();const i=t(205),o=t(206),l=t(10);class _ extends i.BaseSingleIntervalTicker{constructor(t){super(t)}initialize(){super.initialize();const t=this.months;t.length>1?this.interval=(t[1]-t[0])*o.ONE_MONTH:this.interval=12*o.ONE_MONTH}get_ticks_no_defaults(t,e,n,a){const s=function(t,e){const n=(0,o.last_year_no_later_than)(new Date(t)),a=(0,o.last_year_no_later_than)(new Date(e));a.setUTCFullYear(a.getUTCFullYear()+1);const s=[],r=n;for(;s.push((0,o.copy_date)(r)),r.setUTCFullYear(r.getUTCFullYear()+1),!(r>a););return s}(t,e),r=this.months;return{major:(0,l.concat)(s.map((t=>r.map((e=>{const n=(0,o.copy_date)(t);return n.setUTCMonth(e),n}))))).map((t=>t.getTime())).filter((n=>t<=n&&n<=e)),minor:[]}}}n.MonthsTicker=_,r=_,_.__name__=\"MonthsTicker\",r.define((({Int:t,List:e})=>({months:[e(t),[]]})))},\n function _(e,t,a,r,_){r();const n=e(197),s=e(205),i=e(206);class c extends s.BaseSingleIntervalTicker{constructor(e){super(e),this.interval=i.ONE_YEAR,this.basic_ticker=new n.BasicTicker({num_minor_ticks:0})}get_ticks_no_defaults(e,t,a,r){const _=(0,i.last_year_no_later_than)(new Date(e)).getUTCFullYear(),n=(0,i.last_year_no_later_than)(new Date(t)).getUTCFullYear();return{major:this.basic_ticker.get_ticks_no_defaults(_,n,a,r).major.map((e=>Date.UTC(e,0,1))).filter((a=>e<=a&&a<=t)),minor:[]}}}a.YearsTicker=c,c.__name__=\"YearsTicker\"},\n function _(t,e,r,i,n){var s;i();const _=t(199);class c extends _.ContinuousTicker{constructor(t){super(t)}get_ticks_no_defaults(t,e,r,i){return{major:[...this.ticks],minor:[...this.minor_ticks]}}get_interval(t,e,r){return 0}get_min_interval(){return 0}get_max_interval(){return 0}}r.FixedTicker=c,s=c,c.__name__=\"FixedTicker\",s.define((({Float:t,Arrayable:e})=>({ticks:[e(t),[]],minor_ticks:[e(t),[]]})))},\n function _(t,o,e,s,n){var r;s();const i=t(198),a=t(10);class c extends i.AdaptiveTicker{constructor(t){super(t)}get_ticks_no_defaults(t,o,e,s){const n=this.num_minor_ticks,r=[],i=this.base,c=Math.log(t)/Math.log(i),f=Math.log(o)/Math.log(i),l=f-c;let h;if(isFinite(l)&&0!=l)if(l<2){const e=this.get_interval(t,o,s),i=Math.floor(t/e),c=Math.ceil(o/e);if(h=(0,a.range)(i,c+1).filter((t=>0!=t)).map((t=>t*e)).filter((e=>t<=e&&e<=o)),n>0&&h.length>0){const t=e/n,o=(0,a.range)(0,n).map((o=>o*t));for(const t of o.slice(1))r.push(h[0]-t);for(const t of h)for(const e of o)r.push(t+e)}}else{const t=Math.ceil(.999999*c),o=Math.floor(1.000001*f),e=Math.ceil((o-t)/9);if(h=(0,a.range)(t-1,o+1,e).map((t=>i**t)),n>0&&h.length>0){const t=i**e/n,o=(0,a.range)(1,n+1).map((o=>o*t));for(const t of o)r.push(h[0]/t);r.push(h[0]);for(const t of h)for(const e of o)r.push(t*e)}}else h=[];return{major:h.filter((e=>t<=e&&e<=o)),minor:r.filter((e=>t<=e&&e<=o))}}}e.LogTicker=c,r=c,c.__name__=\"LogTicker\",r.override({mantissas:[1,5]})},\n function _(t,o,n,s,r){var e;s();const i=t(197),c=t(20),_=t(134);class a extends i.BasicTicker{constructor(t){super(t)}get_ticks_no_defaults(t,o,n,s){if(null==this.dimension)throw new Error(`${this}.dimension wasn't configured`);return[t,o]=(0,_.clip_mercator)(t,o,this.dimension),\"lon\"==this.dimension?this._get_ticks_lon(t,o,n,s):this._get_ticks_lat(t,o,n,s)}_get_ticks_lon(t,o,n,s){const[r]=_.wgs84_mercator.invert(t,n),[e,i]=_.wgs84_mercator.invert(o,n),c=super.get_ticks_no_defaults(r,e,n,s),a=[];for(const t of c.major)if((0,_.in_bounds)(t,\"lon\")){const[o]=_.wgs84_mercator.compute(t,i);a.push(o)}const m=[];for(const t of c.minor)if((0,_.in_bounds)(t,\"lon\")){const[o]=_.wgs84_mercator.compute(t,i);m.push(o)}return{major:a,minor:m}}_get_ticks_lat(t,o,n,s){const[,r]=_.wgs84_mercator.invert(n,t),[e,i]=_.wgs84_mercator.invert(n,o),c=super.get_ticks_no_defaults(r,i,n,s),a=[];for(const t of c.major)if((0,_.in_bounds)(t,\"lat\")){const[,o]=_.wgs84_mercator.compute(e,t);a.push(o)}const m=[];for(const t of c.minor)if((0,_.in_bounds)(t,\"lat\")){const[,o]=_.wgs84_mercator.compute(e,t);m.push(o)}return{major:a,minor:m}}}n.MercatorTicker=a,e=a,a.__name__=\"MercatorTicker\",e.define((({Nullable:t})=>({dimension:[t(c.LatLon),null]})))},\n function _(e,n,t,r,i){var o;r();const a=e(191),s=e(213),c=e(13);class _ extends a.Ticker{constructor(e){super(e)}get_ticks(e,n,t,r){const{binning:i}=this.mapper.metrics,o=Math.max(0,(0,c.left_edge_index)(e,i)),a=Math.min((0,c.left_edge_index)(n,i)+1,i.length-1),s=[];for(let e=o;e<=a;e++)s.push(i[e]);const{num_major_ticks:_}=this,m=[],h=\"auto\"==_?s.length:_,l=Math.max(1,Math.floor(s.length/h));for(let e=0;e({mapper:[n(s.ScanningColorMapper)],num_major_ticks:[t(e,r),8]})))},\n function _(n,i,e,t,a){t();const o=n(214),_=n(13);class r extends o.ContinuousColorMapper{constructor(n){super(n)}index_to_value(n){return this._scan_data.binning[n]}value_to_index(n,i){const e=this._scan_data;return ne.binning[e.binning.length-1]?i:(0,_.left_edge_index)(n,e.binning)}}e.ScanningColorMapper=r,r.__name__=\"ScanningColorMapper\"},\n function _(t,o,e,n,l){var s;n();const i=t(215),c=t(217),a=t(10),h=t(8);class r extends i.ColorMapper{constructor(t){super(t),this._scan_data=null}connect_signals(){super.connect_signals();const t=()=>{for(const[t]of this.domain)this.connect(t.view.change,(()=>this.update_data())),this.connect(t.data_source.selected.change,(()=>this.update_data()))},{high:o,low:e,high_color:n,low_color:l,palette:s,nan_color:i}=this.properties;this.on_change([o,e,n,l,s,i],(()=>this.update_data())),this.connect(this.properties.domain.change,(()=>t())),t()}update_data(){const{domain:t,palette:o}=this,e=[...this._collect(t)];this._scan_data=this.scan(e,o.length),this.metrics_change.emit(),this.change.emit()}get metrics(){return null==this._scan_data&&this.update_data(),this._scan_data}*_collect(t){for(const[o,e]of t)for(const t of(0,h.isArray)(e)?e:[e]){if(o.view.properties.indices.is_unset)continue;const e=o.data_source.get_column(t);if(null==e)continue;let n=o.view.indices.select(e);const l=o.view.masked,s=o.data_source.selected.indices;let i;if(null!=l&&s.length>0?i=(0,a.intersection)([...l],s):null!=l?i=[...l]:s.length>0&&(i=s),null!=i&&(n=(0,a.map)(i,(t=>n[t]))),n.length>0&&!(0,h.isNumber)(n[0]))for(const t of n)yield*t;else yield*n}}_v_compute(t,o,e,n){const{nan_color:l}=n;let{low_color:s,high_color:i}=n;null==s&&(s=e[0]),null==i&&(i=e[e.length-1]);const{domain:c}=this,h=(0,a.is_empty)(c)?t:[...this._collect(c)];this._scan_data=this.scan(h,e.length),this.metrics_change.emit();for(let n=0,c=t.length;n=o.length?n:o[l]}}e.ContinuousColorMapper=r,s=r,r.__name__=\"ContinuousColorMapper\",s.define((({Float:t,Str:o,Ref:e,Color:n,Or:l,Tuple:s,List:i,Nullable:a})=>({high:[a(t),null],low:[a(t),null],high_color:[a(n),null],low_color:[a(n),null],domain:[i(s(e(c.GlyphRenderer),l(o,i(o)))),[]]})))},\n function _(t,e,r,n,o){var i;n();const _=t(216),c=t(15),a=t(24),s=t(22),l=t(27),p=t(30);function u(t){return(0,s.encode_rgba)((0,s.color2rgba)(t))}function h(t){const e=new Uint32Array(t.length);for(let r=0,n=t.length;rt))),e}get rgba_mapper(){const t=this,e=h(this.palette),r=this._colors(u);return{v_compute(n){const o=(0,p.is_NDArray)(n)&&3==n.dimension?n.shape[2]:1,i=new a.ColorArray(n.length/o);return t._v_compute_uint32(n,i,e,r),new Uint8ClampedArray((0,l.to_big_endian)(i).buffer)}}}_colors(t){return{nan_color:t(this.nan_color)}}_v_compute_uint32(t,e,r,n){this._v_compute(t,e,r,n)}}r.ColorMapper=g,i=g,g.__name__=\"ColorMapper\",i.define((({Color:t,List:e})=>({palette:[e(t)],nan_color:[t,\"gray\"]})))},\n function _(r,e,n,s,o){s();const p=r(97);class t extends p.Transform{constructor(r){super(r)}compute(r){throw new Error(\"mapping single values is not supported\")}}n.Mapper=t,t.__name__=\"Mapper\"},\n function _(e,t,i,s,l){var h;s();const n=e(218),a=e(219),o=e(229),_=e(230),c=e(233),d=e(234),p=e(235),r=e(221),g=e(128),y=e(236),u=e(24),m=e(13),v=e(9),w=e(56),f=e(26),b=e(109),V=e(226),x={fill:{},line:{}},z={fill:{fill_alpha:.3,fill_color:\"grey\"},line:{line_alpha:.3,line_color:\"grey\"}},G={fill:{fill_alpha:.2},line:{}},R={fill:{fill_alpha:.2},line:{}};class k extends n.DataRendererView{get glyph_view(){return this.glyph}*children(){yield*super.children(),yield this.cds_view,yield this.glyph,yield this.selection_glyph,yield this.nonselection_glyph,null!=this.hover_glyph&&(yield this.hover_glyph),yield this.muted_glyph,yield this.decimated_glyph}get data_source(){return this.model.properties.data_source}async lazy_initialize(){await super.lazy_initialize(),this.cds_view=await(0,w.build_view)(this.model.view,{parent:this});const e=this.model.glyph;this.glyph=await this.build_glyph_view(e);const t=\"fill\"in this.glyph.visuals,i=\"line\"in this.glyph.visuals,s={...e.attributes};function l(l){const h=(0,v.clone)(s);return t&&(0,v.extend)(h,l.fill),i&&(0,v.extend)(h,l.line),new e.constructor(h)}function h(e,t){return t instanceof r.Glyph?t:l(\"auto\"==t?e:{fill:{},line:{}})}delete s.id;let{selection_glyph:n,nonselection_glyph:a,hover_glyph:o,muted_glyph:_}=this.model;n=h(x,n),this.selection_glyph=await this.build_glyph_view(n),a=h(G,a),this.nonselection_glyph=await this.build_glyph_view(a),null!=o&&(this.hover_glyph=await this.build_glyph_view(o)),_=h(R,_),this.muted_glyph=await this.build_glyph_view(_);const c=h(z,\"auto\");this.decimated_glyph=await this.build_glyph_view(c),this.selection_glyph.set_base(this.glyph),this.nonselection_glyph.set_base(this.glyph),this.hover_glyph?.set_base(this.glyph),this.muted_glyph.set_base(this.glyph),this.decimated_glyph.set_base(this.glyph),await this.set_data()}async build_glyph_view(e){return(0,w.build_view)(e,{parent:this})}remove(){this.cds_view.remove(),this.glyph.remove(),this.selection_glyph.remove(),this.nonselection_glyph.remove(),this.hover_glyph?.remove(),this.muted_glyph.remove(),this.decimated_glyph.remove(),super.remove()}connect_signals(){super.connect_signals();const e=()=>this.request_paint(),t=()=>this.update_data();this.connect(this.model.change,e),this.connect(this.glyph.model.change,t),this.connect(this.selection_glyph.model.change,t),this.connect(this.nonselection_glyph.model.change,t),null!=this.hover_glyph&&this.connect(this.hover_glyph.model.change,t),this.connect(this.muted_glyph.model.change,t),this.connect(this.decimated_glyph.model.change,t),this.connect(this.model.data_source.change,t),this.connect(this.model.data_source.streaming,t),this.connect(this.model.data_source.patching,(e=>this.update_data(e))),this.connect(this.model.data_source.selected.change,e),this.connect(this.model.data_source._select,e),null!=this.hover_glyph&&this.connect(this.model.data_source.inspect,(()=>{const{inspected:t}=this.model.data_source,i={indices:t.indices,line_indices:t.line_indices,multiline_indices:t.multiline_indices,image_indices:t.image_indices,selected_glyphs:t.selected_glyphs};(0,f.is_equal)(this._previous_inspected,i)||(this._previous_inspected=i,e())})),this.connect(this.model.properties.view.change,(async()=>{this.cds_view.remove(),this.cds_view=await(0,w.build_view)(this.model.view,{parent:this}),await t()})),this.connect(this.model.view.properties.indices.change,t),this.connect(this.model.view.properties.masked.change,(async()=>await this.set_visuals())),this.connect(this.model.properties.visible.change,(()=>this.plot_view.invalidate_dataranges=!0));const{x_ranges:i,y_ranges:s}=this.plot_view.frame;for(const[,e]of i)e instanceof b.FactorRange&&this.connect(e.invalidate_synthetic,t);for(const[,e]of s)e instanceof b.FactorRange&&this.connect(e.invalidate_synthetic,t);const{transformchange:l,exprchange:h}=this.model.glyph;this.connect(l,t),this.connect(h,t)}_update_masked_indices(){const e=this.glyph.mask_data();return this.model.view.masked=e,e}async update_data(e){await this.set_data(e),this.request_paint()}async set_data(e){const t=this.model.data_source;this.all_indices=this.model.view.indices;const{all_indices:i}=this;await this.glyph.set_data(t,i,e),await this.decimated_glyph.set_data(t,i,e),await this.selection_glyph.set_data(t,i,e),await this.nonselection_glyph.set_data(t,i,e),await(this.hover_glyph?.set_data(t,i,e)),await this.muted_glyph.set_data(t,i,e),await this.set_visuals(),this._update_masked_indices();const{lod_factor:s}=this.plot_model,l=this.all_indices.count;this.decimated=new u.Indices(l);for(let e=0;el.is_empty()?[]:this.glyph instanceof a.LineView&&l.selected_glyph===this.glyph.model?this.model.view.convert_indices_from_subset(i):l.indices)(),{inspected:n}=this.model.data_source;this._previous_inspected={indices:n.indices,line_indices:n.line_indices,multiline_indices:n.multiline_indices,image_indices:n.image_indices,selected_glyphs:n.selected_glyphs};const r=new Set((()=>n.is_empty()?[]:null!=n.selected_glyph?this.model.view.convert_indices_from_subset(i):n.indices.length>0?n.indices:Object.keys(n.multiline_indices).map((e=>parseInt(e))))()),g=(0,m.filter)(i,(e=>r.has(t[e]))),{lod_threshold:y}=this.plot_model;let u,v,w;if(null!=this.model.document&&this.model.document.interactive_duration()>0&&!e&&null!=y&&t.length>y?(i=[...this.decimated],u=this.decimated_glyph,v=this.decimated_glyph,w=this.selection_glyph):(u=this.model.muted?this.muted_glyph:this.glyph,v=this.nonselection_glyph,w=this.selection_glyph),null!=this.hover_glyph&&0!=g.length){const e=new Set(i);for(const t of g)e.delete(t);i=[...e]}if(0==h.length)if(this.glyph instanceof a.LineView)null!=this.hover_glyph&&0!=g.length?this.hover_glyph.paint(s,this.model.view.convert_indices_from_subset(g)):u.paint(s,t);else if(this.glyph instanceof o.PatchView||this.glyph instanceof c.HAreaView||this.glyph instanceof p.VAreaView||this.glyph instanceof d.VAreaStepView||this.glyph instanceof _.HAreaStepView)if(0==n.selected_glyphs.length||null==this.hover_glyph)u.paint(s,t);else for(const e of n.selected_glyphs)e==this.glyph.model&&this.hover_glyph.paint(s,t);else u.paint(s,i),null!=this.hover_glyph&&0!=g.length&&this.hover_glyph.paint(s,g);else{const e=new Set(h),l=new Array,n=new Array;if(this.glyph instanceof a.LineView)for(const i of t)e.has(i)?l.push(i):n.push(i);else for(const s of i)e.has(t[s])?l.push(s):n.push(s);v.paint(s,n),w.paint(s,l),null!=this.hover_glyph&&(this.glyph instanceof a.LineView?this.hover_glyph.paint(s,this.model.view.convert_indices_from_subset(g)):this.hover_glyph.paint(s,g))}s.restore()}get_reference_point(e,t){if(null!=e){const i=this.model.data_source.get_column(e);if(null!=i)for(const[e,s]of this.model.view.indices_map)if(i[e]==t)return s}return 0}draw_legend(e,t,i,s,l,h,n,a){if(0==this.glyph.data_size)return;const o=(()=>{if(null==a)return this.get_reference_point(h,n);{const{indices_map:e}=this.model.view;return e.get(a)}})();null!=o&&this.glyph.draw_legend_for_index(e,{x0:t,x1:i,y0:s,y1:l},o)}hit_test(e){if(!this.model.visible)return null;const t=this.glyph.hit_test(e);return null==t?null:this.model.view.convert_selection_from_subset(t)}}i.GlyphRendererView=k,k.__name__=\"GlyphRendererView\";class S extends n.DataRenderer{constructor(e){super(e)}get_selection_manager(){return this.data_source.selection_manager}add_decoration(e,t){const i=new V.Decoration({marking:e,node:t}),s=[this.glyph,this.selection_glyph,this.nonselection_glyph,this.hover_glyph,this.muted_glyph];for(const e of s)e instanceof r.Glyph&&(e.decorations=[...e.decorations,i]);return i}}i.GlyphRenderer=S,h=S,S.__name__=\"GlyphRenderer\",h.prototype.default_view=k,h.define((({Bool:e,Auto:t,Or:i,Ref:s,Null:l,Nullable:h})=>({data_source:[s(g.ColumnarDataSource)],view:[s(y.CDSView),()=>new y.CDSView],glyph:[s(r.Glyph)],hover_glyph:[h(s(r.Glyph)),null],nonselection_glyph:[i(s(r.Glyph),t,l),\"auto\"],selection_glyph:[i(s(r.Glyph),t,l),\"auto\"],muted_glyph:[i(s(r.Glyph),t,l),\"auto\"],muted:[e,!1]})))},\n function _(e,r,t,n,s){var a,o;n();const _=e(83),i=e(106);class c extends _.RendererView{constructor(){super(...arguments),this[a]=!0}get xscale(){return this.coordinates.x_scale}get yscale(){return this.coordinates.y_scale}bounds(){return this.glyph_view.bounds()}log_bounds(){return this.glyph_view.log_bounds()}}t.DataRendererView=c,a=i.auto_ranged,c.__name__=\"DataRendererView\";class d extends _.Renderer{constructor(e){super(e)}get selection_manager(){return this.get_selection_manager()}}t.DataRenderer=d,o=d,d.__name__=\"DataRenderer\",o.override({level:\"glyph\"})},\n function _(e,t,i,n,s){var l;n();const o=e(1),r=e(220),_=e(227),h=o.__importStar(e(80)),a=o.__importStar(e(228)),c=e(130);class d extends r.XYGlyphView{async load_glglyph(){const{LineGL:t}=await Promise.resolve().then((()=>o.__importStar(e(564))));return t}_paint(e,t,i){const{sx:n,sy:s}={...this,...i},l=this.parent.nonselection_glyph==this;let o=null;const r=e=>null!=o&&e-o!=1;let _=!0;e.beginPath();for(const i of t){const t=n[i],h=s[i];l&&!_&&null!=o&&i-o>1&&isFinite(n[o+1]+s[o+1])&&e.lineTo(n[o+1],s[o+1]),isFinite(t+h)?(_||r(i)?(l&&i>0&&isFinite(n[i-1]+s[i-1])?(e.moveTo(n[i-1],s[i-1]),e.lineTo(t,h)):e.moveTo(t,h),_=!1):e.lineTo(t,h),o=i):_=!0}if(l&&!_&&null!=o){const t=n.length;o({x:[a.XCoordinateSpec,{field:\"x\"}],y:[a.YCoordinateSpec,{field:\"y\"}]})))},\n function _(t,e,i,s,n){var r;s();const a=t(1),_=a.__importStar(t(18)),o=a.__importStar(t(64)),h=a.__importStar(t(87)),l=a.__importStar(t(39)),c=t(29),d=t(57),u=t(51),f=t(56),p=t(19),y=t(24),g=t(8),m=t(222),x=t(13),b=t(134),w=t(26),S=t(223),v=t(12),z=t(64),q=t(109),$=t(130),j=t(226),{abs:C,ceil:V}=Math;i.inherit=Symbol(\"inherit\");class A extends d.DOMComponentView{constructor(){super(...arguments),this._index=null,this._data_size=null,this._nohit_warned=new Set,this.decorations=new Map,this._base=null}get renderer(){return this.parent}get has_webgl(){return null!=this.glglyph}get index(){const{_index:t}=this;if(null!=t)return t;throw new Error(`${this}.index_data() wasn't called`)}get data_size(){const{base:t}=this;if(null!=t)return t.data_size;{const{_data_size:t}=this;if(null!=t)return t;throw new Error(`${this}.set_data() wasn't called`)}}initialize(){super.initialize(),this.visuals=new h.Visuals(this)}*children(){yield*super.children(),yield*this.decorations.values()}async lazy_initialize(){await super.lazy_initialize(),await(0,f.build_views)(this.decorations,this.model.decorations,{parent:this.parent});const{webgl:t}=this.canvas;if(null!=t&&null!=this.load_glglyph){const e=await this.load_glglyph();this.glglyph=new e(t.regl_wrapper,this)}}request_paint(){this.parent.request_paint()}get canvas(){return this.renderer.parent.canvas_view}paint(t,e,i){if(null!=this.glglyph)this.glglyph.render(t,e,this.base??this);else{if(null!=this.canvas.webgl&&c.settings.force_webgl)throw new Error(`${this} doesn't support webgl rendering`);this._paint(t,e,i)}}has_finished(){return!0}notify_finished(){this.renderer.notify_finished()}_bounds(t){return t}bounds(){return this._bounds(this.index.bbox)}log_bounds(){const{x0:t,x1:e}=this.index.bounds(o.positive_x()),{y0:i,y1:s}=this.index.bounds(o.positive_y());return this._bounds({x0:t,y0:i,x1:e,y1:s})}get_anchor_point(t,e,[i,s]){switch(t){case\"center\":case\"center_center\":{const[t,n]=this.scenterxy(e,i,s);return{x:t,y:n}}default:return null}}sdist(t,e,i,s=\"edge\",n=!1){const r=e.length,a=new y.ScreenArray(r),_=t.s_compute;if(\"center\"==s)for(let t=0;tV(t))),a}draw_legend_for_index(t,e,i){}hit_test(t){const e=(()=>{switch(t.type){case\"point\":return this._hit_point?.(t);case\"span\":return this._hit_span?.(t);case\"rect\":return this._hit_rect?.(t);case\"poly\":return this._hit_poly?.(t)}})();return null!=e?e:(this._nohit_warned.has(t.type)||(p.logger.debug(`'${t.type}' selection not available for ${this.model.type}`),this._nohit_warned.add(t.type)),null)}_hit_rect_against_index(t){const{sx0:e,sx1:i,sy0:s,sy1:n}=t,[r,a]=this.renderer.coordinates.x_scale.r_invert(e,i),[_,o]=this.renderer.coordinates.y_scale.r_invert(s,n),h=[...this.index.indices({x0:r,x1:a,y0:_,y1:o})];return new $.Selection({indices:h})}_project_xy(t,e,i,s){const n=this._is_inherited(t),r=this._is_inherited(i);if(n||r){if(!n||!r){const[n,r]=(0,b.project_xy)(e,s);this._define_attr(t,n),this._define_attr(i,r)}}else b.inplace.project_xy(e,s)}_project_data(){}*_iter_visuals(){for(const t of this.visuals)for(const e of t)(e instanceof _.VectorSpec||e instanceof _.ScalarSpec)&&(yield e)}get base(){return this._base}set_base(t){t!=this&&t instanceof this.constructor?this._base=t:this._base=null}_define_or_inherit_attr(t,e){const s=e();s===i.inherit?this._inherit_attr(t):this._define_attr(t,s)}_define_attr(t,e){Object.defineProperty(this,t,{configurable:!0,enumerable:!0,value:e}),this._define_inherited(t,!1)}_inherit_attr(t){const{base:e}=this;(0,v.assert)(null!=e),this._inherit_from(t,e)}_inherit_from(t,e){Object.defineProperty(this,t,{configurable:!0,enumerable:!0,get:()=>e[t]}),this._define_inherited(t,!0)}_define_inherited(t,e){Object.defineProperty(this,`inherited_${t}`,{configurable:!0,enumerable:!0,value:e})}_can_inherit_from(t,e){if(null==e)return!1;const i=e.model.property(t.attr),s=t.get_value(),n=i.get_value();try{return(0,w.is_equal)(s,n)}catch(t){if(t instanceof w.EqNotImplemented)return!1;throw t}}_is_inherited(t){return this[`inherited_${(0,g.isString)(t)?t:t.attr}`]}set_visuals(t,e){for(const i of this._iter_visuals()){const{base:s}=this;if(null!=s&&this._can_inherit_from(i,s))this._inherit_from(i.attr,s);else{const s=i.uniform(t).select(e);this._define_attr(i.attr,s)}}for(const t of this.visuals)t.update();this.glglyph?.set_visuals_changed()}_transform_array(t,e){const{x_source:i,y_source:s}=this.renderer.coordinates,n=\"x\"==t.dimension?i:s;if(n instanceof q.FactorRange)if(t instanceof _.CoordinateSpec)e=n.v_synthetic(e);else if(t instanceof _.CoordinateSeqSpec)for(let t=0;t{const s=\"x\"==i.dimension?t:e,n=this[i.attr];return n instanceof m.RaggedArray?new m.RaggedArray(n.offsets,s.v_compute(n.data)):s.v_compute(n)};for(const t of this.model)if(t instanceof _.BaseCoordinateSpec)if(null!=i&&this._is_inherited(t))this._inherit_from(`s${t.attr}`,i);else{const e=s(t);this._define_attr(`s${t.attr}`,e)}this._map_data(),this.glglyph?.set_data_mapped()}_map_data(){}get bbox(){if(null==this.base){const{x0:t,y0:e,x1:i,y1:s}=this.index.bbox,{x_scale:n,y_scale:r}=this.renderer.coordinates,[a,_]=n.r_compute(t,i),[o,h]=r.r_compute(e,s);return z.BBox.from_rect({x0:a,y0:o,x1:_,y1:h})}}}i.GlyphView=A,A.__name__=\"GlyphView\";class R extends u.Model{constructor(t){super(t)}}i.Glyph=R,r=R,R.__name__=\"Glyph\",r.define((({List:t,Ref:e})=>({decorations:[t(e(j.Decoration)),[]]})))},\n function _(t,s,e,r,a){var n;r();const o=t(26),h=t(12);class i{constructor(t,s){this.offsets=t,this.data=s}[(n=Symbol.toStringTag,o.equals)](t,s){return s.arrays(this.offsets,t.offsets)&&s.arrays(this.data,t.data)}get length(){return this.offsets.length}clone(){return new i(this.offsets.slice(),this.data.slice())}static from(t,s){const e=t.length;let r=0;const a=(()=>{const s=new Uint32Array(e);for(let a=0;a>1;i[s]>t?n=s:e=s+1}return i[e]}class r extends d.default{get boxes(){return this._boxes}search_indices(t,i,e,n){if(this._pos!==this._boxes.length)throw new Error(\"Data not yet indexed - call index.finish().\");let s=this._boxes.length-4;const d=[],x=new o.Indices(this.numItems);for(;void 0!==s;){const o=Math.min(s+4*this.nodeSize,h(s,this._levelBounds));for(let h=s;h>2],r=this._boxes[h+0],_=this._boxes[h+1],a=this._boxes[h+2],c=this._boxes[h+3];ea||i>c||(s<4*this.numItems?x.set(o):d.push(o))}s=d.pop()}return x}}r.__name__=\"_FlatBush\";class _{constructor(t){this.index=null,t>0&&(this.index=new r(t))}add_rect(t,i,e,n){isFinite(t+i+e+n)?this.index?.add(t,i,e,n):this.add_empty()}add_point(t,i){isFinite(t+i)?this.index?.add(t,i,t,i):this.add_empty()}add_empty(){this.index?.add(1/0,1/0,-1/0,-1/0)}finish(){this.index?.finish()}_normalize(t){let{x0:i,y0:e,x1:n,y1:s}=t;return i>n&&([i,n]=[n,i]),e>s&&([e,s]=[s,e]),{x0:i,y0:e,x1:n,y1:s}}get bbox(){if(null==this.index)return(0,x.empty)();{const{minX:t,minY:i,maxX:e,maxY:n}=this.index;return{x0:t,y0:i,x1:e,y1:n}}}indices(t){if(null==this.index)return new o.Indices(0);{const{x0:i,y0:e,x1:n,y1:s}=this._normalize(t);return this.index.search_indices(i,e,n,s)}}bounds(t){const i=(0,x.empty)();if(null==this.index)return i;const{boxes:e}=this.index;for(const n of this.indices(t)){const s=e[4*n+0],d=e[4*n+1],o=e[4*n+2],x=e[4*n+3];s>=t.x0&&si.x1&&(i.x1=o),d>=t.y0&&di.y1&&(i.y1=x)}return i}}e.SpatialIndex=_,_.__name__=\"SpatialIndex\"},\n function _(t,i,s,e,n){e();const h=t(1).__importDefault(t(225)),r=[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];class o{static from(t,i=0){if(i%8!=0)throw new Error(\"byteOffset must be 8-byte aligned.\");if(!t||void 0===t.byteLength||t.buffer)throw new Error(\"Data must be an instance of ArrayBuffer or SharedArrayBuffer.\");const[s,e]=new Uint8Array(t,i+0,2);if(251!==s)throw new Error(\"Data does not appear to be in a Flatbush format.\");const n=e>>4;if(3!==n)throw new Error(`Got v${n} data when expected v3.`);const h=r[15&e];if(!h)throw new Error(\"Unrecognized array type.\");const[a]=new Uint16Array(t,i+2,1),[d]=new Uint32Array(t,i+4,1);return new o(d,a,h,void 0,t,i)}constructor(t,i=16,s=Float64Array,e=ArrayBuffer,n,o=0){if(void 0===t)throw new Error(\"Missing required argument: numItems.\");if(isNaN(t)||t<=0)throw new Error(`Unexpected numItems value: ${t}.`);this.numItems=+t,this.nodeSize=Math.min(Math.max(+i,2),65535),this.byteOffset=o;let a=t,d=a;this._levelBounds=[4*a];do{a=Math.ceil(a/this.nodeSize),d+=a,this._levelBounds.push(4*d)}while(1!==a);this.ArrayType=s,this.IndexArrayType=d<16384?Uint16Array:Uint32Array;const _=r.indexOf(this.ArrayType),f=4*d*this.ArrayType.BYTES_PER_ELEMENT;if(_<0)throw new Error(`Unexpected typed array class: ${s}.`);n&&void 0!==n.byteLength&&!n.buffer?(this.data=n,this._boxes=new this.ArrayType(this.data,o+8,4*d),this._indices=new this.IndexArrayType(this.data,o+8+f,d),this._pos=4*d,this.minX=this._boxes[this._pos-4],this.minY=this._boxes[this._pos-3],this.maxX=this._boxes[this._pos-2],this.maxY=this._boxes[this._pos-1]):(this.data=new e(8+f+d*this.IndexArrayType.BYTES_PER_ELEMENT),this._boxes=new this.ArrayType(this.data,8,4*d),this._indices=new this.IndexArrayType(this.data,8+f,d),this._pos=0,this.minX=1/0,this.minY=1/0,this.maxX=-1/0,this.maxY=-1/0,new Uint8Array(this.data,0,2).set([251,48+_]),new Uint16Array(this.data,2,1)[0]=i,new Uint32Array(this.data,4,1)[0]=t),this._queue=new h.default}add(t,i,s=t,e=i){const n=this._pos>>2,h=this._boxes;return this._indices[n]=n,h[this._pos++]=t,h[this._pos++]=i,h[this._pos++]=s,h[this._pos++]=e,tthis.maxX&&(this.maxX=s),e>this.maxY&&(this.maxY=e),n}finish(){if(this._pos>>2!==this.numItems)throw new Error(`Added ${this._pos>>2} items when expected ${this.numItems}.`);const t=this._boxes;if(this.numItems<=this.nodeSize)return t[this._pos++]=this.minX,t[this._pos++]=this.minY,t[this._pos++]=this.maxX,void(t[this._pos++]=this.maxY);const i=this.maxX-this.minX||1,s=this.maxY-this.minY||1,e=new Uint32Array(this.numItems);for(let n=0,h=0;n>2]=i,t[this._pos++]=n,t[this._pos++]=h,t[this._pos++]=r,t[this._pos++]=o}}}search(t,i,s,e,n){if(this._pos!==this._boxes.length)throw new Error(\"Data not yet indexed - call index.finish().\");let h=this._boxes.length-4;const r=[],o=[];for(;void 0!==h;){const a=Math.min(h+4*this.nodeSize,d(h,this._levelBounds));for(let d=h;dthis._boxes[d+2])continue;if(i>this._boxes[d+3])continue;const a=0|this._indices[d>>2];h>=4*this.numItems?r.push(a):(void 0===n||n(a))&&o.push(a)}h=r.pop()}return o}neighbors(t,i,s=1/0,e=1/0,n){if(this._pos!==this._boxes.length)throw new Error(\"Data not yet indexed - call index.finish().\");let h=this._boxes.length-4;const r=this._queue,o=[],_=e*e;t:for(;void 0!==h;){const e=Math.min(h+4*this.nodeSize,d(h,this._levelBounds));for(let s=h;s>2],o=a(t,this._boxes[s],this._boxes[s+2]),d=a(i,this._boxes[s+1],this._boxes[s+3]),f=o*o+d*d;f>_||(h>=4*this.numItems?r.push(e<<1,f):(void 0===n||n(e))&&r.push(1+(e<<1),f))}for(;r.length&&1&r.peek();){if(r.peekValue()>_)break t;if(o.push(r.pop()>>1),o.length===s)break t}h=r.length?r.pop()>>1:void 0}return r.clear(),o}}function a(t,i,s){return t>1;i[n]>t?e=n:s=n+1}return i[s]}function _(t,i,s,e,n,h){if(Math.floor(e/h)>=Math.floor(n/h))return;const r=t[e+n>>1];let o=e-1,a=n+1;for(;;){do{o++}while(t[o]r);if(o>=a)break;f(t,i,s,o,a)}_(t,i,s,e,a,h),_(t,i,s,a+1,n,h)}function f(t,i,s,e,n){const h=t[e];t[e]=t[n],t[n]=h;const r=4*e,o=4*n,a=i[r],d=i[r+1],_=i[r+2],f=i[r+3];i[r]=i[o],i[r+1]=i[o+1],i[r+2]=i[o+2],i[r+3]=i[o+3],i[o]=a,i[o+1]=d,i[o+2]=_,i[o+3]=f;const m=s[e];s[e]=s[n],s[n]=m}function m(t,i){let s=t^i,e=65535^s,n=65535^(t|i),h=t&(65535^i),r=s|e>>1,o=s>>1^s,a=n>>1^e&h>>1^n,d=s&n>>1^h>>1^h;s=r,e=o,n=a,h=d,r=s&s>>2^e&e>>2,o=s&e>>2^e&(s^e)>>2,a^=s&n>>2^e&h>>2,d^=e&n>>2^(s^e)&h>>2,s=r,e=o,n=a,h=d,r=s&s>>4^e&e>>4,o=s&e>>4^e&(s^e)>>4,a^=s&n>>4^e&h>>4,d^=e&n>>4^(s^e)&h>>4,s=r,e=o,n=a,h=d,a^=s&n>>8^e&h>>8,d^=e&n>>8^(s^e)&h>>8,s=a^a>>1,e=d^d>>1;let _=t^i,f=e|65535^(_|s);return _=16711935&(_|_<<8),_=252645135&(_|_<<4),_=858993459&(_|_<<2),_=1431655765&(_|_<<1),f=16711935&(f|f<<8),f=252645135&(f|f<<4),f=858993459&(f|f<<2),f=1431655765&(f|f<<1),(f<<1|_)>>>0}s.default=o},\n function _(s,t,i,h,e){h();i.default=class{constructor(){this.ids=[],this.values=[],this.length=0}clear(){this.length=0}push(s,t){let i=this.length++;for(;i>0;){const s=i-1>>1,h=this.values[s];if(t>=h)break;this.ids[i]=this.ids[s],this.values[i]=h,i=s}this.ids[i]=s,this.values[i]=t}pop(){if(0===this.length)return;const s=this.ids[0];if(this.length--,this.length>0){const s=this.ids[0]=this.ids[this.length],t=this.values[0]=this.values[this.length],i=this.length>>1;let h=0;for(;h=t)break;this.ids[h]=e,this.values[h]=l,h=s}this.ids[h]=s,this.values[h]=t}return s}peek(){if(0!==this.length)return this.ids[0]}peekValue(){if(0!==this.length)return this.values[0]}shrink(){this.ids.length=this.values.length=this.length}}},\n function _(e,i,n,a,t){var r;a();const o=e(169),s=e(51),d=e(58),l=e(56);class c extends d.View{*children(){yield*super.children(),yield this.marking}async lazy_initialize(){await super.lazy_initialize(),this.marking=await(0,l.build_view)(this.model.marking,{parent:this.parent})}}n.DecorationView=c,c.__name__=\"DecorationView\";class _ extends s.Model{constructor(e){super(e)}}n.Decoration=_,r=_,_.__name__=\"Decoration\",r.prototype.default_view=c,r.define((({Enum:e,Ref:i})=>({marking:[i(o.Marking)],node:[e(\"start\",\"middle\",\"end\")]})))},\n function _(e,n,t,a,i){a();const l=e(1).__importStar(e(228));function r(e,n,{x0:t,x1:a,y0:i,y1:l},r){n.save(),n.beginPath(),n.moveTo(t,(i+l)/2),n.lineTo(a,(i+l)/2),e.line.apply(n,r),n.restore()}function c(e,n,{x0:t,x1:a,y0:i,y1:l},r){const c=.1*Math.abs(a-t),_=.1*Math.abs(l-i),o=t+c,s=a-c,y=i+_,p=l-_;n.beginPath(),n.rect(o,y,s-o,p-y),e.fill.apply(n,r),e.hatch?.apply(n,r),e.line?.apply(n,r)}t.generic_line_scalar_legend=function(e,n,{x0:t,x1:a,y0:i,y1:l}){n.save(),n.beginPath(),n.moveTo(t,(i+l)/2),n.lineTo(a,(i+l)/2),e.line.apply(n),n.restore()},t.generic_line_vector_legend=r,t.generic_line_legend=r,t.generic_area_scalar_legend=function(e,n,{x0:t,x1:a,y0:i,y1:l}){const r=.1*Math.abs(a-t),c=.1*Math.abs(l-i),_=t+r,o=a-r,s=i+c,y=l-c;n.beginPath(),n.rect(_,s,o-_,y-s),e.fill.apply(n),e.hatch?.apply(n),e.line?.apply(n)},t.generic_area_vector_legend=c,t.generic_area_legend=c,t.line_interpolation=function(e,n,t,a,i,r){const{sx:c,sy:_}=n;let o,s,y,p;\"point\"==n.type?([y,p]=e.yscale.r_invert(_-1,_+1),[o,s]=e.xscale.r_invert(c-1,c+1)):\"v\"==n.direction?([y,p]=e.yscale.r_invert(_,_),[o,s]=[Math.min(t-1,i-1),Math.max(t+1,i+1)]):([o,s]=e.xscale.r_invert(c,c),[y,p]=[Math.min(a-1,r-1),Math.max(a+1,r+1)]);const{x:g,y:h}=l.check_2_segments_intersect(o,y,s,p,t,a,i,r);return[g,h]}},\n function _(t,n,e,i,r){function s(t,n){return(t.x-n.x)**2+(t.y-n.y)**2}function o(t,n,e){const i=s(n,e);if(0==i)return s(t,n);const r=((t.x-n.x)*(e.x-n.x)+(t.y-n.y)*(e.y-n.y))/i;if(r<0)return s(t,n);if(r>1)return s(t,e);return s(t,{x:n.x+r*(e.x-n.x),y:n.y+r*(e.y-n.y)})}i(),e.point_in_poly=function(t,n,e,i){let r=!1,s=e[e.length-1],o=i[i.length-1];for(let u=0;u0&&_<1&&h>0&&h<1,x:t+_*(e-t),y:n+_*(i-n)}}}},\n function _(t,s,i,e,a){var l;e();const n=t(1),_=t(220),o=t(227),c=n.__importStar(t(228)),h=n.__importStar(t(80)),r=t(130);class p extends _.XYGlyphView{_paint(t,s,i){const{sx:e,sy:a}={...this,...i};let l=!0;t.beginPath();for(const i of s){const s=e[i],n=a[i];isFinite(s+n)?l?(t.moveTo(s,n),l=!1):t.lineTo(s,n):(t.closePath(),l=!0)}t.closePath(),this.visuals.fill.apply(t),this.visuals.hatch.apply(t),this.visuals.line.apply(t)}draw_legend_for_index(t,s,i){(0,o.generic_area_scalar_legend)(this.visuals,t,s)}_hit_point(t){const s=new r.Selection;return c.point_in_poly(t.sx,t.sy,this.sx,this.sy)&&(s.add_to_selected_glyphs(this.model),s.view=this),s}}i.PatchView=p,p.__name__=\"PatchView\";class d extends _.XYGlyph{constructor(t){super(t)}}i.Patch=d,l=d,d.__name__=\"Patch\",l.prototype.default_view=p,l.mixins([h.LineScalar,h.FillScalar,h.HatchScalar])},\n function _(e,t,i,s,n){var o;s();const _=e(1),r=e(231),a=_.__importStar(e(228)),l=_.__importStar(e(18)),h=e(20),c=e(232),p=e(130);class d extends r.AreaView{_index_data(e){const{min:t,max:i}=Math,{x1:s,x2:n,y:o}=this;for(let _=0;_0==n?[t[n],_,_,t[n]]:n==this.data_size-1?[o,t[n],t[n],o]:[o,_,_,o])();if(a.point_in_poly(e.sx,e.sy,r,l))return this._line_selection_for(n)}return new p.Selection}_hit_point(e){switch(this.model.step_mode){case\"before\":return this._hit_point_before(e);case\"after\":return this._hit_point_after(e);case\"center\":return this._hit_point_center(e)}}}i.HAreaStepView=d,d.__name__=\"HAreaStepView\";class f extends r.Area{constructor(e){super(e)}}i.HAreaStep=f,o=f,f.__name__=\"HAreaStep\",o.prototype.default_view=d,o.define((({})=>({x1:[l.XCoordinateSpec,{field:\"x1\"}],x2:[l.XCoordinateSpec,{field:\"x2\"}],y:[l.YCoordinateSpec,{field:\"y\"}],step_mode:[h.StepMode,\"before\"]})))},\n function _(e,a,r,_,s){var n;_();const i=e(1),l=e(221),c=e(227),t=i.__importStar(e(80));class d extends l.GlyphView{draw_legend_for_index(e,a,r){(0,c.generic_area_scalar_legend)(this.visuals,e,a)}}r.AreaView=d,d.__name__=\"AreaView\";class o extends l.Glyph{constructor(e){super(e)}}r.Area=o,n=o,o.__name__=\"Area\",n.mixins([t.FillScalar,t.HatchScalar])},\n function _(e,r,t,n,c){n(),t.flip_step_mode=function(e){switch(e){case\"before\":return\"after\";case\"after\":return\"before\";case\"center\":return\"center\"}}},\n function _(t,s,e,i,n){var a;i();const o=t(1),h=t(231),r=o.__importStar(t(228)),l=o.__importStar(t(18)),_=t(130);class c extends h.AreaView{_index_data(t){const{min:s,max:e}=Math,{data_size:i}=this;for(let n=0;n=0;s--)t.lineTo(n[s],a[s]);t.closePath(),this.visuals.fill.apply(t),this.visuals.hatch.apply(t)}_hit_point(t){const s=this.sy.length,e=new _.Selection;for(let i=0,n=s-1;i({x1:[l.XCoordinateSpec,{field:\"x1\"}],x2:[l.XCoordinateSpec,{field:\"x2\"}],y:[l.YCoordinateSpec,{field:\"y\"}]})))},\n function _(e,t,i,s,n){var o;s();const _=e(1),r=e(231),a=_.__importStar(e(228)),l=_.__importStar(e(18)),h=e(20),c=e(232),p=e(130);class d extends r.AreaView{_index_data(e){const{min:t,max:i}=Math,{x:s,y1:n,y2:o}=this;for(let _=0;_0==n?[t[n],_,_,t[n]]:n==this.data_size-1?[o,t[n],t[n],o]:[o,_,_,o])(),l=[i[n],i[n],s[n],s[n]];if(a.point_in_poly(e.sx,e.sy,r,l))return this._line_selection_for(n)}return new p.Selection}_hit_point(e){switch(this.model.step_mode){case\"before\":return this._hit_point_before(e);case\"after\":return this._hit_point_after(e);case\"center\":return this._hit_point_center(e)}}}i.VAreaStepView=d,d.__name__=\"VAreaStepView\";class f extends r.Area{constructor(e){super(e)}}i.VAreaStep=f,o=f,f.__name__=\"VAreaStep\",o.prototype.default_view=d,o.define((({})=>({x:[l.XCoordinateSpec,{field:\"x\"}],y1:[l.YCoordinateSpec,{field:\"y1\"}],y2:[l.YCoordinateSpec,{field:\"y2\"}],step_mode:[h.StepMode,\"before\"]})))},\n function _(t,s,e,i,n){var a;i();const o=t(1),h=t(231),r=o.__importStar(t(228)),l=o.__importStar(t(18)),_=t(130);class c extends h.AreaView{_index_data(t){const{min:s,max:e}=Math,{data_size:i}=this;for(let n=0;n=0;s--)t.lineTo(i[s],a[s]);t.closePath(),this.visuals.fill.apply(t),this.visuals.hatch.apply(t)}scenterxy(t){return[this.sx[t],(this.sy1[t]+this.sy2[t])/2]}_hit_point(t){const s=this.sx.length,e=new _.Selection;for(let i=0,n=s-1;i({x:[l.XCoordinateSpec,{field:\"x\"}],y1:[l.YCoordinateSpec,{field:\"y1\"}],y2:[l.YCoordinateSpec,{field:\"y2\"}]})))},\n function _(e,t,i,n,s){var c;n();const a=e(51),o=e(58),r=e(24),l=e(237),_=e(238),d=e(239);class h extends o.View{initialize(){super.initialize(),this.compute_indices()}connect_signals(){super.connect_signals();const e=()=>{this.compute_indices()},t=t=>{this.connect(t.change,e)},i=t=>{this.disconnect(t.change,e)};let{filter:n}=this.model;t(n),this.on_change(this.model.properties.filter,(()=>{i(n),n=this.model.filter,t(n),e()}));const s=t=>{this.connect(t.change,e),this.connect(t.streaming,e),this.connect(t.patching,e),this.connect(t.properties.data.change,e)},c=t=>{this.disconnect(t.change,e),this.disconnect(t.streaming,e),this.disconnect(t.patching,e),this.disconnect(t.properties.data.change,e)};let a=this.parent.data_source.get_value();s(a),this.on_change(this.parent.data_source,(()=>{c(a),a=this.parent.data_source.get_value(),s(a),e()}))}compute_indices(){const e=this.parent.data_source.get_value(),t=e.get_length()??1,i=r.Indices.all_set(t),n=this.model.filter.compute_indices(e);i.intersect(n),this.model.indices=i,this.model._indices_map_to_subset()}}i.CDSViewView=h,h.__name__=\"CDSViewView\";class p extends a.Model{constructor(e){super(e)}_indices_map_to_subset(){this._indices=[...this.indices],this.indices_map=new Map;const{_indices:e,indices_map:t}=this,i=e.length;for(let n=0;nthis._indices[e]))}convert_selection_to_subset(e){return e.map((e=>this.indices_map.get(e)))}convert_indices_from_subset(e){return e.map((e=>this._indices[e]))}get filters(){const{filter:e}=this;return e instanceof d.IntersectionFilter?e.operands:e instanceof _.AllIndices?[]:[e]}set filters(e){0==e.length?this.filter=new _.AllIndices:1==e.length?this.filter=e[0]:this.filter=new d.IntersectionFilter({operands:e})}}i.CDSView=p,c=p,p.__name__=\"CDSView\",c.prototype.default_view=h,c.define((({Ref:e})=>({filter:[e(l.Filter),()=>new _.AllIndices]}))),c.internal((({Int:e,Mapping:t,Ref:i,Nullable:n})=>({indices:[i(r.Indices)],indices_map:[t(e,e),new Map],masked:[n(i(r.Indices)),null]})))},\n function _(e,t,n,s,c){s();const o=e(51);class r extends o.Model{constructor(e){super(e)}}n.Filter=r,r.__name__=\"Filter\"},\n function _(e,n,s,t,c){t();const l=e(237),_=e(24);class i extends l.Filter{constructor(e){super(e)}compute_indices(e){const n=e.get_length()??1;return _.Indices.all_set(n)}}s.AllIndices=i,i.__name__=\"AllIndices\"},\n function _(e,t,n,s,c){s();const i=e(240);class o extends i.CompositeFilter{constructor(e){super(e)}_inplace_op(e,t){e.intersect(t)}}n.IntersectionFilter=o,o.__name__=\"IntersectionFilter\"},\n function _(e,t,n,s,o){var i;s();const c=e(237),r=e(24);class a extends c.Filter{constructor(e){super(e)}connect_signals(){super.connect_signals();const e=()=>{this.change.emit()},t=t=>{for(const n of t)this.connect(n.change,e)},n=t=>{for(const n of t)this.disconnect(n.change,e)};let s=(()=>{const{operands:e}=this.properties;return e.is_unset?[]:e.get_value()})();t(s),this.on_change(this.properties.operands,(()=>{n(s),s=this.operands,t(s)}))}compute_indices(e){const{operands:t}=this;if(0==t.length){const t=e.get_length()??1;return r.Indices.all_set(t)}{const[n,...s]=t.map((t=>t.compute_indices(e)));for(const e of s)this._inplace_op(n,e);return n}}}n.CompositeFilter=a,i=a,a.__name__=\"CompositeFilter\",i.define((({List:e,Ref:t})=>({operands:[e(t(c.Filter))]})))},\n function _(t,r,a,e,i){e(),i(\"BasicTickFormatter\",t(196).BasicTickFormatter),i(\"CategoricalTickFormatter\",t(242).CategoricalTickFormatter),i(\"DatetimeTickFormatter\",t(243).DatetimeTickFormatter),i(\"CustomJSTickFormatter\",t(248).CustomJSTickFormatter),i(\"LogTickFormatter\",t(249).LogTickFormatter),i(\"MercatorTickFormatter\",t(250).MercatorTickFormatter),i(\"NumeralTickFormatter\",t(251).NumeralTickFormatter),i(\"PrintfTickFormatter\",t(252).PrintfTickFormatter),i(\"TickFormatter\",t(192).TickFormatter)},\n function _(t,r,o,c,a){c();const e=t(192),n=t(10);class i extends e.TickFormatter{constructor(t){super(t)}doFormat(t,r){return(0,n.copy)(t)}}o.CategoricalTickFormatter=i,i.__name__=\"CategoricalTickFormatter\"},\n function _(e,t,s,n,o){var r;n();const i=e(1),l=e(20),c=e(12),u=e(244),_=e(8),a=e(192),h=e(206),m=i.__importDefault(e(247));function d(e,t){const s=1.1*e*1e3,n=1e3*t;return s=h.ONE_MINUTE?\"minsec\":\"seconds\":s=h.ONE_HOUR?\"hourmin\":\"minutes\":sparseInt(e,10)))}function p(e,t){const s=(0,u.sprintf)(\"$1%06d\",b(e));return-1==(t=t.replace(/((^|[^%])(%%)*)%f/,s)).indexOf(\"%\")?t:(0,m.default)(e,t)}function b(e){let t=Math.round(e/1e3%1*1e6);return e<0&&(t=(1e6+t)%1e6),t}s.resolution_order=[\"microseconds\",\"milliseconds\",\"seconds\",\"minsec\",\"minutes\",\"hourmin\",\"hours\",\"days\",\"months\",\"years\"],s.tm_index_for_resolution={microseconds:0,milliseconds:0,seconds:5,minsec:4,minutes:4,hourmin:3,hours:3,days:0,months:0,years:0},s._get_resolution=d,s._mktime=f,s._strftime=p,s._us=b;class g extends a.TickFormatter{constructor(e){super(e)}doFormat(e,t,s){if(0==e.length)return[];const n=Math.abs(e[e.length-1]-e[0])/1e3,o=n/(e.length-1),r=(0,_.is_undefined)(s)?d(o,n):s;let i=[];for(const t of e){const e=this._compute_label(t,r);i.push(e)}if(this.hide_repeats&&(i=this._hide_repeating_labels(i)),null==this.context)return i;const l=this._compute_context_labels(e,r);return this._build_full_labels(i,l)}_compute_label(e,t){const n=p(e,this[t]),o=f(e),r=s.resolution_order.indexOf(t);let i=t,l=n;if(this.boundary_scaling){let n=!1,c=r,u=t;for(;0==o[s.tm_index_for_resolution[s.resolution_order[c]]]&&(c+=1,c!=s.resolution_order.length);){if((\"minsec\"==t||\"hourmin\"==t)&&!n){if(\"minsec\"==t&&0==o[4]&&0!=o[5]||\"hourmin\"==t&&0==o[3]&&0!=o[4]){u=s.resolution_order[r-1],l=p(e,this[u]);break}n=!0}u=s.resolution_order[c],l=p(e,this[u])}i=u}const{strip_leading_zeros:c}=this;if((0,_.isBoolean)(c)&&c||(0,_.isArray)(c)&&c.includes(i)){const e=l.replace(/^0+/g,\"\");return e==l||Number.isInteger(Number(e[0]))?e:`0${e}`}return l}_compute_context_labels(e,t){const{context:s}=this;(0,c.assert)(null!=s);const n=[];if((0,_.isString)(s))for(const t of e)n.push(p(t,s));else n.push(...s.doFormat(e,{loc:0},t));const o=this.context_which,r=n.length;for(let e=0;e\"\"===e)))return e;for(let o=0;o{switch(s){case\"above\":return`${i}\\n${r}`;case\"below\":return`${r}\\n${i}`;case\"left\":return\"\"==i?r:`${i} ${r}`;case\"right\":return\"\"==i?r:`${r} ${i}`}})();n.push(l)}return n}_hide_repeating_labels(e){if(e.length<=1)return e;const t=[e[0]];let s=0;for(let n=1;n({microseconds:[o,\"%fus\"],milliseconds:[o,\"%3Nms\"],seconds:[o,\"%Ss\"],minsec:[o,\":%M:%S\"],minutes:[o,\":%M\"],hourmin:[o,\"%H:%M\"],hours:[o,\"%Hh\"],days:[o,\"%m/%d\"],months:[o,\"%m/%Y\"],years:[o,\"%Y\"],strip_leading_zeros:[s(e,i(l.ResolutionType)),!1],boundary_scaling:[e,!0],hide_repeats:[e,!1],context:[t(s(o,n(r))),null],context_which:[l.ContextWhich,\"start\"],context_location:[l.Location,\"below\"]})))},\n function _(r,e,n,t,i){t();const u=r(1),l=u.__importStar(r(245)),a=r(246),s=u.__importDefault(r(247)),f=r(19),o=r(9),c=r(30),m=r(8),{abs:_}=Math;function p(r,...e){return(0,a.sprintf)(r,...e)}function N(r,e,n){if((0,m.isNumber)(r)){return p(Number.isInteger(r)?\"%d\":.1<_(r)&&_(r)<1e3?\"%0.3f\":\"%0.3e\",r)}return`${r}`}function S(r,e,t){if(null==e)return n.DEFAULT_FORMATTERS.basic;if(null!=t){const e=(0,o.dict)(t).get(r);if(null!=e){if((0,m.isString)(e)){if(e in n.DEFAULT_FORMATTERS)return n.DEFAULT_FORMATTERS[e];throw new Error(`Unknown tooltip field formatter type '${e}'`)}return function(r,n,t){return e.format(r,n,t)}}}return n.DEFAULT_FORMATTERS.numeral}function d(r,e,n){const t=e.get_column(r);if(null==t)return null;if(null==n)return null;if((0,m.isNumber)(n))return t[n];const i=t[n.index];if((0,m.isTypedArray)(i)||(0,m.isArray)(i)){if((0,m.isArray)(i[0])){return i[n.j][n.i]}return(0,c.is_NDArray)(i)&&3==i.dimension?i.slice(n.flat_index*i.shape[2],(n.flat_index+1)*i.shape[2]):i[n.flat_index]}return i}function T(r,e,t,i,u){switch(r){case\"$\":return function(r,e){return r in e?e[r]:(f.logger.warn(`unknown special variable '$${r}'`),n.MISSING)}(e,u);case\"@\":return d(e,t,i)}}n.DEFAULT_FORMATTERS={raw:(r,e,n)=>`${r}`,basic:(r,e,n)=>N(r,e,n),numeral:(r,e,n)=>l.format(r,e),datetime:(r,e,n)=>(0,s.default)(r,e),printf:(r,e,n)=>p(e,r)},n.sprintf=p,n.basic_formatter=N,n.get_formatter=S,n.MISSING=\"???\",n._get_column_value=d,n.get_value=T,n.replace_placeholders=function(r,e,t,i,u={},l){let a,s;if((0,m.isString)(r)?(a=r,s=!1):(a=r.html,s=!0),a=a.replace(/@\\$name/g,(r=>`@{${u.name}}`)),a=A(a,((r,a,f,o,c)=>{const _=T(r,a,e,t,u);if(\"safe\"==f)return s=!0,null==_?n.MISSING:(0,m.isNumber)(_)&&isNaN(_)?\"NaN\":`${_}`;{const r=(()=>{if(null==_)return n.MISSING;if((0,m.isNumber)(_)&&isNaN(_))return\"NaN\";return`${S(c,f,i)(_,f??\"\",u)}`})();return null!=l?l(r):r}})),s){return[...(new DOMParser).parseFromString(a,\"text/html\").body.childNodes]}return a};const g=/((?:[$@][\\p{Letter}\\p{Number}_]+)|(?:[$@]\\{(?:[^{}]+)\\}))(?:\\{([^{}]+)\\})?/gu;function A(r,e){let t=0;return r.replace(g,((r,i,u)=>{const l=i[0],a=i.substring(1).replace(/^{/,\"\").replace(/}$/,\"\").trim();return e(l,a,u,t++,i)??n.MISSING}))}n.process_placeholders=A},\n function _(e,n,t,r,i){\n /*!\n * numbro.js\n * version : 1.6.2\n * author : Företagsplatsen AB\n * license : MIT\n * http://www.foretagsplatsen.se\n */\n var a,o={},l=o,c=\"en-US\",u=null,s=\"0,0\";void 0!==n&&n.exports;function f(e){this._value=e}function d(e){var n,t=\"\";for(n=0;n-1?function(e,n){var t,r,i,a;return t=(a=e.toString()).split(\"e\")[0],i=a.split(\"e\")[1],a=t.split(\".\")[0]+(r=t.split(\".\")[1]||\"\")+d(i-r.length),n>0&&(a+=\".\"+d(n)),a}(e,n):(t(e*o)/o).toFixed(n),r&&(i=new RegExp(\"0{1,\"+r+\"}$\"),a=a.replace(i,\"\")),a}function p(e,n,t){var r;return r=n.indexOf(\"$\")>-1?function(e,n,t){var r,i,a=n,l=a.indexOf(\"$\"),u=a.indexOf(\"(\"),s=a.indexOf(\"+\"),f=a.indexOf(\"-\"),d=\"\",h=\"\";-1===a.indexOf(\"$\")?\"infix\"===o[c].currency.position?(h=o[c].currency.symbol,o[c].currency.spaceSeparated&&(h=\" \"+h+\" \")):o[c].currency.spaceSeparated&&(d=\" \"):a.indexOf(\" $\")>-1?(d=\" \",a=a.replace(\" $\",\"\")):a.indexOf(\"$ \")>-1?(d=\" \",a=a.replace(\"$ \",\"\")):a=a.replace(\"$\",\"\");if(i=m(e,a,t,h),-1===n.indexOf(\"$\"))switch(o[c].currency.position){case\"postfix\":i.indexOf(\")\")>-1?((i=i.split(\"\")).splice(-1,0,d+o[c].currency.symbol),i=i.join(\"\")):i=i+d+o[c].currency.symbol;break;case\"infix\":break;case\"prefix\":i.indexOf(\"(\")>-1||i.indexOf(\"-\")>-1?(i=i.split(\"\"),r=Math.max(u,f)+1,i.splice(r,0,o[c].currency.symbol+d),i=i.join(\"\")):i=o[c].currency.symbol+d+i;break;default:throw Error('Currency position should be among [\"prefix\", \"infix\", \"postfix\"]')}else l<=1?i.indexOf(\"(\")>-1||i.indexOf(\"+\")>-1||i.indexOf(\"-\")>-1?(r=1,(l-1?((i=i.split(\"\")).splice(-1,0,d+o[c].currency.symbol),i=i.join(\"\")):i=i+d+o[c].currency.symbol;return i}(e,n,t):n.indexOf(\"%\")>-1?function(e,n,t){var r,i=\"\";e*=100,n.indexOf(\" %\")>-1?(i=\" \",n=n.replace(\" %\",\"\")):n=n.replace(\"%\",\"\");r=m(e,n,t),r.indexOf(\")\")>-1?((r=r.split(\"\")).splice(-1,0,i+\"%\"),r=r.join(\"\")):r=r+i+\"%\";return r}(e,n,t):n.indexOf(\":\")>-1?function(e){var n=Math.floor(e/60/60),t=Math.floor((e-60*n*60)/60),r=Math.round(e-60*n*60-60*t);return n+\":\"+(t<10?\"0\"+t:t)+\":\"+(r<10?\"0\"+r:r)}(e):m(e,n,t),r}function m(e,n,t,r){var i,a,l,s,f,d,p,m,x,g,O,b,w,y,M,v,$,B=!1,E=!1,F=!1,k=\"\",U=!1,N=!1,S=!1,j=!1,D=!1,C=\"\",L=\"\",T=Math.abs(e),K=[\"B\",\"KiB\",\"MiB\",\"GiB\",\"TiB\",\"PiB\",\"EiB\",\"ZiB\",\"YiB\"],G=[\"B\",\"KB\",\"MB\",\"GB\",\"TB\",\"PB\",\"EB\",\"ZB\",\"YB\"],I=\"\",P=!1,R=!1;if(0===e&&null!==u)return u;if(!isFinite(e))return\"\"+e;if(0===n.indexOf(\"{\")){var W=n.indexOf(\"}\");if(-1===W)throw Error('Format should also contain a \"}\"');b=n.slice(1,W),n=n.slice(W+1)}else b=\"\";if(n.indexOf(\"}\")===n.length-1){var Y=n.indexOf(\"{\");if(-1===Y)throw Error('Format should also contain a \"{\"');w=n.slice(Y+1,-1),n=n.slice(0,Y+1)}else w=\"\";if(v=null===($=-1===n.indexOf(\".\")?n.match(/([0-9]+).*/):n.match(/([0-9]+)\\..*/))?-1:$[1].length,-1!==n.indexOf(\"-\")&&(P=!0),n.indexOf(\"(\")>-1?(B=!0,n=n.slice(1,-1)):n.indexOf(\"+\")>-1&&(E=!0,n=n.replace(/\\+/g,\"\")),n.indexOf(\"a\")>-1){if(g=n.split(\".\")[0].match(/[0-9]+/g)||[\"0\"],g=parseInt(g[0],10),U=n.indexOf(\"aK\")>=0,N=n.indexOf(\"aM\")>=0,S=n.indexOf(\"aB\")>=0,j=n.indexOf(\"aT\")>=0,D=U||N||S||j,n.indexOf(\" a\")>-1?(k=\" \",n=n.replace(\" a\",\"\")):n=n.replace(\"a\",\"\"),p=0===(p=(f=Math.floor(Math.log(T)/Math.LN10)+1)%3)?3:p,g&&0!==T&&(d=Math.floor(Math.log(T)/Math.LN10)+1-g,m=3*~~((Math.min(g,f)-p)/3),T/=Math.pow(10,m),-1===n.indexOf(\".\")&&g>3))for(n+=\"[.]\",M=(M=0===d?0:3*~~(d/3)-d)<0?M+3:M,i=0;i=Math.pow(10,12)&&!D||j?(k+=o[c].abbreviations.trillion,e/=Math.pow(10,12)):T=Math.pow(10,9)&&!D||S?(k+=o[c].abbreviations.billion,e/=Math.pow(10,9)):T=Math.pow(10,6)&&!D||N?(k+=o[c].abbreviations.million,e/=Math.pow(10,6)):(T=Math.pow(10,3)&&!D||U)&&(k+=o[c].abbreviations.thousand,e/=Math.pow(10,3)))}if(n.indexOf(\"b\")>-1)for(n.indexOf(\" b\")>-1?(C=\" \",n=n.replace(\" b\",\"\")):n=n.replace(\"b\",\"\"),s=0;s<=K.length;s++)if(a=Math.pow(1024,s),l=Math.pow(1024,s+1),e>=a&&e0&&(e/=a);break}if(n.indexOf(\"d\")>-1)for(n.indexOf(\" d\")>-1?(C=\" \",n=n.replace(\" d\",\"\")):n=n.replace(\"d\",\"\"),s=0;s<=G.length;s++)if(a=Math.pow(1e3,s),l=Math.pow(1e3,s+1),e>=a&&e0&&(e/=a);break}if(n.indexOf(\"o\")>-1&&(n.indexOf(\" o\")>-1?(L=\" \",n=n.replace(\" o\",\"\")):n=n.replace(\"o\",\"\"),o[c].ordinal&&(L+=o[c].ordinal(e))),n.indexOf(\"[.]\")>-1&&(F=!0,n=n.replace(\"[.]\",\".\")),x=e.toString().split(\".\")[0],O=n.split(\".\")[1],y=n.indexOf(\",\"),O){if(x=(I=-1!==O.indexOf(\"*\")?h(e,e.toString().split(\".\")[1].length,t):O.indexOf(\"[\")>-1?h(e,(O=(O=O.replace(\"]\",\"\")).split(\"[\"))[0].length+O[1].length,t,O[1].length):h(e,O.length,t)).split(\".\")[0],I.split(\".\")[1].length)I=(r?k+r:o[c].delimiters.decimal)+I.split(\".\")[1];else I=\"\";F&&0===Number(I.slice(1))&&(I=\"\")}else x=h(e,null,t);return x.indexOf(\"-\")>-1&&(x=x.slice(1),R=!0),x.length-1&&(x=x.toString().replace(/(\\d)(?=(\\d{3})+(?!\\d))/g,\"$1\"+o[c].delimiters.thousands)),0===n.indexOf(\".\")&&(x=\"\"),b+(n.indexOf(\"(\")2)&&(o.length<2?!!o[0].match(/^\\d+.*\\d$/)&&!o[0].match(c):1===o[0].length?!!o[0].match(/^\\d+$/)&&!o[0].match(c)&&!!o[1].match(/^\\d+$/):!!o[0].match(/^\\d+.*\\d$/)&&!o[0].match(c)&&!!o[1].match(/^\\d+$/)))))},n.exports={format:function(e,n,t,r){return null!=t&&t!==a.culture()&&a.setCulture(t),p(Number(e),null!=n?n:s,r??Math.round)}}},\n function _(e,n,t,r,i){!function(){\"use strict\";var e={not_string:/[^s]/,not_bool:/[^t]/,not_type:/[^T]/,not_primitive:/[^v]/,number:/[diefg]/,numeric_arg:/[bcdiefguxX]/,json:/[j]/,not_json:/[^j]/,text:/^[^\\x25]+/,modulo:/^\\x25{2}/,placeholder:/^\\x25(?:([1-9]\\d*)\\$|\\(([^)]+)\\))?(\\+)?(0|'[^$])?(-)?(\\d+)?(?:\\.(\\d+))?([b-gijostTuvxX])/,key:/^([a-z_][a-z_\\d]*)/i,key_access:/^\\.([a-z_][a-z_\\d]*)/i,index_access:/^\\[(\\d+)\\]/,sign:/^[+-]/};function n(t){return function(t,r){var i,s,a,o,p,c,l,u,f,d=1,g=t.length,y=\"\";for(s=0;s=0),o.type){case\"b\":i=parseInt(i,10).toString(2);break;case\"c\":i=String.fromCharCode(parseInt(i,10));break;case\"d\":case\"i\":i=parseInt(i,10);break;case\"j\":i=JSON.stringify(i,null,o.width?parseInt(o.width):0);break;case\"e\":i=o.precision?parseFloat(i).toExponential(o.precision):parseFloat(i).toExponential();break;case\"f\":i=o.precision?parseFloat(i).toFixed(o.precision):parseFloat(i);break;case\"g\":i=o.precision?String(Number(i.toPrecision(o.precision))):parseFloat(i);break;case\"o\":i=(parseInt(i,10)>>>0).toString(8);break;case\"s\":i=String(i),i=o.precision?i.substring(0,o.precision):i;break;case\"t\":i=String(!!i),i=o.precision?i.substring(0,o.precision):i;break;case\"T\":i=Object.prototype.toString.call(i).slice(8,-1).toLowerCase(),i=o.precision?i.substring(0,o.precision):i;break;case\"u\":i=parseInt(i,10)>>>0;break;case\"v\":i=i.valueOf(),i=o.precision?i.substring(0,o.precision):i;break;case\"x\":i=(parseInt(i,10)>>>0).toString(16);break;case\"X\":i=(parseInt(i,10)>>>0).toString(16).toUpperCase()}e.json.test(o.type)?y+=i:(!e.number.test(o.type)||u&&!o.sign?f=\"\":(f=u?\"+\":\"-\",i=i.toString().replace(e.sign,\"\")),c=o.pad_char?\"0\"===o.pad_char?\"0\":o.pad_char.charAt(1):\" \",l=o.width-(f+i).length,p=o.width&&l>0?c.repeat(l):\"\",y+=o.align?f+i+p:\"0\"===c?f+p+i:p+f+i)}return y}(function(n){if(i[n])return i[n];var t,r=n,s=[],a=0;for(;r;){if(null!==(t=e.text.exec(r)))s.push(t[0]);else if(null!==(t=e.modulo.exec(r)))s.push(\"%\");else{if(null===(t=e.placeholder.exec(r)))throw new SyntaxError(\"[sprintf] unexpected placeholder\");if(t[2]){a|=1;var o=[],p=t[2],c=[];if(null===(c=e.key.exec(p)))throw new SyntaxError(\"[sprintf] failed to parse named argument key\");for(o.push(c[1]);\"\"!==(p=p.substring(c[0].length));)if(null!==(c=e.key_access.exec(p)))o.push(c[1]);else{if(null===(c=e.index_access.exec(p)))throw new SyntaxError(\"[sprintf] failed to parse named argument key\");o.push(c[1])}t[2]=o}else a|=2;if(3===a)throw new Error(\"[sprintf] mixing positional and named placeholders is not (yet) supported\");s.push({placeholder:t[0],param_no:t[1],keys:t[2],sign:t[3],pad_char:t[4],align:t[5],width:t[6],precision:t[7],type:t[8]})}r=r.substring(t[0].length)}return i[n]=s}(t),arguments)}function r(e,t){return n.apply(null,[e].concat(t||[]))}var i=Object.create(null);void 0!==t&&(t.sprintf=n,t.vsprintf=r),\"undefined\"!=typeof window&&(window.sprintf=n,window.vsprintf=r,\"function\"==typeof define&&define.amd&&define((function(){return{sprintf:n,vsprintf:r}})))}()},\n function _(e,t,n,r,o){!function(e){\"object\"==typeof t&&t.exports?t.exports=e():\"function\"==typeof define?define(e):this.tz=e()}((function(){function e(e,t,n){var r,o=t.day[1];do{r=new Date(Date.UTC(n,t.month,Math.abs(o++)))}while(t.day[0]<7&&r.getUTCDay()!=t.day[0]);return(r={clock:t.clock,sort:r.getTime(),rule:t,save:6e4*t.save,offset:e.offset})[r.clock]=r.sort+6e4*t.time,r.posix?r.wallclock=r[r.clock]+(e.offset+t.saved):r.posix=r[r.clock]-(e.offset+t.saved),r}function t(t,n,r){var o,a,u,i,l,s,c,f=t[t.zone],h=[],T=new Date(r).getUTCFullYear(),g=1;for(o=1,a=f.length;o=T-g;--c)for(o=0,a=s.length;o=h[o][n]&&h[o][h[o].clock]>u[h[o].clock]&&(i=h[o])}return i&&((l=/^(.*)\\/(.*)$/.exec(u.format))?i.abbrev=l[i.save?2:1]:i.abbrev=u.format.replace(/%s/,i.rule.letter)),i||u}function n(e,n){return\"UTC\"==e.zone?n:(e.entry=t(e,\"posix\",n),n+e.entry.offset+e.entry.save)}function r(e,n){return\"UTC\"==e.zone?n:(e.entry=r=t(e,\"wallclock\",n),0<(o=n-r.wallclock)&&o9)t+=s*l[c-10];else{if(a=new Date(n(e,t)),c<7)for(;s;)a.setUTCDate(a.getUTCDate()+i),a.getUTCDay()==c&&(s-=i);else 7==c?a.setUTCFullYear(a.getUTCFullYear()+s):8==c?a.setUTCMonth(a.getUTCMonth()+s):a.setUTCDate(a.getUTCDate()+s);null==(t=r(e,a.getTime()))&&(t=r(e,a.getTime()+864e5*i)-864e5*i)}return t}var a={clock:function(){return+new Date},zone:\"UTC\",entry:{abbrev:\"UTC\",offset:0,save:0},UTC:1,z:function(e,t,n,r){var o,a,u=this.entry.offset+this.entry.save,i=Math.abs(u/1e3),l=[],s=3600;for(o=0;o<3;o++)l.push((\"0\"+Math.floor(i/s)).slice(-2)),i%=s,s/=60;return\"^\"!=n||u?(\"^\"==n&&(r=3),3==r?(a=(a=l.join(\":\")).replace(/:00$/,\"\"),\"^\"!=n&&(a=a.replace(/:00$/,\"\"))):r?(a=l.slice(0,r+1).join(\":\"),\"^\"==n&&(a=a.replace(/:00$/,\"\"))):a=l.slice(0,2).join(\"\"),a=(a=(u<0?\"-\":\"+\")+a).replace(/([-+])(0)/,{_:\" $1\",\"-\":\"$1\"}[n]||\"$1$2\")):\"Z\"},\"%\":function(e){return\"%\"},n:function(e){return\"\\n\"},t:function(e){return\"\\t\"},U:function(e){return s(e,0)},W:function(e){return s(e,1)},V:function(e){return c(e)[0]},G:function(e){return c(e)[1]},g:function(e){return c(e)[1]%100},j:function(e){return Math.floor((e.getTime()-Date.UTC(e.getUTCFullYear(),0))/864e5)+1},s:function(e){return Math.floor(e.getTime()/1e3)},C:function(e){return Math.floor(e.getUTCFullYear()/100)},N:function(e){return e.getTime()%1e3*1e6},m:function(e){return e.getUTCMonth()+1},Y:function(e){return e.getUTCFullYear()},y:function(e){return e.getUTCFullYear()%100},H:function(e){return e.getUTCHours()},M:function(e){return e.getUTCMinutes()},S:function(e){return e.getUTCSeconds()},e:function(e){return e.getUTCDate()},d:function(e){return e.getUTCDate()},u:function(e){return e.getUTCDay()||7},w:function(e){return e.getUTCDay()},l:function(e){return e.getUTCHours()%12||12},I:function(e){return e.getUTCHours()%12||12},k:function(e){return e.getUTCHours()},Z:function(e){return this.entry.abbrev},a:function(e){return this[this.locale].day.abbrev[e.getUTCDay()]},A:function(e){return this[this.locale].day.full[e.getUTCDay()]},h:function(e){return this[this.locale].month.abbrev[e.getUTCMonth()]},b:function(e){return this[this.locale].month.abbrev[e.getUTCMonth()]},B:function(e){return this[this.locale].month.full[e.getUTCMonth()]},P:function(e){return this[this.locale].meridiem[Math.floor(e.getUTCHours()/12)].toLowerCase()},p:function(e){return this[this.locale].meridiem[Math.floor(e.getUTCHours()/12)]},R:function(e,t){return this.convert([t,\"%H:%M\"])},T:function(e,t){return this.convert([t,\"%H:%M:%S\"])},D:function(e,t){return this.convert([t,\"%m/%d/%y\"])},F:function(e,t){return this.convert([t,\"%Y-%m-%d\"])},x:function(e,t){return this.convert([t,this[this.locale].date])},r:function(e,t){return this.convert([t,this[this.locale].time12||\"%I:%M:%S\"])},X:function(e,t){return this.convert([t,this[this.locale].time24])},c:function(e,t){return this.convert([t,this[this.locale].dateTime])},convert:function(e){if(!e.length)return\"1.0.23\";var t,a,u,l,s,c=Object.create(this),f=[];for(t=0;t=o?Math.floor((n-o)/7)+1:0}function c(e){var t,n,r;return n=e.getUTCFullYear(),t=new Date(Date.UTC(n,0)).getUTCDay(),(r=s(e,1)+(t>1&&t<=4?1:0))?53!=r||4==t||3==t&&29==new Date(n,1,29).getDate()?[r,e.getUTCFullYear()]:[1,e.getUTCFullYear()+1]:(n=e.getUTCFullYear()-1,[r=4==(t=new Date(Date.UTC(n,0)).getUTCDay())||3==t&&29==new Date(n,1,29).getDate()?53:52,e.getUTCFullYear()-1])}return u=u.toLowerCase().split(\"|\"),\"delmHMSUWVgCIky\".replace(/./g,(function(e){a[e].pad=2})),a.N.pad=9,a.j.pad=3,a.k.style=\"_\",a.l.style=\"_\",a.e.style=\"_\",function(){return a.convert(arguments)}}))},\n function _(t,e,s,n,r){var c;n();const a=t(192),i=t(9),o=t(40);class u extends a.TickFormatter{constructor(t){super(t)}get names(){return(0,i.keys)(this.args)}get values(){return(0,i.values)(this.args)}_make_func(){const t=(0,o.use_strict)(this.code);return new Function(\"tick\",\"index\",\"ticks\",...this.names,t)}doFormat(t,e){const s=this._make_func().bind({});return t.map(((t,e,n)=>`${s(t,e,n,...this.values)}`))}}s.CustomJSTickFormatter=u,c=u,u.__name__=\"CustomJSTickFormatter\",c.define((({Unknown:t,Str:e,Dict:s})=>({args:[s(t),{}],code:[e,\"\"]})))},\n function _(e,t,n,r,o){var i;r();const s=e(192),a=e(196),c=e(210),l=e(180),{abs:u,log:x,round:_}=Math;class p extends s.TickFormatter{constructor(e){super(e)}initialize(){super.initialize(),this.basic_formatter=new a.BasicTickFormatter}format_graphics(e,t){if(0==e.length)return[];const n=this.ticker?.base??10,r=this._exponents(e,n);return null==r?this.basic_formatter.format_graphics(e,t):r.map((e=>{if(u(e)u(e)({ticker:[n(t(c.LogTicker)),null],min_exponent:[e,0]})))},\n function _(r,t,e,o,n){var i;o();const c=r(196),s=r(20),a=r(134);class l extends c.BasicTickFormatter{constructor(r){super(r)}doFormat(r,t){if(null==this.dimension)throw new Error(\"MercatorTickFormatter.dimension not configured\");if(0==r.length)return[];const e=r.length,o=new Array(e);if(\"lon\"==this.dimension)for(let n=0;n({dimension:[r(s.LatLon),null]})))},\n function _(r,n,t,o,e){var a;o();const u=r(1).__importStar(r(245)),c=r(192),i=r(20);class s extends c.TickFormatter{constructor(r){super(r)}get _rounding_fn(){switch(this.rounding){case\"round\":case\"nearest\":return Math.round;case\"floor\":case\"rounddown\":return Math.floor;case\"ceil\":case\"roundup\":return Math.ceil}}doFormat(r,n){const{format:t,language:o,_rounding_fn:e}=this;return r.map((r=>u.format(r,t,o,e)))}}t.NumeralTickFormatter=s,a=s,s.__name__=\"NumeralTickFormatter\",a.define((({Str:r})=>({format:[r,\"0,0\"],language:[r,\"en\"],rounding:[i.RoundingFunction,\"round\"]})))},\n function _(t,r,n,o,a){var e;o();const i=t(192),s=t(244);class c extends i.TickFormatter{constructor(t){super(t)}doFormat(t,r){return t.map((t=>(0,s.sprintf)(this.format,t)))}}n.PrintfTickFormatter=c,e=c,c.__name__=\"PrintfTickFormatter\",e.define((({Str:t})=>({format:[t,\"%s\"]})))},\n function _(a,e,l,c,o){c(),o(\"CategoricalScale\",a(104).CategoricalScale),o(\"CompositeScale\",a(105).CompositeScale),o(\"ContinuousScale\",a(102).ContinuousScale),o(\"LinearInterpolationScale\",a(254).LinearInterpolationScale),o(\"LinearScale\",a(101).LinearScale),o(\"LogScale\",a(103).LogScale),o(\"Scale\",a(96).Scale)},\n function _(e,n,r,t,a){var i;t();const s=e(96),o=e(101),c=e(13);class _ extends s.Scale{constructor(e){super(e)}initialize(){super.initialize();const{source_range:e,target_range:n}=this.properties;e.is_unset||n.is_unset||(this.linear_scale=new o.LinearScale({source_range:e.get_value(),target_range:n.get_value()}))}connect_signals(){super.connect_signals();const{source_range:e,target_range:n}=this.properties;this.on_change([e,n],(()=>{this.linear_scale=new o.LinearScale({source_range:this.source_range,target_range:this.target_range})}))}get s_compute(){throw new Error(\"not implemented\")}get s_invert(){throw new Error(\"not implemented\")}compute(e){return e}v_compute(e){const{binning:n}=this,{start:r,end:t}=this.source_range,a=r,i=t,s=n.length,o=(t-r)/(s-1),_=new Float64Array(s);for(let e=0;e{if(ei)return i;const r=(0,c.left_edge_index)(e,n);if(-1==r)return a;if(r>=s-1)return i;const t=n[r],o=(e-t)/(n[r+1]-t),l=_[r];return l+o*(_[r+1]-l)}));return this.linear_scale.v_compute(l)}invert(e){return e}v_invert(e){return new Float64Array(e)}}r.LinearInterpolationScale=_,i=_,_.__name__=\"LinearInterpolationScale\",i.internal((({Float:e,Arrayable:n,Ref:r})=>({binning:[n(e)],linear_scale:[r(o.LinearScale)]})))},\n function _(a,n,e,g,R){g(),R(\"DataRange\",a(107).DataRange),R(\"DataRange1d\",a(106).DataRange1d),R(\"FactorRange\",a(109).FactorRange),R(\"Range\",a(98).Range),R(\"Range1d\",a(99).Range1d)},\n function _(a,t,o,e,i){e();var u=a(174);i(\"Sizeable\",u.Sizeable),i(\"SizingPolicy\",u.SizingPolicy);var l=a(175);i(\"Layoutable\",l.Layoutable),i(\"ContentLayoutable\",l.ContentLayoutable),i(\"TextLayout\",l.TextLayout),i(\"FixedLayout\",l.FixedLayout);var n=a(257);i(\"HStack\",n.HStack),i(\"VStack\",n.VStack);var y=a(258);i(\"Grid\",y.Grid),i(\"Row\",y.Row),i(\"Column\",y.Column)},\n function _(t,e,h,i,r){i();const s=t(175),o=t(64),{max:n,round:c}=Math;class a extends s.Layoutable{constructor(){super(...arguments),this.children=[]}*[Symbol.iterator](){yield*this.children}}h.Stack=a,a.__name__=\"Stack\";class l extends a{_measure(t){let e=0,h=0;for(const t of this.children){const i=t.measure({width:0,height:0});e+=i.width,h=n(h,i.height)}return{width:e,height:h}}_set_geometry(t,e){if(super._set_geometry(t,e),t.is_empty)for(const t of this.children)t.set_geometry(new o.BBox);else{const e=this.absolute?t.top:0;let h=this.absolute?t.left:0;const{height:i}=t;for(const t of this.children){const{width:r}=t.measure({width:0,height:0});t.set_geometry(new o.BBox({left:h,width:r,top:e,height:i})),h+=r}}}}h.HStack=l,l.__name__=\"HStack\";class d extends a{_measure(t){let e=0,h=0;for(const t of this.children){const i=t.measure({width:0,height:0});e=n(e,i.width),h+=i.height}return{width:e,height:h}}_set_geometry(t,e){if(super._set_geometry(t,e),t.is_empty)for(const t of this.children)t.set_geometry(new o.BBox);else{const e=this.absolute?t.left:0;let h=this.absolute?t.top:0;const{width:i}=t;for(const t of this.children){const{height:r}=t.measure({width:0,height:0});t.set_geometry(new o.BBox({top:h,height:r,left:e,width:i})),h+=r}}}}h.VStack=d,d.__name__=\"VStack\";class g extends s.Layoutable{constructor(){super(...arguments),this.children=[]}*[Symbol.iterator](){yield*this.children}_measure(t){const{width_policy:e,height_policy:h}=this.sizing,{min:i,max:r}=Math;let s=0,o=0;for(const e of this.children){const{width:h,height:i}=e.measure(t);s=r(s,h),o=r(o,i)}return{width:(()=>{const{width:h}=this.sizing;if(t.width==1/0)return\"fixed\"==e?h??s:s;switch(e){case\"fixed\":return h??s;case\"min\":return s;case\"fit\":return null!=h?i(t.width,h):t.width;case\"max\":return null!=h?r(t.width,h):t.width}})(),height:(()=>{const{height:e}=this.sizing;if(t.height==1/0)return\"fixed\"==h?e??o:o;switch(h){case\"fixed\":return e??o;case\"min\":return o;case\"fit\":return null!=e?i(t.height,e):t.height;case\"max\":return null!=e?r(t.height,e):t.height}})()}}_set_geometry(t,e){if(super._set_geometry(t,e),t.is_empty)for(const t of this.children)t.set_geometry(new o.BBox);else{const e=this.absolute?t:t.relative(),{left:h,right:i,top:r,bottom:s}=e,n=c(e.vcenter),a=c(e.hcenter);for(const e of this.children){const{margin:c,halign:l=\"start\",valign:d=\"start\"}=e.sizing,{width:g,height:u,inner:_}=e.measure(t),w=(()=>{switch(`${d}_${l}`){case\"start_start\":return new o.BBox({left:h+c.left,top:r+c.top,width:g,height:u});case\"start_center\":return new o.BBox({hcenter:a,top:r+c.top,width:g,height:u});case\"start_end\":return new o.BBox({right:i-c.right,top:r+c.top,width:g,height:u});case\"center_start\":return new o.BBox({left:h+c.left,vcenter:n,width:g,height:u});case\"center_center\":return new o.BBox({hcenter:a,vcenter:n,width:g,height:u});case\"center_end\":return new o.BBox({right:i-c.right,vcenter:n,width:g,height:u});case\"end_start\":return new o.BBox({left:h+c.left,bottom:s-c.bottom,width:g,height:u});case\"end_center\":return new o.BBox({hcenter:a,bottom:s-c.bottom,width:g,height:u});case\"end_end\":return new o.BBox({right:i-c.right,bottom:s-c.bottom,width:g,height:u})}})(),m=null==_?w:new o.BBox({left:w.left+_.left,top:w.top+_.top,right:w.right-_.right,bottom:w.bottom-_.bottom});e.set_geometry(w,m)}}}}h.NodeLayout=g,g.__name__=\"NodeLayout\"},\n function _(t,i,s,e,o){e();const n=t(174),r=t(175),h=t(8),l=t(64),c=t(10),{max:a,round:p}=Math;class g{constructor(t){this._map=new Map,this.def=t}get(t){let i=this._map.get(t);return void 0===i&&(i=this.def(),this._map.set(t,i)),i}apply(t,i){const s=this.get(t);this._map.set(t,i(s))}}s.DefaultMap=g,g.__name__=\"DefaultMap\";class _{constructor(){this._items=[],this._nrows=0,this._ncols=0}get size(){return this._items.length}get nrows(){return this._nrows}get ncols(){return this._ncols}add(t,i){const{r1:s,c1:e}=t;this._nrows=a(this._nrows,s+1),this._ncols=a(this._ncols,e+1),this._items.push({span:t,data:i})}at(t,i){return this._items.filter((({span:s})=>s.r0<=t&&t<=s.r1&&s.c0<=i&&i<=s.c1)).map((({data:t})=>t))}row(t){return this._items.filter((({span:i})=>i.r0<=t&&t<=i.r1)).map((({data:t})=>t))}col(t){return this._items.filter((({span:i})=>i.c0<=t&&t<=i.c1)).map((({data:t})=>t))}*[Symbol.iterator](){yield*this._items}foreach(t){for(const{span:i,data:s}of this._items)t(i,s)}map(t){const i=new _;for(const{span:s,data:e}of this._items)i.add(s,t(s,e));return i}}s.Container=_,_.__name__=\"Container\";class f extends r.Layoutable{*[Symbol.iterator](){for(const{layout:t}of this.items)yield t}constructor(t=[]){super(),this.rows=\"auto\",this.cols=\"auto\",this.spacing=0,this.items=t}is_width_expanding(){if(super.is_width_expanding())return!0;if(\"fixed\"==this.sizing.width_policy)return!1;const{cols:t}=this._state;return(0,c.some)(t,(t=>\"max\"==t.policy))}is_height_expanding(){if(super.is_height_expanding())return!0;if(\"fixed\"==this.sizing.height_policy)return!1;const{rows:t}=this._state;return(0,c.some)(t,(t=>\"max\"==t.policy))}_init(){super._init();const t=new _;for(const{layout:i,row:s,col:e,row_span:o=1,col_span:n=1}of this.items)if(i.sizing.visible){const r=s,h=e,l=s+o-1,c=e+n-1;t.add({r0:r,c0:h,r1:l,c1:c},i)}const{nrows:i,ncols:s}=t,e=new Array(i);for(let s=0;s{const t=(0,h.isPlainObject)(this.rows)?this.rows[s]??this.rows[\"*\"]:this.rows;return null==t?{policy:\"auto\"}:(0,h.isNumber)(t)?{policy:\"fixed\",height:t}:(0,h.isString)(t)?{policy:t}:t})(),o=i.align??\"auto\";\"fixed\"==i.policy?e[s]={policy:\"fixed\",height:i.height,align:o}:\"min\"==i.policy?e[s]={policy:\"min\",align:o}:\"fit\"==i.policy||\"max\"==i.policy?e[s]={policy:i.policy,flex:i.flex??1,align:o}:(0,c.some)(t.row(s),(t=>t.is_height_expanding()))?e[s]={policy:\"max\",flex:1,align:o}:e[s]={policy:\"min\",align:o}}const o=new Array(s);for(let i=0;i{const t=(0,h.isPlainObject)(this.cols)?this.cols[i]??this.cols[\"*\"]:this.cols;return null==t?{policy:\"auto\"}:(0,h.isNumber)(t)?{policy:\"fixed\",width:t}:(0,h.isString)(t)?{policy:t}:t})(),e=s.align??\"auto\";\"fixed\"==s.policy?o[i]={policy:\"fixed\",width:s.width,align:e}:\"min\"==s.policy?o[i]={policy:\"min\",align:e}:\"fit\"==s.policy||\"max\"==s.policy?o[i]={policy:s.policy,flex:s.flex??1,align:e}:(0,c.some)(t.col(i),(t=>t.is_width_expanding()))?o[i]={policy:\"max\",flex:1,align:e}:o[i]={policy:\"min\",align:e}}const[n,r]=(0,h.isNumber)(this.spacing)?[this.spacing,this.spacing]:this.spacing;this._state={items:t,nrows:i,ncols:s,rows:e,cols:o,rspacing:n,cspacing:r}}_measure_totals(t,i){const{nrows:s,ncols:e,rspacing:o,cspacing:n}=this._state;return{height:(0,c.sum)(t)+(s-1)*o,width:(0,c.sum)(i)+(e-1)*n}}_measure_cells(t){const{items:i,nrows:s,ncols:e,rows:o,cols:r,rspacing:h,cspacing:l}=this._state,c=new Array(s);for(let t=0;t{const{r0:e,c0:_,r1:d,c1:w}=i,m=(d-e)*h,u=(w-_)*l;let y=0;for(let i=e;i<=d;i++)y+=t(i,_).height;y+=m;let x=0;for(let i=_;i<=w;i++)x+=t(e,i).width;x+=u;const z=s.measure({width:x,height:y});f.add(i,{layout:s,size_hint:z});const b=new n.Sizeable(z).grow_by(s.sizing.margin);b.height-=m,b.width-=u;const B=[];for(let t=e;t<=d;t++){const i=o[t];\"fixed\"==i.policy?b.height-=i.height:B.push(t)}if(b.height>0){const t=p(b.height/B.length);for(const i of B)c[i]=a(c[i],t)}const S=[];for(let t=_;t<=w;t++){const i=r[t];\"fixed\"==i.policy?b.width-=i.width:S.push(t)}if(b.width>0){const t=p(b.width/S.length);for(const i of S)g[i]=a(g[i],t)}}));return{size:this._measure_totals(c,g),row_heights:c,col_widths:g,size_hints:f}}_measure_grid(t){const{nrows:i,ncols:s,rows:e,cols:o,rspacing:n,cspacing:r}=this._state,h=this._measure_cells(((t,i)=>{const s=e[t],n=o[i];return{width:\"fixed\"==n.policy?n.width:1/0,height:\"fixed\"==s.policy?s.height:1/0}}));let l;l=\"fixed\"==this.sizing.height_policy&&null!=this.sizing.height?this.sizing.height:t.height!=1/0&&this.is_height_expanding()?t.height:h.size.height;let c,g=0;for(let t=0;t0)for(let t=0;ti?i:e,t--}}}c=\"fixed\"==this.sizing.width_policy&&null!=this.sizing.width?this.sizing.width:t.width!=1/0&&this.is_width_expanding()?t.width:h.size.width;let _=0;for(let t=0;t0)for(let t=0;ts?s:o,t--}}}const{row_heights:f,col_widths:d,size_hints:w}=this._measure_cells(((t,i)=>({width:h.col_widths[i],height:h.row_heights[t]})));return{size:this._measure_totals(f,d),row_heights:f,col_widths:d,size_hints:w}}_measure(t){const{size:i}=this._measure_grid(t);return i}_set_geometry(t,i){super._set_geometry(t,i);const{nrows:s,ncols:e,rspacing:o,cspacing:n}=this._state,{row_heights:r,col_widths:h,size_hints:c}=this._measure_grid(t),_=this._state.rows.map(((t,i)=>({...t,top:0,height:r[i],get bottom(){return this.top+this.height}}))),f=this._state.cols.map(((t,i)=>({...t,left:0,width:h[i],get right(){return this.left+this.width}}))),d=c.map(((t,i)=>({...i,outer:new l.BBox,inner:new l.BBox})));for(let i=0,e=this.absolute?t.top:this.position.top;i{const{layout:h,size_hint:c}=r,{sizing:a}=h,{width:g,height:d}=c,w=function(t,i){let s=(i-t)*n;for(let e=t;e<=i;e++)s+=f[e].width;return s}(i,e),m=function(t,i){let s=(i-t)*o;for(let e=t;e<=i;e++)s+=_[e].height;return s}(t,s),u=i==e&&\"auto\"!=f[i].align?f[i].align:a.halign,y=t==s&&\"auto\"!=_[t].align?_[t].align:a.valign;let x=f[i].left;\"start\"==u?x+=a.margin.left:\"center\"==u?x+=p((w-g)/2):\"end\"==u&&(x+=w-a.margin.right-g);let z=_[t].top;\"start\"==y?z+=a.margin.top:\"center\"==y?z+=p((m-d)/2):\"end\"==y&&(z+=m-a.margin.bottom-d),r.outer=new l.BBox({left:x,top:z,width:g,height:d})}));const w=_.map((()=>({start:new g((()=>0)),end:new g((()=>0))}))),m=f.map((()=>({start:new g((()=>0)),end:new g((()=>0))})));d.foreach((({r0:t,c0:i,r1:s,c1:e},{size_hint:o,outer:n})=>{const{inner:r}=o;null!=r&&(w[t].start.apply(n.top,(t=>a(t,r.top))),w[s].end.apply(_[s].bottom-n.bottom,(t=>a(t,r.bottom))),m[i].start.apply(n.left,(t=>a(t,r.left))),m[e].end.apply(f[e].right-n.right,(t=>a(t,r.right))))})),d.foreach((({r0:t,c0:i,r1:s,c1:e},o)=>{const{size_hint:n,outer:r}=o,h=t=>{const i=this.absolute?r:r.relative(),s=i.left+t.left,e=i.top+t.top,o=i.right-t.right,n=i.bottom-t.bottom;return new l.BBox({left:s,top:e,right:o,bottom:n})};if(null!=n.inner){let l=h(n.inner);const c=w[t].start.get(r.top),a=w[s].end.get(_[s].bottom-r.bottom),p=m[i].start.get(r.left),g=m[e].end.get(f[e].right-r.right);try{l=h({top:c,bottom:a,left:p,right:g})}catch{}o.inner=l}else o.inner=r})),d.foreach(((t,{layout:i,outer:s,inner:e})=>{i.set_geometry(s,e)}))}}s.Grid=f,f.__name__=\"Grid\";class d extends f{constructor(t){super(),this.items=t.map(((t,i)=>({layout:t,row:0,col:i}))),this.rows=\"fit\"}}s.Row=d,d.__name__=\"Row\";class w extends f{constructor(t){super(),this.items=t.map(((t,i)=>({layout:t,row:i,col:0}))),this.cols=\"fit\"}}s.Column=w,w.__name__=\"Column\"},\n function _(t,e,i,h,o){h();const n=t(174),s=t(175),r=t(64);class _ extends s.Layoutable{constructor(){super(...arguments),this.aligns={left:!0,right:!0,top:!0,bottom:!0},this.min_border={left:0,top:0,right:0,bottom:0},this.padding={left:0,top:0,right:0,bottom:0},this.center_border_width=0}*[Symbol.iterator](){yield this.top_panel,yield this.bottom_panel,yield this.left_panel,yield this.right_panel,yield this.center_panel}_measure(t){t=new n.Sizeable({width:\"fixed\"==this.sizing.width_policy||t.width==1/0?this.sizing.width:t.width,height:\"fixed\"==this.sizing.height_policy||t.height==1/0?this.sizing.height:t.height});const e=this.left_panel.measure({width:0,height:t.height}),i=Math.max(e.width,this.min_border.left)+this.padding.left,h=this.right_panel.measure({width:0,height:t.height}),o=Math.max(h.width,this.min_border.right)+this.padding.right,s=this.top_panel.measure({width:t.width,height:0}),r=Math.max(s.height,this.min_border.top)+this.padding.top,_=this.bottom_panel.measure({width:t.width,height:0}),g=Math.max(_.height,this.min_border.bottom)+this.padding.bottom,l=new n.Sizeable(t).shrink_by({left:i,right:o,top:r,bottom:g}),a=this.center_panel.measure(l);return{width:i+a.width+o,height:r+a.height+g,inner:{left:i,right:o,top:r,bottom:g},align:(()=>{const{width_policy:t,height_policy:e}=this.center_panel.sizing;return{...this.aligns,fixed_width:\"fixed\"==t,fixed_height:\"fixed\"==e}})()}}_set_geometry(t,e){if(super._set_geometry(t,e),this.sizing.visible){this.center_panel.set_geometry(e);const i=this.left_panel.measure({width:0,height:t.height}),h=this.right_panel.measure({width:0,height:t.height}),o=this.top_panel.measure({width:t.width,height:0}),n=this.bottom_panel.measure({width:t.width,height:0}),{left:s,top:_,right:g,bottom:l}=e;this.top_panel.set_geometry(new r.BBox({left:s,right:g,bottom:_,height:o.height})),this.bottom_panel.set_geometry(new r.BBox({left:s,right:g,top:l,height:n.height})),this.left_panel.set_geometry(new r.BBox({top:_,bottom:l,right:s,width:i.width})),this.right_panel.set_geometry(new r.BBox({top:_,bottom:l,left:g,width:h.width}));const a=e.shrink_by(this.center_border_width);if(null!=this.inner_top_panel){const{left:t,right:e,top:i,width:h}=a,o=this.inner_top_panel.measure({width:h,height:0});this.inner_top_panel.set_geometry(new r.BBox({left:t,right:e,top:i,height:o.height}))}if(null!=this.inner_bottom_panel){const{left:t,right:e,bottom:i,width:h}=a,o=this.inner_bottom_panel.measure({width:h,height:0});this.inner_bottom_panel.set_geometry(new r.BBox({left:t,right:e,bottom:i,height:o.height}))}if(null!=this.inner_left_panel){const{top:t,bottom:e,left:i,height:h}=a,o=this.inner_left_panel.measure({width:0,height:h});this.inner_left_panel.set_geometry(new r.BBox({top:t,bottom:e,left:i,width:o.width}))}if(null!=this.inner_right_panel){const{top:t,bottom:e,right:i,height:h}=a,o=this.inner_right_panel.measure({width:0,height:h});this.inner_right_panel.set_geometry(new r.BBox({top:t,bottom:e,right:i,width:o.width}))}}else this.center_panel.set_geometry(new r.BBox),this.top_panel.set_geometry(new r.BBox),this.bottom_panel.set_geometry(new r.BBox),this.left_panel.set_geometry(new r.BBox),this.right_panel.set_geometry(new r.BBox),this.inner_top_panel?.set_geometry(new r.BBox),this.inner_bottom_panel?.set_geometry(new r.BBox),this.inner_left_panel?.set_geometry(new r.BBox),this.inner_right_panel?.set_geometry(new r.BBox)}}i.BorderLayout=_,_.__name__=\"BorderLayout\"},\n function _(e,s,_,i,l){var t;i();const o=e(1),r=e(261),p=o.__importStar(e(80));class h extends r.UpperLowerView{_paint_data(e){e.beginPath(),e.moveTo(this._lower_sx[0],this._lower_sy[0]);for(let s=0,_=this._lower_sx.length;s<_;s++)e.lineTo(this._lower_sx[s],this._lower_sy[s]);for(let s=this._upper_sx.length-1;s>=0;s--)e.lineTo(this._upper_sx[s],this._upper_sy[s]);e.closePath(),this.visuals.fill.apply(e),e.beginPath(),e.moveTo(this._lower_sx[0],this._lower_sy[0]);for(let s=0,_=this._lower_sx.length;s<_;s++)e.lineTo(this._lower_sx[s],this._lower_sy[s]);this.visuals.line.apply(e),e.beginPath(),e.moveTo(this._upper_sx[0],this._upper_sy[0]);for(let s=0,_=this._upper_sx.length;s<_;s++)e.lineTo(this._upper_sx[s],this._upper_sy[s]);this.visuals.line.apply(e)}}_.BandView=h,h.__name__=\"BandView\";class n extends r.UpperLower{constructor(e){super(e)}}_.Band=n,t=n,n.__name__=\"Band\",t.prototype.default_view=h,t.mixins([p.Line,p.Fill]),t.override({fill_color:\"#fff9ba\",fill_alpha:.4,line_color:\"#cccccc\",line_alpha:.3})},\n function _(e,t,s,r,i){var n;r();const a=e(1),o=e(127),_=e(24),c=e(20),p=a.__importStar(e(18));class h extends o.DataAnnotationView{map_data(){const{frame:e}=this.plot_view,t=this.model.dimension,s=this.coordinates.x_scale,r=this.coordinates.y_scale,i=\"height\"==t?r:s,n=\"height\"==t?s:r,a=\"height\"==t?e.bbox.yview:e.bbox.xview,o=\"height\"==t?e.bbox.xview:e.bbox.yview,c=(()=>{switch(this.model.properties.lower.units){case\"canvas\":return new _.ScreenArray(this._lower);case\"screen\":return a.v_compute(this._lower);case\"data\":return i.v_compute(this._lower)}})(),p=(()=>{switch(this.model.properties.upper.units){case\"canvas\":return new _.ScreenArray(this._upper);case\"screen\":return a.v_compute(this._upper);case\"data\":return i.v_compute(this._upper)}})(),h=(()=>{switch(this.model.properties.base.units){case\"canvas\":return new _.ScreenArray(this._base);case\"screen\":return o.v_compute(this._base);case\"data\":return n.v_compute(this._base)}})(),[u,d]=\"height\"==t?[1,0]:[0,1],w=[c,h],l=[p,h];this._lower_sx=w[u],this._lower_sy=w[d],this._upper_sx=l[u],this._upper_sy=l[d]}}s.UpperLowerView=h,h.__name__=\"UpperLowerView\";class u extends p.CoordinateSpec{constructor(){super(...arguments),this._value=p.unset}get dimension(){return\"width\"==this.obj.dimension?\"x\":\"y\"}get units(){return this._value===p.unset?\"data\":this._value.units??\"data\"}}s.XOrYCoordinateSpec=u,u.__name__=\"XOrYCoordinateSpec\";class d extends o.DataAnnotation{constructor(e){super(e)}}s.UpperLower=d,n=d,d.__name__=\"UpperLower\",n.define((()=>({dimension:[c.Dimension,\"height\"],lower:[u,{field:\"lower\"}],upper:[u,{field:\"upper\"}],base:[u,{field:\"base\"}]})))},\n function _(t,e,o,i,r){var n,s,l;i();const a=t(1),h=t(81),_=t(51),u=t(79),c=t(106),m=a.__importStar(t(80)),b=t(20),d=t(10),p=t(64),g=t(15),f=t(11),v=t(12),w=t(9),x=t(8),y=t(185),B=a.__importStar(t(263)),N=t(187),z=a.__importStar(t(186)),C=t(59),A=t(60);o.EDGE_TOLERANCE=2.5;const{abs:V}=Math;class E extends _.Model{constructor(t){super(t)}}o.BoxInteractionHandles=E,n=E,E.__name__=\"BoxInteractionHandles\",n.define((({Ref:t,Nullable:e})=>({all:[t(u.AreaVisuals)],move:[e(t(u.AreaVisuals)),null],resize:[e(t(u.AreaVisuals)),null],sides:[e(t(u.AreaVisuals)),null],corners:[e(t(u.AreaVisuals)),null],left:[e(t(u.AreaVisuals)),null],right:[e(t(u.AreaVisuals)),null],top:[e(t(u.AreaVisuals)),null],bottom:[e(t(u.AreaVisuals)),null],top_left:[e(t(u.AreaVisuals)),null],top_right:[e(t(u.AreaVisuals)),null],bottom_left:[e(t(u.AreaVisuals)),null],bottom_right:[e(t(u.AreaVisuals)),null]})));const L=()=>new E({all:new u.AreaVisuals({fill_color:\"white\",fill_alpha:1,line_color:\"black\",line_alpha:1,hover_fill_color:\"lightgray\",hover_fill_alpha:1})});class F extends h.AnnotationView{constructor(){super(...arguments),this._bbox=new p.BBox,this._handles_views={},this[s]=!0,this._pan_state=null,this._pinch_state=null,this._is_hovered=!1}get bbox(){return this._bbox}initialize(){super.initialize(),this._update_handles()}_update_handles(){const{editable:t,use_handles:e,handles:o}=this.model;if(t&&e){const{movable:i,resizable:r}=this,n={visible:!0,resizable:\"none\",left_units:\"canvas\",right_units:\"canvas\",top_units:\"canvas\",bottom_units:\"canvas\",level:this.model.level};function s(t){return{...m.attrs_of(t,\"\",m.Line,!0),...m.attrs_of(t,\"\",m.Fill,!0),...m.attrs_of(t,\"\",m.Hatch,!0),...m.attrs_of(t,\"hover_\",m.Line,!0),...m.attrs_of(t,\"hover_\",m.Fill,!0),...m.attrs_of(t,\"hover_\",m.Hatch,!0)}}const l=o,a={area:s(l.move??l.all),left:s(l.left??l.sides??l.resize??l.all),right:s(l.right??l.sides??l.resize??l.all),top:s(l.top??l.sides??l.resize??l.all),bottom:s(l.bottom??l.sides??l.resize??l.all),top_left:s(l.top_left??l.corners??l.resize??l.all),top_right:s(l.top_right??l.corners??l.resize??l.all),bottom_left:s(l.bottom_left??l.corners??l.resize??l.all),bottom_right:s(l.bottom_right??l.corners??l.resize??l.all)},{tl_cursor:h,tr_cursor:_,bl_cursor:u,br_cursor:c,ew_cursor:b,ns_cursor:d}=this.model;this._handles={area:i?new R({...n,...a.area,movable:this.model.movable}):null,left:r.left?new R({...n,...a.left,in_cursor:b}):null,right:r.right?new R({...n,...a.right,in_cursor:b}):null,top:r.top?new R({...n,...a.top,in_cursor:d}):null,bottom:r.bottom?new R({...n,...a.bottom,in_cursor:d}):null,top_left:r.top_left?new R({...n,...a.top_left,in_cursor:h}):null,top_right:r.top_right?new R({...n,...a.top_right,in_cursor:_}):null,bottom_left:r.bottom_left?new R({...n,...a.bottom_left,in_cursor:u}):null,bottom_right:r.bottom_right?new R({...n,...a.bottom_right,in_cursor:c}):null}}else this._handles={area:null,left:null,right:null,top:null,bottom:null,top_left:null,top_right:null,bottom_left:null,bottom_right:null}}get computed_renderers(){return[...super.computed_renderers,...(0,w.values)(this._handles).filter(x.non_null)]}connect_signals(){super.connect_signals();const{editable:t,use_handles:e,handles:o,resizable:i,movable:r}=this.model.properties;this.on_change([t,e,o,i,r],(async()=>{this._update_handles(),await this._update_renderers()})),this.connect(this.model.change,(()=>this.request_paint()))}async _build_renderers(){const t=await super._build_renderers(),e=t=>null!=t?this._renderer_views.get(t):void 0;return this._handles_views={area:e(this._handles.area),left:e(this._handles.left),right:e(this._handles.right),top:e(this._handles.top),bottom:e(this._handles.bottom),top_left:e(this._handles.top_left),top_right:e(this._handles.top_right),bottom_left:e(this._handles.bottom_left),bottom_right:e(this._handles.bottom_right)},t}bounds(){const{left:t,left_units:e,right:o,right_units:i,top:r,top_units:n,bottom:s,bottom_units:l}=this.model,a=\"data\"==e&&!(t instanceof A.Coordinate),h=\"data\"==i&&!(o instanceof A.Coordinate),_=\"data\"==n&&!(r instanceof A.Coordinate),u=\"data\"==l&&!(s instanceof A.Coordinate),[c,m]=a&&h?t<=o?[t,o]:[o,t]:a?[t,t]:h?[o,o]:[NaN,NaN],[b,d]=_&&u?r<=s?[r,s]:[s,r]:_?[r,r]:u?[s,s]:[NaN,NaN];return{x0:c,x1:m,y0:b,y1:d}}log_bounds(){return(0,p.empty)()}get mappers(){function t(t,e,o,i){switch(t){case\"canvas\":return i;case\"screen\":return o;case\"data\":return e}}const e=this.model,{x_scale:o,y_scale:i}=this.coordinates,{x_view:r,y_view:n}=this.plot_view.frame.bbox,{x_screen:s,y_screen:l}=this.plot_view.canvas.bbox;return{left:t(e.left_units,o,r,s),right:t(e.right_units,o,r,s),top:t(e.top_units,i,n,l),bottom:t(e.bottom_units,i,n,l)}}get border_radius(){return z.border_radius(this.model.border_radius)}compute_geometry(){super.compute_geometry();const t=(()=>{const t=(t,e,o)=>e instanceof A.Coordinate?this.resolve_as_scalar(e,t):o.compute(e),{left:e,right:o,top:i,bottom:r}=this.model,{mappers:n}=this;return p.BBox.from_lrtb({left:t(\"x\",e,n.left),right:t(\"x\",o,n.right),top:t(\"y\",i,n.top),bottom:t(\"y\",r,n.bottom)})})();this._bbox=t;const e=10,o=10;function i(t,e){const{left:o,right:i,top:r,bottom:n}=e;t?.setv({left:o,right:i,top:r,bottom:n},{silent:!0})}i(this._handles.area,new p.BBox({...t.center,width:e,height:o,origin:\"center\"})),i(this._handles.left,new p.BBox({...t.center_left,width:e,height:o,origin:\"center\"})),i(this._handles.right,new p.BBox({...t.center_right,width:e,height:o,origin:\"center\"})),i(this._handles.top,new p.BBox({...t.top_center,width:e,height:o,origin:\"center\"})),i(this._handles.bottom,new p.BBox({...t.bottom_center,width:e,height:o,origin:\"center\"})),i(this._handles.top_left,new p.BBox({...t.top_left,width:e,height:o,origin:\"center\"})),i(this._handles.top_right,new p.BBox({...t.top_right,width:e,height:o,origin:\"center\"})),i(this._handles.bottom_left,new p.BBox({...t.bottom_left,width:e,height:o,origin:\"center\"})),i(this._handles.bottom_right,new p.BBox({...t.bottom_right,width:e,height:o,origin:\"center\"}))}_paint(){if(!this.bbox.is_valid)return;const{_is_hovered:t,visuals:e}=this,o=t&&e.hover_fill.doit?e.hover_fill:e.fill,i=t&&e.hover_hatch.doit?e.hover_hatch:e.hatch,r=t&&e.hover_line.doit?e.hover_line:e.line,{ctx:n}=this.layer;n.save();const{inverted:s}=this.model;if(s){n.beginPath();const t=this.layout??this.plot_view.frame,{x:e,y:s,width:l,height:a}=t.bbox;n.rect(e,s,l,a),(0,N.round_rect)(n,this.bbox,this.border_radius),o.apply(n,\"evenodd\"),i.apply(n,\"evenodd\"),n.beginPath(),(0,N.round_rect)(n,this.bbox,this.border_radius),r.apply(n)}else n.beginPath(),(0,N.round_rect)(n,this.bbox,this.border_radius),o.apply(n),i.apply(n),r.apply(n);n.restore()}interactive_bbox(){const t=this.model.line_width+o.EDGE_TOLERANCE;return this.bbox.grow_by(t)}interactive_hit(t,e){if(!this.model.visible)return!1;return this.interactive_bbox().contains(t,e)}_hit_test(t,e){const{left:i,right:r,bottom:n,top:s}=this.bbox,l=Math.max(o.EDGE_TOLERANCE,this.model.line_width/2),a=V(i-t),h=V(r-t),_=V(s-e),u=V(n-e),c={left:a{if(!m[o])return!1;const r=this._handles_views[o];return null!=r?r.bbox.contains(t,e):i};return b(\"top_left\",c.top&&c.left)?\"top_left\":b(\"top_right\",c.top&&c.right)?\"top_right\":b(\"bottom_left\",c.bottom&&c.left)?\"bottom_left\":b(\"bottom_right\",c.bottom&&c.right)?\"bottom_right\":b(\"left\",c.left)?\"left\":b(\"right\",c.right)?\"right\":b(\"top\",c.top)?\"top\":b(\"bottom\",c.bottom)?\"bottom\":b(\"area\",this.bbox.contains(t,e))?\"area\":null}get resizable(){const{resizable:t}=this.model,e=\"left\"==t||\"x\"==t||\"all\"==t,o=\"right\"==t||\"x\"==t||\"all\"==t,i=\"top\"==t||\"y\"==t||\"all\"==t,r=\"bottom\"==t||\"y\"==t||\"all\"==t;return{left:e,right:o,top:i,bottom:r,top_left:i&&e,top_right:i&&o,bottom_left:r&&e,bottom_right:r&&o}}get movable(){return\"none\"!=this.model.movable}_hittable(){const{left:t,right:e,top:o,bottom:i}=this.resizable;return{top_left:o&&t,top_right:o&&e,bottom_left:i&&t,bottom_right:i&&e,left:t,right:e,top:o,bottom:i,area:this.movable}}_can_hit(t){const{left:e,right:o,top:i,bottom:r}=this.resizable;switch(t){case\"top_left\":return i&&e;case\"top_right\":return i&&o;case\"bottom_left\":return r&&e;case\"bottom_right\":return r&&o;case\"left\":return e;case\"right\":return o;case\"top\":return i;case\"bottom\":return r;case\"area\":return this.movable}}on_pan_start(t){if(this.model.visible&&this.model.editable){const{sx:e,sy:o}=t,i=this._hit_test(e,o);if(null!=i&&this._can_hit(i))return this._pan_state={bbox:this.bbox.clone(),target:i},this.model.pan.emit([\"pan:start\",t.modifiers]),!0}return!1}on_pan(t){(0,v.assert)(null!=this._pan_state);const{mappers:e}=this,o=(t,e,o)=>e instanceof A.Coordinate?this.resolve_as_scalar(e,t):null==e?NaN:o.compute(e),i=p.BBox.from_lrtb({left:o(\"x\",this.model.left_limit,e.left),right:o(\"x\",this.model.right_limit,e.right),top:o(\"y\",this.model.top_limit,e.top),bottom:o(\"y\",this.model.bottom_limit,e.bottom)}),[r,n,s,l]=(()=>{const{dx:e,dy:o}=t,{target:i}=this._pan_state,{symmetric:r}=this.model,[n,s]=r?[-e,-o]:[0,0];switch(i){case\"top_left\":return[e,n,o,s];case\"top_right\":return[n,e,o,s];case\"bottom_left\":return[e,n,s,o];case\"bottom_right\":return[n,e,s,o];case\"left\":return[e,n,0,0];case\"right\":return[n,e,0,0];case\"top\":return[0,0,o,s];case\"bottom\":return[0,0,s,o];case\"area\":switch(this.model.movable){case\"both\":return[e,e,o,o];case\"x\":return[e,e,0,0];case\"y\":return[0,0,o,o];case\"none\":return[0,0,0,0]}}})(),a=(()=>{const t=(t,e)=>(0,d.min)([t,e]),e=t=>t<0?-1:t>0?1:0,{bbox:o}=this._pan_state;let{left:a,right:h,left_sign:_,right_sign:u}=(()=>{const t=o.left+r,i=o.right+n,s=e(r),l=e(n);return t<=i?{left:t,right:i,left_sign:s,right_sign:l}:{left:i,right:t,left_sign:l,right_sign:s}})(),{top:c,bottom:m,top_sign:b,bottom_sign:g}=(()=>{const t=o.top+s,i=o.bottom+l,r=e(s),n=e(l);return t<=i?{top:t,bottom:i,top_sign:r,bottom_sign:n}:{top:i,bottom:t,top_sign:n,bottom_sign:r}})();const f=a-i.left,v=i.right-h,w=t(f<0?f:NaN,v<0?v:NaN);isFinite(w)&&w<0&&(a+=-_*-w,h+=-u*-w);const x=c-i.top,y=i.bottom-m,B=t(x<0?x:NaN,y<0?y:NaN);return isFinite(B)&&B<0&&(c+=-b*-B,m+=-g*-B),p.BBox.from_lrtb({left:a,right:h,top:c,bottom:m})})(),{min_width:h,min_height:_,max_width:u,max_height:c}=this.model,{left:m,right:b,top:g,bottom:w}=this.model,x={left:e.left.invert(a.left),right:e.right.invert(a.right),top:e.top.invert(a.top),bottom:e.bottom.invert(a.bottom)};if(0{const{scale:e}=t,{bbox:o}=this._pinch_state,{left:i,top:r,right:n,bottom:s,width:l,height:a}=o,h=l*(e-1),_=a*(e-1),{resizable:u}=this,c=u.left?-h/2:0,m=u.right?h/2:0,b=u.top?-_/2:0,d=u.bottom?_/2:0;return p.BBox.from_lrtb({left:i+c,right:n+m,top:r+b,bottom:s+d})})(),o=(()=>{const{left:t,right:o,top:i,bottom:r}=this.model,{mappers:n}=this;return{left:t instanceof A.Coordinate?t:n.left.invert(e.left),right:o instanceof A.Coordinate?o:n.right.invert(e.right),top:i instanceof A.Coordinate?i:n.top.invert(e.top),bottom:r instanceof A.Coordinate?r:n.bottom.invert(e.bottom)}})();this.model.update(o),this.model.pan.emit([\"pan\",t.modifiers])}on_pinch_end(t){this._pinch_state=null,this.model.pan.emit([\"pan:end\",t.modifiers])}get _has_hover(){const{hover_line:t,hover_fill:e,hover_hatch:o}=this.visuals;return t.doit||e.doit||o.doit}on_enter(t){const{_has_hover:e}=this;return e&&(this._is_hovered=!0,this.request_paint()),e}on_move(t){}on_leave(t){this._has_hover&&(this._is_hovered=!1,this.request_paint())}cursor(t,e){const o=this._pan_state?.target??this._hit_test(t,e);if(null==o||!this._can_hit(o))return null;const{tl_cursor:i,tr_cursor:r,bl_cursor:n,br_cursor:s,ew_cursor:l,ns_cursor:a,in_cursor:h}=this.model;switch(o){case\"top_left\":return null==this._handles.top_left?i:null;case\"top_right\":return null==this._handles.top_right?r:null;case\"bottom_left\":return null==this._handles.bottom_left?n:null;case\"bottom_right\":return null==this._handles.bottom_right?s:null;case\"left\":return null==this._handles.left?l:null;case\"right\":return null==this._handles.right?l:null;case\"top\":return null==this._handles.top?a:null;case\"bottom\":return null==this._handles.bottom?a:null;case\"area\":if(null!=this._handles.area)return null;switch(this.model.movable){case\"both\":return h;case\"x\":return l;case\"y\":return a;case\"none\":return null}}}}o.BoxAnnotationView=F,s=c.auto_ranged,F.__name__=\"BoxAnnotationView\";class R extends h.Annotation{constructor(t){super(t),this.pan=new g.Signal(this,\"pan\"),this.nodes=(()=>{const t=new Map,e=e=>{let o=t.get(e);return void 0===o&&t.set(e,o=new C.Node({target:this,symbol:e})),o};return{get left(){return e(\"left\")},get right(){return e(\"right\")},get top(){return e(\"top\")},get bottom(){return e(\"bottom\")},get top_left(){return e(\"top_left\")},get top_center(){return e(\"top_center\")},get top_right(){return e(\"top_right\")},get center_left(){return e(\"center_left\")},get center(){return e(\"center\")},get center_right(){return e(\"center_right\")},get bottom_left(){return e(\"bottom_left\")},get bottom_center(){return e(\"bottom_center\")},get bottom_right(){return e(\"bottom_right\")},get width(){return e(\"width\")},get height(){return e(\"height\")}}})()}clone(t){return super.clone(t)}update({left:t,right:e,top:o,bottom:i}){this.setv({left:t,right:e,top:o,bottom:i,visible:!0})}clear(){this.visible=!1}}o.BoxAnnotation=R,l=R,R.__name__=\"BoxAnnotation\",l.prototype.default_view=F,l.mixins([m.Line,m.Fill,m.Hatch,[\"hover_\",m.Line],[\"hover_\",m.Fill],[\"hover_\",m.Hatch]]),l.define((({Bool:t,Float:e,Ref:o,Or:i,NonNegative:r,Positive:n})=>({top:[i(e,o(A.Coordinate)),()=>new C.Node({target:\"frame\",symbol:\"top\"})],bottom:[i(e,o(A.Coordinate)),()=>new C.Node({target:\"frame\",symbol:\"bottom\"})],left:[i(e,o(A.Coordinate)),()=>new C.Node({target:\"frame\",symbol:\"left\"})],right:[i(e,o(A.Coordinate)),()=>new C.Node({target:\"frame\",symbol:\"right\"})],top_units:[b.CoordinateUnits,\"data\"],bottom_units:[b.CoordinateUnits,\"data\"],left_units:[b.CoordinateUnits,\"data\"],right_units:[b.CoordinateUnits,\"data\"],top_limit:[B.Limit,null],bottom_limit:[B.Limit,null],left_limit:[B.Limit,null],right_limit:[B.Limit,null],min_width:[r(e),0],min_height:[r(e),0],max_width:[n(e),1/0],max_height:[n(e),1/0],border_radius:[y.BorderRadius,0],editable:[t,!1],resizable:[B.Resizable,\"all\"],movable:[B.Movable,\"both\"],symmetric:[t,!1],use_handles:[t,!1],handles:[o(E),L],inverted:[t,!1]}))),l.internal((({Str:t})=>({tl_cursor:[t,\"nwse-resize\"],tr_cursor:[t,\"nesw-resize\"],bl_cursor:[t,\"nesw-resize\"],br_cursor:[t,\"nwse-resize\"],ew_cursor:[t,\"ew-resize\"],ns_cursor:[t,\"ns-resize\"],in_cursor:[t,\"move\"]}))),l.override({fill_color:\"#fff9ba\",fill_alpha:.4,line_color:\"#cccccc\",line_alpha:.3,hover_fill_color:null,hover_fill_alpha:.4,hover_line_color:null,hover_line_alpha:.3})},\n function _(t,o,e,n,l){n();const r=t(21),m=t(59);e.Corner=(0,r.Enum)(\"top_left\",\"top_right\",\"bottom_left\",\"bottom_right\"),e.Edge=(0,r.Enum)(\"left\",\"right\",\"top\",\"bottom\"),e.HitTarget=(0,r.Enum)(...e.Corner,...e.Edge,\"area\"),e.Resizable=(0,r.Enum)(\"none\",\"left\",\"right\",\"top\",\"bottom\",\"x\",\"y\",\"all\"),e.Movable=(0,r.Enum)(\"none\",\"x\",\"y\",\"both\"),e.Limit=(0,r.Nullable)((0,r.Or)(r.Float,(0,r.Ref)(m.Node)))},\n function _(e,i,n,t,a){var o;t();const l=e(170),r=e(265),s=e(241),_=e(215),c=e(270),h=e(255),p=e(253),g=e(200),m=e(10),d=e(12);class u extends l.BaseColorBarView{connect_signals(){super.connect_signals(),this.connect(this.model.properties.color_mapper.change,(async()=>{this._title_view.remove(),this._axis_view.remove(),this.initialize(),await this.lazy_initialize(),this.plot_view.invalidate_layout()})),this.connect(this.model.color_mapper.metrics_change,(()=>this._metrics_changed())),this.connect(this.model.properties.display_low.change,(()=>this._metrics_changed())),this.connect(this.model.properties.display_high.change,(()=>this._metrics_changed()))}get color_mapper(){let e=this.model.color_mapper;return e instanceof c.WeightedStackColorMapper&&(e=e.alpha_mapper),e}update_layout(){super.update_layout(),this._set_canvas_image()}_create_axis(){const{color_mapper:e}=this;return e instanceof c.CategoricalColorMapper?new r.CategoricalAxis:e instanceof c.LogColorMapper?new r.LogAxis:new r.LinearAxis}_create_formatter(){const{color_mapper:e}=this;return this._ticker instanceof g.LogTicker?new s.LogTickFormatter:e instanceof c.CategoricalColorMapper?new s.CategoricalTickFormatter:new s.BasicTickFormatter}_create_major_range(){const{color_mapper:e}=this;if(e instanceof c.CategoricalColorMapper)return new h.FactorRange({factors:e.factors});if(e instanceof c.ContinuousColorMapper){const{min:i,max:n}=this._continuous_metrics(e);return new h.Range1d({start:i,end:n})}(0,d.unreachable)()}_create_major_scale(){const{color_mapper:e}=this;return e instanceof c.LinearColorMapper?new p.LinearScale:e instanceof c.LogColorMapper?new p.LogScale:e instanceof c.ScanningColorMapper?new p.LinearInterpolationScale({binning:this._scanning_binning(e)}):e instanceof c.CategoricalColorMapper?new p.CategoricalScale:void(0,d.unreachable)()}_create_ticker(){const{color_mapper:e}=this;return e instanceof c.LogColorMapper?new g.LogTicker:e instanceof c.ScanningColorMapper?new g.BinnedTicker({mapper:e}):e instanceof c.CategoricalColorMapper?new g.CategoricalTicker:new g.BasicTicker}_continuous_metrics(e){const{display_low:i,display_high:n}=this.model;let{min:t,max:a}=e.metrics;if(null!=n&&null!=i&&n0&&(this._index_low=n,t=e.index_to_value(n))}return{min:t,max:a}}_get_major_size_factor(){return this.color_mapper.palette.length}_metrics_changed(){const e=this._major_range,i=this._major_scale,{color_mapper:n}=this;if(n instanceof c.ScanningColorMapper&&i instanceof p.LinearInterpolationScale){const e=this._scanning_binning(n);i.binning=e;const t=\"vertical\"==this.orientation,a=t?this._frame_view.y_scale:this._frame_view.x_scale;if(a instanceof p.LinearInterpolationScale){a.binning=e;const i=t?this._frame_view.y_range:this._frame_view.x_range;i instanceof h.Range1d&&(i.start=e[0],i.end=e[e.length-1])}}else if(n instanceof c.ContinuousColorMapper&&e instanceof h.Range1d){const{min:i,max:t}=this._continuous_metrics(n);e.setv({start:i,end:t})}this._set_canvas_image(),this.plot_view.request_layout()}_paint_colors(e,i){const{x:n,y:t,width:a,height:o}=i;e.save(),e.globalAlpha=this.model.scale_alpha,null!=this._image&&(e.imageSmoothingEnabled=!1,e.drawImage(this._image,n,t,a,o)),this.visuals.bar_line.doit&&(this.visuals.bar_line.set_value(e),e.strokeRect(n,t,a,o)),e.restore()}_scanning_binning(e){let{binning:i,force_low_cutoff:n}=e.metrics;const{display_high:t}=this.model;let{display_low:a}=this.model;if(n&&(null==a||e.metrics.min>a)&&(a=e.metrics.min),null!=t&&null!=a&&t0&&(this._index_low=n)}if(null!=this._index_low||null!=this._index_high){const e=null!=this._index_low?this._index_low:0,n=(null!=this._index_high?this._index_high+1:i.length-1)-e+1;if(n>0){const t=new Array(n);for(let a=0;a({color_mapper:[n(_.ColorMapper)],display_low:[e(i),null],display_high:[e(i),null]})))},\n function _(i,s,x,A,o){A(),o(\"Axis\",i(189).Axis),o(\"CategoricalAxis\",i(266).CategoricalAxis),o(\"ContinuousAxis\",i(195).ContinuousAxis),o(\"DatetimeAxis\",i(267).DatetimeAxis),o(\"LinearAxis\",i(194).LinearAxis),o(\"LogAxis\",i(268).LogAxis),o(\"MercatorAxis\",i(269).MercatorAxis)},\n function _(t,s,o,e,i){var r;e();const a=t(1),l=t(189),_=t(201),n=t(242),p=a.__importStar(t(80)),c=t(20),h=t(180),m=t(8);class u extends l.AxisView{_paint(){const{tick_coords:t,extents:s}=this,o=this.layer.ctx;super._paint(),this._draw_group_separators(o,s,t)}_draw_group_separators(t,s,o){const[e]=this.ranges,[i,r]=this.computed_bounds;if(null==e.tops||e.tops.length<2||!this.visuals.separator_line.doit)return;const a=this.dimension,l=1-a,_=[[],[]];let n=0;for(let t=0;ti&&pnew h.GraphicsBoxes(t.map((t=>(0,m.isString)(t)?new h.TextBox({text:t}):t))),_=t=>l(this.model.formatter.doFormat(t,this));if(1==t.levels){const t=_(i.major);a.push([t,r.major,this.model.major_label_orientation,this.visuals.major_label_text])}else if(2==t.levels){const t=_(i.major.map((t=>t[1])));a.push([t,r.major,this.model.major_label_orientation,this.visuals.major_label_text]),a.push([l(i.tops),r.tops,this.model.group_label_orientation,this.visuals.group_text])}else if(3==t.levels){const t=_(i.major.map((t=>t[2]))),s=i.mids.map((t=>t[1]));a.push([t,r.major,this.model.major_label_orientation,this.visuals.major_label_text]),a.push([l(s),r.mids,this.model.subgroup_label_orientation,this.visuals.subgroup_text]),a.push([l(i.tops),r.tops,this.model.group_label_orientation,this.visuals.group_text])}return a}get tick_coords(){const t=this.dimension,s=1-t,[o]=this.ranges,[e,i]=this.computed_bounds,r=this.model.ticker.get_ticks(e,i,o,this.loc),a={major:[[],[]],mids:[[],[]],tops:[[],[]],minor:[[],[]]};return a.major[t]=r.major,a.major[s]=r.major.map((()=>this.loc)),3==o.levels&&(a.mids[t]=r.mids,a.mids[s]=r.mids.map((()=>this.loc))),o.levels>1&&(a.tops[t]=r.tops,a.tops[s]=r.tops.map((()=>this.loc))),a}}o.CategoricalAxisView=u,u.__name__=\"CategoricalAxisView\";class d extends l.Axis{constructor(t){super(t)}}o.CategoricalAxis=d,r=d,d.__name__=\"CategoricalAxis\",r.prototype.default_view=u,r.mixins([[\"separator_\",p.Line],[\"group_\",p.Text],[\"subgroup_\",p.Text]]),r.define((({Float:t,Or:s})=>({group_label_orientation:[s(c.LabelOrientation,t),\"parallel\"],subgroup_label_orientation:[s(c.LabelOrientation,t),\"parallel\"]}))),r.override({ticker:()=>new _.CategoricalTicker,formatter:()=>new n.CategoricalTickFormatter,separator_line_color:\"lightgrey\",separator_line_width:2,group_text_font_style:\"bold\",group_text_font_size:\"11px\",group_text_color:\"grey\",subgroup_text_font_style:\"bold\",subgroup_text_font_size:\"11px\"})},\n function _(e,t,i,s,a){var n;s();const o=e(195),r=e(243),m=e(203);class _ extends o.ContinuousAxisView{}i.DatetimeAxisView=_,_.__name__=\"DatetimeAxisView\";class c extends o.ContinuousAxis{constructor(e){super(e)}}i.DatetimeAxis=c,n=c,c.__name__=\"DatetimeAxis\",n.prototype.default_view=_,n.override({ticker:()=>new m.DatetimeTicker,formatter:()=>new r.DatetimeTickFormatter})},\n function _(e,o,i,s,t){var n;s();const r=e(195),_=e(249),c=e(210);class a extends r.ContinuousAxisView{}i.LogAxisView=a,a.__name__=\"LogAxisView\";class u extends r.ContinuousAxis{constructor(e){super(e)}}i.LogAxis=u,n=u,u.__name__=\"LogAxis\",n.prototype.default_view=a,n.override({ticker:()=>new c.LogTicker,formatter:()=>new _.LogTickFormatter})},\n function _(e,r,t,i,a){var o;i();const s=e(189),c=e(194),n=e(250),_=e(211);class x extends s.AxisView{}t.MercatorAxisView=x,x.__name__=\"MercatorAxisView\";class d extends c.LinearAxis{constructor(e){super(e)}}t.MercatorAxis=d,o=d,d.__name__=\"MercatorAxis\",o.prototype.default_view=x,o.override({ticker:()=>new _.MercatorTicker({dimension:\"lat\"}),formatter:()=>new n.MercatorTickFormatter({dimension:\"lat\"})})},\n function _(r,o,a,p,e){p(),e(\"CategoricalColorMapper\",r(271).CategoricalColorMapper),e(\"CategoricalMarkerMapper\",r(273).CategoricalMarkerMapper),e(\"CategoricalPatternMapper\",r(274).CategoricalPatternMapper),e(\"ContinuousColorMapper\",r(214).ContinuousColorMapper),e(\"ColorMapper\",r(215).ColorMapper),e(\"LinearColorMapper\",r(275).LinearColorMapper),e(\"LogColorMapper\",r(276).LogColorMapper),e(\"ScanningColorMapper\",r(213).ScanningColorMapper),e(\"EqHistColorMapper\",r(277).EqHistColorMapper),e(\"StackColorMapper\",r(278).StackColorMapper),e(\"WeightedStackColorMapper\",r(279).WeightedStackColorMapper)},\n function _(t,o,a,r,e){var c;r();const l=t(272),s=t(215),n=t(109);class _ extends s.ColorMapper{constructor(t){super(t)}_v_compute(t,o,a,{nan_color:r}){(0,l.cat_v_compute)(t,this.factors,a,o,this.start,this.end,r)}}a.CategoricalColorMapper=_,c=_,_.__name__=\"CategoricalColorMapper\",c.define((({Float:t,Nullable:o})=>({factors:[n.FactorSeq],start:[t,0],end:[o(t),null]})))},\n function _(n,t,e,o,i){o();const c=n(13),f=n(8);function l(n,t){if(n.length!=t.length)return!1;const e=n.length;for(let o=0;ol(n,e)))}else n=(0,c.index_of)(t,_);const d=n in e?e[n]:s;o[u++]=d}}},\n function _(e,r,a,t,s){var c;t();const l=e(272),n=e(109),o=e(216),u=e(20);class i extends o.Mapper{constructor(e){super(e)}v_compute(e){const r=new Array(e.length);return(0,l.cat_v_compute)(e,this.factors,this.markers,r,this.start,this.end,this.default_value),r}}a.CategoricalMarkerMapper=i,c=i,i.__name__=\"CategoricalMarkerMapper\",c.define((({Float:e,List:r,Nullable:a})=>({factors:[n.FactorSeq],markers:[r(u.MarkerType)],start:[e,0],end:[a(e),null],default_value:[u.MarkerType,\"circle\"]})))},\n function _(t,e,a,r,n){var s;r();const c=t(272),l=t(109),o=t(216),p=t(20);class u extends o.Mapper{constructor(t){super(t)}v_compute(t){const e=new Array(t.length);return(0,c.cat_v_compute)(t,this.factors,this.patterns,e,this.start,this.end,this.default_value),e}}a.CategoricalPatternMapper=u,s=u,u.__name__=\"CategoricalPatternMapper\",s.define((({Float:t,List:e,Nullable:a})=>({factors:[l.FactorSeq],patterns:[e(p.HatchPatternType)],start:[t,0],end:[a(t),null],default_value:[p.HatchPatternType,\" \"]})))},\n function _(n,r,o,t,a){t();const e=n(214),i=n(13),s=n(11);class _ extends e.ContinuousColorMapper{constructor(n){super(n)}scan(n,r){const o=null!=this.low?this.low:(0,i.min)(n),t=null!=this.high?this.high:(0,i.max)(n);return{max:t,min:o,norm_factor:1/(t-o),normed_interval:1/r}}index_to_value(n){const r=this._scan_data;return r.min+r.normed_interval*n/r.norm_factor}value_to_index(n,r){const o=this._scan_data;if(n==o.max)return r-1;const t=(n-o.min)*o.norm_factor,a=Math.floor(t/o.normed_interval);return(0,s.clamp)(a,-1,r)}}o.LinearColorMapper=_,_.__name__=\"LinearColorMapper\"},\n function _(n,t,a,o,s){o();const r=n(214),e=n(13),i=n(11);class l extends r.ContinuousColorMapper{constructor(n){super(n)}scan(n,t){const a=null!=this.low?this.low:(0,e.min)(n),o=null!=this.high?this.high:(0,e.max)(n);return{max:o,min:a,scale:t/Math.log(o/a)}}index_to_value(n){const t=this._scan_data;return t.min*Math.exp(n/t.scale)}value_to_index(n,t){const a=this._scan_data;if(n==a.max)return t-1;if(n>a.max)return t;if(n1?d=1-n:m=!1}const g=(0,c.linspace)(d,1,n+1),w=(0,r.interpolate)(g,f,h);let b=!1;if(m){const e=(0,r.sorted_index)(w,s);s0&&(w[e-1]=s),b=!0}else w[0]=s;return w[w.length-1]=t,{min:s,max:t,binning:w,force_low_cutoff:b}}}s.EqHistColorMapper=a,o=a,a.__name__=\"EqHistColorMapper\",o.define((({Bool:e,Int:n})=>({bins:[n,65536],rescale_discrete_levels:[e,!1]})))},\n function _(o,r,a,c,e){c();const p=o(215);class t extends p.ColorMapper{constructor(o){super(o)}}a.StackColorMapper=t,t.__name__=\"StackColorMapper\"},\n function _(e,t,o,a,r){var l;a();const n=e(215),s=e(214),c=e(278),_=e(24),i=e(13),p=e(12),u=e(22);class h extends c.StackColorMapper{constructor(e){super(e)}_mix_colors(e,t,o,a){if(isNaN(a))return t;let r=0,l=0,n=0,s=0;const c=o.length;if(0!=a)for(let t=0;t({alpha_mapper:[a(s.ContinuousColorMapper)],color_baseline:[t(o),null],stack_labels:[t(e(r)),null]})))},\n function _(e,t,i,r,l){var n;r();const o=e(170),s=e(255),a=e(217),_=e(56),h=e(12);class d extends o.BaseColorBarView{*children(){yield*super.children(),yield this._fill_view,yield this._line_view}async lazy_initialize(){await super.lazy_initialize();const{fill_renderer:e,line_renderer:t}=this.model;this._fill_view=await(0,_.build_view)(e,{parent:this.parent}),this._line_view=await(0,_.build_view)(t,{parent:this.parent})}remove(){this._fill_view.remove(),this._line_view.remove(),super.remove()}_create_major_range(){const e=this.model.levels;return e.length>0?new s.Range1d({start:e[0],end:e[e.length-1]}):new s.Range1d({start:0,end:1})}_paint_colors(e,t){const i=\"vertical\"==this.orientation,r=this.model.levels,l=this._major_scale;l.source_range=this._major_range,l.target_range=i?new s.Range1d({start:t.bottom,end:t.top}):new s.Range1d({start:t.left,end:t.right});const n=l.v_compute(r),o=this._fill_view.glyph,a=o.data_size;if(a>0){(0,h.assert)(r.length==a+1,\"Inconsistent number of filled contour levels\"),e.save();for(let r=0;r0){(0,h.assert)(r.length==d,\"Inconsistent number of line contour levels\"),e.save();for(let r=0;r({fill_renderer:[i(a.GlyphRenderer)],line_renderer:[i(a.GlyphRenderer)],levels:[e(t),[]]})))},\n function _(t,e,n,s,a){var i;s();const o=t(1),r=t(172),_=t(11),l=t(20),h=t(12),c=t(15),u=t(114),d=t(185),x=o.__importStar(t(186)),p=t(60);function m(t,e){return{x:t,y:e}}class g extends r.TextAnnotationView{constructor(){super(...arguments),this._pan_state=null}get mappers(){function t(t,e,n,s){switch(t){case\"canvas\":return s;case\"screen\":return n;case\"data\":return e}}const e=this.model,n=this.layout??this.plot_view.frame,{x_scale:s,y_scale:a}=this.coordinates,{x_view:i,y_view:o}=n.bbox,{x_screen:r,y_screen:_}=this.plot_view.canvas.bbox;return{x:t(e.x_units,s,i,r),y:t(e.y_units,a,o,_)}}get anchor(){const{align:t,baseline:e}=this.visuals.text.values();return x.text_anchor(this.model.anchor,t,e)}get angle(){const{angle:t,angle_units:e,direction:n}=this.model;return(0,_.compute_angle)(t,e,n)}get origin(){const{mappers:t}=this,{x:e,y:n,x_offset:s,y_offset:a}=this.model,i=(t,e,n)=>e instanceof p.Coordinate?this.resolve_as_scalar(e,t):n.compute(e);return{sx:i(\"x\",e,t.x)+s,sy:i(\"y\",n,t.y)-a}}interactive_hit(t,e){return!(!this.model.visible||!this.model.editable)&&\"area\"==this._hit_test(t,e)}_hit_test(t,e){const{sx:n,sy:s,anchor:a,angle:i,width:o,height:r}=this._rect,{x:_,y:l}=(0,u.rotate_around)(m(t,e),m(n,s),-i),h=n-a.x*o,c=s-a.y*r;return h<=_&&_<=h+o&&c<=l&&l<=c+r?\"area\":null}_can_hit(t){return!0}on_pan_start(t){if(this.model.visible&&this.model.editable){const{sx:e,sy:n}=t,s=this._hit_test(e,n);if(null!=s&&this._can_hit(s))return this._pan_state={angle:this.angle,base:{sx:e,sy:n},target:s,action:\"rotate\"},this.model.pan.emit([\"pan:start\",t.modifiers]),!0}return!1}on_pan(t){(0,h.assert)(null!=this._pan_state);const{dx:e,dy:n}=t,{angle:s,base:a}=this._pan_state,{origin:i}=this,o=(0,_.atan2)([i.sx,i.sy],[a.sx,a.sy]),r=(s+((0,_.atan2)([i.sx,i.sy],[a.sx+e,a.sy+n])-o))%(2*Math.PI),{angle_units:l,direction:c}=this.model;this.model.angle=(0,_.invert_angle)(r,l,c),this.model.pan.emit([\"pan\",t.modifiers])}on_pan_end(t){this._pan_state=null,this.model.pan.emit([\"pan:end\",t.modifiers])}cursor(t,e){const n=this._pan_state?.target??this._hit_test(t,e);return null!=n&&this._can_hit(n)?\"var(--bokeh-cursor-rotate)\":null}}n.LabelView=g,g.__name__=\"LabelView\";class y extends r.TextAnnotation{constructor(t){super(t),this.pan=new c.Signal(this,\"pan\")}}n.Label=y,i=y,y.__name__=\"Label\",i.prototype.default_view=g,i.define((({Bool:t,Float:e,Angle:n,Or:s,Ref:a})=>({anchor:[d.TextAnchor,\"auto\"],x:[s(e,a(p.Coordinate))],y:[s(e,a(p.Coordinate))],x_units:[l.CoordinateUnits,\"data\"],y_units:[l.CoordinateUnits,\"data\"],x_offset:[e,0],y_offset:[e,0],angle:[n,0],angle_units:[l.AngleUnits,\"rad\"],direction:[l.Direction,\"anticlock\"],editable:[t,!1]})))},\n function _(t,e,i,s,a){var n;s();const o=t(1),l=t(127),r=o.__importStar(t(80)),_=t(20),c=t(180),u=o.__importStar(t(18)),x=t(24);class h extends l.DataAnnotationView{map_data(){const{x_scale:t,y_scale:e}=this.coordinates,i=null!=this.layout?this.layout:this.plot_view.frame;this.sx=(()=>{switch(this.model.x_units){case\"canvas\":return new x.ScreenArray(this._x);case\"screen\":return i.bbox.xview.v_compute(this._x);case\"data\":return t.v_compute(this._x)}})(),this.sy=(()=>{switch(this.model.y_units){case\"canvas\":return new x.ScreenArray(this._y);case\"screen\":return i.bbox.yview.v_compute(this._y);case\"data\":return e.v_compute(this._y)}})()}_paint_data(){const{ctx:t}=this.layer;for(let e=0,i=this.text.length;e({x:[u.XCoordinateSpec,{field:\"x\"}],y:[u.YCoordinateSpec,{field:\"y\"}],x_units:[_.CoordinateUnits,\"data\"],y_units:[_.CoordinateUnits,\"data\"],text:[u.NullStringSpec,{field:\"text\"}],angle:[u.AngleSpec,0],x_offset:[u.NumberSpec,{value:0}],y_offset:[u.NumberSpec,{value:0}]}))),n.override({background_fill_color:null,border_line_color:null})},\n function _(t,e,i,s,n){var o;s();const l=t(1),r=t(81),a=t(284),c=t(20),h=l.__importStar(t(80)),_=t(15),d=t(173),b=t(64),u=t(10),g=t(9),p=t(34),m=t(8),f=t(180),x=t(256),w=t(53),{max:v,ceil:y}=Math;class k extends x.ContentLayoutable{constructor(t,e,i,s){super(),this.item=t,this.label=e,this.text=i,this.settings=s}get field(){return this.item.get_field_from_label_prop()}_content_size(){const t=this.text.size(),{glyph_width:e,glyph_height:i,label_standoff:s,label_width:n,label_height:o}=this.settings,l=e+s+v(t.width,n),r=v(i,t.height,o);return new x.Sizeable({width:l,height:r})}}k.__name__=\"LegendEntry\";class L extends r.AnnotationView{constructor(){super(...arguments),this._bbox=new b.BBox}_get_size(){const{width:t,height:e}=this.bbox,{margin:i}=this.model;return{width:t+2*i,height:e+2*i}}update_layout(){this.update_geometry();const{panel:t}=this;this.layout=null!=t?new d.SideLayout(t,(()=>this.get_size())):void 0}connect_signals(){super.connect_signals();const t=()=>this.request_paint();this.connect(this.model.change,t),this.connect(this.model.item_change,t)}get bbox(){return this._bbox}get padding(){return null!=this.model.border_line_color?this.model.padding:0}update_geometry(){super.update_geometry();const{spacing:t,orientation:e}=this.model,i=\"vertical\"==e,{padding:s}=this,n=s,o=s,{title:l}=this.model,r=new f.TextBox({text:l??\"\"});r.position={sx:0,sy:0,x_anchor:\"left\",y_anchor:\"top\"},r.visuals=this.visuals.title_text.values();const a=new d.SidePanel(this.model.title_location);r.angle=a.get_label_angle_heuristic(\"parallel\");const c=[];for(const t of this.model.items){t.legend=this.model;const e=t.get_labels_list_from_label_prop();for(const i of e){const e=new f.TextBox({text:`${i}`});e.position={sx:0,sy:0,x_anchor:\"left\",y_anchor:\"center\"},e.visuals=this.visuals.label_text.values();const s=new k(t,i,e,this.model);s.set_sizing({visible:t.visible}),c.push({layout:s,row:0,col:0})}}const{ncols:h,nrows:_}=(()=>{let{ncols:t,nrows:e}=this.model;const s=c.length;return i?(\"auto\"!=e||(e=\"auto\"!=t?y(s/t):1/0),t=1/0):(\"auto\"!=t||(t=\"auto\"!=e?y(s/e):1/0),e=1/0),{ncols:t,nrows:e}})();let u=0,g=0;for(const t of c)t.row=u,t.col=g,i?(u+=1,u>=_&&(u=0,g+=1)):(g+=1,g>=h&&(g=0,u+=1));const p=new x.Grid(c);this.grid=p,p.spacing=t,p.set_sizing();const m=new x.TextLayout(r);this.title_panel=m;const w=\"\"!=r.text&&this.visuals.title_text.doit;m.set_sizing({visible:w});const v=(()=>{if(!w)return new x.Column([p]);switch(this.model.title_location){case\"above\":return new x.Column([m,p]);case\"below\":return new x.Column([p,m]);case\"left\":return new x.Row([m,p]);case\"right\":return new x.Row([p,m])}})();this.border_box=v,v.position={left:n,top:o},v.spacing=this.model.title_standoff,v.set_sizing(),v.compute();const L=s+v.bbox.width+s,z=s+v.bbox.height+s;this._bbox=new b.BBox({left:0,top:0,width:L,height:z})}compute_geometry(){super.compute_geometry();const{margin:t,location:e}=this.model,{width:i,height:s}=this.bbox,n=null!=this.layout?this.layout:this.plot_view.frame,[o,l]=n.bbox.ranges;let r,a;if((0,m.isString)(e))switch(e){case\"top_left\":r=o.start+t,a=l.start+t;break;case\"top\":case\"top_center\":r=(o.end+o.start)/2-i/2,a=l.start+t;break;case\"top_right\":r=o.end-t-i,a=l.start+t;break;case\"bottom_right\":r=o.end-t-i,a=l.end-t-s;break;case\"bottom\":case\"bottom_center\":r=(o.end+o.start)/2-i/2,a=l.end-t-s;break;case\"bottom_left\":r=o.start+t,a=l.end-t-s;break;case\"left\":case\"center_left\":r=o.start+t,a=(l.end+l.start)/2-s/2;break;case\"center\":case\"center_center\":r=(o.end+o.start)/2-i/2,a=(l.end+l.start)/2-s/2;break;case\"right\":case\"center_right\":r=o.end-t-i,a=(l.end+l.start)/2-s/2}else{const[t,i]=e;r=n.bbox.xview.compute(t),a=n.bbox.yview.compute(i)-s}this._bbox=new b.BBox({left:r,top:a,width:i,height:s})}interactive_hit(t,e){return this.bbox.contains(t,e)}_hit_test(t,e){const{left:i,top:s}=this.bbox;t-=i+this.grid.bbox.left,e-=s+this.grid.bbox.top;for(const i of this.grid)if(i.bbox.contains(t,e))return{type:\"entry\",entry:i};return null}cursor(t,e){return(\"none\"!=this.model.click_policy||(0,g.dict)(this.model.js_event_callbacks).has(\"legend_item_click\"))&&null!=this._hit_test(t,e)?\"pointer\":null}on_hit(t,e){const i=(()=>{switch(this.model.click_policy){case\"hide\":return t=>t.visible=!t.visible;case\"mute\":return t=>t.muted=!t.muted;case\"none\":return t=>{}}})(),s=this._hit_test(t,e);if(null!=s){const{item:t}=s.entry;this.model.trigger_event(new w.LegendItemClick(this.model,t));for(const e of t.renderers)i(e);return!0}return!1}_paint(){if(0==this.model.items.length)return;if(!(0,u.some)(this.model.items,(t=>t.visible)))return;const{ctx:t}=this.layer;t.save(),this._draw_legend_box(t),this._draw_legend_items(t),this._draw_title(t),t.restore()}_draw_legend_box(t){const{x:e,y:i,width:s,height:n}=this.bbox;t.beginPath(),t.rect(e,i,s,n),this.visuals.background_fill.apply(t),this.visuals.border_line.apply(t)}_draw_title(t){const{title:e}=this.model;if(null==e||0==e.length||!this.visuals.title_text.doit)return;const{left:i,top:s}=this.bbox;switch(t.save(),t.translate(i,s),t.translate(this.title_panel.bbox.left,this.title_panel.bbox.top),this.model.title_location){case\"left\":t.translate(0,this.title_panel.bbox.height);break;case\"right\":t.translate(this.title_panel.bbox.width,0)}this.title_panel.text.paint(t),t.restore()}_draw_legend_items(t){const e=(()=>{switch(this.model.click_policy){case\"none\":return t=>!0;case\"hide\":return t=>(0,u.every)(t.renderers,(t=>t.visible));case\"mute\":return t=>(0,u.every)(t.renderers,(t=>!t.muted))}})(),i=(t,e,i)=>{if(!this.visuals.item_background_fill.doit)return!1;switch(this.model.item_background_policy){case\"every\":return!0;case\"even\":return e%2==0==(i%2==0);case\"odd\":return e%2==0!=(i%2==0);case\"none\":return!1}},{left:s,top:n}=this.bbox;t.translate(s,n),t.translate(this.grid.bbox.left,this.grid.bbox.top);for(const[{layout:s,row:n,col:o},l]of(0,p.enumerate)(this.grid.items)){const{bbox:l,text:r,item:a,label:c,field:h,settings:_}=s,{glyph_width:d,glyph_height:b,label_standoff:u}=_,{left:g,top:p,width:m,height:f}=l;t.translate(g,p),i(0,n,o)&&(t.beginPath(),t.rect(0,0,m,f),this.visuals.item_background_fill.apply(t));const x=f/2,w=0,v=x-b/2,y=w+d,k=v+b;for(const e of a.renderers){const i=this.plot_view.views.find_one(e);i?.draw_legend(t,w,y,v,k,h,c,a.index)}t.translate(y+u,x),r.paint(t),t.translate(-y-u,-x),e(a)||(t.beginPath(),t.rect(0,0,m,f),this.visuals.inactive_fill.set_value(t),t.fill()),t.translate(-g,-p)}t.translate(-this.grid.bbox.left,-this.grid.bbox.top),t.translate(-s,-n)}}i.LegendView=L,L.__name__=\"LegendView\";class z extends r.Annotation{constructor(t){super(t)}initialize(){super.initialize(),this.item_change=new _.Signal0(this,\"item_change\")}}i.Legend=z,o=z,z.__name__=\"Legend\",o.prototype.default_view=L,o.mixins([[\"label_\",h.Text],[\"title_\",h.Text],[\"inactive_\",h.Fill],[\"border_\",h.Line],[\"background_\",h.Fill],[\"item_background_\",h.Fill]]),o.define((({Float:t,Int:e,Str:i,List:s,Tuple:n,Or:o,Ref:l,Nullable:r,Positive:h,Auto:_})=>({orientation:[c.Orientation,\"vertical\"],ncols:[o(h(e),_),\"auto\"],nrows:[o(h(e),_),\"auto\"],location:[o(c.LegendLocation,n(t,t)),\"top_right\"],title:[r(i),null],title_location:[c.Location,\"above\"],title_standoff:[t,5],label_standoff:[t,5],glyph_height:[t,20],glyph_width:[t,20],label_height:[t,20],label_width:[t,20],margin:[t,10],padding:[t,10],spacing:[t,3],items:[s(l(a.LegendItem)),[]],click_policy:[c.LegendClickPolicy,\"none\"],item_background_policy:[c.AlternationPolicy,\"none\"]}))),o.override({border_line_color:\"#e5e5e5\",border_line_alpha:.5,border_line_width:1,background_fill_color:\"#ffffff\",background_fill_alpha:.95,item_background_fill_color:\"#f1f1f1\",item_background_fill_alpha:.8,inactive_fill_color:\"white\",inactive_fill_alpha:.7,label_text_font_size:\"13px\",label_text_baseline:\"middle\",title_text_font_size:\"13px\",title_text_font_style:\"italic\"})},\n function _(e,r,n,l,t){var i;l();const s=e(1),o=e(51),_=e(217),a=e(128),u=e(28),d=s.__importStar(e(18)),c=e(19),h=e(10);class f extends o.Model{constructor(e){super(e)}_check_data_sources_on_renderers(){if(null!=this.get_field_from_label_prop()){if(this.renderers.length<1)return!1;const e=this.renderers[0].data_source;for(const r of this.renderers)if(r.data_source!=e)return!1}return!0}_check_field_label_on_data_source(){const e=this.get_field_from_label_prop();if(null!=e){if(this.renderers.length<1)return!1;const r=this.renderers[0].data_source;if(!(0,h.includes)(r.columns(),e))return!1}return!0}initialize(){super.initialize(),this.legend=null,this.connect(this.change,(()=>this.legend?.item_change.emit()));this._check_data_sources_on_renderers()||c.logger.error(\"Non matching data sources on legend item renderers\");this._check_field_label_on_data_source()||c.logger.error(`Bad column name on label: ${this.label}`)}get_field_from_label_prop(){const{label:e}=this;return(0,u.isField)(e)?e.field:null}get_labels_list_from_label_prop(){if(!this.visible)return[];const{index:e}=this;if(null!=e&&this.renderers.every((r=>!r.view.indices_map.has(e))))return[];if((0,u.isValue)(this.label)){const{value:e}=this.label;return null!=e?[e]:[]}const r=this.get_field_from_label_prop();if(null!=r){let e;if(0==this.renderers.length)return[\"No source found\"];if(e=this.renderers[0].data_source,e instanceof a.ColumnarDataSource){const n=e.get_column(r);return null!=n?(0,h.uniq)(Array.from(n)):[\"Invalid field\"]}}return[]}}n.LegendItem=f,i=f,f.__name__=\"LegendItem\",i.define((({Bool:e,Int:r,List:n,Ref:l,Nullable:t})=>({label:[d.NullStringSpec,null],renderers:[n(l(_.GlyphRenderer)),[]],index:[t(r),null],visible:[e,!0]})))},\n function _(t,e,s,n,i){var o,a;n();const l=t(1),r=t(81),h=t(106),_=l.__importStar(t(80)),c=t(20),y=t(228),p=t(15),d=t(64),u=t(13),v=t(12);class x{constructor(t=[],e=[]){this.xs=t,this.ys=e,(0,v.assert)(t.length==e.length)}clone(){return new x(this.xs.slice(),this.ys.slice())}[Symbol.iterator](){return this.nodes()}*nodes(){const{xs:t,ys:e,n:s}=this;for(let n=0;n=3){const n={x:t[s-1],y:e[s-1]},i={x:t[0],y:e[0]};yield[n,i,s-1]}}contains(t,e){return(0,y.point_in_poly)(t,e,this.xs,this.ys)}get bbox(){const[t,e,s,n]=(0,u.minmax2)(this.xs,this.ys);return new d.BBox({x0:t,x1:e,y0:s,y1:n})}get n(){return this.xs.length}translate(t,e,...s){const n=this.clone(),{xs:i,ys:o,n:a}=n;if(0!=s.length)for(const n of s){const s=n%a;i[s]+=t,o[s]+=e}else for(let s=0;sthis.request_paint()))}bounds(){const{xs_units:t,ys_units:e}=this.model;if(\"data\"==t&&\"data\"==e){const{xs:t,ys:e}=this.model,[s,n,i,o]=(0,u.minmax2)(t,e);return{x0:s,x1:n,y0:i,y1:o}}return(0,d.empty)()}log_bounds(){return(0,d.empty)()}_mappers(){const t=(t,e,s,n)=>{switch(t){case\"canvas\":return n;case\"screen\":return s;case\"data\":return e}},e=this.model,{frame:s,canvas:n}=this.plot_view,{x_scale:i,y_scale:o}=s,{x_view:a,y_view:l}=s.bbox,{x_screen:r,y_screen:h}=n.bbox;return{x:t(e.xs_units,i,a,r),y:t(e.ys_units,o,l,h)}}_paint(){const{xs:t,ys:e}=this.model;(0,v.assert)(t.length==e.length),this.poly=(()=>{const{x:s,y:n}=this._mappers();return new x(s.v_compute(t),n.v_compute(e))})();const{ctx:s}=this.layer;s.beginPath();for(const[t,e]of this.poly)s.lineTo(t,e);const{_is_hovered:n,visuals:i}=this,o=n&&i.hover_fill.doit?i.hover_fill:i.fill,a=n&&i.hover_hatch.doit?i.hover_hatch:i.hatch,l=n&&i.hover_line.doit?i.hover_line:i.line;this.poly.n>=3&&(s.closePath(),o.apply(s),a.apply(s)),l.apply(s)}interactive_hit(t,e){return!(!this.model.visible||!this.model.editable)&&this.poly.contains(t,e)}_hit_test(t,e){const{abs:s}=Math,n=Math.max(2.5,this.model.line_width/2);for(const[i,o,a]of this.poly)if(s(i-t){const{poly:e,target:s}=this._pan_state,{dx:n,dy:i}=t;switch(s.type){case\"node\":{const{i:t}=s;return e.translate(n,i,t)}case\"edge\":{const{i:t}=s;return e.translate(n,i,t,t+1)}case\"area\":return e.translate(n,i)}})(),{x:s,y:n}=this._mappers(),i=s.v_invert(e.xs),o=n.v_invert(e.ys);this.model.update({xs:i,ys:o}),this.model.pan.emit([\"pan\",t.modifiers])}on_pan_end(t){this._pan_state=null,this.model.pan.emit([\"pan:end\",t.modifiers])}get _has_hover(){const{hover_line:t,hover_fill:e,hover_hatch:s}=this.visuals;return t.doit||e.doit||s.doit}on_enter(t){const{_has_hover:e}=this;return e&&(this._is_hovered=!0,this.request_paint()),e}on_move(t){}on_leave(t){this._has_hover&&(this._is_hovered=!1,this.request_paint())}cursor(t,e){const s=this._pan_state?.target??this._hit_test(t,e);if(null==s||!this._can_hit(s))return null;switch(s.type){case\"node\":case\"edge\":case\"area\":return\"move\"}}}s.PolyAnnotationView=m,o=h.auto_ranged,m.__name__=\"PolyAnnotationView\";class f extends r.Annotation{constructor(t){super(t),this.pan=new p.Signal(this,\"pan\")}update({xs:t,ys:e}){this.setv({xs:t.slice(),ys:e.slice(),visible:!0})}clear(){this.setv({xs:[],ys:[],visible:!1})}}s.PolyAnnotation=f,a=f,f.__name__=\"PolyAnnotation\",a.prototype.default_view=m,a.mixins([_.Line,_.Fill,_.Hatch,[\"hover_\",_.Line],[\"hover_\",_.Fill],[\"hover_\",_.Hatch]]),a.define((({Bool:t,Float:e,Arrayable:s})=>({xs:[s(e),[]],ys:[s(e),[]],xs_units:[c.CoordinateUnits,\"data\"],ys_units:[c.CoordinateUnits,\"data\"],editable:[t,!1]}))),a.override({fill_color:\"#fff9ba\",fill_alpha:.4,line_color:\"#cccccc\",line_alpha:.3,hover_fill_color:null,hover_fill_alpha:.4,hover_line_color:null,hover_line_alpha:.3})},\n function _(t,e,i,a,s){var o;a();const n=t(1),l=t(81),r=t(287),h=t(98),_=t(99),c=t(20),u=n.__importStar(t(80)),d=t(180),g=t(173),b=t(64),x=t(256),w=t(258),p=t(194),m=t(191),f=t(209),y=t(101),v=t(95),z=t(56),k=t(11),L=t(12),S=t(34),B=t(244),T=t(21),{round:A}=Math,R=(0,T.Enum)(\"adaptive\",\"exact\");class F extends l.AnnotationView{constructor(){super(...arguments),this._bbox=new b.BBox}get bbox(){return this._bbox}_get_size(){const{width:t,height:e}=this.bbox,{margin:i}=this.model;return{width:t+2*i,height:e+2*i}}initialize(){super.initialize();const{ticker:t}=this.model;this.axis=new p.LinearAxis({ticker:t,...u.attrs_of(this.model,\"bar_\",u.Line,\"axis_\")}),this.range=(()=>{const{range:t,orientation:e}=this.model;if(\"auto\"!=t)return t;{const{frame:t}=this.parent;switch(e){case\"horizontal\":return t.x_range;case\"vertical\":return t.y_range}}})()}async lazy_initialize(){await super.lazy_initialize();const t=(()=>{const t=new _.Range1d,e=new _.Range1d,i=new _.Range1d,a=new _.Range1d;return this.axis_scale=new y.LinearScale({source_range:t,target_range:e}),this.cross_scale=new y.LinearScale({source_range:i,target_range:a}),\"horizontal\"==this.model.orientation?new v.CoordinateTransform(this.axis_scale,this.cross_scale):new v.CoordinateTransform(this.cross_scale,this.axis_scale)})();this.axis_view=await(0,z.build_view)(this.axis,{parent:this.plot_view}),this.axis_view.coordinates=t,this.axis_view.panel=new g.SidePanel(\"horizontal\"==this.model.orientation?\"below\":\"right\"),this.axis_view.update_layout()}remove(){this.axis_view.remove(),super.remove()}connect_signals(){super.connect_signals(),this.connect(this.model.change,(()=>{this.request_paint()})),this.connect(this.range.change,(()=>{this.request_paint()}))}update_layout(){this.update_geometry();const{panel:t}=this;this.layout=null!=t?new g.SideLayout(t,(()=>this.get_size())):void 0}update_geometry(){super.update_geometry()}get horizontal(){return\"horizontal\"==this.model.orientation}text_layout(t){const{text:e,location:i,align:a,visuals:s}=t,{orientation:o}=this.model,n=new d.TextBox({text:e}),l=new g.SidePanel(i);n.visuals=s.values();const r=(()=>{switch(i){case\"above\":case\"below\":return\"horizontal\";default:return o}})();n.angle=l.get_label_angle_heuristic(r),n.base_font_size=this.plot_view.base_font_size,n.position={sx:0,sy:0,x_anchor:\"left\",y_anchor:\"top\"},n.align=\"auto\";const h=new x.TextLayout(n);h.absolute=!0;const _=\"horizontal\"==o,c=_?a:void 0,u=_?void 0:a;return h.set_sizing({width_policy:\"min\",height_policy:\"min\",visible:\"\"!=e&&s.doit,halign:c,valign:u}),h}compute_geometry(){super.compute_geometry();const{orientation:t,bar_length:e,length_sizing:i,padding:a,margin:s,location:o}=this.model,{border_line:n,bar_line:l}=this.visuals,r=l.line_width.get_value(),h=n.line_width.get_value(),{frame:_}=this.parent,c=\"horizontal\"==t?_.bbox.width:_.bbox.height,u=0<=e&&e<=1?e:(0,k.clamp)(e/c,0,1),{new_value:d,new_unit:g,scale_factor:p,exact:m}=(()=>{const{unit:t,dimensional:e}=this.model,a=this.range.span*u;return e.compute(a,t,\"exact\"==i)})(),f=A(c*u*p),y=(()=>{const{label:t}=this.model;return(0,B.process_placeholders)(t,((t,e,i)=>{switch(e){case\"value\":return m?null!=i?(0,B.sprintf)(i,d):d.toFixed(2):`${d}`;case\"unit\":if(\"short\"===(i??\"short\"))return g;default:return null}}))})();this.label_layout=this.text_layout({text:y,location:this.model.label_location,align:this.model.label_align,visuals:this.visuals.label_text}),this.title_layout=this.text_layout({text:this.model.title,location:this.model.title_location,align:this.model.title_align,visuals:this.visuals.title_text});const v=\"horizontal\"==t?{width:f,height:r}:{width:r,height:f},z=this.axis_view.layout;(0,L.assert)(null!=z),this.axis_layout=z,z.absolute=!0,\"horizontal\"==t?z.set_sizing({width_policy:\"fixed\",width:v.width,height_policy:\"min\",valign:\"center\"}):z.set_sizing({width_policy:\"min\",height_policy:\"fixed\",height:v.height,halign:\"center\"}),this.box_layout=(()=>{const t={above:[],below:[],left:[],right:[]};function e(e,i,a){e.visible&&t[i].push(function(t,e){const i=new x.FixedLayout;return i.absolute=!0,i.set_sizing(\"left\"==t||\"right\"==t?{width_policy:\"fixed\",width:e}:{height_policy:\"fixed\",height:e}),i}(i,a),e)}e(this.label_layout,this.model.label_location,this.model.label_standoff),e(this.title_layout,this.model.title_location,this.model.title_standoff);const i=t.above.length,a=t.left.length,s=[{layout:z,row:i,col:a}];for(const[e,o]of(0,S.enumerate)(t.above))s.push({layout:e,row:i-o-1,col:a});for(const[e,o]of(0,S.enumerate)(t.below))s.push({layout:e,row:i+o+1,col:a});for(const[e,o]of(0,S.enumerate)(t.left))s.push({layout:e,row:i,col:a-o-1});for(const[e,o]of(0,S.enumerate)(t.right))s.push({layout:e,row:i,col:a+o+1});return new w.Grid(s)})();const{box_layout:T}=this;T.absolute=!0,T.position={left:a,top:a},T.set_sizing(),T.compute();const[R,F]=(()=>{const{x_range:e,y_range:i}=this.axis_view.bbox;return\"horizontal\"==t?[e,i]:[i,e]})();this.axis_scale.source_range.end=d,this.axis_scale.target_range.setv(R),this.cross_scale.source_range.end=1,this.cross_scale.target_range.setv(F);const P=h+a+T.bbox.width+a+h,V=h+a+T.bbox.height+a+h,q=null!=this.layout?this.layout:this.plot_view.frame,[C,M]=q.bbox.ranges,{sx:N,sy:O}=(()=>{switch(o){case\"top_left\":return{sx:C.start+s,sy:M.start+s};case\"top\":case\"top_center\":return{sx:(C.end+C.start)/2-P/2,sy:M.start+s};case\"top_right\":return{sx:C.end-s-P,sy:M.start+s};case\"bottom_right\":return{sx:C.end-s-P,sy:M.end-s-V};case\"bottom\":case\"bottom_center\":return{sx:(C.end+C.start)/2-P/2,sy:M.end-s-V};case\"bottom_left\":return{sx:C.start+s,sy:M.end-s-V};case\"left\":case\"center_left\":return{sx:C.start+s,sy:(M.end+M.start)/2-V/2};case\"center\":case\"center_center\":return{sx:(C.end+C.start)/2-P/2,sy:(M.end+M.start)/2-V/2};case\"right\":case\"center_right\":return{sx:C.end-s-P,sy:(M.end+M.start)/2-V/2}}})();this._bbox=new b.BBox({left:N,top:O,width:P,height:V})}_draw_box(t){const{width:e,height:i}=this.bbox;t.beginPath(),t.rect(0,0,e,i),this.visuals.background_fill.apply(t),this.visuals.background_hatch.apply(t),this.visuals.border_line.apply(t)}_draw_axis(t){this.axis_view.paint()}_draw_text(t,e,i){const{bbox:a}=e,[s,o]=(()=>{const{orientation:t}=this.model,e=\"horizontal\"==t;switch(i){case\"left\":return e?[0,0]:[0,a.height];case\"right\":return e?[0,0]:[a.width,0];case\"above\":case\"below\":return[0,0]}})(),{left:n,top:l}=a.translate(s,o);t.translate(n,l),e.text.paint(t),t.translate(-n,-l)}_draw_label(t){this._draw_text(t,this.label_layout,this.model.label_location)}_draw_title(t){this._draw_text(t,this.title_layout,this.model.title_location)}_paint(){const{ctx:t}=this.layer,{left:e,top:i}=this.bbox;t.translate(e,i),this.box_layout.visible&&this._draw_box(t),this.axis_layout.visible&&this._draw_axis(t),this.label_layout.visible&&this._draw_label(t),this.title_layout.visible&&this._draw_title(t),t.translate(-e,-i)}}i.ScaleBarView=F,F.__name__=\"ScaleBarView\";class P extends l.Annotation{constructor(t){super(t)}}i.ScaleBar=P,o=P,P.__name__=\"ScaleBar\",o.prototype.default_view=F,o.mixins([[\"bar_\",u.Line],[\"label_\",u.Text],[\"title_\",u.Text],[\"border_\",u.Line],[\"background_\",u.Fill],[\"background_\",u.Hatch]]),o.define((({NonNegative:t,Float:e,Str:i,Ref:a,Or:s,Auto:o})=>({range:[s(a(h.Range),o),\"auto\"],unit:[i,\"m\"],dimensional:[a(r.Dimensional),()=>new r.MetricLength],orientation:[c.Orientation,\"horizontal\"],bar_length:[t(e),.2],length_sizing:[R,\"adaptive\"],location:[c.Anchor,\"top_right\"],label:[i,\"@{value} @{unit}\"],label_align:[c.Align,\"center\"],label_location:[c.Location,\"below\"],label_standoff:[e,5],title:[i,\"\"],title_align:[c.Align,\"center\"],title_location:[c.Location,\"above\"],title_standoff:[e,5],margin:[e,10],padding:[e,10],ticker:[a(m.Ticker),()=>new f.FixedTicker({ticks:[]})]}))),o.override({bar_line_width:2,border_line_color:\"#e5e5e5\",border_line_alpha:.5,border_line_width:1,background_fill_color:\"#ffffff\",background_fill_alpha:.95,label_text_font_size:\"13px\",label_text_baseline:\"middle\",title_text_font_size:\"13px\",title_text_font_style:\"italic\"})},\n function _(e,t,n,s,r){var i,c,a,o,l,u,_;s();const m=e(51),d=e(12),f=e(9),p=e(13),{min:h}=Math;class g extends m.Model{constructor(e){super(e)}compute(e,t,n){const s=(()=>{const{include:e,exclude:t}=this,n=(0,f.entries)(this.get_basis()).map((([e,[t,n,s]])=>({name:e,factor:t,tex_name:n,long_name:s}))).filter((({name:n})=>(null==e||e.includes(n))&&!t.includes(n)));return(0,p.sort_by)(n,(({factor:e})=>e))})(),{ticks:r}=this,i=s.find((({name:e})=>e==t));(0,d.assert)(null!=i);const c=e*i.factor,[a,o]=(()=>{const e=(0,p.bisect_right_by)(s,c,(({factor:e})=>e));if(e>0){const{name:t,factor:n}=s[e-1];return[t,c/n]}return[t,c]})();n=n??0==r.length;const l=(()=>{if(n)return o;{const e=(0,p.bisect_right)(r,o);return r[h(e,r.length-1)]}})();return{new_value:l,new_unit:a,scale_factor:l*(c/o)/e/i.factor,exact:n}}}n.Dimensional=g,i=g,g.__name__=\"Dimensional\",i.define((({Nullable:e,List:t,Str:n,Float:s})=>({ticks:[t(s)],include:[e(t(n)),null],exclude:[t(n),[]]})));class b extends g{constructor(e){super(e)}get_basis(){return this.basis}}n.CustomDimensional=b,c=b,b.__name__=\"CustomDimensional\",c.define((({Dict:e,Tuple:t,Float:n,Str:s,Or:r})=>({basis:[e(r(t(n,s),t(n,s,s)))]})));class x extends g{constructor(e){super(e)}get_basis(){const{base_unit:e,full_unit:t}=this,n={};for(const[s,r,i,c]of a._metric_basis){const a=`${i}${e}`,o=null!=t?`${c}${t}`:void 0;n[`${s}${e}`]=[r,a,o]}return n}}n.Metric=x,a=x,x.__name__=\"Metric\",a.define((({Str:e,Nullable:t})=>({base_unit:[e],full_unit:[t(e),null]}))),a.override({ticks:[1,2,5,10,15,20,25,50,75,100,125,150,200,250,500,750]}),x._metric_basis=[[\"Q\",1e30,\"Q\",\"quetta\"],[\"R\",1e27,\"R\",\"ronna\"],[\"Y\",1e24,\"Y\",\"yotta\"],[\"Z\",1e21,\"Z\",\"zetta\"],[\"E\",1e18,\"E\",\"exa\"],[\"P\",1e15,\"P\",\"peta\"],[\"T\",1e12,\"T\",\"tera\"],[\"G\",1e9,\"G\",\"giga\"],[\"M\",1e6,\"M\",\"mega\"],[\"k\",1e3,\"k\",\"kilo\"],[\"h\",100,\"h\",\"hecto\"],[\"\",1,\"\",\"\"],[\"d\",.1,\"d\",\"deci\"],[\"c\",.01,\"c\",\"centi\"],[\"m\",.001,\"m\",\"milli\"],[\"\\xb5\",1e-6,\"\\\\mu\",\"micro\"],[\"n\",1e-9,\"n\",\"nano\"],[\"p\",1e-12,\"p\",\"pico\"],[\"f\",1e-15,\"f\",\"femto\"],[\"a\",1e-18,\"a\",\"atto\"],[\"z\",1e-21,\"z\",\"zepto\"],[\"y\",1e-24,\"y\",\"yocto\"],[\"r\",1e-27,\"r\",\"ronto\"],[\"q\",1e-30,\"q\",\"quecto\"]];class M extends x{constructor(e){super(e)}get_basis(){const e=super.get_basis(),t={};for(const[n,[s,r]]of(0,f.entries)(e))t[`${n}\\u207b1`]=[s**-1,`${r}^{-1}`];return t}}n.ReciprocalMetric=M,M.__name__=\"ReciprocalMetric\";class y extends x{constructor(e){super(e)}}n.MetricLength=y,o=y,y.__name__=\"MetricLength\",o.override({base_unit:\"m\",exclude:[\"dm\",\"hm\"]});class k extends M{constructor(e){super(e)}}n.ReciprocalMetricLength=k,l=k,k.__name__=\"ReciprocalMetricLength\",l.override({base_unit:\"m\",exclude:[\"dm\",\"hm\"]});class v extends b{constructor(e){super(e)}}n.ImperialLength=v,u=v,v.__name__=\"ImperialLength\",u.override({basis:{in:[1/12,\"in\",\"inch\"],ft:[1,\"ft\",\"foot\"],yd:[3,\"yd\",\"yard\"],ch:[66,\"ch\",\"chain\"],fur:[660,\"fur\",\"furlong\"],mi:[5280,\"mi\",\"mile\"],lea:[15840,\"lea\",\"league\"]},ticks:[1,3,6,12,60]});class $ extends b{constructor(e){super(e)}}n.Angular=$,_=$,$.__name__=\"Angular\",_.override({basis:{\"\\xb0\":[1,\"^\\\\circ\",\"degree\"],\"'\":[1/60,\"^\\\\prime\",\"minute\"],\"''\":[1/3600,\"^{\\\\prime\\\\prime}\",\"second\"]},ticks:[1,3,6,12,60,120,240,360]})},\n function _(l,o,e,i,t){var n;i();const s=l(1),a=l(81),_=s.__importStar(l(80));class c extends a.AnnotationView{connect_signals(){super.connect_signals(),this.connect(this.model.change,(()=>this.request_paint()))}_paint(){const{gradient:l,y_intercept:o}=this.model;if(null==l||null==o)return;const{frame:e}=this.plot_view,i=this.coordinates.x_scale,t=this.coordinates.y_scale,[n,s,a,_]=(()=>{if(0==l){const l=t.compute(o),i=l;return[e.bbox.left,e.bbox.right,l,i]}{const n=e.bbox.top,s=e.bbox.bottom,a=t.invert(n),_=t.invert(s),c=(a-o)/l,h=(_-o)/l,b=i.compute(c),r=i.compute(h);return b<=r?[b,r,n,s]:[r,b,s,n]}})(),{ctx:c}=this.layer;if(c.save(),this.visuals.above_fill.doit||this.visuals.above_hatch.doit){const{left:l,right:o,top:i,bottom:t}=e.bbox;c.beginPath(),c.moveTo(n,a),c.lineTo(n,a),c.lineTo(s,_),c.lineTo(s,_),a<=_?(sl&&c.lineTo(l,t)),c.closePath(),this.visuals.above_fill.apply(c),this.visuals.above_hatch.apply(c)}if(this.visuals.below_fill.doit||this.visuals.below_hatch.doit){const{left:l,right:o,top:i,bottom:t}=e.bbox;c.beginPath(),c.moveTo(n,a),c.lineTo(n,a),c.lineTo(s,_),a<=_?(c.lineTo(o,t),c.lineTo(l,t),n>l&&c.lineTo(l,i)):(s({gradient:[o(l),null],y_intercept:[o(l),null]}))),n.override({line_color:\"black\",above_fill_color:null,above_fill_alpha:.4,below_fill_color:null,below_fill_alpha:.4})},\n function _(t,e,i,n,s){var o;n();const a=t(1),l=t(81),r=a.__importStar(t(80)),h=t(20),_=t(228),c=t(15),d=t(12);class u{constructor(t,e){this.p0=t,this.p1=e}clone(){return new u({...this.p0},{...this.p1})}hit_test(t,e=2.5){return(0,_.dist_to_segment)(t,this.p0,this.p1)this.plot_view.request_paint(this)))}_paint(){const{location:t,location_units:e}=this.model;if(null==t)return;function i(t,e,i,n,s){switch(e){case\"canvas\":return s.compute(t);case\"screen\":return n.compute(t);case\"data\":return i.compute(t)}}const{frame:n,canvas:s}=this.plot_view,{x_scale:o,y_scale:a}=this.coordinates;let l,r,h,_;\"width\"==this.model.dimension?(h=i(t,e,a,n.bbox.yview,s.bbox.y_screen),r=n.bbox.left,_=n.bbox.width,l=this.model.line_width):(h=n.bbox.top,r=i(t,e,o,n.bbox.xview,s.bbox.y_screen),_=this.model.line_width,l=n.bbox.height);const c={x:r,y:h},d={x:r+_,y:h+l};this.line=new u(c,d);const{_is_hovered:p,visuals:m}=this,v=p&&m.hover_line.doit?m.hover_line:m.line,{ctx:b}=this.layer;b.save(),b.beginPath(),this.visuals.line.set_value(b),b.moveTo(r,h),\"width\"==this.model.dimension?b.lineTo(r+_,h):b.lineTo(r,h+l),v.apply(b),b.restore()}interactive_hit(t,e){return!(!this.model.visible||!this.model.editable)&&null!=this._hit_test(t,e)}_hit_test(t,e){const i=Math.max(2.5,this.model.line_width/2);return this.line.hit_test({x:t,y:e},i)?\"edge\":null}_can_hit(t){return!0}on_pan_start(t){if(this.model.visible&&this.model.editable){const{sx:e,sy:i}=t,n=this._hit_test(e,i);if(null!=n&&this._can_hit(n))return this._pan_state={line:this.line.clone(),target:n},this.model.pan.emit([\"pan:start\",t.modifiers]),!0}return!1}on_pan(t){function e(t,e,i,n,s){switch(e){case\"canvas\":return s.invert(t);case\"screen\":return n.invert(t);case\"data\":return i.invert(t)}}(0,d.assert)(null!=this._pan_state);const i=(()=>{const{dx:e,dy:i}=t,{line:n}=this._pan_state;return\"width\"==this.model.dimension?n.translate(0,i).p0.y:n.translate(e,0).p0.x})(),n=(()=>{const{location_units:t}=this.model,{frame:n,canvas:s}=this.plot_view,{x_scale:o,y_scale:a}=this.coordinates;return\"width\"==this.model.dimension?e(i,t,a,n.bbox.yview,s.bbox.y_screen):e(i,t,o,n.bbox.xview,s.bbox.y_screen)})();this.model.location=n,this.model.pan.emit([\"pan\",t.modifiers])}on_pan_end(t){this._pan_state=null,this.model.pan.emit([\"pan:end\",t.modifiers])}get _has_hover(){const{hover_line:t}=this.visuals;return t.doit}on_enter(t){const{_has_hover:e}=this;return e&&(this._is_hovered=!0,this.request_paint()),e}on_move(t){}on_leave(t){this._has_hover&&(this._is_hovered=!1,this.request_paint())}cursor(t,e){const i=this._pan_state?.target??this._hit_test(t,e);return null!=i&&this._can_hit(i)?\"width\"==this.model.dimension?\"ns-resize\":\"ew-resize\":null}}i.SpanView=p,p.__name__=\"SpanView\";class m extends l.Annotation{constructor(t){super(t),this.pan=new c.Signal(this,\"pan\")}}i.Span=m,o=m,m.__name__=\"Span\",o.prototype.default_view=p,o.mixins([r.Line,[\"hover_\",r.Line]]),o.define((({Bool:t,Float:e,Nullable:i})=>({location:[i(e),null],location_units:[h.CoordinateUnits,\"data\"],dimension:[h.Dimension,\"width\"],editable:[t,!1]}))),o.override({line_color:\"black\",hover_line_color:null,hover_line_alpha:.3})},\n function _(e,t,i,o,s){var l;o();const a=e(81),n=e(291),r=e(56),_=e(63),h=e(173),u=e(64);class b extends a.AnnotationView{constructor(){super(...arguments),this._previous_bbox=new u.BBox}rendering_target(){return this.plot_view.canvas_view.events_el}update_layout(){this.layout=new h.SideLayout(this.panel,(()=>this.get_size()),!0)}after_layout(){this.toolbar_view.after_render()}has_finished(){return super.has_finished()&&this.toolbar_view.has_finished()}*children(){yield*super.children(),yield this.toolbar_view}async lazy_initialize(){await super.lazy_initialize(),this.toolbar_view=await(0,r.build_view)(this.model.toolbar,{parent:this.canvas})}connect_signals(){super.connect_signals(),this.plot_view.mouseenter.connect((()=>{this.toolbar_view.set_visibility(!0)})),this.plot_view.mouseleave.connect((()=>{this.toolbar_view.set_visibility(!1)}))}remove(){this.toolbar_view.remove(),super.remove()}render(){super.render(),this.toolbar_view.render_to(this.shadow_el)}_paint(){(0,_.display)(this.el);const{bbox:e}=this.layout;if(!this._previous_bbox.equals(e)){(0,_.position)(this.el,e),this._previous_bbox=e,(0,_.empty)(this.el),this.el.style.position=\"absolute\";const{style:t}=this.toolbar_view.el;this.toolbar_view.model.horizontal?(t.width=\"100%\",t.height=\"unset\"):(t.width=\"unset\",t.height=\"100%\")}this.model.visible||(0,_.undisplay)(this.el)}_get_size(){const{tools:e,logo:t}=this.model.toolbar;return{width:30*e.length+(null!=t?25:0)+15,height:30}}}i.ToolbarPanelView=b,b.__name__=\"ToolbarPanelView\";class v extends a.Annotation{constructor(e){super(e)}}i.ToolbarPanel=v,l=v,v.__name__=\"ToolbarPanel\",l.prototype.default_view=b,l.define((({Ref:e})=>({toolbar:[e(n.Toolbar)]})))},\n function _(t,e,o,s,i){var l;s();const n=t(1),a=t(19),r=t(63),c=t(56),_=t(111),h=t(20),u=t(10),v=t(34),p=t(9),f=t(8),d=t(292),g=t(293),m=t(294),b=t(299),w=t(301),y=t(302),T=t(304),x=t(296),z=n.__importStar(t(305)),L=z,P=n.__importStar(t(306)),I=P,C=n.__importDefault(t(123));class k extends _.UIElementView{constructor(){super(...arguments),this._tool_button_views=new Map,this._items=[],this._visible=null}get tool_buttons(){return this._tool_buttons.flat()}get overflow_el(){return this._overflow_el}get visible(){return!!this.model.visible&&(!this.model.autohide||(this._visible??!1))}*children(){yield*super.children(),yield*this._tool_button_views.values()}has_finished(){if(!super.has_finished())return!1;for(const t of this._tool_button_views.values())if(!t.has_finished())return!1;return!0}initialize(){super.initialize();const{location:t}=this.model,e=\"left\"==t||\"above\"==t,o=this.model.horizontal?\"vertical\":\"horizontal\";this._overflow_menu=new x.ContextMenu([],{target:this.el,orientation:o,reversed:e,prevent_hide:t=>t.composedPath().includes(this._overflow_el)})}async lazy_initialize(){await super.lazy_initialize(),await this._build_tool_button_views()}connect_signals(){super.connect_signals();const{buttons:t,tools:e,location:o,autohide:s}=this.model.properties;this.on_change([t,e],(async()=>{await this._build_tool_button_views(),this.render()})),this.on_change(o,(()=>{this.render()})),this.on_change(s,(()=>{this._on_visible_change()}))}stylesheets(){return[...super.stylesheets(),z.default,P.default,C.default]}remove(){(0,c.remove_views)(this._tool_button_views),super.remove()}async _build_tool_button_views(){this._tool_buttons=(()=>{const{buttons:t}=this.model;if(\"auto\"==t){return[...(0,p.values)(this.model.gestures).map((t=>t.tools)),this.model.actions,this.model.inspectors,this.model.auxiliaries].map((t=>t.filter((t=>t.visible)).map((t=>t.tool_button()))))}return(0,u.split)(t,null)})(),await(0,c.build_views)(this._tool_button_views,this._tool_buttons.flat(),{parent:this})}set_visibility(t){t!=this._visible&&(this._visible=t,this._on_visible_change())}_on_visible_change(){this.el.classList.toggle(L.hidden,!this.visible)}_after_resize(){super._after_resize(),this._after_render()}_menu_at(){switch(this.model.location){case\"right\":return{left_of:this._overflow_el};case\"left\":return{right_of:this._overflow_el};case\"above\":return{below:this._overflow_el};case\"below\":return{above:this._overflow_el}}}toggle_menu(){this._overflow_menu.toggle(this._menu_at())}render(){super.render(),this.el.classList.add(L[this.model.location]),this.el.classList.toggle(L.inner,this.model.inner),this._on_visible_change();const{horizontal:t}=this.model;if(this._overflow_el=(0,r.div)({class:L.tool_overflow,tabIndex:0},t?\"\\u22ee\":\"\\u22ef\"),this._overflow_el.addEventListener(\"click\",(t=>{this.toggle_menu()})),this._overflow_el.addEventListener(\"keydown\",(t=>{\"Enter\"==t.key&&this.toggle_menu()})),this._items=[],null!=this.model.logo){const t=\"grey\"===this.model.logo?I.grey:null,e=(0,r.a)({href:\"https://bokeh.org/\",target:\"_blank\",class:[I.logo,I.logo_small,t]});this._items.push(e),this.shadow_el.appendChild(e)}for(const[,t]of this._tool_button_views)t.render_to(this.shadow_el);const e=this._tool_buttons.map((t=>t.map((t=>this._tool_button_views.get(t).el)))).filter((t=>0!=t.length)),o=()=>(0,r.div)({class:L.divider});for(const t of(0,v.join)(e,o))this._items.push(t),this.shadow_el.append(t)}_after_render(){super._after_render(),(0,u.clear)(this._overflow_menu.items),this.shadow_el.contains(this._overflow_el)&&this.shadow_el.removeChild(this._overflow_el);for(const t of this._items)this.shadow_el.contains(t)||this.shadow_el.append(t);const{horizontal:t}=this.model,{bbox:e}=this,o=t?L.right:L.above;let s=0,i=!1;for(const l of this._items)if(i)this.shadow_el.removeChild(l),this._overflow_menu.items.push({custom:l,class:o});else{const{width:n,height:a}=l.getBoundingClientRect();s+=t?n:a,i=t?s>e.width-15:s>e.height-15,i&&(this.shadow_el.removeChild(l),this.shadow_el.appendChild(this._overflow_el),this._overflow_menu.items.push({custom:l,class:o}))}this._overflow_menu.is_open&&this._overflow_menu.show(this._menu_at())}}o.ToolbarView=k,k.__name__=\"ToolbarView\";const A=t(21),E=(0,A.Or)((0,A.Ref)(b.GestureTool),(0,A.Ref)(g.ToolProxy)),R=(0,A.Struct)({tools:(0,A.List)(E),active:(0,A.Nullable)(E)}),S=(0,A.Struct)({pan:R,scroll:R,pinch:R,rotate:R,move:R,tap:R,doubletap:R,press:R,pressup:R,multi:R});function B(){return{pan:{tools:[],active:null},scroll:{tools:[],active:null},pinch:{tools:[],active:null},rotate:{tools:[],active:null},move:{tools:[],active:null},tap:{tools:[],active:null},doubletap:{tools:[],active:null},press:{tools:[],active:null},pressup:{tools:[],active:null},multi:{tools:[],active:null}}}o.Inspection=d.Tool;class $ extends _.UIElement{constructor(t){super(t)}get horizontal(){return\"above\"==this.location||\"below\"==this.location}get vertical(){return\"left\"==this.location||\"right\"==this.location}connect_signals(){super.connect_signals();const{tools:t,active_drag:e,active_inspect:o,active_scroll:s,active_tap:i,active_multi:l}=this.properties;this.on_change([t,e,o,s,i,l],(()=>{this._init_tools(),this._activate_tools()}))}initialize(){super.initialize(),this._init_tools(),this._activate_tools()}_init_tools(){const t=new Set;function e(e,o){const s=(e instanceof g.ToolProxy?e.underlying:e)instanceof o;return s&&t.add(e),s}const o=this.tools.filter((t=>e(t,w.InspectTool)));this.inspectors=o;const s=this.tools.filter((t=>e(t,T.HelpTool)));this.help=s;const i=this.tools.filter((t=>e(t,y.ActionTool)));this.actions=i;const l={pan:{tools:[],active:null},scroll:{tools:[],active:null},pinch:{tools:[],active:null},rotate:{tools:[],active:null},move:{tools:[],active:null},tap:{tools:[],active:null},doubletap:{tools:[],active:null},press:{tools:[],active:null},pressup:{tools:[],active:null},multi:{tools:[],active:null}};for(const t of this.tools)e(t,b.GestureTool)&&l[t.event_role].tools.push(t);for(const t of(0,p.typed_keys)(l)){const e=this.gestures[t];e.tools=(0,u.sort_by)(l[t].tools,(t=>t.default_order)),null!=e.active&&(0,u.every)(e.tools,(t=>t.id!=e.active?.id))&&(e.active=null)}const n=this.tools.filter((e=>!t.has(e)));this.auxiliaries=n}_activate_tools(){if(\"auto\"==this.active_inspect);else if(null==this.active_inspect)for(const t of this.inspectors)t.active=!1;else if((0,f.isArray)(this.active_inspect)){const t=(0,u.intersection)(this.active_inspect,this.inspectors);t.length!=this.active_inspect.length&&(this.active_inspect=t);for(const t of this.inspectors)(0,u.includes)(this.active_inspect,t)||(t.active=!1)}else{let t=!1;for(const e of this.inspectors)e!=this.active_inspect?e.active=!1:t=!0;t||(this.active_inspect=null)}const t=t=>{t.active?this._active_change(t):t.active=!0};for(const t of(0,p.values)(this.gestures))for(const e of t.tools)this.connect(e.properties.active.change,(()=>this._active_change(e)));function e(t){switch(t){case\"tap\":return\"active_tap\";case\"pan\":return\"active_drag\";case\"pinch\":case\"scroll\":return\"active_scroll\";case\"multi\":return\"active_multi\";default:return null}}function o(t,e){return\"tap\"==t||\"pan\"==t||e.supports_auto()}const s=t=>this.tools.includes(t)||t instanceof d.Tool&&this.tools.some((e=>e instanceof g.ToolProxy&&e.tools.includes(t)));for(const[i,l]of(0,p.entries)(this.gestures)){const n=i,a=e(n);if(null!=a){const e=this[a];if(\"auto\"==e){if(0!=l.tools.length){const[e]=l.tools;o(n,e)&&t(e)}}else if(null!=e)s(e)?t(e):this[a]=null;else{this.gestures[n].active=null;for(const t of this.gestures[n].tools)t.active=!1}}}}_active_change(t){const{event_types:e}=t;for(const o of e)if(t.active){const e=this.gestures[o].active;null!=e&&t!=e&&(a.logger.debug(`Toolbar: deactivating tool: ${e} for event type '${o}'`),e.active=!1),this.gestures[o].active=t,a.logger.debug(`Toolbar: activating tool: ${t} for event type '${o}'`)}else this.gestures[o].active=null}}o.Toolbar=$,l=$,$.__name__=\"Toolbar\",l.prototype.default_view=k,l.define((({Bool:t,List:e,Or:s,Ref:i,Nullable:l,Auto:n})=>({tools:[e(s(i(d.Tool),i(g.ToolProxy))),[]],logo:[l(h.Logo),\"normal\"],autohide:[t,!1],active_drag:[l(s(E,n)),\"auto\"],active_inspect:[l(s(i(o.Inspection),e(i(o.Inspection)),i(g.ToolProxy),n)),\"auto\"],active_scroll:[l(s(E,n)),\"auto\"],active_tap:[l(s(E,n)),\"auto\"],active_multi:[l(s(E,n)),\"auto\"]}))),l.internal((({List:t,Bool:e,Ref:o,Or:s,Null:i,Auto:l})=>({buttons:[s(t(s(o(m.ToolButton),i)),l),\"auto\"],location:[h.Location,\"right\"],inner:[e,!1],gestures:[S,B],actions:[t(s(o(y.ActionTool),o(g.ToolProxy))),[]],inspectors:[t(s(o(w.InspectTool),o(g.ToolProxy))),[]],auxiliaries:[t(s(o(d.Tool),o(g.ToolProxy))),[]],help:[t(s(o(T.HelpTool),o(g.ToolProxy))),[]]})))},\n function _(t,e,n,o,i){var s;o();const r=t(58),a=t(20),l=t(10),c=t(8),_=t(51);class u extends r.View{connect_signals(){super.connect_signals(),this.connect(this.model.properties.active.change,(()=>{this.model.active?this.activate():this.deactivate()}))}get overlays(){return[]}activate(){}deactivate(){}}n.ToolView=u,u.__name__=\"ToolView\";class d extends _.Model{constructor(t){super(t)}get event_role(){const{event_type:t}=this;return(0,c.isString)(t)?t:\"multi\"}get event_types(){const{event_type:t}=this;return null==t?[]:(0,c.isString)(t)?[t]:t}get tooltip(){return this.description??this.tool_name}get computed_icon(){const{icon:t,tool_icon:e}=this;return t??(null!=e?`.${e}`:void 0)}get menu(){return null}supports_auto(){return!1}_get_dim_limits([t,e],[n,o],i,s){const r=i.bbox.h_range;let a;\"width\"==s||\"both\"==s?(a=[(0,l.min)([t,n]),(0,l.max)([t,n])],a=[(0,l.max)([a[0],r.start]),(0,l.min)([a[1],r.end])]):a=[r.start,r.end];const c=i.bbox.v_range;let _;return\"height\"==s||\"both\"==s?(_=[(0,l.min)([e,o]),(0,l.max)([e,o])],_=[(0,l.max)([_[0],c.start]),(0,l.min)([_[1],c.end])]):_=[c.start,c.end],[a,_]}_get_dim_tooltip(t){const{description:e,tool_name:n}=this;return null!=e?e:\"both\"==t?n:\"auto\"==t?`${n} (either x, y or both dimensions)`:`${n} (${\"width\"==t?\"x\":\"y\"}-axis)`}static register_alias(t,e){this.prototype._known_aliases.set(t,e)}static from_string(t){const e=this.prototype._known_aliases.get(t);if(null!=e)return e();{const e=[...this.prototype._known_aliases.keys()];throw new Error(`unexpected tool name '${t}', possible tools are ${e.join(\", \")}`)}}}n.Tool=d,s=d,d.__name__=\"Tool\",s.prototype._known_aliases=new Map,s.define((({Bool:t,Str:e,Regex:n,Nullable:o,Or:i})=>({icon:[o(i(a.ToolIcon,n(/^--/),n(/^\\./),n(/^data:image/))),null],description:[o(e),null],visible:[t,!0]}))),s.internal((({Bool:t})=>({active:[t,!1],disabled:[t,!1]})))},\n function _(t,o,e,s,n){var i;s();const l=t(15),r=t(51),c=t(292),u=t(34);class h extends r.Model{constructor(t){super(t)}get underlying(){return this.tools[0]}tool_button(){const t=this.tools[0].tool_button();return t.tool=this,t}get event_type(){return this.tools[0].event_type}get event_role(){return this.tools[0].event_role}get event_types(){return this.tools[0].event_types}get default_order(){return this.tools[0].default_order}get tooltip(){return this.tools[0].tooltip}get tool_name(){return this.tools[0].tool_name}get computed_icon(){return this.tools[0].computed_icon}get toggleable(){const t=this.tools[0];return\"toggleable\"in t&&t.toggleable}get visible(){return this.tools[0].visible}initialize(){super.initialize(),this.do=new l.Signal0(this,\"do\")}connect_signals(){super.connect_signals(),this.connect(this.do,(()=>this.doit())),this.connect(this.properties.active.change,(()=>this.set_active()));for(const t of this.tools)this.connect(t.properties.active.change,(()=>{this.active=t.active}))}doit(){for(const t of this.tools)t.do.emit()}set_active(){for(const t of this.tools)t.active=this.active}get menu(){const{menu:t}=this.tools[0];if(null==t)return null;const o=[];for(const[e,s]of(0,u.enumerate)(t))if(null==e)o.push(null);else{const t=()=>{for(const t of this.tools)t.menu?.[s]?.handler?.()};o.push({...e,handler:t})}return o}supports_auto(){return this.tools[0].supports_auto()}}e.ToolProxy=h,i=h,h.__name__=\"ToolProxy\",i.define((({Bool:t,List:o,Ref:e})=>({tools:[o(e(c.Tool)),[]],active:[t,t=>(0,u.some)(t.tools,(t=>t.active))],disabled:[t,!1]})))},\n function _(e,t,s,o,i){var l;o();const n=e(1),a=e(111),r=e(292),c=e(293),h=e(295),d=e(63),u=e(20),_=e(296),m=e(10),p=n.__importStar(e(298)),g=p,v=n.__importDefault(e(123));class f extends a.UIElementView{initialize(){super.initialize();const{location:e}=this.parent.model,t=\"left\"==e||\"above\"==e,s=this.parent.model.horizontal?\"vertical\":\"horizontal\",o=this.model.tool.menu??[];this._menu=new _.ContextMenu(t?(0,m.reversed)(o):o,{target:this.parent.el,orientation:s,prevent_hide:e=>e.composedPath().includes(this.el)}),this._ui_gestures=new h.UIGestures(this.el,{on_tap:e=>{this._menu.is_open?this._menu.hide():e.native.composedPath().includes(this.el)&&this._clicked()},on_press:()=>{this._pressed()}}),this.el.addEventListener(\"keydown\",(e=>{switch(e.key){case\"Enter\":this._clicked();break;case\" \":this._pressed()}}))}connect_signals(){super.connect_signals(),this._ui_gestures.connect_signals(),this.connect(this.model.change,(()=>this.render())),this.connect(this.model.tool.change,(()=>this.render()))}remove(){this._ui_gestures.remove(),this._menu.remove(),super.remove()}stylesheets(){return[...super.stylesheets(),p.default,v.default]}render(){super.render(),this.class_list.add(g[this.parent.model.location]),this.model.tool.disabled&&this.class_list.add(g.disabled);const e=(0,d.div)({class:g.tool_icon});this.shadow_el.appendChild(e);const t=this.model.icon??this.model.tool.computed_icon;if(null!=t)if(t.startsWith(\"data:image\")){const s=`url(\"${encodeURI(t)}\")`;e.style.backgroundImage=s}else if(t.startsWith(\"--\"))e.style.backgroundImage=`var(${t})`;else if(t.startsWith(\".\")){const s=t.substring(1);e.classList.add(s)}else if(u.ToolIcon.valid(t)){const s=`bk-tool-icon-${t.replace(/_/g,\"-\")}`;e.classList.add(s)}if(null!=this.model.tool.menu){const e=(0,d.div)({class:g.tool_chevron});this.shadow_el.appendChild(e)}const s=this.model.tooltip??this.model.tool.tooltip;this.el.title=s,this.el.tabIndex=0}_pressed(){const e=(()=>{switch(this.parent.model.location){case\"right\":return{left_of:this.el};case\"left\":return{right_of:this.el};case\"above\":return{below:this.el};case\"below\":return{above:this.el}}})();this._menu.toggle(e)}}s.ToolButtonView=f,f.__name__=\"ToolButtonView\";class b extends a.UIElement{constructor(e){super(e)}}s.ToolButton=b,l=b,b.__name__=\"ToolButton\",l.define((({Str:e,Regex:t,Ref:s,Nullable:o,Or:i})=>({tool:[i(s(r.Tool),s(c.ToolProxy))],icon:[o(i(u.ToolIcon,t(/^--/),t(/^\\./),t(/^data:image/))),null],tooltip:[o(e),null]})))},\n function _(t,s,e,i,n){i();const a=t(63),_=t(12);class r{constructor(t,s,e={}){this.phase=\"idle\",this.pointers=new Map,this.press_timer=null,this.tap_timestamp=-1/0,this.last_scale=null,this.last_rotation=null,this.hit_area=t,this.handlers=s,this.must_be_target=e.must_be_target??!1,this._pointer_over=this._pointer_over.bind(this),this._pointer_out=this._pointer_out.bind(this),this._pointer_down=this._pointer_down.bind(this),this._pointer_move=this._pointer_move.bind(this),this._pointer_up=this._pointer_up.bind(this),this._pointer_cancel=this._pointer_cancel.bind(this)}connect_signals(){this.hit_area.addEventListener(\"pointerover\",this._pointer_over),this.hit_area.addEventListener(\"pointerout\",this._pointer_out),this.hit_area.addEventListener(\"pointerdown\",this._pointer_down),this.hit_area.addEventListener(\"pointermove\",this._pointer_move),this.hit_area.addEventListener(\"pointerup\",this._pointer_up),this.hit_area.addEventListener(\"pointercancel\",this._pointer_cancel)}disconnect_signals(){this.hit_area.removeEventListener(\"pointerover\",this._pointer_over),this.hit_area.removeEventListener(\"pointerout\",this._pointer_out),this.hit_area.removeEventListener(\"pointerdown\",this._pointer_down),this.hit_area.removeEventListener(\"pointermove\",this._pointer_move),this.hit_area.removeEventListener(\"pointerup\",this._pointer_up),this.hit_area.removeEventListener(\"pointercancel\",this._pointer_cancel)}remove(){this.disconnect_signals()}_self_is_target(t){return t.composedPath()[0]==this.hit_area}_is_event_target(t){return!this.must_be_target||this._self_is_target(t)}reset(){this._cancel_timeout(),this.phase=\"idle\",this.pointers.clear(),this.press_timer=null,this.tap_timestamp=-1/0,this.last_scale=null,this.last_rotation=null}get _is_multi_gesture(){return this.pointers.size>=2}_within_threshold(t){const{dx:s,dy:e}=this._movement(t);return s**2+e**2<=r.move_threshold**2}get _any_movement(){return[...this.pointers.values()].some((t=>!this._within_threshold(t)))}_start_timeout(){(0,_.assert)(null==this.press_timer),this.press_timer=setTimeout((()=>this._pointer_timeout()),r.press_threshold)}_cancel_timeout(){const{press_timer:t}=this;null!=t&&(clearTimeout(t),this.press_timer=null)}_pointer_timeout(){(0,_.assert)(\"started\"==this.phase),(0,_.assert)(!this._is_multi_gesture),this.phase=\"pressing\",this.press_timer=null;const[t]=this.pointers.values();this.on_press(t.init)}_pointer_over(t){this._is_event_target(t)&&t.isPrimary&&this.on_enter(t)}_pointer_out(t){this._is_event_target(t)&&t.isPrimary&&this.on_leave(t)}_pointer_down(t){if(this._is_event_target(t)&&!this._is_multi_gesture&&!this.pointers.has(t.pointerId)&&(!t.isPrimary||\"mouse\"!=t.pointerType||t.buttons==a.MouseButton.Left)&&this.hit_area.isConnected)switch(this.pointers.set(t.pointerId,{init:t,last:t}),this.hit_area.setPointerCapture(t.pointerId),this.phase){case\"idle\":this.phase=\"started\",this._start_timeout();break;case\"started\":this._cancel_timeout()}}_pointer_move(t){if(!this._is_event_target(t))return;t.isPrimary&&this.on_move(t);const s=this.pointers.get(t.pointerId);if(null!=s)switch(s.last=t,this.phase){case\"idle\":this.reset(),(0,_.unreachable)();case\"started\":case\"transitional\":if(!this._any_movement)return;if(this._cancel_timeout(),this._is_multi_gesture){const[t,s]=this.pointers.values(),e=this._scale(t,s),i=this._rotation(t,s);Math.abs(e-1)>r.pinch_threshold?(this.phase=\"pinching\",this.on_pinch_start(t.init,s.init,1),this.on_pinch(t.last,s.last,e),this.last_scale=e):Math.abs(i)>r.rotate_threshold&&(this.phase=\"rotating\",this.on_rotate_start(t.init,s.init,0),this.on_rotate(s.last,s.last,i),this.last_rotation=i)}else{this.phase=\"panning\";const[t]=this.pointers.values(),{dx:s,dy:e}=this._movement(t);this.on_pan_start(t.init,0,0),this.on_pan(t.last,s,e)}break;case\"pressing\":break;case\"panning\":{const[s]=this.pointers.values(),{dx:e,dy:i}=this._movement(s);this.on_pan(t,e,i);break}case\"pinching\":{const[t,s]=this.pointers.values(),e=this._scale(t,s);e!=this.last_scale&&(this.on_pinch(t.last,s.last,e),this.last_scale=e);break}case\"rotating\":{const[t,s]=this.pointers.values(),e=this._rotation(t,s);e!=this.last_rotation&&(this.on_rotate(t.last,s.last,e),this.last_rotation=e);break}}}_pointer_up(t){if(!this._is_event_target(t))return;const s=this.pointers.get(t.pointerId);if(null!=s){switch(s.last=t,this._cancel_timeout(),this.phase){case\"idle\":this.reset(),(0,_.unreachable)();case\"started\":{const[t]=this.pointers.values(),{tap_timestamp:s}=this;t.last.timeStamp-s{this.entry_handler?.(t,e),t.handler?.(),this.hide()},this._on_mousedown=t=>{t.composedPath().includes(this.el)||this.prevent_hide?.(t)||this.hide()},this._on_keydown=t=>{\"Escape\"==t.key&&this.hide()},this._on_blur=()=>{this.hide()},this.items=t,this.target=e.target,this.orientation=e.orientation??\"vertical\",this.reversed=e.reversed??!1,this.prevent_hide=e.prevent_hide,this.extra_styles=e.extra_styles??[],this.entry_handler=e.entry_handler,this.shadow_el=this.el.attachShadow({mode:\"open\"}),this.class_list=new l.ClassList(this.el.classList)}remove(){this._unlisten(),this.el.remove()}_listen(){document.addEventListener(\"mousedown\",this._on_mousedown),document.addEventListener(\"keydown\",this._on_keydown),window.addEventListener(\"blur\",this._on_blur)}_unlisten(){document.removeEventListener(\"mousedown\",this._on_mousedown),document.removeEventListener(\"keydown\",this._on_keydown),window.removeEventListener(\"blur\",this._on_blur)}_position(t){const e=(()=>{if(\"left_of\"in t){const{left:e,top:i}=t.left_of.getBoundingClientRect();return{right:e,top:i}}if(\"right_of\"in t){const{top:e,right:i}=t.right_of.getBoundingClientRect();return{left:i,top:e}}if(\"below\"in t){const{left:e,bottom:i}=t.below.getBoundingClientRect();return{left:e,top:i}}if(\"above\"in t){const{left:e,top:i}=t.above.getBoundingClientRect();return{left:e,bottom:i}}return t})(),i=this.el.offsetParent??document.body,s=(()=>{const t=i.getBoundingClientRect(),e=getComputedStyle(i);return{left:t.left-parseFloat(e.marginLeft),right:t.right+parseFloat(e.marginRight),top:t.top-parseFloat(e.marginTop),bottom:t.bottom+parseFloat(e.marginBottom)}})(),{style:n}=this.el;n.left=null!=e.left?e.left-s.left+\"px\":\"auto\",n.top=null!=e.top?e.top-s.top+\"px\":\"auto\",n.right=null!=e.right?s.right-e.right+\"px\":\"auto\",n.bottom=null!=e.bottom?s.bottom-e.bottom+\"px\":\"auto\"}stylesheets(){return[c.default,a.default,u.default,...this.extra_styles]}empty(){(0,l.empty)(this.shadow_el),this.class_list.clear()}render(){this.empty();for(const t of this.stylesheets()){((0,r.isString)(t)?new l.InlineStyleSheet(t):t).install(this.shadow_el)}this.class_list.add(_[this.orientation]);const t=this.reversed?(0,h.reversed)(this.items):this.items;for(const[e,i]of(0,d.enumerate)(t)){let t;if(null==e)t=(0,l.div)({class:_.divider});else{if(null!=e.if&&!e.if())continue;if(null!=e.custom)t=e.custom;else{const s=null!=e.icon?(0,l.div)({class:[_.menu_icon,e.icon]}):null,n=[e.active?.()?_.active:null,e.class];t=(0,l.div)({class:n,title:e.tooltip,tabIndex:0},s,e.label,e.content),t.addEventListener(\"click\",(()=>{this._item_click(e,i)})),t.addEventListener(\"keydown\",(t=>{\"Enter\"==t.key&&this._item_click(e,i)}))}}this.shadow_el.appendChild(t)}}show(t){0!=this.items.length&&(this.render(),0!=this.shadow_el.children.length&&((this.target.shadowRoot??this.target).appendChild(this.el),this._position(t??{left:0,top:0}),this._listen(),this._open=!0))}hide(){this._open&&(this._open=!1,this._unlisten(),this.el.remove())}toggle(t){this._open?this.hide():this.show(t)}}i.ContextMenu=m,m.__name__=\"ContextMenu\"},\n function _(r,o,e,i,t){i(),e.menu_icon=\"bk-menu-icon\",e.horizontal=\"bk-horizontal\",e.vertical=\"bk-vertical\",e.divider=\"bk-divider\",e.active=\"bk-active\",e.default=\".bk-menu-icon{width:28px;height:28px;mask-size:60% 60%;mask-position:center center;mask-repeat:no-repeat;-webkit-mask-size:60% 60%;-webkit-mask-position:center center;-webkit-mask-repeat:no-repeat;background-size:60%;background-color:transparent;background-repeat:no-repeat;background-position:center center;}:host{position:absolute;display:inline-flex;flex-wrap:nowrap;user-select:none;-webkit-user-select:none;width:auto;height:auto;z-index:var(--bokeh-top-level);cursor:pointer;font-size:var(--font-size);background-color:#fff;border:1px solid #ccc;border-radius:var(--border-radius);box-shadow:2px 4px 8px rgba(0, 0, 0, 0.175);}:host(.bk-horizontal){flex-direction:row;}:host(.bk-vertical){flex-direction:column;}.bk-divider{cursor:default;overflow:hidden;background-color:#e5e5e5;}:host(.bk-horizontal) > .bk-divider{width:1px;margin:5px 0;}:host(.bk-vertical) > .bk-divider{height:1px;margin:0 5px;}:host > :not(.bk-divider){border:1px solid transparent;--active-tool-highlight:#26aae1;}:host > :not(.bk-divider).bk-active{border-color:var(--active-tool-highlight);}:host > :not(.bk-divider):hover{background-color:#f9f9f9;}:host > :not(.bk-divider):focus,:host > :not(.bk-divider):focus-visible{outline:1px dotted var(--active-tool-highlight);outline-offset:-1px;}:host > :not(.bk-divider)::-moz-focus-inner{border:0;}:host(.bk-horizontal) > :not(.bk-divider):first-child{border-top-left-radius:var(--border-radius);border-bottom-left-radius:var(--border-radius);}:host(.bk-horizontal) > :not(.bk-divider):last-child{border-top-right-radius:var(--border-radius);border-bottom-right-radius:var(--border-radius);}:host(.bk-vertical) > :not(.bk-divider):first-child{border-top-left-radius:var(--border-radius);border-top-right-radius:var(--border-radius);}:host(.bk-vertical) > :not(.bk-divider):last-child{border-bottom-left-radius:var(--border-radius);border-bottom-right-radius:var(--border-radius);}\"},\n function _(o,t,e,r,i){r(),e.tool_icon=\"bk-tool-icon\",e.disabled=\"bk-disabled\",e.tool_chevron=\"bk-tool-chevron\",e.above=\"bk-above\",e.below=\"bk-below\",e.left=\"bk-left\",e.right=\"bk-right\",e.active=\"bk-active\",e.default=\":host{--button-width:30px;--button-height:30px;--button-color:lightgray;--button-border:2px;--active-tool-highlight:#26aae1;--active-tool-border:var(--button-border) solid transparent;}:host{position:relative;width:var(--button-width);height:var(--button-height);cursor:pointer;user-select:none;-webkit-user-select:none;touch-action:none;}.bk-tool-icon{position:relative;top:calc(var(--button-border)/2);width:calc(var(--button-width) - var(--button-border));height:calc(var(--button-height) - var(--button-border));mask-size:60% 60%;mask-position:center center;mask-repeat:no-repeat;-webkit-mask-size:60% 60%;-webkit-mask-position:center center;-webkit-mask-repeat:no-repeat;background-size:60% 60%;background-origin:border-box;background-position:center center;background-repeat:no-repeat;}:host(.bk-disabled) .bk-tool-icon{background-color:var(--bokeh-icon-color-disabled);cursor:not-allowed;}.bk-tool-chevron{position:absolute;visibility:hidden;width:8px;height:8px;mask-size:100% 100%;mask-position:center center;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;-webkit-mask-position:center center;-webkit-mask-repeat:no-repeat;}:host(:hover) .bk-tool-chevron{visibility:visible;}:host(.bk-above) .bk-tool-chevron{right:0;bottom:0;background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-chevron-down);-webkit-mask-image:var(--bokeh-icon-chevron-down);}:host(.bk-below) .bk-tool-chevron{right:0;top:0;background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-chevron-up);-webkit-mask-image:var(--bokeh-icon-chevron-up);}:host(.bk-left) .bk-tool-chevron{right:0;bottom:0;background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-chevron-right);-webkit-mask-image:var(--bokeh-icon-chevron-right);}:host(.bk-right) .bk-tool-chevron{left:0;bottom:0;background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-chevron-left);-webkit-mask-image:var(--bokeh-icon-chevron-left);}:host(:hover){background-color:rgba(192, 192, 192, 0.15);}:host(:focus),:host(:focus-visible){outline:1px dotted var(--active-tool-highlight);outline-offset:-1px;}:host::-moz-focus-inner{border:0;}:host(.bk-above){border-bottom:var(--active-tool-border);}:host(.bk-above.bk-active){border-bottom-color:var(--active-tool-highlight);}:host(.bk-below){border-top:var(--active-tool-border);}:host(.bk-below.bk-active){border-top-color:var(--active-tool-highlight);}:host(.bk-right){border-left:var(--active-tool-border);}:host(.bk-right.bk-active){border-left-color:var(--active-tool-highlight);}:host(.bk-left){border-right:var(--active-tool-border);}:host(.bk-left.bk-active){border-right-color:var(--active-tool-highlight);}\"},\n function _(e,o,t,n,s){n();const r=e(292),l=e(300);class u extends r.ToolView{get plot_view(){return this.parent}}t.GestureToolView=u,u.__name__=\"GestureToolView\";class _ extends r.Tool{constructor(e){super(e)}tool_button(){return new l.OnOffButton({tool:this})}}t.GestureTool=_,_.__name__=\"GestureTool\"},\n function _(t,e,o,n,s){var i;n();const c=t(1),_=t(294),l=c.__importStar(t(298));class a extends _.ToolButtonView{_toggle_active(){this.class_list.toggle(l.active,this.model.tool.active)}connect_signals(){super.connect_signals();const{active:t}=this.model.tool.properties;this.on_change(t,(()=>{this._toggle_active()}))}render(){super.render(),this._toggle_active()}_clicked(){const{active:t}=this.model.tool;this.model.tool.active=!t}}o.OnOffButtonView=a,a.__name__=\"OnOffButtonView\";class r extends _.ToolButton{constructor(t){super(t)}}o.OnOffButton=r,i=r,r.__name__=\"OnOffButton\",i.prototype.default_view=a},\n function _(e,t,o,n,s){var i;n();const l=e(1),r=e(292),_=e(300),c=l.__importStar(e(18));class a extends r.ToolView{get plot_view(){return this.parent}}o.InspectToolView=a,a.__name__=\"InspectToolView\";class p extends r.Tool{constructor(e){super(e),this.event_type=\"move\"}tool_button(){return new _.OnOffButton({tool:this})}}o.InspectTool=p,i=p,p.__name__=\"InspectTool\",i.define((()=>({toggleable:[new c.PropertyAlias(\"visible\")]}))),i.override({active:!0})},\n function _(o,t,n,s,i){s();const e=o(292),c=o(303),l=o(15);class _ extends e.ToolView{connect_signals(){super.connect_signals(),this.connect(this.model.do,(o=>this.doit(o)))}}n.ActionToolView=_,_.__name__=\"ActionToolView\";class a extends e.Tool{constructor(o){super(o),this.do=new l.Signal(this,\"do\")}tool_button(){return new c.ClickButton({tool:this})}}n.ActionTool=a,a.__name__=\"ActionTool\"},\n function _(t,o,e,n,i){var c;n();const l=t(294);class _ extends l.ToolButtonView{_clicked(){this.model.tool.do.emit(void 0)}}e.ClickButtonView=_,_.__name__=\"ClickButtonView\";class s extends l.ToolButton{constructor(t){super(t)}}e.ClickButton=s,c=s,s.__name__=\"ClickButton\",c.prototype.default_view=_},\n function _(o,e,t,l,i){var n;l();const s=o(302),r=o(123);class c extends s.ActionToolView{doit(){window.open(this.model.redirect)}}t.HelpToolView=c,c.__name__=\"HelpToolView\";class _ extends s.ActionTool{constructor(o){super(o),this.tool_name=\"Help\",this.tool_icon=r.tool_icon_help}}t.HelpTool=_,n=_,_.__name__=\"HelpTool\",n.prototype.default_view=c,n.define((({Str:o})=>({redirect:[o,\"https://docs.bokeh.org/en/latest/docs/user_guide/interaction/tools.html\"]}))),n.override({description:\"Click the question mark to learn more about Bokeh plot tools.\"}),n.register_alias(\"help\",(()=>new n))},\n function _(o,t,e,i,l){i(),e.inner=\"bk-inner\",e.hidden=\"bk-hidden\",e.logo=\"bk-logo\",e.above=\"bk-above\",e.below=\"bk-below\",e.left=\"bk-left\",e.right=\"bk-right\",e.divider=\"bk-divider\",e.tool_overflow=\"bk-tool-overflow\",e.horizontal=\"bk-horizontal\",e.vertical=\"bk-vertical\",e.default=':host{--button-width:30px;--button-height:30px;--button-color:lightgray;}:host{display:flex;flex-wrap:nowrap;align-items:center;user-select:none;-webkit-user-select:none;}:host(.bk-inner){background-color:white;opacity:0.8;}:host(.bk-hidden){visibility:hidden;opacity:0;transition:visibility 0.3s linear, opacity 0.3s linear;}.bk-logo{flex-shrink:0;}:host(.bk-above),:host(.bk-below){flex-direction:row;justify-content:flex-end;}:host(.bk-above) .bk-logo,:host(.bk-below) .bk-logo{order:1;margin-left:5px;margin-right:0px;}:host(.bk-left),:host(.bk-right){flex-direction:column;justify-content:flex-start;}:host(.bk-left) .bk-logo,:host(.bk-right) .bk-logo{order:0;margin-bottom:5px;margin-top:0px;}.bk-divider{content:\" \";display:inline-block;background-color:var(--button-color);}:host(.bk-above) .bk-divider,:host(.bk-below) .bk-divider{height:10px;width:1px;}:host(.bk-left) .bk-divider,:host(.bk-right) .bk-divider{height:1px;width:10px;}.bk-tool-overflow{color:gray;display:flex;align-items:center;}.bk-tool-overflow:hover{background-color:rgba(192, 192, 192, 0.15);}.bk-tool-overflow:focus,.bk-tool-overflow:focus-visible{outline:1px dotted var(--active-tool-highlight);outline-offset:-1px;}.bk-tool-overflow::-moz-focus-inner{border:0;}:host(.bk-above) .bk-tool-overflow,:host(.bk-below) .bk-tool-overflow,:host(.bk-horizontal) .bk-tool-overflow{width:calc(var(--button-width)/2);height:var(--button-height);flex-direction:row;}:host(.bk-left) .bk-tool-overflow,:host(.bk-right) .bk-tool-overflow,:host(.bk-vertical) .bk-tool-overflow{width:var(--button-width);height:calc(var(--button-height)/2);flex-direction:column;}'},\n function _(A,l,g,o,d){o(),g.logo=\"bk-logo\",g.grey=\"bk-grey\",g.logo_small=\"bk-logo-small\",g.default=\".bk-logo{margin:5px;position:relative;display:block;background-repeat:no-repeat;}.bk-logo.bk-grey{filter:grayscale(100%);}.bk-logo-small{width:20px;height:20px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAOkSURBVDiNjZRtaJVlGMd/1/08zzln5zjP1LWcU9N0NkN8m2CYjpgQYQXqSs0I84OLIC0hkEKoPtiH3gmKoiJDU7QpLgoLjLIQCpEsNJ1vqUOdO7ppbuec5+V+rj4ctwzd8IIbbi6u+8f1539dt3A78eXC7QizUF7gyV1fD1Yqg4JWz84yffhm0qkFqBogB9rM8tZdtwVsPUhWhGcFJngGeWrPzHm5oaMmkfEg1usvLFyc8jLRqDOMru7AyC8saQr7GG7f5fvDeH7Ej8CM66nIF+8yngt6HWaKh7k49Soy9nXurCi1o3qUbS3zWfrYeQDTB/Qj6kX6Ybhw4B+bOYoLKCC9H3Nu/leUTZ1JdRWkkn2ldcCamzrcf47KKXdAJllSlxAOkRgyHsGC/zRday5Qld9DyoM4/q/rUoy/CXh3jzOu3bHUVZeU+DEn8FInkPBFlu3+nW3Nw0mk6vCDiWg8CeJaxEwuHS3+z5RgY+YBR6V1Z1nxSOfoaPa4LASWxxdNp+VWTk7+4vzaou8v8PN+xo+KY2xsw6une2frhw05CTYOmQvsEhjhWjn0bmXPjpE1+kplmmkP3suftwTubK9Vq22qKmrBhpY4jvd5afdRA3wGjFAgcnTK2s4hY0/GPNIb0nErGMCRxWOOX64Z8RAC4oCXdklmEvcL8o0BfkNK4lUg9HTl+oPlQxdNo3Mg4Nv175e/1LDGzZen30MEjRUtmXSfiTVu1kK8W4txyV6BMKlbgk3lMwYCiusNy9fVfvvwMxv8Ynl6vxoByANLTWplvuj/nF9m2+PDtt1eiHPBr1oIfhCChQMBw6Aw0UulqTKZdfVvfG7VcfIqLG9bcldL/+pdWTLxLUy8Qq38heUIjh4XlzZxzQm19lLFlr8vdQ97rjZVOLf8nclzckbcD4wxXMidpX30sFd37Fv/GtwwhzhxGVAprjbg0gCAEeIgwCZyTV2Z1REEW8O4py0wsjeloKoMr6iCY6dP92H6Vw/oTyICIthibxjm/DfN9lVz8IqtqKYLUXfoKVMVQVVJOElGjrnnUt9T9wbgp8AyYKaGlqingHZU/uG2NTZSVqwHQTWkx9hxjkpWDaCg6Ckj5qebgBVbT3V3NNXMSiWSDdGV3hrtzla7J+duwPOToIg42ChPQOQjspnSlp1V+Gjdged7+8UN5CRAV7a5EdFNwCjEaBR27b3W890TE7g24NAP/mMDXRWrGoFPQI9ls/MWO2dWFAar/xcOIImbbpA3zgAAAABJRU5ErkJggg==);}\"},\n function _(e,t,i,s,a){var l;s();const r=e(261),_=e(168),h=e(24),n=e(56),o=e(80);class d extends r.UpperLowerView{*children(){yield*super.children();const{lower_head:e,upper_head:t}=this;null!=e&&(yield e),null!=t&&(yield t)}async lazy_initialize(){await super.lazy_initialize();const{lower_head:e,upper_head:t}=this.model;null!=e&&(this.lower_head=await(0,n.build_view)(e,{parent:this})),null!=t&&(this.upper_head=await(0,n.build_view)(t,{parent:this}))}set_data(e){super.set_data(e);const t=h.Indices.all_set(this._lower.length);this.lower_head?.set_data(e,t),this.upper_head?.set_data(e,t)}_paint_data(e){if(this.visuals.line.doit)for(let t=0,i=this._lower_sx.length;t({lower_head:[t(e(_.ArrowHead)),()=>new _.TeeHead({size:10})],upper_head:[t(e(_.ArrowHead)),()=>new _.TeeHead({size:10})]}))),l.override({level:\"underlay\"})},\n function _(L,e,T,l,H){l(),H(\"HTMLLabel\",L(309).HTMLLabel),H(\"HTMLLabelSet\",L(311).HTMLLabelSet),H(\"HTMLTitle\",L(312).HTMLTitle)},\n function _(t,e,s,i,o){var n;i();const a=t(1),l=t(310),r=t(11),_=t(20),c=t(180),u=t(173),d=a.__importStar(t(80));class h extends l.TextAnnotationView{update_layout(){const{panel:t}=this;this.layout=null!=t?new u.SideLayout(t,(()=>this.get_size()),!1):void 0}_get_size(){const{text:t}=this.model,e=new c.TextBox({text:t}),{angle:s,angle_units:i}=this.model;e.angle=(0,r.compute_angle)(s,i),e.visuals=this.visuals.text.values();const o=e.size(),{padding:n}=this;return{width:o.width+n.left+n.right,height:o.height+n.top+n.bottom}}_paint(){const{angle:t,angle_units:e}=this.model,s=(0,r.compute_angle)(t,e),i=null!=this.layout?this.layout:this.plot_view.frame,o=this.coordinates.x_scale,n=this.coordinates.y_scale;let a=(()=>{switch(this.model.x_units){case\"canvas\":return this.model.x;case\"screen\":return i.bbox.xview.compute(this.model.x);case\"data\":return o.compute(this.model.x)}})(),l=(()=>{switch(this.model.y_units){case\"canvas\":return this.model.y;case\"screen\":return i.bbox.yview.compute(this.model.y);case\"data\":return n.compute(this.model.y)}})();a+=this.model.x_offset,l-=this.model.y_offset,this._paint_text(this.layer.ctx,this.model.text,a,l,s)}}s.HTMLLabelView=h,h.__name__=\"HTMLLabelView\";class x extends l.TextAnnotation{constructor(t){super(t)}}s.HTMLLabel=x,n=x,x.__name__=\"HTMLLabel\",n.prototype.default_view=h,n.mixins([d.Text,[\"border_\",d.Line],[\"background_\",d.Fill],[\"background_\",d.Hatch]]),n.define((({Float:t,Str:e,Angle:s})=>({x:[t],x_units:[_.CoordinateUnits,\"data\"],y:[t],y_units:[_.CoordinateUnits,\"data\"],text:[e,\"\"],angle:[s,0],angle_units:[_.AngleUnits,\"rad\"],x_offset:[t,0],y_offset:[t,0]}))),n.override({background_fill_color:null,background_hatch_color:null,border_line_color:null})},\n function _(t,e,n,i,s){var o;i();const r=t(1),a=t(81),l=t(63),d=t(173),p=t(185),h=r.__importStar(t(186));class u extends a.AnnotationView{rendering_target(){return this.plot_view.canvas_view.overlays_el}update_layout(){const{panel:t}=this;this.layout=null!=t?new d.SideLayout(t,(()=>this.get_size()),!0):void 0}initialize(){super.initialize()}connect_signals(){super.connect_signals(),this.connect(this.model.change,(()=>this.paint()))}paint(){this.model.visible?super.paint():(0,l.undisplay)(this.el)}get padding(){return h.padding(this.model.padding)}get border_radius(){return h.border_radius(this.model.border_radius)}render(){super.render(),this.text_el=document.createTextNode(\"\"),this.shadow_el.append(this.text_el)}_paint_text(t,e,n,i,s){const{el:o}=this;(0,l.undisplay)(o),this.text_el.textContent=e,this.visuals.text.set_value(t);const{padding:r,border_radius:a}=this;this.position.replace(`\\n :host {\\n position: absolute;\\n left: ${n}px;\\n top: ${i}px;\\n }\\n `),this.style.replace(`\\n :host {\\n color: ${t.fillStyle};\\n -webkit-text-stroke: 1px ${t.strokeStyle};\\n font: ${t.font};\\n white-space: pre;\\n\\n padding-left: ${r.left}px;\\n padding-right: ${r.right}px;\\n padding-top: ${r.top}px;\\n padding-bottom: ${r.bottom}px;\\n\\n border-top-left-radius: ${a.top_left}px;\\n border-top-right-radius: ${a.top_right}px;\\n border-bottom-right-radius: ${a.bottom_right}px;\\n border-bottom-left-radius: ${a.bottom_left}px;\\n }\\n `);const[d,p]=(()=>{switch(this.visuals.text.text_align.get_value()){case\"left\":return[\"left\",\"0%\"];case\"center\":return[\"center\",\"-50%\"];case\"right\":return[\"right\",\"-100%\"]}})(),[h,u]=(()=>{switch(this.visuals.text.text_baseline.get_value()){case\"top\":return[\"top\",\"0%\"];case\"middle\":default:return[\"center\",\"-50%\"];case\"bottom\":return[\"bottom\",\"-100%\"]}})();let _=`translate(${p}, ${u})`;0!=s&&(_+=` rotate(${s}rad)`),this.style.append(`\\n :host {\\n transform-origin: ${d} ${h};\\n transform: ${_};\\n }\\n `),this.layout,this.visuals.background_fill.doit&&(this.visuals.background_fill.set_value(t),this.style.append(`\\n :host {\\n background-color: ${t.fillStyle};\\n }\\n `)),this.visuals.border_line.doit&&(this.visuals.border_line.set_value(t),this.style.append(`\\n :host {\\n border-style: ${t.getLineDash().length<2?\"solid\":\"dashed\"};\\n border-width: ${t.lineWidth}px;\\n border-color: ${t.strokeStyle};\\n }\\n `)),(0,l.display)(o)}}n.TextAnnotationView=u,u.__name__=\"TextAnnotationView\";class _ extends a.Annotation{constructor(t){super(t)}}n.TextAnnotation=_,o=_,_.__name__=\"TextAnnotation\",o.define((()=>({padding:[p.Padding,0],border_radius:[p.BorderRadius,0]})))},\n function _(t,e,s,i,r){var l;i();const a=t(1),o=t(127),n=a.__importStar(t(80)),c=t(20),_=t(63),h=a.__importStar(t(18)),u=t(24),d=t(12);class y extends o.DataAnnotationView{constructor(){super(...arguments),this.els=[]}set_data(t){super.set_data(t),this.els.forEach((t=>t.remove())),this.els=[...this.text.map((()=>(0,_.div)({style:{display:\"none\"}})))],this.plot_view.canvas_view.overlays_el.append(...this.els)}remove(){this.els.forEach((t=>t.remove())),this.els=[],super.remove()}_rerender(){this.paint()}map_data(){const{x_scale:t,y_scale:e}=this.coordinates,s=null!=this.layout?this.layout:this.plot_view.frame;this.sx=(()=>{switch(this.model.x_units){case\"canvas\":return new u.ScreenArray(this._x);case\"screen\":return s.bbox.xview.v_compute(this._x);case\"data\":return t.v_compute(this._x)}})(),this.sy=(()=>{switch(this.model.y_units){case\"canvas\":return new u.ScreenArray(this._y);case\"screen\":return s.bbox.yview.v_compute(this._y);case\"data\":return e.v_compute(this._y)}})()}_paint_data(){const{ctx:t}=this.layer;for(let e=0,s=this.text.length;e{switch(this.visuals.text.text_align.get(e)){case\"left\":return[\"left\",\"0%\"];case\"center\":return[\"center\",\"-50%\"];case\"right\":return[\"right\",\"-100%\"]}})(),[c,h]=(()=>{switch(this.visuals.text.text_baseline.get(e)){case\"top\":return[\"top\",\"0%\"];case\"middle\":default:return[\"center\",\"-50%\"];case\"bottom\":return[\"bottom\",\"-100%\"]}})();let u=`translate(${n}, ${h})`;0!=l&&(u+=`rotate(${l}rad)`),a.style.transformOrigin=`${o} ${c}`,a.style.transform=u,this.layout,this.visuals.background_fill.doit&&(this.visuals.background_fill.set_vectorize(t,e),a.style.backgroundColor=t.fillStyle),this.visuals.border_line.doit&&(this.visuals.border_line.set_vectorize(t,e),a.style.borderStyle=t.getLineDash().length<2?\"solid\":\"dashed\",a.style.borderWidth=`${t.lineWidth}px`,a.style.borderColor=t.strokeStyle),(0,_.display)(a)}}s.HTMLLabelSetView=y,y.__name__=\"HTMLLabelSetView\";class p extends o.DataAnnotation{constructor(t){super(t)}}s.HTMLLabelSet=p,l=p,p.__name__=\"HTMLLabelSet\",l.prototype.default_view=y,l.mixins([n.TextVector,[\"border_\",n.LineVector],[\"background_\",n.FillVector]]),l.define((()=>({x:[h.XCoordinateSpec,{field:\"x\"}],y:[h.YCoordinateSpec,{field:\"y\"}],x_units:[c.CoordinateUnits,\"data\"],y_units:[c.CoordinateUnits,\"data\"],text:[h.NullStringSpec,{field:\"text\"}],angle:[h.AngleSpec,0],x_offset:[h.NumberSpec,{value:0}],y_offset:[h.NumberSpec,{value:0}]}))),l.override({background_fill_color:null,border_line_color:null})},\n function _(t,e,i,o,a){var l;o();const s=t(1),n=t(310),r=t(20),c=t(180),h=s.__importStar(t(80));class _ extends n.TextAnnotationView{_get_location(){const t=this.model.offset,e=this.model.standoff/2;let i,o;const{bbox:a}=this.layout;switch(this.panel.side){case\"above\":case\"below\":switch(this.model.vertical_align){case\"top\":o=a.top+e;break;case\"middle\":o=a.vcenter;break;case\"bottom\":o=a.bottom-e}switch(this.model.align){case\"left\":i=a.left+t;break;case\"center\":i=a.hcenter;break;case\"right\":i=a.right-t}break;case\"left\":switch(this.model.vertical_align){case\"top\":i=a.left+e;break;case\"middle\":i=a.hcenter;break;case\"bottom\":i=a.right-e}switch(this.model.align){case\"left\":o=a.bottom-t;break;case\"center\":o=a.vcenter;break;case\"right\":o=a.top+t}break;case\"right\":switch(this.model.vertical_align){case\"top\":i=a.right-e;break;case\"middle\":i=a.hcenter;break;case\"bottom\":i=a.left+e}switch(this.model.align){case\"left\":o=a.top+t;break;case\"center\":o=a.vcenter;break;case\"right\":o=a.bottom-t}}return[i,o]}_paint(){const{text:t}=this.model;if(0==t.length)return;this.model.text_baseline=this.model.vertical_align,this.model.text_align=this.model.align;const[e,i]=this._get_location(),o=this.panel.get_label_angle_heuristic(\"parallel\");this._paint_text(this.layer.ctx,t,e,i,o)}_get_size(){const{text:t}=this.model,e=new c.TextBox({text:t});e.visuals=this.visuals.text.values();const i=e.size(),{padding:o}=this,a=i.width+o.left+o.right,l=i.height+o.top+o.bottom;return{width:a,height:0==l?0:2+l+this.model.standoff}}}i.HTMLTitleView=_,_.__name__=\"HTMLTitleView\";class d extends n.TextAnnotation{constructor(t){super(t)}}i.HTMLTitle=d,l=d,d.__name__=\"HTMLTitle\",l.prototype.default_view=_,l.mixins([h.Text,[\"border_\",h.Line],[\"background_\",h.Fill],[\"background_\",h.Hatch]]),l.define((({Float:t,Str:e})=>({text:[e,\"\"],vertical_align:[r.VerticalAlign,\"bottom\"],align:[r.TextAlign,\"left\"],offset:[t,0],standoff:[t,10]}))),l.prototype._props.text_align.options.internal=!0,l.prototype._props.text_baseline.options.internal=!0,l.override({text_font_size:\"13px\",text_font_style:\"bold\",text_line_height:1,background_fill_color:null,background_hatch_color:null,border_line_color:null})},\n function _(i,e,l,o,g){o(),g(\"CustomJS\",i(314).CustomJS),g(\"OpenURL\",i(315).OpenURL),g(\"SetValue\",i(316).SetValue),g(\"ToggleVisibility\",i(317).ToggleVisibility),g(\"OpenDialog\",i(318).OpenDialog),g(\"CloseDialog\",i(322).CloseDialog)},\n function _(require,module,exports,__esModule,__esExport){var _a;__esModule();const callback_1=require(119),object_1=require(9),array_1=require(10),string_1=require(40),logging_1=require(19),types_1=require(8),standalone_1=require(54);class CustomJS extends callback_1.Callback{constructor(t){super(t),this._state=null}connect_signals(){super.connect_signals();const{args:t,code:e,module:s}=this.properties;this.on_change([t,e,s],(()=>this._state=null))}async _compile_module(){const url=URL.createObjectURL(new Blob([this.code],{type:\"text/javascript\"}));try{const module=await eval(`import(\"${url}\")`);return(0,types_1.isFunction)(module.default)?module.default:(logging_1.logger.warn(\"custom ES module didn't export a default function\"),()=>{})}finally{URL.revokeObjectURL(url)}}async _compile_function(){const[t=[],e=[]]=(0,array_1.unzip)((0,object_1.entries)(this.args)),s=(0,string_1.use_strict)(this.code),o=new Function(...t,\"cb_obj\",\"cb_data\",\"cb_context\",s);return function(...t){return o.call(this,...e,...t)}}_is_es_module(t){return t.split(\"\\n\").some((t=>t.trimStart().startsWith(\"export default\")))}async _compile(){const t=(()=>\"auto\"==this.module?this._is_es_module(this.code):this.module)();return t?{func:await this._compile_module(),module:t}:{func:await this._compile_function(),module:t}}async state(){return null==this._state&&(this._state=await this._compile()),this._state}async execute(t,e={}){const{func:s,module:o}=await this.state(),n={index:standalone_1.index};return o?s((0,object_1.to_object)(this.args),t,e,n):s.call(t,t,e,n)}}exports.CustomJS=CustomJS,_a=CustomJS,CustomJS.__name__=\"CustomJS\",_a.define((({Unknown:t,Str:e,Dict:s,Auto:o,Or:n,Bool:i})=>({args:[s(t),{}],code:[e],module:[n(o,i),\"auto\"]})))},\n function _(e,t,o,n,s){var i;n();const c=e(119),r=e(244),a=e(8);class d extends c.Callback{constructor(e){super(e)}navigate(e){this.same_tab?window.location.href=e:window.open(e)}execute(e,{source:t}){const o=e=>{const o=(0,r.replace_placeholders)(this.url,t,e,void 0,void 0,encodeURI);if(!(0,a.isString)(o))throw new Error(\"HTML output is not supported in this context\");this.navigate(o)},{selected:n}=t;for(const e of n.indices)o(e);for(const e of n.line_indices)o(e)}}o.OpenURL=d,i=d,d.__name__=\"OpenURL\",i.define((({Bool:e,Str:t})=>({url:[t,\"http://\"],same_tab:[e,!1]})))},\n function _(e,t,r,o,a){var n;o();const s=e(119),c=e(14),l=e(19);class u extends s.Callback{constructor(e){super(e)}execute(){const{obj:e,attr:t,value:r}=this;t in e.properties?e.setv({[t]:r}):l.logger.error(`${e.type}.${t} is not a property`)}}r.SetValue=u,n=u,u.__name__=\"SetValue\",n.define((({Str:e,Unknown:t,Ref:r})=>({obj:[r(c.HasProps)],attr:[e],value:[t]})))},\n function _(e,i,t,s,l){var n;s();const c=e(119),a=e(111);class o extends c.Callback{constructor(e){super(e)}execute(){const{target:e}=this;e.visible=!e.visible}}t.ToggleVisibility=o,n=o,o.__name__=\"ToggleVisibility\",n.define((({Ref:e})=>({target:[e(a.UIElement)]})))},\n function _(e,n,a,i,l){var o;i();const t=e(119),c=e(319);class s extends t.Callback{constructor(e){super(e)}async execute(){const{dialog:e}=this,n=e.document?.views_manager;if(null!=n){let a=n.find_one(e);null==a&&(a=await n.build_view(e)),a.open()}}}a.OpenDialog=s,o=s,s.__name__=\"OpenDialog\",o.define((({Ref:e})=>({dialog:[e(c.Dialog)]})))},\n function _(t,e,i,s,n){var o;s();const l=t(1),r=t(111),a=t(125),h=t(320),_=t(63),m=t(8),c=t(56),d=t(64),p=t(10),b=t(34),u=t(12),g=l.__importStar(t(263)),f=t(60),x=t(21),v=l.__importStar(t(321)),z=v,w=l.__importDefault(t(123)),y=((0,x.Or)((0,x.Ref)(r.UIElement),(0,x.Ref)(a.DOMNode)),[]),L=(()=>{const t=(0,_.div)(),e=t.attachShadow({mode:\"open\"});return new _.InlineStyleSheet(\"\\n:host {\\n display: flex;\\n flex-direction: column;\\n flex-wrap: nowrap;\\n position: fixed;\\n left: 0;\\n bottom: 0;\\n width: max-content;\\n height: max-content;\\n}\\n:host:empty {\\n display: none;\\n}\\n\").install(e),(0,_.dom_ready)().then((()=>document.body.append(t))),t})();class E extends r.UIElementView{constructor(){super(...arguments),this._position=new _.InlineStyleSheet,this._stacking=new _.InlineStyleSheet,this._has_rendered=!1,this._pinned=!1,this._normal_bbox=null,this._collapsed=!1,this._minimized=!1,this._maximized=!1}*children(){yield*super.children(),yield this._title,yield this._content}stylesheets(){return[...super.stylesheets(),v.default,w.default,this._position,this._stacking]}async lazy_initialize(){await super.lazy_initialize();const t=(()=>{const{title:t}=this.model;return(0,m.isString)(t)||null==t?new h.Text({content:t??\"\"}):t})(),e=(()=>{const{content:t}=this.model;return(0,m.isString)(t)?new h.Text({content:t}):t})();this._title=await(0,c.build_view)(t,{parent:this}),this._content=await(0,c.build_view)(e,{parent:this})}connect_signals(){super.connect_signals();const{visible:t}=this.model.properties;this.connect(t.change,(()=>this._toggle(this.model.visible)))}remove(){(0,p.remove)(y,this),this._content.remove(),this._title.remove(),super.remove()}_reposition(t){this._position.replace(\":host\",{left:\"left\"in t?(0,_.px)(t.left):\"unset\",right:\"right\"in t?(0,_.px)(t.right):\"unset\",top:\"top\"in t?(0,_.px)(t.top):\"unset\",bottom:\"bottom\"in t?(0,_.px)(t.bottom):\"unset\",width:\"width\"in t?(0,_.px)(t.width):\"unset\",height:\"height\"in t?(0,_.px)(t.height):\"unset\"}),this.update_bbox()}render(){super.render(),this._title.render(),this._content.render();const t=(0,_.div)({class:z.inner});this.shadow_el.append(t);const e=(0,_.div)({class:z.header}),i=(0,_.div)({class:z.content},this._content.el),s=(0,_.div)({class:z.footer});t.append(e),t.append(i),t.append(s);const n=(0,_.div)({class:z.grip}),o=(0,_.div)({class:z.title},n,this._title.el),l=(0,_.div)({class:z.controls});e.append(o,l);const r=(0,_.div)({class:[z.ctrl,z.pin],title:\"Pin\"});r.addEventListener(\"click\",(()=>this.pin())),this._pin_el=r;const a=(0,_.div)({class:[z.ctrl,z.collapse],title:\"Collapse\"});a.addEventListener(\"click\",(()=>this.collapse())),this._collapse_el=a;const h=(0,_.div)({class:[z.ctrl,z.minimize],title:\"Minimize\"});h.addEventListener(\"click\",(()=>this.minimize())),this._minimize_el=h;const m=(0,_.div)({class:[z.ctrl,z.maximize],title:\"Maximize\"});m.addEventListener(\"click\",(()=>this.maximize())),this._maximize_el=m;const c=(0,_.div)({class:[z.ctrl,z.close],title:\"Close\"});c.addEventListener(\"click\",(()=>this.close())),this._close_el=c,this.model.pinnable&&l.append(r),this.model.collapsible&&l.append(a),this.model.minimizable&&l.append(h),this.model.maximizable&&l.append(m),this.model.closable&&l.append(c);const d=this._handles={area:o,top:(0,_.div)({class:[z.handle,z.resize_top]}),bottom:(0,_.div)({class:[z.handle,z.resize_bottom]}),left:(0,_.div)({class:[z.handle,z.resize_left]}),right:(0,_.div)({class:[z.handle,z.resize_right]}),top_left:(0,_.div)({class:[z.handle,z.resize_top_left]}),top_right:(0,_.div)({class:[z.handle,z.resize_top_right]}),bottom_left:(0,_.div)({class:[z.handle,z.resize_bottom_left]}),bottom_right:(0,_.div)({class:[z.handle,z.resize_bottom_right]})};this.shadow_el.append(d.top,d.bottom,d.left,d.right,d.top_left,d.top_right,d.bottom_left,d.bottom_right);let p=null;const b=()=>{p=null,document.removeEventListener(\"pointermove\",g),document.removeEventListener(\"pointerup\",f),document.removeEventListener(\"keydown\",x),this.el.classList.remove(z.interacting)},g=t=>{(0,u.assert)(null!=p),t.preventDefault(),this.el.classList.add(z.interacting);const e=t.x-p.xy.x,i=t.y-p.xy.y,{target:s,bbox:n}=p,o=this._move_bbox(s,n,e,i);this._reposition(o)},f=t=>{(0,u.assert)(null!=p),t.preventDefault(),b()},x=t=>{if(\"Escape\"==t.key){(0,u.assert)(null!=p),t.preventDefault();const{left:e,top:i,width:s,height:n}=p.bbox;this._reposition({left:e,top:i,width:s,height:n}),b()}};this.el.addEventListener(\"pointerdown\",(t=>{(0,u.assert)(null==p),this.bring_to_front();const e=this._hit_target(t.composedPath());if(null==e||!this._can_hit(e))return;t.preventDefault();const{x:i,y:s}=t;p={bbox:(0,_.bounding_box)(this.el),xy:{x:i,y:s},target:e},document.addEventListener(\"pointermove\",g),document.addEventListener(\"pointerup\",f),document.addEventListener(\"keydown\",x);this._handles[e].setPointerCapture(t.pointerId)})),o.addEventListener(\"wheel\",(t=>{const e=t.deltaY;(e<0&&!this._collapsed||e>0&&this._collapsed)&&(t.preventDefault(),t.stopPropagation(),this.collapse())})),this._has_rendered=!0,this.model.visible&&this.bring_to_front()}get resizable(){const{resizable:t}=this.model;return{left:\"left\"==t||\"x\"==t||\"all\"==t,right:\"right\"==t||\"x\"==t||\"all\"==t,top:\"top\"==t||\"y\"==t||\"all\"==t,bottom:\"bottom\"==t||\"y\"==t||\"all\"==t}}_hit_target(t){const{_handles:e}=this;for(const i of t)switch(i){case e.area:return\"area\";case e.top:return\"top\";case e.bottom:return\"bottom\";case e.left:return\"left\";case e.right:return\"right\";case e.top_left:return\"top_left\";case e.top_right:return\"top_right\";case e.bottom_left:return\"bottom_left\";case e.bottom_right:return\"bottom_right\"}return null}_can_hit(t){if(this._minimized||this._maximized)return!1;const{left:e,right:i,top:s,bottom:n}=this.resizable;switch(t){case\"top_left\":return s&&e;case\"top_right\":return s&&i;case\"bottom_left\":return n&&e;case\"bottom_right\":return n&&i;case\"left\":return e;case\"right\":return i;case\"top\":return s;case\"bottom\":return n;case\"area\":return\"none\"!=this.model.movable}}_move_bbox(t,e,i,s){const n=(t,e)=>e instanceof f.Coordinate?this.resolve_as_scalar(e,t):NaN,o=d.BBox.from_lrtb({left:n(\"x\",this.model.left_limit),right:n(\"x\",this.model.right_limit),top:n(\"y\",this.model.top_limit),bottom:n(\"y\",this.model.bottom_limit)}),[l,r,a,h]=(()=>{const{symmetric:e}=this.model,[n,o]=e?[-i,-s]:[0,0];switch(t){case\"top_left\":return[i,n,s,o];case\"top_right\":return[n,i,s,o];case\"bottom_left\":return[i,n,o,s];case\"bottom_right\":return[n,i,o,s];case\"left\":return[i,n,0,0];case\"right\":return[n,i,0,0];case\"top\":return[0,0,s,o];case\"bottom\":return[0,0,o,s];case\"area\":switch(this.model.movable){case\"both\":return[i,i,s,s];case\"x\":return[i,i,0,0];case\"y\":return[0,0,s,s];case\"none\":return[0,0,0,0]}}})(),_=(t,e)=>(0,p.min)([t,e]),m=t=>t<0?-1:t>0?1:0;let{left:c,right:b,left_sign:u,right_sign:g}=(()=>{const t=e.left+l,i=e.right+r,s=m(l),n=m(r);return t<=i?{left:t,right:i,left_sign:s,right_sign:n}:{left:i,right:t,left_sign:n,right_sign:s}})(),{top:x,bottom:v,top_sign:z,bottom_sign:w}=(()=>{const t=e.top+a,i=e.bottom+h,s=m(a),n=m(h);return t<=i?{top:t,bottom:i,top_sign:s,bottom_sign:n}:{top:i,bottom:t,top_sign:n,bottom_sign:s}})();const y=c-o.left,L=o.right-b,E=_(y<0?y:NaN,L<0?L:NaN);isFinite(E)&&E<0&&(c+=-u*-E,b+=-g*-E);const N=x-o.top,k=o.bottom-v,D=_(N<0?N:NaN,k<0?k:NaN);return isFinite(D)&&D<0&&(x+=-z*-D,v+=-w*-D),d.BBox.from_lrtb({left:c,right:b,top:x,bottom:v})}pin(){const{_pinned:t}=this;for(const e of y)e==this?this._pin(!t):e._pin(!1);t||this.bring_to_front()}_pin(t){this._pinned!=t&&(this._pinned=t,this.el.classList.toggle(z.pinned,this._pinned),this._pin_el.title=this._pinned?\"Unpin\":\"Pin\")}collapse(){const t=(()=>{if(this._collapsed){const{_normal_bbox:t}=this;return(0,u.assert)(null!=t),this._normal_bbox=null,t}{this._minimize(!1),this._maximize(!1),null==this._normal_bbox&&(this._normal_bbox=(0,_.bounding_box)(this.el));const{left:t,top:e,width:i}=this._normal_bbox;return{left:t,top:e,width:i,height:\"max-content\"}}})();this._reposition(t),this._collapse(!this._collapsed)}_collapse(t){this._collapsed!=t&&(this._collapsed=t,this.el.classList.toggle(z.collapsed,this._collapsed),this._collapse_el.title=this._collapsed?\"Restore\":\"Collapse\")}minimize(){const t=(()=>{if(this._minimized){const{_normal_bbox:t}=this;return(0,u.assert)(null!=t),this._normal_bbox=null,t}return this._pin(!1),this._collapse(!1),this._maximize(!1),null==this._normal_bbox&&(this._normal_bbox=(0,_.bounding_box)(this.el)),{width:\"auto\",height:\"max-content\"}})();this._reposition(t),this._minimize(!this._minimized)}_minimize(t){if(this._minimized!=t){this._minimized=t;(t?L.shadowRoot??L:document.body).append(this.el),this.el.classList.toggle(z.minimized,this._minimized),this._minimize_el.title=this._minimized?\"Restore\":\"Minimize\"}}maximize(){const t=(()=>{if(this._maximized){const{_normal_bbox:t}=this;return(0,u.assert)(null!=t),this._normal_bbox=null,t}return this._collapse(!1),this._minimize(!1),null==this._normal_bbox&&(this._normal_bbox=(0,_.bounding_box)(this.el)),{left:0,top:0,width:\"100%\",height:\"100%\"}})();this._reposition(t),this._maximize(!this._maximized)}_maximize(t){this._maximized!=t&&(this._maximized=t,this.el.classList.toggle(z.maximized,this._maximized),this._maximize_el.title=this._maximized?\"Restore\":\"Maximize\")}restore(){this._collapse(!1),this._minimize(!1),this._maximize(!1);const{_normal_bbox:t}=this;null!=t&&(this._reposition(t),this._normal_bbox=null)}_toggle(t){t?(this._has_rendered||(this.render_to(document.body),this.r_after_render()),this.bring_to_front()):((0,p.remove)(y,this),this.el.remove())}open(){this.model.setv({visible:!0},{check_eq:!1})}close(){switch(this.model.close_action){case\"hide\":this.model.visible=!1;break;case\"destroy\":this.remove()}}bring_to_front(){y.includes(this)||y.push(this);const t=(0,p.find)(y,(t=>t._pinned));null!=t&&(0,p.remove)(y,t),(0,p.remove)(y,this),y.push(this),null!=t&&y.push(t);for(const[t,e]of(0,b.enumerate)(y))t._stacking.replace(\":host\",{\"z-index\":`${1e3+e}`})}}i.DialogView=E,E.__name__=\"DialogView\";class N extends r.UIElement{constructor(t){super(t)}}i.Dialog=N,o=N,N.__name__=\"Dialog\",o.prototype.default_view=E,o.define((({Bool:t,Str:e,Ref:i,Or:s,Nullable:n,Enum:o})=>({title:[n(s(e,i(a.DOMNode),i(r.UIElement))),null],content:[s(e,i(a.DOMNode),i(r.UIElement))],pinnable:[t,!0],collapsible:[t,!0],minimizable:[t,!0],maximizable:[t,!0],closable:[t,!0],close_action:[o(\"hide\",\"destroy\"),\"destroy\"],resizable:[g.Resizable,\"all\"],movable:[g.Movable,\"both\"],symmetric:[t,!1],top_limit:[g.Limit,null],bottom_limit:[g.Limit,null],left_limit:[g.Limit,null],right_limit:[g.Limit,null]})))},\n function _(e,t,n,r,o){var s;r();const _=e(125);class c extends _.DOMNodeView{render(){this.el.textContent=this.model.content}after_render(){this.finish()}_create_element(){return document.createTextNode(\"\")}}n.TextView=c,c.__name__=\"TextView\";class d extends _.DOMNode{constructor(e){super(e)}}n.Text=d,s=d,d.__name__=\"Text\",s.prototype.default_view=c,s.define((({Str:e})=>({content:[e,\"\"]})))},\n function _(e,i,o,r,t){r(),o.interacting=\"bk-interacting\",o.inner=\"bk-inner\",o.header=\"bk-header\",o.content=\"bk-content\",o.collapsed=\"bk-collapsed\",o.minimized=\"bk-minimized\",o.footer=\"bk-footer\",o.grip=\"bk-grip\",o.title=\"bk-title\",o.controls=\"bk-controls\",o.ctrl=\"bk-ctrl\",o.pin=\"bk-pin\",o.pinned=\"bk-pinned\",o.collapse=\"bk-collapse\",o.minimize=\"bk-minimize\",o.maximize=\"bk-maximize\",o.maximized=\"bk-maximized\",o.close=\"bk-close\",o.handle=\"bk-handle\",o.resize_top=\"bk-resize-top\",o.resize_bottom=\"bk-resize-bottom\",o.resize_left=\"bk-resize-left\",o.resize_right=\"bk-resize-right\",o.resize_top_left=\"bk-resize-top-left\",o.resize_top_right=\"bk-resize-top-right\",o.resize_bottom_left=\"bk-resize-bottom-left\",o.resize_bottom_right=\"bk-resize-bottom-right\",o.default=\":host{--bokeh-bg-color:white;--bokeh-border-color:#e5e5e5;--bokeh-shadow-color:#e5e5e5;--bokeh-ctrl-size:16px;--bokeh-ctrl-color:gray;}:host{position:fixed;left:200px;top:200px;width:600px;height:600px;width:80vw;height:60vh;}:host(.bk-interacting){opacity:0.9;}.bk-inner{position:relative;display:flex;flex-direction:column;flex-wrap:nowrap;width:100%;height:100%;overflow:hidden;border-radius:4px;background-color:var(--bokeh-bg-color);border:1px solid var(--bokeh-border-color);box-shadow:5px 5px 10px var(--bokeh-shadow-color);}.bk-header{position:relative;display:flex;flex:0;gap:1em;padding:5px;background-color:lightgray;}.bk-content{position:relative;display:flex;flex:1;overflow:auto;}:host(.bk-collapsed) .bk-content,:host(.bk-minimized) .bk-content,:host(.bk-collapsed) .bk-footer,:host(.bk-minimized) .bk-footer{display:none;}.bk-footer{position:relative;display:flex;flex:0;}.bk-grip{width:var(--bokeh-ctrl-size);height:var(--bokeh-ctrl-size);background-color:var(--bokeh-ctrl-color);background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-y-grip);-webkit-mask-image:var(--bokeh-icon-y-grip);mask-size:100% 100%;-webkit-mask-size:100% 100%;mask-position:center center;-webkit-mask-position:center center;mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;}.bk-title{position:relative;display:flex;flex:1;cursor:move;white-space:nowrap;}.bk-controls{position:relative;display:flex;flex:0;}.bk-ctrl{width:var(--bokeh-ctrl-size);height:var(--bokeh-ctrl-size);cursor:pointer;mask-size:100% 100%;-webkit-mask-size:100% 100%;mask-position:center center;-webkit-mask-position:center center;mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;background-color:var(--bokeh-ctrl-color);}.bk-ctrl:hover{background-color:red;}.bk-pin{mask-image:var(--bokeh-icon-pin);-webkit-mask-image:var(--bokeh-icon-pin);}:host(.bk-pinned) .bk-pin{mask-image:var(--bokeh-icon-unpin);-webkit-mask-image:var(--bokeh-icon-unpin);}.bk-collapse{mask-image:var(--bokeh-icon-chevron-up);-webkit-mask-image:var(--bokeh-icon-chevron-up);}:host(.bk-collapsed) .bk-collapse{mask-image:var(--bokeh-icon-chevron-down);-webkit-mask-image:var(--bokeh-icon-chevron-down);}.bk-minimize{mask-image:var(--bokeh-icon-arrow-down-to-bar);-webkit-mask-image:var(--bokeh-icon-arrow-down-to-bar);}:host(.bk-minimized) .bk-minimize{mask-image:var(--bokeh-icon-arrow-up-from-bar);-webkit-mask-image:var(--bokeh-icon-arrow-up-from-bar);}.bk-maximize{mask-image:var(--bokeh-icon-maximize);-webkit-mask-image:var(--bokeh-icon-maximize);}:host(.bk-maximized) .bk-maximize{mask-image:var(--bokeh-icon-minimize);-webkit-mask-image:var(--bokeh-icon-minimize);}.bk-close{mask-image:var(--bokeh-icon-x);-webkit-mask-image:var(--bokeh-icon-x);}:host{--resize-radius:3px;--resize-depth:calc(2*var(--resize-radius));}:host(.bk-minimized){position:relative;}:host(.bk-minimized) .bk-handle,:host(.bk-maximized) .bk-handle{display:none;}:host(.bk-minimized) .bk-title,:host(.bk-maximized) .bk-title{cursor:default;}.bk-resize-top{position:absolute;top:0;left:var(--resize-radius);width:calc(100% - var(--resize-depth));height:var(--resize-depth);transform:translate(0, -50%);cursor:ns-resize;}.bk-resize-bottom{position:absolute;bottom:0;left:var(--resize-radius);width:calc(100% - var(--resize-depth));height:var(--resize-depth);transform:translate(0, 50%);cursor:ns-resize;}.bk-resize-left{position:absolute;left:0;top:var(--resize-radius);width:var(--resize-depth);height:calc(100% - var(--resize-depth));transform:translate(-50%, 0);cursor:ew-resize;}.bk-resize-right{position:absolute;right:0;top:var(--resize-radius);width:var(--resize-depth);height:calc(100% - var(--resize-depth));transform:translate(50%, 0);cursor:ew-resize;}.bk-resize-top-left{position:absolute;top:0;left:0;width:var(--resize-depth);height:var(--resize-depth);transform:translate(-50%, -50%);cursor:nw-resize;}.bk-resize-top-right{position:absolute;top:0;right:0;width:var(--resize-depth);height:var(--resize-depth);transform:translate(50%, -50%);cursor:ne-resize;}.bk-resize-bottom-left{position:absolute;bottom:0;left:0;width:var(--resize-depth);height:var(--resize-depth);transform:translate(-50%, 50%);cursor:sw-resize;}.bk-resize-bottom-right{position:absolute;bottom:0;right:0;width:var(--resize-depth);height:var(--resize-depth);transform:translate(50%, 50%);cursor:se-resize;}\"},\n function _(e,o,a,n,s){var c;n();const l=e(119),i=e(319);class t extends l.Callback{constructor(e){super(e)}async execute(){const{dialog:e}=this;e.document?.views_manager?.find_one(e)?.close()}}a.CloseDialog=t,c=t,t.__name__=\"CloseDialog\",c.define((({Ref:e})=>({dialog:[e(i.Dialog)]})))},\n function _(a,n,e,r,s){r(),s(\"Canvas\",a(324).Canvas),s(\"CartesianFrame\",a(188).CartesianFrame)},\n function _(e,t,s,i,a){var r;i();const l=e(1),n=e(29),o=e(19),h=e(63),_=e(20),c=e(325),p=e(183),u=e(112),b=e(111),d=e(63),v=l.__importStar(e(327)),w=l.__importDefault(e(123));const g=(()=>{let t;return async()=>void 0!==t?t:t=await async function(){const t=document.createElement(\"canvas\"),s=t.getContext(\"webgl\",{alpha:!0,antialias:!1,depth:!1,premultipliedAlpha:!0});if(null!=s){const i=await(0,p.load_module)(Promise.resolve().then((()=>l.__importStar(e(539)))));if(null!=i){const e=i.get_regl(s);if(e.has_webgl)return{canvas:t,regl_wrapper:e};o.logger.trace(\"WebGL is supported, but not the required extensions\")}else o.logger.trace(\"WebGL is supported, but bokehjs(.min).js bundle is not available\")}else o.logger.trace(\"WebGL is not supported\");return null}()})();class y extends b.UIElementView{constructor(){super(...arguments),this.webgl=null,this._size=new d.InlineStyleSheet,this.plot_views=[]}initialize(){super.initialize(),this.underlays_el=(0,h.div)({class:v.layer}),this.primary=this.create_layer(),this.overlays=this.create_layer(),this.overlays_el=(0,h.div)({class:v.layer}),this.events_el=(0,h.div)({class:[v.layer,v.events]}),this.ui_event_bus=new c.UIEventBus(this)}get layers(){return[this.underlays_el,this.primary,this.overlays,this.overlays_el,this.events_el]}async lazy_initialize(){if(await super.lazy_initialize(),\"webgl\"==this.model.output_backend&&(this.webgl=await g(),n.settings.force_webgl&&null==this.webgl))throw new Error(\"webgl is not available\")}remove(){this.ui_event_bus.remove(),super.remove()}stylesheets(){return[...super.stylesheets(),v.default,w.default,this._size]}render(){super.render();const e=[this.underlays_el,this.primary.el,this.overlays.el,this.overlays_el,this.events_el];this.shadow_el.append(...e)}get pixel_ratio(){return this.primary.pixel_ratio}_update_bbox(){const e=super._update_bbox();if(e){const{width:e,height:t}=this.bbox;this._size.replace(`.${v.layer}`,{width:(0,h.px)(e),height:(0,h.px)(t)}),this.primary.resize(e,t),this.overlays.resize(e,t)}return e}after_resize(){0!=this.plot_views.length?this.finish():super.after_resize()}_after_resize(){super._after_resize();const{width:e,height:t}=this.bbox;this.primary.resize(e,t),this.overlays.resize(e,t)}resize(){this._update_bbox(),this._after_resize()}prepare_webgl(e){const{webgl:t}=this;if(null!=t){const{width:s,height:i}=this.bbox;t.canvas.width=this.pixel_ratio*s,t.canvas.height=this.pixel_ratio*i;const[a,r,l,n]=e,{xview:o,yview:h}=this.bbox,_=o.compute(a),c=h.compute(r+n),p=this.pixel_ratio;t.regl_wrapper.set_scissor(p*_,p*c,p*l,p*n),this._clear_webgl()}}blit_webgl(e){const{webgl:t}=this;if(null!=t&&t.canvas.width*t.canvas.height>0){if(o.logger.debug(\"Blitting WebGL canvas\"),e.restore(),e.drawImage(t.canvas,0,0),e.save(),this.model.hidpi){const t=this.pixel_ratio;e.scale(t,t),e.translate(.5,.5)}this._clear_webgl()}}_clear_webgl(){const{webgl:e}=this;if(null!=e){const{regl_wrapper:t,canvas:s}=e;t.clear(s.width,s.height)}}compose(){const e=this.create_layer(),{width:t,height:s}=this.bbox;return e.resize(t,s),e.ctx.drawImage(this.primary.canvas,0,0),e.ctx.drawImage(this.overlays.canvas,0,0),e}create_layer(){const{output_backend:e,hidpi:t}=this.model;return new u.CanvasLayer(e,t)}to_blob(){return this.compose().to_blob()}}s.CanvasView=y,y.__name__=\"CanvasView\";class m extends b.UIElement{constructor(e){super(e)}}s.Canvas=m,r=m,m.__name__=\"Canvas\",r.prototype.default_view=y,r.define((({Bool:e})=>({hidpi:[e,!0],output_backend:[_.OutputBackend,\"canvas\"]})))},\n function _(t,e,n,i,s){i();const r=t(1),_=t(295),o=t(15),a=t(63),h=r.__importStar(t(53)),c=t(326),l=t(10),u=t(8),p=t(293);function v(t){return(0,u.isObject)(t)&&\"on_enter\"in t&&\"on_move\"in t&&\"on_leave\"in t}function d(t){return(0,u.isObject)(t)&&\"on_pan_start\"in t&&\"on_pan\"in t&&\"on_pan_end\"in t}function g(t){return(0,u.isObject)(t)&&\"on_pinch_start\"in t&&\"on_pinch\"in t&&\"on_pinch_end\"in t}function w(t){return(0,u.isObject)(t)&&\"on_rotate_start\"in t&&\"on_rotate\"in t&&\"on_rotate_end\"in t}n.is_Tapable=function(t){return(0,u.isObject)(t)&&\"on_tap\"in t},n.is_Moveable=v,n.is_Pannable=d,n.is_Pinchable=g,n.is_Rotatable=w,n.is_Scrollable=function(t){return(0,u.isObject)(t)&&\"on_scroll\"in t},n.is_Keyable=function(t){return(0,u.isObject)(t)&&\"on_keydown\"in t&&\"on_keyup\"in t};class f{constructor(t){this.pan_start=new o.Signal(this,\"pan:start\"),this.pan=new o.Signal(this,\"pan\"),this.pan_end=new o.Signal(this,\"pan:end\"),this.pinch_start=new o.Signal(this,\"pinch:start\"),this.pinch=new o.Signal(this,\"pinch\"),this.pinch_end=new o.Signal(this,\"pinch:end\"),this.rotate_start=new o.Signal(this,\"rotate:start\"),this.rotate=new o.Signal(this,\"rotate\"),this.rotate_end=new o.Signal(this,\"rotate:end\"),this.tap=new o.Signal(this,\"tap\"),this.doubletap=new o.Signal(this,\"doubletap\"),this.press=new o.Signal(this,\"press\"),this.pressup=new o.Signal(this,\"pressup\"),this.move_enter=new o.Signal(this,\"move:enter\"),this.move=new o.Signal(this,\"move\"),this.move_exit=new o.Signal(this,\"move:exit\"),this.scroll=new o.Signal(this,\"scroll\"),this.keydown=new o.Signal(this,\"keydown\"),this.keyup=new o.Signal(this,\"keyup\"),this._tools=new Map,this._prev_move=null,this._curr_pan=null,this._curr_pinch=null,this._curr_rotate=null,this._current_pan_view=null,this._current_pinch_view=null,this._current_rotate_view=null,this._current_move_views=[],this.canvas_view=t,this.hit_area=t.events_el,this.on_tap=this.on_tap.bind(this),this.on_doubletap=this.on_doubletap.bind(this),this.on_press=this.on_press.bind(this),this.on_pressup=this.on_pressup.bind(this),this.on_enter=this.on_enter.bind(this),this.on_move=this.on_move.bind(this),this.on_leave=this.on_leave.bind(this),this.on_pan_start=this.on_pan_start.bind(this),this.on_pan=this.on_pan.bind(this),this.on_pan_end=this.on_pan_end.bind(this),this.on_pinch_start=this.on_pinch_start.bind(this),this.on_pinch=this.on_pinch.bind(this),this.on_pinch_end=this.on_pinch_end.bind(this),this.on_rotate_start=this.on_rotate_start.bind(this),this.on_rotate=this.on_rotate.bind(this),this.on_rotate_end=this.on_rotate_end.bind(this),this.ui_gestures=new _.UIGestures(this.hit_area,this,{must_be_target:!0}),this.ui_gestures.connect_signals(),this.on_context_menu=this.on_context_menu.bind(this),this.on_mouse_wheel=this.on_mouse_wheel.bind(this),this.on_key_down=this.on_key_down.bind(this),this.on_key_up=this.on_key_up.bind(this),this.hit_area.addEventListener(\"contextmenu\",this.on_context_menu),this.hit_area.addEventListener(\"wheel\",this.on_mouse_wheel),document.addEventListener(\"keydown\",this.on_key_down),document.addEventListener(\"keyup\",this.on_key_up)}remove(){this.ui_gestures.remove(),this.hit_area.removeEventListener(\"contextmenu\",this.on_context_menu),this.hit_area.removeEventListener(\"wheel\",this.on_mouse_wheel),document.removeEventListener(\"keydown\",this.on_key_down),document.removeEventListener(\"keyup\",this.on_key_up)}register_tool(t){const{model:e}=t;if(this._tools.has(e))throw new Error(`${e} already registered`);this._tools.set(e,t)}hit_test_renderers(t,e,n){const i=[];for(const s of(0,l.reversed)(t.all_renderer_views))s.interactive_hit?.(e,n)&&i.push(s);return i}set_cursor(t){this.hit_area.style.cursor=t??\"default\"}hit_test_frame(t,e,n){return t.frame.bbox.contains(e,n)}hit_test_plot(t,e){for(const n of this.canvas_view.plot_views)if(n.bbox.relative().contains(t,e))return n;return null}_trigger(t,e){if(!this.hit_area.isConnected)return;const{sx:n,sy:i,native:s}=e,r=this.hit_test_plot(n,i),_=t=>{const[s,r]=[n,i];return{...e,sx:s,sy:r}};if(\"pan_start\"==e.type||\"pan\"==e.type||\"pan_end\"==e.type){let n;if(\"pan_start\"==e.type&&null!=r?(this._curr_pan={plot_view:r},n=r):\"pan\"==e.type&&null!=this._curr_pan?n=this._curr_pan.plot_view:\"pan_end\"==e.type&&null!=this._curr_pan?(n=this._curr_pan.plot_view,this._curr_pan=null):n=null,null!=n){const e=_();this.__trigger(n,t,e,s)}}else if(\"pinch_start\"==e.type||\"pinch\"==e.type||\"pinch_end\"==e.type){let n;if(\"pinch_start\"==e.type&&null!=r?(this._curr_pinch={plot_view:r},n=r):\"pinch\"==e.type&&null!=this._curr_pinch?n=this._curr_pinch.plot_view:\"pinch_end\"==e.type&&null!=this._curr_pinch?(n=this._curr_pinch.plot_view,this._curr_pinch=null):n=null,null!=n){const e=_();this.__trigger(n,t,e,s)}}else if(\"rotate_start\"==e.type||\"rotate\"==e.type||\"rotate_end\"==e.type){let n;if(\"rotate_start\"==e.type&&null!=r?(this._curr_rotate={plot_view:r},n=r):\"rotate\"==e.type&&null!=this._curr_rotate?n=this._curr_rotate.plot_view:\"rotate_end\"==e.type&&null!=this._curr_rotate?(n=this._curr_rotate.plot_view,this._curr_rotate=null):n=null,null!=n){const e=_();this.__trigger(n,t,e,s)}}else if(\"enter\"==e.type||\"move\"==e.type||\"leave\"==e.type){const o=this._prev_move?.plot_view;if(null!=o&&(\"leave\"==e.type||o!=r)){const{sx:t,sy:e}=_();this.__trigger(o,this.move_exit,{type:\"leave\",sx:t,sy:e,modifiers:{shift:!1,ctrl:!1,alt:!1},native:s},s)}if(null!=r&&(\"enter\"==e.type||o!=r)){const{sx:t,sy:e}=_();this.__trigger(r,this.move_enter,{type:\"enter\",sx:t,sy:e,modifiers:{shift:!1,ctrl:!1,alt:!1},native:s},s)}if(null!=r&&\"move\"==e.type){const e=_();this.__trigger(r,t,e,s)}this._prev_move={sx:n,sy:i,plot_view:r}}else if(null!=r){const e=_();this.__trigger(r,t,e,s)}}__trigger(t,e,n,i){const s=t.model.toolbar.gestures,r=e.name,_=r.split(\":\")[0],o=this.hit_test_renderers(t,n.sx,n.sy);if(\"pan\"==_){const t=n;if(null!=this._current_pan_view)return\"pan\"==r?this._current_pan_view.on_pan(t):\"pan:end\"==r&&(this._current_pan_view.on_pan_end(t),this._current_pan_view=null),void i.preventDefault();if(\"pan:start\"==r)for(const e of o)if(d(e)&&e.on_pan_start(t))return this._current_pan_view=e,void i.preventDefault()}else if(\"pinch\"==_){const t=n;if(null!=this._current_pinch_view)return\"pinch\"==r?this._current_pinch_view.on_pinch(t):\"pinch:end\"==r&&(this._current_pinch_view.on_pinch_end(t),this._current_pinch_view=null),void i.preventDefault();if(\"pinch:start\"==r)for(const e of o)if(g(e)&&e.on_pinch_start(t))return this._current_pinch_view=e,void i.preventDefault()}else if(\"rotate\"==_){const t=n;if(null!=this._current_rotate_view)return\"rotate\"==r?this._current_rotate_view.on_rotate(t):\"rotate:end\"==r&&(this._current_rotate_view.on_rotate_end(t),this._current_rotate_view=null),void i.preventDefault();if(\"rotate:start\"==r)for(const e of o)if(w(e)&&e.on_rotate_start(t))return this._current_rotate_view=e,void i.preventDefault()}else if(\"move\"==_){const t=n,e=new Set(o),i=new Set(this._current_move_views);this._current_move_views=[];for(const n of i)e.has(n)||(i.delete(n),n.on_leave(t));for(const e of o)v(e)&&(i.has(e)?(this._current_move_views.push(e),e.on_move(t)):e.on_enter(n)&&this._current_move_views.push(e))}const a=o.at(0);switch(_){case\"move\":{const i=s.move.active;null!=i&&this.trigger(e,n,i);const r=t.model.toolbar.inspectors.filter((t=>t.active)),_=(()=>{const e=this._current_pan_view??this._current_pinch_view??this._current_rotate_view??this._current_move_views.at(0)??a??function(e){if(null!=e){const n=e instanceof p.ToolProxy?e.tools[0]:e;return t.tool_views.get(n)??null}return null}(i);if(null!=e){const t=e.cursor(n.sx,n.sy);if(null!=t)return t}return this.hit_test_frame(t,n.sx,n.sy)&&!(0,l.is_empty)(r)?\"crosshair\":null})();this.set_cursor(_),null==a||a.model.propagate_hover||(0,l.is_empty)(r)||(e=this.move_exit),r.map((t=>this.trigger(e,n,t)));break}case\"tap\":{const r=i.composedPath();if(0!=r.length&&r[0]!=this.hit_area)return;if(a?.on_hit?.(n.sx,n.sy),this.hit_test_frame(t,n.sx,n.sy)){const t=s.tap.active;null!=t&&this.trigger(e,n,t)}break}case\"doubletap\":if(this.hit_test_frame(t,n.sx,n.sy)){const t=s.doubletap.active??s.tap.active;null!=t&&this.trigger(e,n,t)}break;case\"press\":if(this.hit_test_frame(t,n.sx,n.sy)){const t=s.press.active??s.tap.active;null!=t&&this.trigger(e,n,t)}break;case\"pinch\":{const t=s.pinch.active??s.scroll.active;null!=t&&this.trigger(e,n,t)&&(i.preventDefault(),i.stopPropagation());break}case\"scroll\":{const t=s.scroll.active;null!=t&&this.trigger(e,n,t)&&(i.preventDefault(),i.stopPropagation());break}case\"pan\":{const t=s.pan.active;null!=t&&this.trigger(e,n,t)&&(i.preventDefault(),i.stopPropagation());break}default:{const t=s[_].active;null!=t&&this.trigger(e,n,t)}}this._trigger_bokeh_event(t,n)}trigger(t,e,n=null){const i=n=>{const i=this._tools.get(n);if(null==i)return!1;const s=(()=>{switch(t){case this.pan_start:return i._pan_start;case this.pan:return i._pan;case this.pan_end:return i._pan_end;case this.pinch_start:return i._pinch_start;case this.pinch:return i._pinch;case this.pinch_end:return i._pinch_end;case this.rotate_start:return i._rotate_start;case this.rotate:return i._rotate;case this.rotate_end:return i._rotate_end;case this.move_enter:return i._move_enter;case this.move:return i._move;case this.move_exit:return i._move_exit;case this.tap:return i._tap;case this.doubletap:return i._doubletap;case this.press:return i._press;case this.pressup:return i._pressup;case this.scroll:return i._scroll;case this.keydown:return i._keydown;case this.keyup:return i._keyup;default:return null}})();if(null==s)return!1;const r=s.bind(i)(e);return!(0,u.isBoolean)(r)||r};if(null!=n)return i(n);{let t=!1;for(const e of this._tools.keys())t||(t=i(e));return t}}_trigger_bokeh_event(t,e){const n=(()=>{const{sx:n,sy:i,modifiers:s}=e,r=t.frame.x_scale.invert(n),_=t.frame.y_scale.invert(i);switch(e.type){case\"wheel\":return new h.MouseWheel(n,i,r,_,e.delta,s);case\"enter\":return new h.MouseEnter(n,i,r,_,s);case\"move\":return new h.MouseMove(n,i,r,_,s);case\"leave\":return new h.MouseLeave(n,i,r,_,s);case\"tap\":return new h.Tap(n,i,r,_,s);case\"double_tap\":return new h.DoubleTap(n,i,r,_,s);case\"press\":return new h.Press(n,i,r,_,s);case\"press_up\":return new h.PressUp(n,i,r,_,s);case\"pan_start\":return new h.PanStart(n,i,r,_,s);case\"pan\":return new h.Pan(n,i,r,_,e.dx,e.dy,s);case\"pan_end\":return new h.PanEnd(n,i,r,_,s);case\"pinch_start\":return new h.PinchStart(n,i,r,_,s);case\"pinch\":return new h.Pinch(n,i,r,_,e.scale,s);case\"pinch_end\":return new h.PinchEnd(n,i,r,_,s);case\"rotate_start\":return new h.RotateStart(n,i,r,_,s);case\"rotate\":return new h.Rotate(n,i,r,_,e.rotation,s);case\"rotate_end\":return new h.RotateEnd(n,i,r,_,s);default:return null}})();null!=n&&t.model.trigger_event(n)}_get_sxy(t){const{pageX:e,pageY:n}=t,{left:i,top:s}=(0,a.offset_bbox)(this.hit_area);return{sx:e-i,sy:n-s}}_get_modifiers(t){return{shift:t.shiftKey,ctrl:t.ctrlKey,alt:t.altKey}}_scroll_event(t){return{type:t.type,...this._get_sxy(t),delta:(0,c.getDeltaY)(t),modifiers:this._get_modifiers(t),native:t}}_key_event(t){return{type:t.type,key:t.key,modifiers:this._get_modifiers(t),native:t}}on_tap(t){this._trigger(this.tap,t)}on_doubletap(t){this._trigger(this.doubletap,t)}on_press(t){this._trigger(this.press,t)}on_pressup(t){this._trigger(this.pressup,t)}on_enter(t){this._trigger(this.move_enter,t)}on_move(t){this._trigger(this.move,t)}on_leave(t){this._trigger(this.move_exit,t)}on_pan_start(t){this._trigger(this.pan_start,t)}on_pan(t){this._trigger(this.pan,t)}on_pan_end(t){this._trigger(this.pan_end,t)}on_pinch_start(t){this._trigger(this.pinch_start,t)}on_pinch(t){this._trigger(this.pinch,t)}on_pinch_end(t){this._trigger(this.pinch_end,t)}on_rotate_start(t){this._trigger(this.rotate_start,t)}on_rotate(t){this._trigger(this.rotate,t)}on_rotate_end(t){this._trigger(this.rotate_end,t)}on_mouse_wheel(t){this._trigger(this.scroll,this._scroll_event(t))}on_context_menu(t){}on_key_down(t){this.trigger(this.keydown,this._key_event(t))}on_key_up(t){this.trigger(this.keyup,this._key_event(t))}}n.UIEventBus=f,f.__name__=\"UIEventBus\"},\n function _(t,e,n,a,r){\n /*!\n * jQuery Mousewheel 3.1.13\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license\n * http://jquery.org/license\n */\n function o(t){const e=getComputedStyle(t).fontSize,n=parseInt(e,10);return isNaN(n)?null:n}a(),n.getDeltaY=function(t){let e=-t.deltaY;if(t.target instanceof HTMLElement)switch(t.deltaMode){case t.DOM_DELTA_LINE:e*=o((n=t.target).offsetParent??document.body)??o(n)??16;break;case t.DOM_DELTA_PAGE:e*=function(t){return t.clientHeight}(t.target)}var n;return e}},\n function _(e,t,o,n,i){n(),o.layer=\"bk-layer\",o.events=\"bk-events\",o.default=\".bk-layer{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;}.bk-events{touch-action:none;overflow:visible;}\"},\n function _(d,e,n,o,i){o(),i(\"CoordinateMapping\",d(95).CoordinateMapping),i(\"Node\",d(59).Node),i(\"XY\",d(61).XY),i(\"Indexed\",d(62).Indexed)},\n function _(m,o,n,r,a){r(),a(\"Expression\",m(330).Expression),a(\"CustomJSExpr\",m(331).CustomJSExpr),a(\"Stack\",m(332).Stack),a(\"CumSum\",m(333).CumSum),a(\"ScalarExpression\",m(330).ScalarExpression),a(\"Minimum\",m(334).Minimum),a(\"Maximum\",m(335).Maximum);var s=m(336);a(\"XComponent\",s.XComponent),a(\"YComponent\",s.YComponent),a(\"PolarTransform\",m(337).PolarTransform)},\n function _(e,t,s,i,r){i();const n=e(51);class _ extends n.Model{constructor(e){super(e)}initialize(){super.initialize(),this._result=new Map}v_compute(e){let t=this._result.get(e);return(void 0===t||e.changed_for(this))&&(t=this._v_compute(e),this._result.set(e,t)),t}}s.Expression=_,_.__name__=\"Expression\";class o extends n.Model{constructor(e){super(e)}initialize(){super.initialize(),this._result=new Map}compute(e){let t=this._result.get(e);return(void 0===t||e.changed_for(this))&&(t=this._compute(e),this._result.set(e,t)),t}}s.ScalarExpression=o,o.__name__=\"ScalarExpression\"},\n function _(e,s,t,n,r){var a;n();const o=e(14),c=e(330),i=e(24),u=e(10),l=e(9),h=e(40),g=e(8);class p extends c.Expression{constructor(e){super(e)}connect_signals(){super.connect_signals();for(const e of(0,l.values)(this.args))e instanceof o.HasProps&&e.change.connect((()=>{this._result.clear(),this.change.emit()}))}get names(){return(0,l.keys)(this.args)}get values(){return(0,l.values)(this.args)}get func(){const e=(0,h.use_strict)(this.code);return new i.GeneratorFunction(...this.names,e)}_v_compute(e){const s=this.func.apply(e,this.values);let t=s.next();if(t.done&&void 0!==t.value){const{value:s}=t;return(0,g.isArray)(s)||(0,g.isTypedArray)(s)?s:(0,g.isIterable)(s)?[...s]:(0,u.repeat)(s,e.length)}{const e=[];do{e.push(t.value),t=s.next()}while(!t.done);return e}}}t.CustomJSExpr=p,a=p,p.__name__=\"CustomJSExpr\",a.define((({Unknown:e,Str:s,Dict:t})=>({args:[t(e),{}],code:[s,\"\"]})))},\n function _(t,n,e,s,o){var c;s();const r=t(330),a=t(9);class i extends r.Expression{constructor(t){super(t)}_v_compute(t){const n=t.get_length()??0,e=new Float64Array(n);for(const s of this.fields){const o=(0,a.dict)(t.data).get(s);if(null!=o){const t=Math.min(n,o.length);for(let n=0;n({fields:[n(t),[]]})))},\n function _(e,t,n,o,r){var i;o();const s=e(330),u=e(9);class c extends s.Expression{constructor(e){super(e)}_v_compute(e){const t=new Float64Array(e.get_length()??0),n=(0,u.dict)(e.data).get(this.field)??[],o=this.include_zero?1:0;t[0]=this.include_zero?0:n[0];for(let e=1;e({field:[t],include_zero:[e,!1]})))},\n function _(i,t,n,e,a){var s;e();const c=i(330),r=i(9),m=i(10);class o extends c.ScalarExpression{constructor(i){super(i)}_compute(i){const t=(0,r.dict)(i.data).get(this.field)??[];return Math.min(this.initial,(0,m.min)(t))}}n.Minimum=o,s=o,o.__name__=\"Minimum\",s.define((({Float:i,Str:t})=>({field:[t],initial:[i,1/0]})))},\n function _(t,i,a,e,n){var s;e();const c=t(330),r=t(9),m=t(10);class o extends c.ScalarExpression{constructor(t){super(t)}_compute(t){const i=(0,r.dict)(t.data).get(this.field)??[];return Math.max(this.initial,(0,m.max)(i))}}a.Maximum=o,s=o,o.__name__=\"Maximum\",s.define((({Float:t,Str:i})=>({field:[i],initial:[t,-1/0]})))},\n function _(n,e,t,o,r){var s;o();const _=n(330);class m extends _.Expression{constructor(n){super(n)}get x(){return new c({transform:this})}get y(){return new u({transform:this})}}t.CoordinateTransform=m,m.__name__=\"CoordinateTransform\";class a extends _.Expression{constructor(n){super(n)}}t.XYComponent=a,s=a,a.__name__=\"XYComponent\",s.define((({Ref:n})=>({transform:[n(m)]})));class c extends a{constructor(n){super(n)}_v_compute(n){return this.transform.v_compute(n).x}}t.XComponent=c,c.__name__=\"XComponent\";class u extends a{constructor(n){super(n)}_v_compute(n){return this.transform.v_compute(n).y}}t.YComponent=u,u.__name__=\"YComponent\"},\n function _(r,t,e,n,o){var i;n();const a=r(1),s=r(336),c=r(20),l=a.__importStar(r(18));class d extends s.CoordinateTransform{constructor(r){super(r)}_v_compute(r){const t=this.properties.radius.uniform(r),e=this.properties.angle.uniform(r),n=\"anticlock\"==this.direction?-1:1,o=Math.min(t.length,e.length),i=new Float64Array(o),a=new Float64Array(o);for(let r=0;r({radius:[l.DistanceSpec,{field:\"radius\"}],angle:[l.AngleSpec,{field:\"angle\"}],direction:[c.Direction,\"anticlock\"]})))},\n function _(e,i,r,t,l){t(),l(\"BooleanFilter\",e(339).BooleanFilter),l(\"CustomJSFilter\",e(340).CustomJSFilter),l(\"Filter\",e(237).Filter),l(\"GroupFilter\",e(341).GroupFilter),l(\"IndexFilter\",e(342).IndexFilter),l(\"AllIndices\",e(238).AllIndices),l(\"InversionFilter\",e(343).InversionFilter),l(\"IntersectionFilter\",e(239).IntersectionFilter),l(\"UnionFilter\",e(344).UnionFilter),l(\"DifferenceFilter\",e(345).DifferenceFilter),l(\"SymmetricDifferenceFilter\",e(346).SymmetricDifferenceFilter)},\n function _(e,l,n,o,t){var s;o();const a=e(237),r=e(24);class c extends a.Filter{constructor(e){super(e)}compute_indices(e){const l=e.get_length()??1,{booleans:n}=this;return null==n?r.Indices.all_set(l):r.Indices.from_booleans(l,n)}}n.BooleanFilter=c,s=c,c.__name__=\"BooleanFilter\",s.define((({Bool:e,Iterable:l,Nullable:n})=>({booleans:[n(l(e)),null]})))},\n function _(e,r,s,t,n){var i;t();const o=e(237),c=e(24),u=e(9),a=e(8),l=e(40);class f extends o.Filter{constructor(e){super(e)}get names(){return(0,u.keys)(this.args)}get values(){return(0,u.values)(this.args)}get func(){const e=(0,l.use_strict)(this.code);return new Function(...this.names,\"source\",e)}compute_indices(e){const r=e.get_length()??1,s=this.func(...this.values,e);if(null==s)return c.Indices.all_set(r);if((0,a.isArrayOf)(s,a.isInteger))return c.Indices.from_indices(r,s);if((0,a.isArrayOf)(s,a.isBoolean))return c.Indices.from_booleans(r,s);throw new Error(`expect an array of integers or booleans, or null, got ${s}`)}}s.CustomJSFilter=f,i=f,f.__name__=\"CustomJSFilter\",i.define((({Unknown:e,Str:r,Dict:s})=>({args:[s(e),{}],code:[r,\"\"]})))},\n function _(e,n,t,o,r){var s;o();const u=e(237),c=e(24),i=e(19);class l extends u.Filter{constructor(e){super(e)}compute_indices(e){const n=e.get_column(this.column_name),t=e.get_length()??1;if(null==n)return i.logger.warn(`${this}: groupby column '${this.column_name}' not found in the data source`),c.Indices.all_set(t);{const e=new c.Indices(t,0);for(let t=0;t({column_name:[e],group:[e]})))},\n function _(e,n,t,i,l){var s;i();const c=e(237),r=e(24);class d extends c.Filter{constructor(e){super(e)}compute_indices(e){const n=e.get_length()??1,{indices:t}=this;return null==t?r.Indices.all_set(n):r.Indices.from_indices(n,t)}}t.IndexFilter=d,s=d,d.__name__=\"IndexFilter\",s.define((({Int:e,Iterable:n,Nullable:t})=>({indices:[t(n(e)),null]})))},\n function _(e,n,t,s,o){var i;s();const r=e(237);class c extends r.Filter{constructor(e){super(e)}connect_signals(){super.connect_signals();const e=()=>{this.change.emit()},n=n=>{for(const t of n)this.connect(t.change,e)},t=n=>{for(const t of n)this.disconnect(t.change,e)};let s=(()=>{const{operand:e}=this.properties;return e.is_unset?[]:[e.get_value()]})();n(s),this.on_change(this.properties.operand,(()=>{t(s),s=[this.operand],n(s)}))}compute_indices(e){const n=this.operand.compute_indices(e);return n.invert(),n}}t.InversionFilter=c,i=c,c.__name__=\"InversionFilter\",i.define((({Ref:e})=>({operand:[e(r.Filter)]})))},\n function _(n,e,o,t,i){t();const s=n(240);class _ extends s.CompositeFilter{constructor(n){super(n)}_inplace_op(n,e){n.add(e)}}o.UnionFilter=_,_.__name__=\"UnionFilter\"},\n function _(e,t,c,n,r){n();const i=e(240);class s extends i.CompositeFilter{constructor(e){super(e)}_inplace_op(e,t){e.subtract(t)}}c.DifferenceFilter=s,s.__name__=\"DifferenceFilter\"},\n function _(e,t,c,r,i){r();const n=e(240);class s extends n.CompositeFilter{constructor(e){super(e)}_inplace_op(e,t){e.symmetric_subtract(t)}}c.SymmetricDifferenceFilter=s,s.__name__=\"SymmetricDifferenceFilter\"},\n function _(e,a,t,l,r){l(),r(\"AnnularWedge\",e(348).AnnularWedge),r(\"Annulus\",e(349).Annulus),r(\"Arc\",e(350).Arc),r(\"Bezier\",e(351).Bezier),r(\"Block\",e(353).Block),r(\"Circle\",e(355).Circle),r(\"Ellipse\",e(356).Ellipse),r(\"Glyph\",e(221).Glyph),r(\"HArea\",e(233).HArea),r(\"HAreaStep\",e(230).HAreaStep),r(\"HBar\",e(358).HBar),r(\"HexTile\",e(359).HexTile),r(\"HSpan\",e(360).HSpan),r(\"HStrip\",e(361).HStrip),r(\"Image\",e(362).Image),r(\"ImageRGBA\",e(364).ImageRGBA),r(\"ImageStack\",e(365).ImageStack),r(\"ImageURL\",e(366).ImageURL),r(\"Line\",e(219).Line),r(\"MathMLGlyph\",e(367).MathMLGlyph),r(\"MultiLine\",e(370).MultiLine),r(\"MultiPolygons\",e(371).MultiPolygons),r(\"Patch\",e(229).Patch),r(\"Patches\",e(372).Patches),r(\"Quad\",e(373).Quad),r(\"Quadratic\",e(374).Quadratic),r(\"Ray\",e(375).Ray),r(\"Rect\",e(376).Rect),r(\"Scatter\",e(377).Scatter),r(\"Segment\",e(380).Segment),r(\"Spline\",e(381).Spline),r(\"Step\",e(383).Step),r(\"TeXGlyph\",e(384).TeXGlyph),r(\"Text\",e(369).Text),r(\"VArea\",e(235).VArea),r(\"VAreaStep\",e(234).VAreaStep),r(\"VBar\",e(385).VBar),r(\"VSpan\",e(386).VSpan),r(\"VStrip\",e(387).VStrip),r(\"Wedge\",e(388).Wedge)},\n function _(e,t,i,s,r){var n;s();const a=e(1),_=e(220),h=e(221),d=e(227),o=e(80),u=e(24),l=e(20),c=a.__importStar(e(18)),g=e(11),p=e(130),x=e(13);class m extends _.XYGlyphView{async load_glglyph(){const{AnnularWedgeGL:t}=await Promise.resolve().then((()=>a.__importStar(e(552))));return t}_map_data(){this._define_or_inherit_attr(\"sinner_radius\",(()=>\"data\"==this.model.properties.inner_radius.units?this.inherited_x&&this.inherited_inner_radius?h.inherit:this.sdist(this.renderer.xscale,this.x,this.inner_radius):this.inherited_inner_radius?h.inherit:(0,u.to_screen)(this.inner_radius))),this._define_or_inherit_attr(\"souter_radius\",(()=>\"data\"==this.model.properties.outer_radius.units?this.inherited_x&&this.inherited_outer_radius?h.inherit:this.sdist(this.renderer.xscale,this.x,this.outer_radius):this.inherited_outer_radius?h.inherit:(0,u.to_screen)(this.outer_radius))),this._define_or_inherit_attr(\"max_souter_radius\",(()=>(0,x.max)(this.souter_radius)))}_paint(e,t,i){const{sx:s,sy:r,start_angle:n,end_angle:a,sinner_radius:_,souter_radius:h}={...this,...i},d=\"anticlock\"==this.model.direction;for(const i of t){const t=s[i],o=r[i],u=_[i],l=h[i],c=n.get(i),g=a.get(i);if(!isFinite(t+o+u+l+c+g))continue;const p=g-c;e.translate(t,o),e.rotate(c),e.beginPath(),e.moveTo(l,0),e.arc(0,0,l,0,p,d),e.rotate(p),e.lineTo(u,0),e.arc(0,0,u,0,-p,!d),e.closePath(),e.rotate(-p-c),e.translate(-t,-o),this.visuals.fill.apply(e,i),this.visuals.hatch.apply(e,i),this.visuals.line.apply(e,i)}}_hit_point(e){const{sx:t,sy:i}=e,s=this.renderer.xscale.invert(t),r=this.renderer.yscale.invert(i),n=t-this.max_souter_radius,a=t+this.max_souter_radius,[_,h]=this.renderer.xscale.r_invert(n,a),d=i-this.max_souter_radius,o=i+this.max_souter_radius,[u,l]=this.renderer.yscale.r_invert(d,o),c=[];for(const e of this.index.indices({x0:_,x1:h,y0:u,y1:l})){const t=this.souter_radius[e]**2,i=this.sinner_radius[e]**2,[n,a]=this.renderer.xscale.r_compute(s,this.x[e]),[_,h]=this.renderer.yscale.r_compute(r,this.y[e]),d=(n-a)**2+(_-h)**2;d<=t&&d>=i&&c.push(e)}const x=\"anticlock\"==this.model.direction,m=[];for(const e of c){const s=Math.atan2(i-this.sy[e],t-this.sx[e]);(Math.abs(this.start_angle.get(e)-this.end_angle.get(e))>=2*Math.PI||(0,g.angle_between)(-s,-this.start_angle.get(e),-this.end_angle.get(e),x))&&m.push(e)}return new p.Selection({indices:m})}draw_legend_for_index(e,t,i){(0,d.generic_area_vector_legend)(this.visuals,e,t,i)}scenterxy(e){const t=(this.sinner_radius[e]+this.souter_radius[e])/2,i=(this.start_angle.get(e)+this.end_angle.get(e))/2;return[this.sx[e]+t*Math.cos(i),this.sy[e]+t*Math.sin(i)]}}i.AnnularWedgeView=m,m.__name__=\"AnnularWedgeView\";class f extends _.XYGlyph{constructor(e){super(e)}}i.AnnularWedge=f,n=f,f.__name__=\"AnnularWedge\",n.prototype.default_view=m,n.mixins([o.LineVector,o.FillVector,o.HatchVector]),n.define((({})=>({direction:[l.Direction,\"anticlock\"],inner_radius:[c.DistanceSpec,{field:\"inner_radius\"}],outer_radius:[c.DistanceSpec,{field:\"outer_radius\"}],start_angle:[c.AngleSpec,{field:\"start_angle\"}],end_angle:[c.AngleSpec,{field:\"end_angle\"}]})))},\n function _(i,s,e,t,r){var n;t();const a=i(1),_=i(220),u=i(221),h=i(24),d=i(80),o=a.__importStar(i(18)),c=i(130);class l extends _.XYGlyphView{async load_glglyph(){const{AnnulusGL:s}=await Promise.resolve().then((()=>a.__importStar(i(559))));return s}_map_data(){this._define_or_inherit_attr(\"sinner_radius\",(()=>\"data\"==this.model.properties.inner_radius.units?this.inherited_x&&this.inherited_inner_radius?u.inherit:this.sdist(this.renderer.xscale,this.x,this.inner_radius):this.inherited_inner_radius?u.inherit:(0,h.to_screen)(this.inner_radius))),this._define_or_inherit_attr(\"souter_radius\",(()=>\"data\"==this.model.properties.outer_radius.units?this.inherited_x&&this.inherited_outer_radius?u.inherit:this.sdist(this.renderer.xscale,this.x,this.outer_radius):this.inherited_outer_radius?u.inherit:(0,h.to_screen)(this.outer_radius)))}_paint(i,s,e){const{sx:t,sy:r,sinner_radius:n,souter_radius:a}={...this,...e};for(const e of s){const s=t[e],_=r[e],u=n[e],h=a[e];isFinite(s+_+u+h)&&(i.beginPath(),i.arc(s,_,u,0,2*Math.PI,!0),i.moveTo(s+h,_),i.arc(s,_,h,2*Math.PI,0,!1),this.visuals.fill.apply(i,e),this.visuals.hatch.apply(i,e),this.visuals.line.apply(i,e))}}_hit_point(i){const{sx:s,sy:e}=i,t=this.renderer.xscale.invert(s),r=this.renderer.yscale.invert(e);let n,a,_,u;if(\"data\"==this.model.properties.outer_radius.units)n=t-this.max_outer_radius,_=t+this.max_outer_radius,a=r-this.max_outer_radius,u=r+this.max_outer_radius;else{const i=s-this.max_outer_radius,t=s+this.max_outer_radius;[n,_]=this.renderer.xscale.r_invert(i,t);const r=e-this.max_outer_radius,h=e+this.max_outer_radius;[a,u]=this.renderer.yscale.r_invert(r,h)}const h=[];for(const i of this.index.indices({x0:n,x1:_,y0:a,y1:u})){const s=this.souter_radius[i]**2,e=this.sinner_radius[i]**2,[n,a]=this.renderer.xscale.r_compute(t,this.x[i]),[_,u]=this.renderer.yscale.r_compute(r,this.y[i]),d=(n-a)**2+(_-u)**2;d<=s&&d>=e&&h.push(i)}return new c.Selection({indices:h})}draw_legend_for_index(i,{x0:s,y0:e,x1:t,y1:r},n){const a=n+1,_=new Array(a);_[n]=(s+t)/2;const u=new Array(a);u[n]=(e+r)/2;const h=.5*Math.min(Math.abs(t-s),Math.abs(r-e)),d=new Array(a);d[n]=.4*h;const o=new Array(a);o[n]=.8*h,this._paint(i,[n],{sx:_,sy:u,sinner_radius:d,souter_radius:o})}}e.AnnulusView=l,l.__name__=\"AnnulusView\";class x extends _.XYGlyph{constructor(i){super(i)}}e.Annulus=x,n=x,x.__name__=\"Annulus\",n.prototype.default_view=l,n.mixins([d.LineVector,d.FillVector,d.HatchVector]),n.define((({})=>({inner_radius:[o.DistanceSpec,{field:\"inner_radius\"}],outer_radius:[o.DistanceSpec,{field:\"outer_radius\"}]})))},\n function _(e,i,t,s,n){var r;s();const a=e(1),o=e(220),d=e(221),c=e(227),_=e(80),l=e(24),h=e(20),u=a.__importStar(e(18));class p extends o.XYGlyphView{_map_data(){this._define_or_inherit_attr(\"sradius\",(()=>\"data\"==this.model.properties.radius.units?this.inherited_x&&this.inherited_radius?d.inherit:this.sdist(this.renderer.xscale,this.x,this.radius):this.inherited_radius?d.inherit:(0,l.to_screen)(this.radius)))}_paint(e,i,t){if(!this.visuals.line.doit)return;const{sx:s,sy:n,sradius:r,start_angle:a,end_angle:o}={...this,...t},d=\"anticlock\"==this.model.direction;for(const t of i){const i=s[t],c=n[t],_=r[t],l=a.get(t),h=o.get(t);isFinite(i+c+_+l+h)&&(this._render_decorations(e,t,i,c,_,l,h,d),e.beginPath(),e.arc(i,c,_,l,h,d),this.visuals.line.apply(e,t))}}_render_decorations(e,i,t,s,n,r,a,o){const{sin:d,cos:c,PI:_}=Math;for(const o of this.decorations.values()){if(e.save(),\"start\"==o.model.node){const i=n*c(r)+t,a=n*d(r)+s;e.translate(i,a),e.rotate(r+_)}else if(\"end\"==o.model.node){const i=n*Math.cos(a)+t,r=n*Math.sin(a)+s;e.translate(i,r),e.rotate(a)}o.marking.paint(e,i),e.restore()}}draw_legend_for_index(e,i,t){(0,c.generic_line_vector_legend)(this.visuals,e,i,t)}}t.ArcView=p,p.__name__=\"ArcView\";class f extends o.XYGlyph{constructor(e){super(e)}}t.Arc=f,r=f,f.__name__=\"Arc\",r.prototype.default_view=p,r.mixins(_.LineVector),r.define((({})=>({direction:[h.Direction,\"anticlock\"],radius:[u.DistanceSpec,{field:\"radius\"}],start_angle:[u.AngleSpec,{field:\"start_angle\"}],end_angle:[u.AngleSpec,{field:\"end_angle\"}]})))},\n function _(e,i,t,s,c){var o;s();const n=e(1),r=e(80),d=e(221),x=e(227),y=e(352),a=n.__importStar(e(18));class _ extends d.GlyphView{_project_data(){this._project_xy(\"x0\",this.x0,\"y0\",this.y0),this._project_xy(\"x1\",this.x1,\"y1\",this.y1)}_index_data(e){const{data_size:i,x0:t,y0:s,x1:c,y1:o,cx0:n,cy0:r,cx1:d,cy1:x}=this;for(let a=0;a({x0:[a.XCoordinateSpec,{field:\"x0\"}],y0:[a.YCoordinateSpec,{field:\"y0\"}],x1:[a.XCoordinateSpec,{field:\"x1\"}],y1:[a.YCoordinateSpec,{field:\"y1\"}],cx0:[a.XCoordinateSpec,{field:\"cx0\"}],cy0:[a.YCoordinateSpec,{field:\"cy0\"}],cx1:[a.XCoordinateSpec,{field:\"cx1\"}],cy1:[a.YCoordinateSpec,{field:\"cy1\"}]}))),o.mixins(r.LineVector)},\n function _(n,t,o,c,s){c();const r=n(13),{abs:u,sqrt:e,min:i,max:f}=Math;o.qbb=function(n,t,o,c,s,r){function u(n,t,o){if(t==(n+o)/2)return[n,o];{const c=(n-t)/(n-2*t+o),s=n*(1-c)**2+2*t*(1-c)*c+o*c**2;return[i(n,o,s),f(n,o,s)]}}const[e,a]=u(n,o,s),[x,y]=u(t,c,r);return{x0:e,x1:a,y0:x,y1:y}},o.cbb=function(n,t,o,c,s,i,f,a){const x=f,y=a;f=o,a=c;const b=s,h=i,l=[];for(let o=0;o<=2;o++){let c,s,r;if(0==o?(s=6*n-12*f+6*b,c=-3*n+9*f-9*b+3*x,r=3*f-3*n):(s=6*t-12*a+6*h,c=-3*t+9*a-9*h+3*y,r=3*a-3*t),u(c)<1e-12){if(u(s)<1e-12)continue;const n=-r/s;00;){const o=l[p],c=1-o,s=c**3*n+3*c**2*o*f+3*c*o**2*b+o**3*x,r=c**3*t+3*c**2*o*a+3*c*o**2*h+o**3*y;q[p]=s,A[p]=r}q[m]=n,A[m]=t,q[m+1]=x,A[m+1]=y;const[g,M,_,d]=(0,r.minmax2)(q,A);return{x0:g,x1:M,y0:_,y1:d}}},\n function _(t,e,i,s,r){var h;s();const n=t(1),_=t(354),a=t(11),o=t(24),c=n.__importStar(t(18));class d extends _.LRTBView{scenterxy(t){return[this.sleft[t]/2+this.sright[t]/2,this.stop[t]/2+this.sbottom[t]/2]}_lrtb(t){const e=this.x[t],i=this.y[t],s=this.width.get(t),r=this.height.get(t),[h,n]=(0,a.minmax)(e,e+s),[_,o]=(0,a.minmax)(i,i+r);return{l:h,r:n,t:o,b:_}}_map_data(){const{sx:t,sy:e}=this,i=t.length;if(this.inherited_x&&this.inherited_width)this._inherit_attr(\"sleft\"),this._inherit_attr(\"sright\");else{const e=this.sdist(this.renderer.xscale,this.x,this.width,\"edge\"),s=new o.ScreenArray(i),r=new o.ScreenArray(i);for(let h=0;h({x:[c.XCoordinateSpec,{field:\"x\"}],y:[c.YCoordinateSpec,{field:\"y\"}],width:[c.DistanceSpec,{value:1}],height:[c.DistanceSpec,{value:1}]})))},\n function _(t,e,r,s,i){var n;s();const a=t(1),o=t(80),_=t(221),c=t(227),h=t(130),d=t(64),l=t(13),p=t(185),x=a.__importStar(t(186)),m=t(187);class u extends _.GlyphView{async load_glglyph(){const{LRTBGL:e}=await Promise.resolve().then((()=>a.__importStar(t(566))));return e}get_anchor_point(t,e,r){const s=Math.min(this.sleft[e],this.sright[e]),i=Math.max(this.sright[e],this.sleft[e]),n=Math.min(this.stop[e],this.sbottom[e]),a=Math.max(this.sbottom[e],this.stop[e]);switch(t){case\"top_left\":return{x:s,y:n};case\"top\":case\"top_center\":return{x:(s+i)/2,y:n};case\"top_right\":return{x:i,y:n};case\"bottom_left\":return{x:s,y:a};case\"bottom\":case\"bottom_center\":return{x:(s+i)/2,y:a};case\"bottom_right\":return{x:i,y:a};case\"left\":case\"center_left\":return{x:s,y:(n+a)/2};case\"center\":case\"center_center\":return{x:(s+i)/2,y:(n+a)/2};case\"right\":case\"center_right\":return{x:i,y:(n+a)/2}}}_set_data(t){super._set_data(t),this.border_radius=x.border_radius(this.model.border_radius)}_index_data(t){const{min:e,max:r}=Math,{data_size:s}=this;for(let i=0;ie(t,r.start))),this.inherited_sright||(0,l.inplace_map)(this.sright,(e=>t(e,r.end))),this.inherited_stop||(0,l.inplace_map)(this.stop,(t=>e(t,s.start))),this.inherited_sbottom||(0,l.inplace_map)(this.sbottom,(e=>t(e,s.end)))}_hit_rect(t){return this._hit_rect_against_index(t)}_hit_point(t){const{sx:e,sy:r}=t,s=this.renderer.xscale.invert(e),i=this.renderer.yscale.invert(r),n=[...this.index.indices({x0:s,y0:i,x1:s,y1:i})];return new h.Selection({indices:n})}_hit_span(t){const{sx:e,sy:r}=t;let s;if(\"v\"==t.direction){const t=this.renderer.yscale.invert(r),e=this.renderer.plot_view.frame.bbox.h_range,[i,n]=this.renderer.xscale.r_invert(e.start,e.end);s=[...this.index.indices({x0:i,y0:t,x1:n,y1:t})]}else{const t=this.renderer.xscale.invert(e),r=this.renderer.plot_view.frame.bbox.v_range,[i,n]=this.renderer.yscale.r_invert(r.start,r.end);s=[...this.index.indices({x0:t,y0:i,x1:t,y1:n})]}return new h.Selection({indices:s})}draw_legend_for_index(t,e,r){(0,c.generic_area_vector_legend)(this.visuals,t,e,r)}}r.LRTBView=u,u.__name__=\"LRTBView\";class y extends _.Glyph{constructor(t){super(t)}}r.LRTB=y,n=y,y.__name__=\"LRTB\",n.mixins([o.LineVector,o.FillVector,o.HatchVector]),n.define((()=>({border_radius:[p.BorderRadius,0]})))},\n function _(i,e,s,t,r){var n;t();const a=i(1),d=i(220),h=i(221),c=i(80),o=i(24),_=i(20),l=a.__importStar(i(228)),u=a.__importStar(i(18)),x=i(10),y=i(13),p=i(130);class m extends d.XYGlyphView{async load_glglyph(){const{CircleGL:e}=await Promise.resolve().then((()=>a.__importStar(i(561))));return e}_index_data(i){const{x:e,y:s,radius:t,data_size:r}=this;for(let n=0;n{if(\"data\"!=this.model.properties.radius.units)return this.inherited_sradius?h.inherit:(0,o.to_screen)(this.radius);{const i=()=>this.sdist(this.renderer.xscale,this.x,this.radius),e=()=>this.sdist(this.renderer.yscale,this.y,this.radius),{radius_dimension:s}=this.model;switch(s){case\"x\":return this.inherited_x&&this.inherited_radius?h.inherit:i();case\"y\":return this.inherited_y&&this.inherited_radius?h.inherit:e();case\"min\":case\"max\":return this.inherited_x&&this.inherited_y&&this.inherited_radius?h.inherit:(0,x.elementwise)(i(),e(),Math[s])}}}))}_mask_data(){const{frame:i}=this.renderer.plot_view,e=i.x_target,s=i.y_target;let t,r;return\"data\"==this.model.properties.radius.units?(t=e.map((i=>this.renderer.xscale.invert(i))).widen(this.max_radius),r=s.map((i=>this.renderer.yscale.invert(i))).widen(this.max_radius)):(t=e.widen(this.max_radius).map((i=>this.renderer.xscale.invert(i))),r=s.widen(this.max_radius).map((i=>this.renderer.yscale.invert(i)))),this.index.indices({x0:t.start,x1:t.end,y0:r.start,y1:r.end})}_paint(i,e,s){const{sx:t,sy:r,sradius:n}={...this,...s};for(const s of e){const e=t[s],a=r[s],d=n[s];isFinite(e+a+d)&&(i.beginPath(),i.arc(e,a,d,0,2*Math.PI,!1),this.visuals.fill.apply(i,s),this.visuals.hatch.apply(i,s),this.visuals.line.apply(i,s))}}_hit_point(i){const{sx:e,sy:s}=i,t=this.renderer.xscale.invert(e),r=this.renderer.yscale.invert(s),{hit_dilation:n}=this.model,[a,d,h,c]=(()=>{if(\"data\"==this.model.properties.radius.units){const i=this.max_radius*n;return[t-i,t+i,r-i,r+i]}{const i=this.max_radius*n,t=e-i,r=e+i,a=s-i,d=s+i,[h,c]=this.renderer.xscale.r_invert(t,r),[o,_]=this.renderer.yscale.r_invert(a,d);return[h,c,o,_]}})(),o=this.index.indices({x0:a,x1:d,y0:h,y1:c}),_=[];if(\"data\"==this.model.properties.radius.units)for(const i of o){const e=(this.sradius[i]*n)**2,[s,a]=this.renderer.xscale.r_compute(t,this.x[i]),[d,h]=this.renderer.yscale.r_compute(r,this.y[i]);(s-a)**2+(d-h)**2<=e&&_.push(i)}else for(const i of o){const t=(this.sradius[i]*n)**2;(this.sx[i]-e)**2+(this.sy[i]-s)**2<=t&&_.push(i)}return new p.Selection({indices:_})}_hit_span(i){const{sx:e,sy:s}=i,t=this.bounds(),[r,n,a,d]=(()=>{const r=this.max_radius;if(\"h\"==i.direction){const i=e-r,s=e+r,[n,a]=this.renderer.xscale.r_invert(i,s),{y0:d,y1:h}=t;return[n,a,d,h]}{const i=s-r,e=s+r,{x0:n,x1:a}=t,[d,h]=this.renderer.yscale.r_invert(i,e);return[n,a,d,h]}})(),h=[...this.index.indices({x0:r,x1:n,y0:a,y1:d})];return new p.Selection({indices:h})}_hit_rect(i){const{sx0:e,sx1:s,sy0:t,sy1:r}=i,[n,a]=this.renderer.xscale.r_invert(e,s),[d,h]=this.renderer.yscale.r_invert(t,r),c=this.index.indices({x0:n,x1:a,y0:d,y1:h}),o=[];for(const i of c){const n=this.sx[i],a=this.sy[i];e<=n&&n<=s&&t<=a&&a<=r&&o.push(i)}return new p.Selection({indices:o})}_hit_poly(i){const{sx:e,sy:s}=i,t=(()=>{const[i,t,r,n]=(0,y.minmax2)(e,s),[a,d]=this.renderer.xscale.r_invert(i,t),[h,c]=this.renderer.yscale.r_invert(r,n);return this.index.indices({x0:a,x1:d,y0:h,y1:c})})(),r=[];for(const i of t)l.point_in_poly(this.sx[i],this.sy[i],e,s)&&r.push(i);return new p.Selection({indices:r})}draw_legend_for_index(i,{x0:e,y0:s,x1:t,y1:r},n){const a=n+1,d=new Array(a);d[n]=(e+t)/2;const h=new Array(a);h[n]=(s+r)/2;const c=new Array(a);c[n]=.2*Math.min(Math.abs(t-e),Math.abs(r-s)),this._paint(i,[n],{sx:d,sy:h,sradius:c})}}s.CircleView=m,m.__name__=\"CircleView\";class f extends d.XYGlyph{constructor(i){super(i)}}s.Circle=f,n=f,f.__name__=\"Circle\",n.prototype.default_view=m,n.mixins([c.LineVector,c.FillVector,c.HatchVector]),n.define((({Float:i})=>({radius:[u.DistanceSpec,{field:\"radius\"}],radius_dimension:[_.RadiusDimension,\"x\"],hit_dilation:[i,1]})))},\n function _(t,i,e,s,h){var r;s();const n=t(1),a=t(357),_=t(221),d=n.__importStar(t(228)),l=t(24),o=t(130),c=n.__importStar(t(18));class p extends a.CenterRotatableView{_map_data(){this._define_or_inherit_attr(\"swidth\",(()=>\"data\"==this.model.properties.width.units?this.inherited_x&&this.inherited_width?_.inherit:this.sdist(this.renderer.xscale,this.x,this.width,\"center\"):this.inherited_width?_.inherit:(0,l.to_screen)(this.width))),this._define_or_inherit_attr(\"sheight\",(()=>\"data\"==this.model.properties.height.units?this.inherited_y&&this.inherited_height?_.inherit:this.sdist(this.renderer.yscale,this.y,this.height,\"center\"):this.inherited_height?_.inherit:(0,l.to_screen)(this.height)))}_paint(t,i,e){const{sx:s,sy:h,swidth:r,sheight:n,angle:a}={...this,...e};for(const e of i){const i=s[e],_=h[e],d=r[e],l=n[e],o=a.get(e);isFinite(i+_+d+l+o)&&(t.beginPath(),t.ellipse(i,_,d/2,l/2,o,0,2*Math.PI),this.visuals.fill.apply(t,e),this.visuals.hatch.apply(t,e),this.visuals.line.apply(t,e))}}_hit_point(t){let i,e,s,h,r,n,a,_,l;const{sx:c,sy:p}=t,w=this.renderer.xscale.invert(c),x=this.renderer.yscale.invert(p);\"data\"==this.model.properties.width.units?(i=w-this.max_width,e=w+this.max_width):(n=c-this.max_width,a=c+this.max_width,[i,e]=this.renderer.xscale.r_invert(n,a)),\"data\"==this.model.properties.height.units?(s=x-this.max_height,h=x+this.max_height):(_=p-this.max_height,l=p+this.max_height,[s,h]=this.renderer.yscale.r_invert(_,l));const g=this.index.indices({x0:i,x1:e,y0:s,y1:h}),y=[];for(const t of g)r=d.point_in_ellipse(c,p,this.angle.get(t),this.sheight[t]/2,this.swidth[t]/2,this.sx[t],this.sy[t]),r&&y.push(t);return new o.Selection({indices:y})}draw_legend_for_index(t,{x0:i,y0:e,x1:s,y1:h},r){const n=r+1,a=new Array(n);a[r]=(i+s)/2;const _=new Array(n);_[r]=(e+h)/2;const d=this.swidth[r]/this.sheight[r],l=.8*Math.min(Math.abs(s-i),Math.abs(h-e)),o=new Array(n),p=new Array(n);d>1?(o[r]=l,p[r]=l/d):(o[r]=l*d,p[r]=l);const w=new c.UniformScalar(0,n);this._paint(t,[r],{sx:a,sy:_,swidth:o,sheight:p,angle:w})}}e.EllipseView=p,p.__name__=\"EllipseView\";class w extends a.CenterRotatable{constructor(t){super(t)}}e.Ellipse=w,r=w,w.__name__=\"Ellipse\",r.prototype.default_view=p},\n function _(e,t,i,a,n){var r;a();const s=e(1),h=e(220),o=e(80),_=s.__importStar(e(18));class c extends h.XYGlyphView{get max_w2(){return\"data\"==this.model.properties.width.units?this.max_width/2:0}get max_h2(){return\"data\"==this.model.properties.height.units?this.max_height/2:0}_bounds({x0:e,x1:t,y0:i,y1:a}){const{max_w2:n,max_h2:r}=this;return{x0:e-n,x1:t+n,y0:i-r,y1:a+r}}}i.CenterRotatableView=c,c.__name__=\"CenterRotatableView\";class l extends h.XYGlyph{constructor(e){super(e)}}i.CenterRotatable=l,r=l,l.__name__=\"CenterRotatable\",r.mixins([o.LineVector,o.FillVector,o.HatchVector]),r.define((({})=>({angle:[_.AngleSpec,0],width:[_.DistanceSpec,{field:\"width\"}],height:[_.DistanceSpec,{field:\"height\"}]})))},\n function _(t,e,i,s,r){var h;s();const n=t(1),a=t(354),_=t(24),o=n.__importStar(t(18));class c extends a.LRTBView{scenterxy(t){return[(this.sleft[t]+this.sright[t])/2,this.sy[t]]}_lrtb(t){const e=this.left[t],i=this.right[t],s=this.y[t],r=this.height.get(t)/2;return{l:Math.min(e,i),r:Math.max(e,i),t:s+r,b:s-r}}_map_data(){if(this.inherited_y&&this.inherited_height)this._inherit_attr(\"sheight\"),this._inherit_attr(\"stop\"),this._inherit_attr(\"sbottom\");else{const t=this.sdist(this.renderer.yscale,this.y,this.height,\"center\"),{sy:e}=this,i=this.sy.length,s=new _.ScreenArray(i),r=new _.ScreenArray(i);for(let h=0;h({left:[o.XCoordinateSpec,{value:0}],y:[o.YCoordinateSpec,{field:\"y\"}],height:[o.DistanceSpec,{value:1}],right:[o.XCoordinateSpec,{field:\"right\"}]})))},\n function _(e,t,s,i,r){var n;i();const a=e(1),o=e(221),c=a.__importStar(e(228)),l=a.__importStar(e(18)),_=e(80),h=e(20),d=e(227),p=e(130);class x extends o.GlyphView{async load_glglyph(){const{HexTileGL:t}=await Promise.resolve().then((()=>a.__importStar(e(562))));return t}scenterxy(e){return[this.sx[e],this.sy[e]]}_set_data(){const{orientation:e,size:t,aspect_scale:s}=this.model,{q:i,r}=this,n=this.q.length,a=new Float64Array(n),o=new Float64Array(n),c=Math.sqrt(3);if(\"pointytop\"==e)for(let e=0;e{if(\"v\"==e.direction){const{sy:t}=e,s=this.renderer.yscale.invert(t),i=this.renderer.plot_view.frame.bbox.h_range,[r,n]=this.renderer.xscale.r_invert(i.start,i.end);return{x0:r,y0:s,x1:n,y1:s}}{const{sx:t}=e,s=this.renderer.xscale.invert(t),i=this.renderer.plot_view.frame.bbox.v_range,[r,n]=this.renderer.yscale.r_invert(i.start,i.end);return{x0:s,y0:r,x1:s,y1:n}}})(),s=[...this.index.indices(t)];return new p.Selection({indices:s})}_hit_rect(e){const{sx0:t,sx1:s,sy0:i,sy1:r}=e,[n,a]=this.renderer.xscale.r_invert(t,s),[o,c]=this.renderer.yscale.r_invert(i,r),l=[...this.index.indices({x0:n,x1:a,y0:o,y1:c})];return new p.Selection({indices:l})}draw_legend_for_index(e,t,s){(0,d.generic_area_vector_legend)(this.visuals,e,t,s)}}s.HexTileView=x,x.__name__=\"HexTileView\";class y extends o.Glyph{constructor(e){super(e)}}s.HexTile=y,n=y,y.__name__=\"HexTile\",n.prototype.default_view=x,n.mixins([_.LineVector,_.FillVector,_.HatchVector]),n.define((({Float:e})=>({r:[l.NumberSpec,{field:\"r\"}],q:[l.NumberSpec,{field:\"q\"}],scale:[l.NumberSpec,1],size:[e,1],aspect_scale:[e,1],orientation:[h.HexTileOrientation,\"pointytop\"]}))),n.override({line_color:null})},\n function _(t,e,i,n,s){var _;n();const r=t(1),a=t(221),o=t(227),d=t(130),c=t(80),h=r.__importStar(t(39)),p=t(13),l=t(10),y=r.__importStar(t(18)),{abs:f,max:u}=Math;class x extends a.GlyphView{after_visuals(){super.after_visuals(),this.max_line_width=h.max(this.line_width)}_index_data(t){for(const e of this.y)t.add_point(0,e)}_bounds(t){const{y0:e,y1:i}=t;return{x0:NaN,x1:NaN,y0:e,y1:i}}_map_data(){super._map_data();const{round:t}=Math;if(!this.inherited_sy){const e=(0,p.map)(this.sy,(e=>t(e)));this._define_attr(\"sy\",e)}}scenterxy(t){const{hcenter:e}=this.renderer.plot_view.frame.bbox;return[e,this.sy[t]]}_paint(t,e,i){const{sy:n}={...this,...i},{left:s,right:_}=this.renderer.plot_view.frame.bbox;for(const i of e){const e=n[i];isFinite(e)&&(t.beginPath(),t.moveTo(s,e),t.lineTo(_,e),this.visuals.line.apply(t,i))}}_get_candidates(t,e){const{max_line_width:i}=this,[n,s]=this.renderer.yscale.r_invert(t-i,(e??t)+i);return this.index.indices({x0:0,x1:0,y0:n,y1:s})}_find_spans(t,e){const{sy:i,line_width:n}=this,s=[];for(const _ of t){e(i[_],n.get(_))&&s.push(_)}return s}_hit_point(t){const{sy:e}=t,i=this._get_candidates(e),n=this._find_spans(i,((t,i)=>f(t-e)<=u(i/2,2)));return new d.Selection({indices:n})}_hit_span(t){const e=(()=>{if(\"v\"==t.direction)return(0,l.range)(0,this.data_size);{const{sy:e}=t,i=this._get_candidates(e);return this._find_spans(i,((t,i)=>f(t-e)<=u(i/2,2)))}})();return new d.Selection({indices:e})}_hit_rect(t){const e=(()=>{const{sy0:e,sy1:i}=t,n=this._get_candidates(e,i);return this._find_spans(n,((t,n)=>e-n/2<=t&&t<=i+n/2))})();return new d.Selection({indices:e})}draw_legend_for_index(t,e,i){(0,o.generic_line_vector_legend)(this.visuals,t,e,i)}}i.HSpanView=x,x.__name__=\"HSpanView\";class m extends a.Glyph{constructor(t){super(t)}}i.HSpan=m,_=m,m.__name__=\"HSpan\",_.prototype.default_view=x,_.mixins([c.LineVector]),_.define((()=>({y:[y.YCoordinateSpec,{field:\"y\"}]})))},\n function _(t,e,i,s,r){var n;s();const a=t(1),_=t(221),o=t(227),h=t(130),c=t(80),d=t(24),l=t(13),p=a.__importStar(t(34)),y=t(10),f=a.__importStar(t(18));class u extends _.GlyphView{async lazy_initialize(){await super.lazy_initialize();const{webgl:e}=this.renderer.plot_view.canvas_view;if(null!=e&&e.regl_wrapper.has_webgl){const{LRTBGL:i}=await Promise.resolve().then((()=>a.__importStar(t(566))));this.glglyph=new i(e.regl_wrapper,this)}}get sleft(){const{left:t}=this.renderer.plot_view.frame.bbox,e=this.data_size,i=new d.ScreenArray(e);return i.fill(t),i}get sright(){const{right:t}=this.renderer.plot_view.frame.bbox,e=this.data_size,i=new d.ScreenArray(e);return i.fill(t),i}get stop(){return this.sy0}get sbottom(){return this.sy1}_set_data(t){super._set_data(t);const{abs:e}=Math,{max:i,map:s,zip:r}=p,{y0:n,y1:a}=this;if(this.inherited_y0&&this.inherited_y1)this._inherit_attr(\"max_height\");else{const t=i(s(r(n,a),(([t,i])=>e(t-i))));this._define_attr(\"max_height\",t)}}_index_data(t){const{y0:e,y1:i,data_size:s}=this;for(let r=0;rt(e)));this._define_attr(\"sy0\",e)}if(!this.inherited_sy1){const e=(0,l.map)(this.sy1,(e=>t(e)));this._define_attr(\"sy1\",e)}}scenterxy(t){const{hcenter:e}=this.renderer.plot_view.frame.bbox;return[e,(this.sy0[t]+this.sy1[t])/2]}_paint(t,e,i){const{sy0:s,sy1:r}={...this,...i},{left:n,right:a,width:_}=this.renderer.plot_view.frame.bbox;for(const i of e){const e=s[i],o=r[i];isFinite(e+o)&&(t.beginPath(),t.rect(n,e,_,o-e),this.visuals.fill.apply(t,i),this.visuals.hatch.apply(t,i),t.beginPath(),t.moveTo(n,e),t.lineTo(a,e),t.moveTo(n,o),t.lineTo(a,o),this.visuals.line.apply(t,i))}}_get_candidates(t,e){const{max_height:i}=this,[s,r]=this.renderer.yscale.r_invert(t,e??t),n=s-i,a=r+i;return this.index.indices({x0:0,x1:0,y0:n,y1:a})}_find_strips(t,e){function i(t,i){return t<=i?e(t,i):e(i,t)}const{sy0:s,sy1:r}=this,n=[];for(const e of t){i(s[e],r[e])&&n.push(e)}return n}_hit_point(t){const{sy:e}=t,i=this._get_candidates(e),s=this._find_strips(i,((t,i)=>t<=e&&e<=i));return new h.Selection({indices:s})}_hit_span(t){const e=(()=>{if(\"v\"==t.direction)return(0,y.range)(0,this.data_size);{const{sy:e}=t,i=this._get_candidates(e);return this._find_strips(i,((t,i)=>t<=e&&e<=i))}})();return new h.Selection({indices:e})}_hit_rect(t){const e=(()=>{const{sy0:e,sy1:i}=t,s=this._get_candidates(e,i);return this._find_strips(s,((t,s)=>e<=t&&t<=i&&e<=s&&s<=i))})();return new h.Selection({indices:e})}draw_legend_for_index(t,e,i){(0,o.generic_area_vector_legend)(this.visuals,t,e,i)}}i.HStripView=u,u.__name__=\"HStripView\";class g extends _.Glyph{constructor(t){super(t)}}i.HStrip=g,n=g,g.__name__=\"HStrip\",n.prototype.default_view=u,n.mixins([c.LineVector,c.FillVector,c.HatchVector]),n.define((()=>({y0:[f.YCoordinateSpec,{field:\"y0\"}],y1:[f.YCoordinateSpec,{field:\"y1\"}]})))},\n function _(e,t,a,r,n){var i,_=this&&this.__createBinding||(Object.create?function(e,t,a,r){void 0===r&&(r=a);var n=Object.getOwnPropertyDescriptor(t,a);n&&!(\"get\"in n?!t.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return t[a]}}),Object.defineProperty(e,r,n)}:function(e,t,a,r){void 0===r&&(r=a),e[r]=t[a]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,\"default\",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var a in e)\"default\"!==a&&Object.prototype.hasOwnProperty.call(e,a)&&_(t,e,a);return o(t,e),t};r();const l=e(363),c=e(215),u=e(275);class p extends l.ImageBaseView{async load_glglyph(){const{ImageGL:t}=await Promise.resolve().then((()=>s(e(563))));return t}connect_signals(){super.connect_signals(),this.connect(this.model.color_mapper.change,(()=>this._update_image()))}_update_image(){null!=this.glglyph&&this.glglyph.set_image_changed(),null!=this.image_data&&(this._set_data(null),this.renderer.request_paint())}get _can_inherit_image_data(){return super._can_inherit_image_data&&this._can_inherit_from(this.model.properties.color_mapper,this.base)}_flat_img_to_buf8(e){return this.model.color_mapper.rgba_mapper.v_compute(e)}}a.ImageView=p,p.__name__=\"ImageView\";class d extends l.ImageBase{constructor(e){super(e)}}a.Image=d,i=d,d.__name__=\"Image\",i.prototype.default_view=p,i.define((({Ref:e})=>({color_mapper:[e(c.ColorMapper),()=>new u.LinearColorMapper({palette:[\"#000000\",\"#252525\",\"#525252\",\"#737373\",\"#969696\",\"#bdbdbd\",\"#d9d9d9\",\"#f0f0f0\",\"#ffffff\"]})]})))},\n function _(t,e,i,s,a){var r;s();const n=t(1),h=t(220),_=t(221),d=t(24),o=t(20),g=n.__importStar(t(18)),c=n.__importStar(t(80)),m=t(130),l=t(12),x=t(185),u=t(186);class f extends h.XYGlyphView{connect_signals(){super.connect_signals(),this.connect(this.model.properties.global_alpha.change,(()=>this.renderer.request_paint()))}get image_dimension(){return 2}get xy_scale(){switch(this.model.origin){case\"bottom_left\":return{x:1,y:-1};case\"top_left\":return{x:1,y:1};case\"bottom_right\":return{x:-1,y:-1};case\"top_right\":return{x:-1,y:1}}}get xy_offset(){switch(this.model.origin){case\"bottom_left\":return{x:0,y:1};case\"top_left\":return{x:0,y:0};case\"bottom_right\":return{x:1,y:1};case\"top_right\":return{x:1,y:0}}}get xy_anchor(){return(0,u.anchor)(this.model.anchor)}get xy_sign(){const t=this.renderer.xscale.source_range,e=this.renderer.yscale.source_range;return{x:t.is_reversed?-1:1,y:e.is_reversed?-1:1}}_paint(t,e,i){const{image_data:s,sx:a,sy:r,sdw:n,sdh:h}={...this,...i},{xy_sign:_,xy_scale:d,xy_offset:o,xy_anchor:g}=this;if((0,l.assert)(null!=s),t.save(),t.imageSmoothingEnabled=!1,this.visuals.image.doit)for(const i of e){const e=s[i],c=a[i],m=r[i],l=n[i],x=h[i];if(null==e||!isFinite(c+m+l+x))continue;const u=_.x*g.x*l,f=_.y*g.y*x;t.save(),t.translate(c-u,m-f),t.scale(_.x*d.x,_.y*d.y),this.visuals.image.set_vectorize(t,i),t.drawImage(e,-o.x*l,-o.y*x,l,x),t.restore()}t.restore()}get _can_inherit_image_data(){return this.inherited_image}_set_data(t){const e=this.data_size;if(this._can_inherit_image_data)this._inherit_attr(\"image_data\"),this._inherit_attr(\"image_width\"),this._inherit_attr(\"image_height\");else{void 0!==this.image_data&&this.image_data.length==e||(this._define_attr(\"image_data\",new Array(e).fill(null)),this._define_attr(\"image_width\",new Uint32Array(e)),this._define_attr(\"image_height\",new Uint32Array(e)));const{image_dimension:i}=this;for(let s=0;s\"data\"==this.model.properties.dw.units?this.inherited_x&&this.inherited_dw?_.inherit:this.sdist(this.renderer.xscale,this.x,this.dw,\"edge\",this.model.dilate):this.inherited_dw?_.inherit:(0,d.to_screen)(this.dw))),this._define_or_inherit_attr(\"sdh\",(()=>\"data\"==this.model.properties.dh.units?this.inherited_y&&this.inherited_dh?_.inherit:this.sdist(this.renderer.yscale,this.y,this.dh,\"edge\",this.model.dilate):this.inherited_dh?_.inherit:(0,d.to_screen)(this.dh)))}_image_index(t,e,i){const[s,a,r,n]=this._lrtb(t),h=this.image_width[t],_=(a-s)/h,d=(r-n)/this.image_height[t],o=Math.floor((e-s)/_),g=Math.floor((i-n)/d);return{index:t,i:o,j:g,flat_index:g*h+o}}_hit_point(t){const{sx:e,sy:i}=t,s=this.renderer.xscale.invert(e),a=this.renderer.yscale.invert(i),r=this.index.indices({x0:s,x1:s,y0:a,y1:a}),n=new m.Selection,h=[];for(const t of r)isFinite(e)&&isFinite(i)&&(h.push(t),n.image_indices.push(this._image_index(t,s,a)));return n.indices=h,n}}i.ImageBaseView=f,f.__name__=\"ImageBaseView\";class y extends h.XYGlyph{constructor(t){super(t)}}i.ImageBase=y,r=y,y.__name__=\"ImageBase\",r.mixins(c.ImageVector),r.define((({Bool:t})=>({image:[g.NDArraySpec,{field:\"image\"}],dw:[g.DistanceSpec,{field:\"dw\"}],dh:[g.DistanceSpec,{field:\"dh\"}],dilate:[t,!1],origin:[o.ImageOrigin,\"bottom_left\"],anchor:[x.Anchor,\"bottom_left\"]})))},\n function _(e,t,r,n,a){var i,o=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r);var a=Object.getOwnPropertyDescriptor(t,r);a&&!(\"get\"in a?!t.__esModule:a.writable||a.configurable)||(a={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,a)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),u=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,\"default\",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)\"default\"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&o(t,e,r);return u(t,e),t};n();const c=e(363),l=e(8);class _ extends c.ImageBaseView{async load_glglyph(){const{ImageGL:t}=await Promise.resolve().then((()=>s(e(563))));return t}_flat_img_to_buf8(e){const t=(0,l.isTypedArray)(e)?e:new Uint32Array(e);return new Uint8ClampedArray(t.buffer)}}r.ImageRGBAView=_,_.__name__=\"ImageRGBAView\";class f extends c.ImageBase{constructor(e){super(e)}}r.ImageRGBA=f,i=f,f.__name__=\"ImageRGBA\",i.prototype.default_view=_},\n function _(e,t,a,r,i){var n,_=this&&this.__createBinding||(Object.create?function(e,t,a,r){void 0===r&&(r=a);var i=Object.getOwnPropertyDescriptor(t,a);i&&!(\"get\"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[a]}}),Object.defineProperty(e,r,i)}:function(e,t,a,r){void 0===r&&(r=a),e[r]=t[a]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,\"default\",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var a in e)\"default\"!==a&&Object.prototype.hasOwnProperty.call(e,a)&&_(t,e,a);return o(t,e),t};r();const c=e(363),l=e(278);class u extends c.ImageBaseView{async load_glglyph(){const{ImageGL:t}=await Promise.resolve().then((()=>s(e(563))));return t}connect_signals(){super.connect_signals(),this.connect(this.model.color_mapper.change,(()=>this._update_image()))}get image_dimension(){return 3}_update_image(){null!=this.glglyph&&this.glglyph.set_image_changed(),null!=this.image_data&&(this._set_data(null),this.renderer.request_paint())}get _can_inherit_image_data(){return super._can_inherit_image_data&&this._can_inherit_from(this.model.properties.color_mapper,this.base)}_flat_img_to_buf8(e){return this.model.color_mapper.rgba_mapper.v_compute(e)}}a.ImageStackView=u,u.__name__=\"ImageStackView\";class p extends c.ImageBase{constructor(e){super(e)}}a.ImageStack=p,n=p,p.__name__=\"ImageStack\",n.prototype.default_view=u,n.define((({Ref:e})=>({color_mapper:[e(l.StackColorMapper)]})))},\n function _(e,t,i,s,r){var n;s();const a=e(1),h=e(220),l=e(221),o=e(24),d=e(20),_=a.__importStar(e(18)),c=e(10),u=e(13),m=e(179),g=a.__importStar(e(186));class p extends h.XYGlyphView{constructor(){super(...arguments),this._images_rendered=!1,this.image=new Array(0),this._set_data_iteration=0}connect_signals(){super.connect_signals(),this.connect(this.model.properties.global_alpha.change,(()=>this.renderer.request_paint()))}_index_data(e){const{data_size:t}=this;for(let i=0;i{this._set_data_iteration!=r||this.resolved.get(n)||(this.resolved.set(n),this.image[n]=e,this.loaders[n]=null,this.renderer.request_paint())},failed:()=>{if(this._set_data_iteration==r){this.resolved.set(n),this.loaders[n]=null;null!=this.image[n]&&(this.image[n]=null,this.renderer.request_paint())}},attempts:i+1,timeout:s});this.loaders[n]=a}const n=\"data\"==this.model.properties.w.units,a=\"data\"==this.model.properties.h.units,h=this.data_size,l=new o.ScreenArray(n?2*h:h),d=new o.ScreenArray(a?2*h:h);this.anchor=g.anchor(this.model.anchor);const{x:_,y:p}=this.anchor;function f(e,t){const i=e-_*t;return[i,i+t]}function w(e,t){const i=e+p*t;return[i,i-t]}if(n)for(let e=0;ethis.w.map((e=>e??NaN)),t=()=>this.h.map((e=>e??NaN));this._define_or_inherit_attr(\"sw\",(()=>\"data\"==this.model.properties.w.units?this.inherited_x&&this.inherited_w?l.inherit:this.sdist(this.renderer.xscale,this.x,e(),\"edge\",this.model.dilate):this.inherited_w?l.inherit:(0,o.to_screen)(e()))),this._define_or_inherit_attr(\"sh\",(()=>\"data\"==this.model.properties.h.units?this.inherited_y&&this.inherited_h?l.inherit:this.sdist(this.renderer.yscale,this.y,t(),\"edge\",this.model.dilate):this.inherited_h?l.inherit:(0,o.to_screen)(t())))}_paint(e,t,i){const{sx:s,sy:r,sw:n,sh:a,angle:h,global_alpha:l}={...this,...i},{image:o,loaders:d,resolved:_}=this,{frame:c}=this.renderer.plot_view,{left:u,top:m,width:g,height:p}=c.bbox;e.beginPath(),e.rect(u+1,m+1,g-2,p-2),e.clip();let f=!0;for(const i of t){const t=d[i];if(!isFinite(s[i]+r[i]+h.get(i)+l.get(i)))continue;_.get(i)||(null!=t&&t.image.complete?(o[i]=t.image,d[i]=null,_.set(i)):f=!1);const c=o[i];null!=c&&(0==c.naturalWidth&&0==c.naturalHeight||this._render_image(e,i,c,s,r,n,a,h,l))}f&&!this._images_rendered&&(this._images_rendered=!0,this.notify_finished())}_render_image(e,t,i,s,r,n,a,h,l){isFinite(n[t])||(n[t]=i.width),isFinite(a[t])||(a[t]=i.height);const o=n[t],d=a[t],{anchor:_}=this,c=_.x*o,u=_.y*d,m=s[t]-c,g=r[t]-u,p=h.get(t),f=l.get(t);e.save(),e.globalAlpha=f;const w=o/2,y=d/2;0!=p?(e.translate(m,g),e.translate(w,y),e.rotate(p),e.translate(-w,-y),e.drawImage(i,0,0,o,d),e.translate(w,y),e.rotate(-p),e.translate(-w,-y),e.translate(-m,-g)):e.drawImage(i,m,g,o,d),e.restore()}bounds(){return this._bounds_rect}}i.ImageURLView=p,p.__name__=\"ImageURLView\";class f extends h.XYGlyph{constructor(e){super(e)}}i.ImageURL=f,n=f,f.__name__=\"ImageURL\",n.prototype.default_view=p,n.define((({Bool:e,Int:t})=>({url:[_.StringSpec,{field:\"url\"}],anchor:[d.Anchor,\"top_left\"],global_alpha:[_.NumberSpec,{value:1}],angle:[_.AngleSpec,0],w:[_.NullDistanceSpec,null],h:[_.NullDistanceSpec,null],dilate:[e,!1],retry_attempts:[t,0],retry_timeout:[t,0]})))},\n function _(t,e,a,h,l){var M;h();const _=t(368),n=t(178);class p extends _.MathTextGlyphView{_build_label(t){return new n.MathML({text:t})}}a.MathMLGlyphView=p,p.__name__=\"MathMLGlyphView\";class s extends _.MathTextGlyph{constructor(t){super(t)}}a.MathMLGlyph=s,M=s,s.__name__=\"MathMLGlyph\",M.prototype.default_view=p},\n function _(e,t,s,i,l){i();const a=e(369),n=e(178),r=e(56),h=e(8),_=e(34);class u extends a.TextView{constructor(){super(...arguments),this._label_views=new Map}remove(){(0,r.remove_views)(this._label_views),super.remove()}*children(){yield*super.children(),yield*this._label_views.values()}has_finished(){if(!super.has_finished())return!1;for(const e of this._label_views.values())if(!e.has_finished())return!1;return!0}async _build_labels(e){const t=Array.from(e,(e=>null==e?null:this._build_label(e)));return await(0,r.build_views)(this._label_views,t.filter(h.non_null),{parent:this.renderer}),t.map((e=>null==e?null:this._label_views.get(e).graphics()))}async after_lazy_visuals(){await super.after_lazy_visuals();const e=[...this._label_views.values()].map((e=>e instanceof n.MathTextView?e.request_image():null));await Promise.allSettled(e);const{left:t,right:s,top:i,bottom:l}=this.padding;for(const[e,a]of(0,_.enumerate)(this.labels)){if(null==e)continue;if(!(e instanceof n.MathTextView))continue;const r=e.size(),h=t+r.width+s,_=i+r.height+l;this.swidth[a]=h,this.sheight[a]=_}}}s.MathTextGlyphView=u,u.__name__=\"MathTextGlyphView\";class o extends a.Text{constructor(e){super(e)}}s.MathTextGlyph=o,o.__name__=\"MathTextGlyph\"},\n function _(t,e,a,i,s){var n;i();const o=t(1),r=t(220),l=o.__importStar(t(80)),h=o.__importStar(t(18)),c=t(39),_=t(130),d=t(64),u=t(34),x=t(114),p=t(180),g=t(185),f=o.__importStar(t(186)),y=t(187),b=t(11);class w extends h.DataSpec{}w.__name__=\"TextAnchorSpec\";class m extends h.DataSpec{}m.__name__=\"OutlineShapeSpec\";class v extends r.XYGlyphView{async _build_labels(t){return Array.from(t,(t=>{if(null==t)return null;{const e=`${t}`;return new p.TextBox({text:e})}}))}async _set_lazy_data(){this.inherited_text?this._inherit_attr(\"labels\"):this._define_attr(\"labels\",await this._build_labels(this.text))}after_visuals(){super.after_visuals();const t=this.data_size,{anchor:e}=this.base??this,{padding:a,border_radius:i}=this.model,{text_align:s,text_baseline:n}=this.visuals.text;if(e.is_Scalar()&&\"auto\"!=e.value)this.anchor_=new c.UniformScalar(f.anchor(e.value),t);else if(e.is_Scalar()&&s.is_Scalar()&&n.is_Scalar())this.anchor_=new c.UniformScalar(f.text_anchor(e.value,s.value,n.value),t);else{const a=new Array(t);for(let i=0;i{const{x:t,y:e,width:a,height:s}=i;if(a>s){const i=(a-s)/2;return new d.BBox({x:t,y:e-i,width:a,height:a})}{const i=(s-a)/2;return new d.BBox({x:t-i,y:e,width:s,height:s})}})();(0,y.round_rect)(t,e,n);break}case\"circle\":{const e=i.x_center,a=i.y_center,s=(0,b.sqrt)(i.width**2+i.height**2)/2;t.arc(e,a,s,0,2*b.PI,!1);break}case\"ellipse\":{const e=i.x_center,a=i.y_center,s=1.5,n=i.width/2,o=i.height/2,r=(0,b.sqrt)(n**2+n**(2/s)*o**(2-2/s)),l=(0,b.sqrt)(o**2+o**(2/s)*n**(2-2/s));t.ellipse(e,a,r,l,0,0,2*b.PI);break}case\"trapezoid\":{const{left:e,right:a,top:s,bottom:n,width:o}=i,r=.2*o;t.moveTo(e,s),t.lineTo(a,s),t.lineTo(a+r,n),t.lineTo(e-r,n),t.closePath();break}case\"parallelogram\":{const{left:e,right:a,top:s,bottom:n,width:o}=i,r=.2*o;t.moveTo(e,s),t.lineTo(a+r,s),t.lineTo(a,n),t.lineTo(e-r,n),t.closePath();break}case\"diamond\":{const{x_center:e,y_center:a,width:s,height:n}=i;t.moveTo(e,a-n),t.lineTo(s+s/2,a),t.lineTo(e,a+n),t.lineTo(-s/2,a),t.closePath();break}case\"triangle\":{const e=i.width,a=i.height,s=(0,b.sqrt)(3)/2*e,n=a+s;t.translate(e/2,-s),t.moveTo(0,0),t.lineTo(n/2,n),t.lineTo(-n/2,n),t.closePath(),t.translate(-e/2,s);break}}s.fill.apply(t,e),s.hatch.apply(t,e),s.line.apply(t,e)}_hit_point(t){const e={x:t.sx,y:t.sy},{sx:a,sy:i,x_offset:s,y_offset:n,angle:o,labels:r}=this,{anchor_:l}=this,{swidth:h,sheight:c}=this,d=this.data_size,u=[];for(let t=0;t({text:[h.NullStringSpec,{field:\"text\"}],angle:[h.AngleSpec,0],x_offset:[h.NumberSpec,0],y_offset:[h.NumberSpec,0],anchor:[w,{value:\"auto\"}],padding:[g.Padding,0],border_radius:[g.BorderRadius,0],outline_shape:[m,\"box\"]}))),n.override({border_line_color:null,background_fill_color:null,background_hatch_color:null})},\n function _(t,e,s,i,n){var o;i();const r=t(1),l=t(80),_=r.__importStar(t(228)),a=r.__importStar(t(18)),c=t(13),h=t(221),d=t(227),x=t(130);class g extends h.GlyphView{async load_glglyph(){const{MultiLineGL:e}=await Promise.resolve().then((()=>r.__importStar(t(567))));return e}_project_data(){this._project_xy(\"xs\",this.xs.data,\"ys\",this.ys.data)}_index_data(t){const{data_size:e}=this;for(let s=0;s0&&o.set(t,s)}return new x.Selection({indices:[...o.keys()],multiline_indices:o})}get_interpolation_hit(t,e,s){const i=this.xs.get(t),n=this.ys.get(t),o=i[e],r=n[e],l=i[e+1],_=n[e+1];return(0,d.line_interpolation)(this.renderer,s,o,r,l,_)}draw_legend_for_index(t,e,s){(0,d.generic_line_vector_legend)(this.visuals,t,e,s)}scenterxy(){throw new Error(`${this}.scenterxy() is not implemented`)}}s.MultiLineView=g,g.__name__=\"MultiLineView\";class y extends h.Glyph{constructor(t){super(t)}}s.MultiLine=y,o=y,y.__name__=\"MultiLine\",o.prototype.default_view=g,o.define((({})=>({xs:[a.XCoordinateSeqSpec,{field:\"xs\"}],ys:[a.YCoordinateSeqSpec,{field:\"ys\"}]}))),o.mixins(l.LineVector)},\n function _(t,e,s,n,i){var o;n();const r=t(1),l=t(223),h=t(221),a=t(227),_=t(13),c=t(13),d=t(80),y=r.__importStar(t(228)),f=r.__importStar(t(18)),x=t(130),g=t(12);class p extends h.GlyphView{_project_data(){}_index_data(t){const{min:e,max:s}=Math,{data_size:n}=this;for(let i=0;i1&&c.length>1)for(let s=1,n=i.length;s{const t=this.renderer.xscale.v_invert(e),n=this.renderer.yscale.v_invert(s),[i,o,r,l]=(0,_.minmax2)(t,n);return this.index.indices({x0:i,x1:o,y0:r,y1:l})})(),o=[];for(const t of i){const i=this.sxs[t],r=this.sys[t];let l=!n;const h=i.length;for(let t=0;t1){let r=!1;for(let t=1;t({xs:[f.XCoordinateSeqSeqSeqSpec,{field:\"xs\"}],ys:[f.YCoordinateSeqSeqSeqSpec,{field:\"ys\"}]}))),o.mixins([d.LineVector,d.FillVector,d.HatchVector])},\n function _(e,t,s,i,n){var r;i();const o=e(1),a=e(221),c=e(227),_=e(13),h=e(80),l=o.__importStar(e(228)),y=o.__importStar(e(18)),d=e(130),x=e(12);class p extends a.GlyphView{_project_data(){this._project_xy(\"xs\",this.xs.data,\"ys\",this.ys.data)}_index_data(e){const{data_size:t}=this;for(let s=0;s{const e=this.renderer.xscale.v_invert(t),i=this.renderer.yscale.v_invert(s),[n,r,o,a]=(0,_.minmax2)(e,i);return this.index.indices({x0:n,x1:r,y0:o,y1:a})})(),r=[];for(const e of n){const n=this.sxs.get(e),o=this.sys.get(e),a=n.length;if(0==a)continue;let c=!i;for(let e=0;e({xs:[y.XCoordinateSeqSpec,{field:\"xs\"}],ys:[y.YCoordinateSeqSpec,{field:\"ys\"}]}))),r.mixins([h.LineVector,h.FillVector,h.HatchVector])},\n function _(t,e,i,o,r){var s;o();const d=t(1),n=t(354),a=d.__importStar(t(18));class l extends n.LRTBView{scenterxy(t){return[this.sleft[t]/2+this.sright[t]/2,this.stop[t]/2+this.sbottom[t]/2]}_lrtb(t){return{l:this.left[t],r:this.right[t],t:this.top[t],b:this.bottom[t]}}}i.QuadView=l,l.__name__=\"QuadView\";class _ extends n.LRTB{constructor(t){super(t)}}i.Quad=_,s=_,_.__name__=\"Quad\",s.prototype.default_view=l,s.define((({})=>({right:[a.XCoordinateSpec,{field:\"right\"}],bottom:[a.YCoordinateSpec,{field:\"bottom\"}],left:[a.XCoordinateSpec,{field:\"left\"}],top:[a.YCoordinateSpec,{field:\"top\"}]})))},\n function _(e,i,t,s,n){var o;s();const c=e(1),a=e(80),r=e(221),d=e(227),_=e(352),x=c.__importStar(e(18));class y extends r.GlyphView{_project_data(){this._project_xy(\"x0\",this.x0,\"y0\",this.y0),this._project_xy(\"x1\",this.x1,\"y1\",this.y1)}_index_data(e){const{x0:i,x1:t,y0:s,y1:n,cx:o,cy:c,data_size:a}=this;for(let r=0;r({x0:[x.XCoordinateSpec,{field:\"x0\"}],y0:[x.YCoordinateSpec,{field:\"y0\"}],x1:[x.XCoordinateSpec,{field:\"x1\"}],y1:[x.YCoordinateSpec,{field:\"y1\"}],cx:[x.XCoordinateSpec,{field:\"cx\"}],cy:[x.YCoordinateSpec,{field:\"cy\"}]}))),o.mixins(a.LineVector)},\n function _(e,t,i,n,s){var h;n();const r=e(1),a=e(220),l=e(221),_=e(227),o=e(80),d=e(24),g=r.__importStar(e(18));class c extends a.XYGlyphView{_map_data(){if(this._define_or_inherit_attr(\"slength\",(()=>\"data\"==this.model.properties.length.units?this.inherited_x&&this.inherited_length?l.inherit:this.sdist(this.renderer.xscale,this.x,this.length):this.inherited_length?l.inherit:(0,d.to_screen)(this.length))),!this.inherited_slength){const{width:e,height:t}=this.renderer.plot_view.frame.bbox,i=2*(e+t),{slength:n}=this,s=n.length;for(let e=0;e({length:[g.DistanceSpec,0],angle:[g.AngleSpec,0]})))},\n function _(t,e,i,s,r){var _;s();const n=t(1),h=t(357),a=t(227),d=t(24),o=t(13),l=t(130),c=t(64),x=t(114),y=t(185),f=n.__importStar(t(186)),g=t(187),{abs:w,sqrt:u}=Math;class p extends h.CenterRotatableView{async load_glglyph(){const{RectGL:e}=await Promise.resolve().then((()=>n.__importStar(t(569))));return e}_set_data(t){super._set_data(t),this.border_radius=f.border_radius(this.model.border_radius)}_map_data(){const t=this.data_size;if(this.inherited_x&&this.inherited_width)this._inherit_attr(\"swidth\"),this._inherit_attr(\"sx0\");else{let e,i;if(\"data\"==this.model.properties.width.units)[e,i]=this._map_dist_corner_for_data_side_length(this.x,this.width,this.renderer.xscale);else{e=(0,d.to_screen)(this.width),i=new d.ScreenArray(t);const{sx:s}=this;for(let r=0;r({border_radius:[y.BorderRadius,0],dilate:[t,!1]})))},\n function _(e,t,r,a,n){var s;a();const i=e(1),o=e(378),_=e(379),c=i.__importStar(e(18));class l extends o.MarkerView{async load_glglyph(){const{MultiMarkerGL:t}=await Promise.resolve().then((()=>i.__importStar(e(568))));return t}_paint(e,t,r){const{sx:a,sy:n,size:s,angle:i,marker:o}={...this,...r};for(const r of t){const t=a[r],c=n[r],l=s.get(r),g=i.get(r),m=o.get(r);if(!isFinite(t+c+l+g)||null==m)continue;const u=l/2;e.beginPath(),e.translate(t,c),0!=g&&e.rotate(g),_.marker_funcs[m](e,r,u,this.visuals),0!=g&&e.rotate(-g),e.translate(-t,-c)}}draw_legend_for_index(e,{x0:t,x1:r,y0:a,y1:n},s){const i=s+1,o=this.marker.get(s),_={...this._get_legend_args({x0:t,x1:r,y0:a,y1:n},s),marker:new c.UniformScalar(o,i)};this._paint(e,[s],_)}}r.ScatterView=l,l.__name__=\"ScatterView\";class g extends o.Marker{constructor(e){super(e)}}r.Scatter=g,s=g,g.__name__=\"Scatter\",s.prototype.default_view=l,s.define((()=>({marker:[c.MarkerSpec,{value:\"circle\"}]})))},\n function _(e,t,s,i,n){var r;i();const a=e(1),c=e(220),o=e(80),_=a.__importStar(e(228)),h=a.__importStar(e(18)),x=e(13),d=e(130);class l extends c.XYGlyphView{_paint(e,t,s){const{sx:i,sy:n,size:r,angle:a}={...this,...s};for(const s of t){const t=i[s],c=n[s],o=r.get(s),_=a.get(s);if(!isFinite(t+c+o+_))continue;const h=o/2;e.beginPath(),e.translate(t,c),0!=_&&e.rotate(_),this._render_one(e,s,h,this.visuals),0!=_&&e.rotate(-_),e.translate(-t,-c)}}_mask_data(){const{x_target:e,y_target:t}=this.renderer.plot_view.frame,s=e.widen(this.max_size).map((e=>this.renderer.xscale.invert(e))),i=t.widen(this.max_size).map((e=>this.renderer.yscale.invert(e)));return this.index.indices({x0:s.start,x1:s.end,y0:i.start,y1:i.end})}_hit_point(e){const{sx:t,sy:s}=e,{max_size:i}=this,{hit_dilation:n}=this.model,r=t-i*n,a=t+i*n,[c,o]=this.renderer.xscale.r_invert(r,a),_=s-i*n,h=s+i*n,[x,l]=this.renderer.yscale.r_invert(_,h),y=this.index.indices({x0:c,x1:o,y0:x,y1:l}),m=[];for(const e of y){const i=this.size.get(e)/2*n;Math.abs(this.sx[e]-t)<=i&&Math.abs(this.sy[e]-s)<=i&&m.push(e)}return new d.Selection({indices:m})}_hit_span(e){const{sx:t,sy:s}=e,i=this.bounds(),n=this.max_size/2,[r,a,c,o]=(()=>{if(\"h\"==e.direction){const{y0:e,y1:s}=i,r=t-n,a=t+n,[c,o]=this.renderer.xscale.r_invert(r,a);return[c,o,e,s]}{const{x0:e,x1:t}=i,r=s-n,a=s+n,[c,o]=this.renderer.yscale.r_invert(r,a);return[e,t,c,o]}})(),_=[...this.index.indices({x0:r,x1:a,y0:c,y1:o})];return new d.Selection({indices:_})}_hit_rect(e){const{sx0:t,sx1:s,sy0:i,sy1:n}=e,[r,a]=this.renderer.xscale.r_invert(t,s),[c,o]=this.renderer.yscale.r_invert(i,n),_=[...this.index.indices({x0:r,x1:a,y0:c,y1:o})];return new d.Selection({indices:_})}_hit_poly(e){const{sx:t,sy:s}=e,i=(()=>{const e=this.renderer.xscale.v_invert(t),i=this.renderer.yscale.v_invert(s),[n,r,a,c]=(0,x.minmax2)(e,i);return this.index.indices({x0:n,x1:r,y0:a,y1:c})})(),n=[];for(const e of i)_.point_in_poly(this.sx[e],this.sy[e],t,s)&&n.push(e);return new d.Selection({indices:n})}_get_legend_args({x0:e,x1:t,y0:s,y1:i},n){const r=n+1,a=new Array(r),c=new Array(r);a[n]=(e+t)/2,c[n]=(s+i)/2;const o=.4*Math.min(Math.abs(t-e),Math.abs(i-s));return{sx:a,sy:c,size:new h.UniformScalar(o,r),angle:new h.UniformScalar(0,r)}}draw_legend_for_index(e,{x0:t,x1:s,y0:i,y1:n},r){const a=this._get_legend_args({x0:t,x1:s,y0:i,y1:n},r);this._paint(e,[r],a)}}s.MarkerView=l,l.__name__=\"MarkerView\";class y extends c.XYGlyph{constructor(e){super(e)}}s.Marker=y,r=y,y.__name__=\"Marker\",r.mixins([o.LineVector,o.FillVector,o.HatchVector]),r.define((({Float:e})=>({size:[h.ScreenSizeSpec,{value:4}],angle:[h.AngleSpec,0],hit_dilation:[e,1]})))},\n function _(l,n,o,i,a){i();const t=Math.sqrt(3),e=Math.sqrt(5),p=(e+1)/4,c=Math.sqrt((5-e)/8),h=(e-1)/4,u=Math.sqrt((5+e)/8);function f(l,n){l.rotate(Math.PI/4),y(l,n),l.rotate(-Math.PI/4)}function r(l,n){const o=n*t,i=o/3;l.moveTo(-o/2,-i),l.lineTo(0,0),l.lineTo(o/2,-i),l.lineTo(0,0),l.lineTo(0,n)}function y(l,n){l.moveTo(0,n),l.lineTo(0,-n),l.moveTo(-n,0),l.lineTo(n,0)}function T(l,n){l.moveTo(0,n),l.lineTo(n/1.5,0),l.lineTo(0,-n),l.lineTo(-n/1.5,0),l.closePath()}function s(l,n){const o=n*t,i=o/3;l.moveTo(-n,i),l.lineTo(n,i),l.lineTo(0,i-o),l.closePath()}function v(l,n,o,i){l.arc(0,0,o,0,2*Math.PI,!1),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)}function d(l,n,o,i){T(l,o),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)}function P(l,n,o,i){!function(l,n){l.beginPath(),l.arc(0,0,n/4,0,2*Math.PI,!1),l.closePath()}(l,o),i.line.set_vectorize(l,n),l.fillStyle=l.strokeStyle,l.fill()}function m(l,n,o,i){!function(l,n){const o=n/2,i=t*o;l.moveTo(n,0),l.lineTo(o,-i),l.lineTo(-o,-i),l.lineTo(-n,0),l.lineTo(-o,i),l.lineTo(o,i),l.closePath()}(l,o),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)}function _(l,n,o,i){const a=2*o;l.rect(-o,-o,a,a),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)}function q(l,n,o,i){!function(l,n){const o=Math.sqrt(5-2*e)*n;l.moveTo(0,-n),l.lineTo(o*h,o*u-n),l.lineTo(o*(1+h),o*u-n),l.lineTo(o*(1+h-p),o*(u+c)-n),l.lineTo(o*(1+2*h-p),o*(2*u+c)-n),l.lineTo(0,2*o*u-n),l.lineTo(-o*(1+2*h-p),o*(2*u+c)-n),l.lineTo(-o*(1+h-p),o*(u+c)-n),l.lineTo(-o*(1+h),o*u-n),l.lineTo(-o*h,o*u-n),l.closePath()}(l,o),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)}function M(l,n,o,i){s(l,o),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)}o.marker_funcs={asterisk:function(l,n,o,i){y(l,o),f(l,o),i.line.apply(l,n)},circle:v,circle_cross:function(l,n,o,i){l.arc(0,0,o,0,2*Math.PI,!1),i.fill.apply(l,n),i.hatch.apply(l,n),y(l,o),i.line.apply(l,n)},circle_dot:function(l,n,o,i){v(l,n,o,i),P(l,n,o,i)},circle_y:function(l,n,o,i){l.arc(0,0,o,0,2*Math.PI,!1),i.fill.apply(l,n),i.hatch.apply(l,n),r(l,o),i.line.apply(l,n)},circle_x:function(l,n,o,i){l.arc(0,0,o,0,2*Math.PI,!1),i.fill.apply(l,n),i.hatch.apply(l,n),f(l,o),i.line.apply(l,n)},cross:function(l,n,o,i){y(l,o),i.line.apply(l,n)},diamond:d,diamond_dot:function(l,n,o,i){d(l,n,o,i),P(l,n,o,i)},diamond_cross:function(l,n,o,i){T(l,o),i.fill.apply(l,n),i.hatch.apply(l,n),l.moveTo(0,o),l.lineTo(0,-o),l.moveTo(-o/1.5,0),l.lineTo(o/1.5,0),i.line.apply(l,n)},dot:P,hex:m,hex_dot:function(l,n,o,i){m(l,n,o,i),P(l,n,o,i)},inverted_triangle:function(l,n,o,i){l.rotate(Math.PI),s(l,o),l.rotate(-Math.PI),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)},plus:function(l,n,o,i){const a=3*o/8,t=[a,a,o,o,a,a,-a,-a,-o,-o,-a,-a],e=[o,a,a,-a,-a,-o,-o,-a,-a,a,a,o];l.beginPath();for(let n=0;n<12;n++)l.lineTo(t[n],e[n]);l.closePath(),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)},square:_,square_cross:function(l,n,o,i){const a=2*o;l.rect(-o,-o,a,a),i.fill.apply(l,n),i.hatch.apply(l,n),y(l,o),i.line.apply(l,n)},square_dot:function(l,n,o,i){_(l,n,o,i),P(l,n,o,i)},square_pin:function(l,n,o,i){const a=3*o/8;l.moveTo(-o,-o),l.quadraticCurveTo(0,-a,o,-o),l.quadraticCurveTo(a,0,o,o),l.quadraticCurveTo(0,a,-o,o),l.quadraticCurveTo(-a,0,-o,-o),l.closePath(),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)},square_x:function(l,n,o,i){const a=2*o;l.rect(-o,-o,a,a),i.fill.apply(l,n),i.hatch.apply(l,n),l.moveTo(-o,o),l.lineTo(o,-o),l.moveTo(-o,-o),l.lineTo(o,o),i.line.apply(l,n)},star:q,star_dot:function(l,n,o,i){q(l,n,o,i),P(l,n,o,i)},triangle:M,triangle_dot:function(l,n,o,i){M(l,n,o,i),P(l,n,o,i)},triangle_pin:function(l,n,o,i){const a=o*t,e=a/3,p=3*e/8;l.moveTo(-o,e),l.quadraticCurveTo(0,p,o,e),l.quadraticCurveTo(t*p/2,p/2,0,e-a),l.quadraticCurveTo(-t*p/2,p/2,-o,e),l.closePath(),i.fill.apply(l,n),i.hatch.apply(l,n),i.line.apply(l,n)},dash:function(l,n,o,i){!function(l,n){l.moveTo(-n,0),l.lineTo(n,0)}(l,o),i.line.apply(l,n)},x:function(l,n,o,i){f(l,o),i.line.apply(l,n)},y:function(l,n,o,i){r(l,o),i.line.apply(l,n)}}},\n function _(e,t,s,i,n){var r;i();const o=e(1),a=o.__importStar(e(228)),h=o.__importStar(e(18)),d=e(80),c=e(11),_=e(221),x=e(227),y=e(130);class l extends _.GlyphView{_project_data(){this._project_xy(\"x0\",this.x0,\"y0\",this.y0),this._project_xy(\"x1\",this.x1,\"y1\",this.y1)}_index_data(e){const{min:t,max:s}=Math,{x0:i,x1:n,y0:r,y1:o,data_size:a}=this;for(let h=0;h({x0:[h.XCoordinateSpec,{field:\"x0\"}],y0:[h.YCoordinateSpec,{field:\"y0\"}],x1:[h.XCoordinateSpec,{field:\"x1\"}],y1:[h.YCoordinateSpec,{field:\"y1\"}]}))),r.mixins(d.LineVector)},\n function _(t,e,s,i,n){var _;i();const o=t(1),a=t(220),l=o.__importStar(t(80)),c=t(382);class r extends a.XYGlyphView{_set_data(){const{tension:t,closed:e}=this.model,[s,i]=(0,c.catmullrom_spline)(this.x,this.y,20,t,e);this._define_attr(\"xt\",s),this._define_attr(\"yt\",i)}_map_data(){const{x_scale:t,y_scale:e}=this.renderer.coordinates,s=t.v_compute(this.xt),i=e.v_compute(this.yt);this._define_attr(\"sxt\",s),this._define_attr(\"syt\",i)}_paint(t,e,s){const{sxt:i,syt:n}={...this,...s};let _=!0;t.beginPath();const o=i.length;for(let e=0;e({tension:[e,.5],closed:[t,!1]})))},\n function _(n,t,e,o,s){o();const c=n(24),l=n(12);e.catmullrom_spline=function(n,t,e=10,o=.5,s=!1){(0,l.assert)(n.length==t.length);const r=n.length,f=s?r+1:r,w=(0,c.infer_type)(n,t),i=new w(f+2),u=new w(f+2);i.set(n,1),u.set(t,1),s?(i[0]=n[r-1],u[0]=t[r-1],i[f]=n[0],u[f]=t[0],i[f+1]=n[1],u[f+1]=t[1]):(i[0]=n[0],u[0]=t[0],i[f+1]=n[r-1],u[f+1]=t[r-1]);const g=new w(4*(e+1));for(let n=0,t=0;n<=e;n++){const o=n/e,s=o**2,c=o*s;g[t++]=2*c-3*s+1,g[t++]=-2*c+3*s,g[t++]=c-2*s+o,g[t++]=c-s}const h=new w((f-1)*(e+1)),_=new w((f-1)*(e+1));for(let n=1,t=0;n_.__importStar(e(570))));return t}_paint(e,t,r){const n=t.length;if(n<2)return;const{sx:s,sy:i}={...this,...r},_=this.model.mode;this.visuals.line.set_value(e);let a=!1,o=!1;const l=t[0];let c=isFinite(s[l]+i[l]);\"center\"==_&&(a=this._render_xy(e,a,c?s[l]:NaN,i[l]));for(const r of t){const t=isFinite(s[r+1]+i[r+1]);switch(_){case\"before\":a=this._render_xy(e,a,c?s[r]:NaN,i[r]),r({mode:[c.StepMode,\"before\"]})))},\n function _(e,t,i,l,n){var s;l();const a=e(368),o=e(178),r=e(21),c=e(177);i.DisplayMode=(0,r.Or)((0,r.Enum)(\"inline\",\"block\"),r.Auto);class p extends a.MathTextGlyphView{_build_label(e){const{macros:t,display:i}=this.model;if(\"auto\"==i){const i=(0,c.parse_delimited_string)(e);return i instanceof o.TeX&&(i.macros=t),i}return new o.TeX({text:e,macros:t,inline:\"inline\"==i})}}i.TeXGlyphView=p,p.__name__=\"TeXGlyphView\";class _ extends a.MathTextGlyph{constructor(e){super(e)}}i.TeXGlyph=_,s=_,_.__name__=\"TeXGlyph\",s.prototype.default_view=p,s.define((({Float:e,Str:t,Dict:l,Tuple:n,Or:s})=>({macros:[l(s(t,n(t,e))),{}],display:[i.DisplayMode,\"auto\"]})))},\n function _(t,e,i,s,r){var h;s();const n=t(1),a=t(354),_=t(24),o=n.__importStar(t(18));class d extends a.LRTBView{scenterxy(t){return[this.sx[t],(this.stop[t]+this.sbottom[t])/2]}_lrtb(t){const e=this.width.get(t)/2,i=this.x[t],s=this.top[t],r=this.bottom[t];return{l:i-e,r:i+e,t:Math.max(s,r),b:Math.min(s,r)}}_map_data(){if(this.inherited_x&&this.inherited_width)this._inherit_attr(\"swidth\"),this._inherit_attr(\"sleft\"),this._inherit_attr(\"sright\");else{const t=this.sdist(this.renderer.xscale,this.x,this.width,\"center\"),{sx:e}=this,i=e.length,s=new _.ScreenArray(i),r=new _.ScreenArray(i);for(let h=0;h({x:[o.XCoordinateSpec,{field:\"x\"}],bottom:[o.YCoordinateSpec,{value:0}],width:[o.DistanceSpec,{value:1}],top:[o.YCoordinateSpec,{field:\"top\"}]})))},\n function _(t,e,i,n,s){var _;n();const r=t(1),a=t(221),o=t(227),d=t(130),c=t(80),h=r.__importStar(t(39)),x=t(13),p=t(10),l=r.__importStar(t(18)),{abs:u,max:f}=Math;class m extends a.GlyphView{after_visuals(){super.after_visuals(),this.max_line_width=h.max(this.line_width)}_index_data(t){for(const e of this.x)t.add_point(e,0)}_bounds(t){const{x0:e,x1:i}=t;return{x0:e,x1:i,y0:NaN,y1:NaN}}_map_data(){super._map_data();const{round:t}=Math;if(!this.inherited_sx){const e=(0,x.map)(this.sx,(e=>t(e)));this._define_attr(\"sx\",e)}}scenterxy(t){const{vcenter:e}=this.renderer.plot_view.frame.bbox;return[this.sx[t],e]}_paint(t,e,i){const{sx:n}={...this,...i},{top:s,bottom:_}=this.renderer.plot_view.frame.bbox;for(const i of e){const e=n[i];isFinite(e)&&(t.beginPath(),t.moveTo(e,s),t.lineTo(e,_),this.visuals.line.apply(t,i))}}_get_candidates(t,e){const{max_line_width:i}=this,[n,s]=this.renderer.xscale.r_invert(t-i,(e??t)+i);return this.index.indices({x0:n,x1:s,y0:0,y1:0})}_find_spans(t,e){const{sx:i,line_width:n}=this,s=[];for(const _ of t){e(i[_],n.get(_))&&s.push(_)}return s}_hit_point(t){const{sx:e}=t,i=this._get_candidates(e),n=this._find_spans(i,((t,i)=>u(t-e)<=f(i,2)));return new d.Selection({indices:n})}_hit_span(t){const e=(()=>{if(\"h\"==t.direction)return(0,p.range)(0,this.data_size);{const{sx:e}=t,i=this._get_candidates(e);return this._find_spans(i,((t,i)=>u(t-e)<=f(i/2,2)))}})();return new d.Selection({indices:e})}_hit_rect(t){const e=(()=>{const{sx0:e,sx1:i}=t,n=this._get_candidates(e,i);return this._find_spans(n,((t,n)=>e-n/2<=t&&t<=i+n/2))})();return new d.Selection({indices:e})}draw_legend_for_index(t,e,i){(0,o.generic_line_vector_legend)(this.visuals,t,e,i)}}i.VSpanView=m,m.__name__=\"VSpanView\";class w extends a.Glyph{constructor(t){super(t)}}i.VSpan=w,_=w,w.__name__=\"VSpan\",_.prototype.default_view=m,_.mixins([c.LineVector]),_.define((()=>({x:[l.XCoordinateSpec,{field:\"x\"}]})))},\n function _(t,e,i,s,r){var n;s();const a=t(1),_=t(221),o=t(227),h=t(130),c=t(80),d=t(24),l=t(13),p=a.__importStar(t(34)),x=t(10),f=a.__importStar(t(18));class u extends _.GlyphView{async lazy_initialize(){await super.lazy_initialize();const{webgl:e}=this.renderer.plot_view.canvas_view;if(null!=e&&e.regl_wrapper.has_webgl){const{LRTBGL:i}=await Promise.resolve().then((()=>a.__importStar(t(566))));this.glglyph=new i(e.regl_wrapper,this)}}get sleft(){return this.sx0}get sright(){return this.sx1}get stop(){const{top:t}=this.renderer.plot_view.frame.bbox,e=this.data_size,i=new d.ScreenArray(e);return i.fill(t),i}get sbottom(){const{bottom:t}=this.renderer.plot_view.frame.bbox,e=this.data_size,i=new d.ScreenArray(e);return i.fill(t),i}_set_data(t){super._set_data(t);const{abs:e}=Math,{max:i,map:s,zip:r}=p,{x0:n,x1:a}=this;if(this.inherited_x0&&this.inherited_x1)this._inherit_attr(\"max_width\");else{const t=i(s(r(n,a),(([t,i])=>e(t-i))));this._define_attr(\"max_width\",t)}}_index_data(t){const{x0:e,x1:i,data_size:s}=this;for(let r=0;rt(e)));this._define_attr(\"sx0\",e)}if(!this.inherited_sx1){const e=(0,l.map)(this.sx1,(e=>t(e)));this._define_attr(\"sx1\",e)}}scenterxy(t){const{vcenter:e}=this.renderer.plot_view.frame.bbox;return[(this.sx0[t]+this.sx1[t])/2,e]}_paint(t,e,i){const{sx0:s,sx1:r}={...this,...i},{top:n,bottom:a,height:_}=this.renderer.plot_view.frame.bbox;for(const i of e){const e=s[i],o=r[i];isFinite(e+o)&&(t.beginPath(),t.rect(e,n,o-e,_),this.visuals.fill.apply(t,i),this.visuals.hatch.apply(t,i),t.beginPath(),t.moveTo(e,n),t.lineTo(e,a),t.moveTo(o,n),t.lineTo(o,a),this.visuals.line.apply(t,i))}}_get_candidates(t,e){const{max_width:i}=this,[s,r]=this.renderer.xscale.r_invert(t,e??t),n=s-i,a=r+i;return this.index.indices({x0:n,x1:a,y0:0,y1:0})}_find_strips(t,e){function i(t,i){return t<=i?e(t,i):e(i,t)}const{sx0:s,sx1:r}=this,n=[];for(const e of t){i(s[e],r[e])&&n.push(e)}return n}_hit_point(t){const{sx:e}=t,i=this._get_candidates(e),s=this._find_strips(i,((t,i)=>t<=e&&e<=i));return new h.Selection({indices:s})}_hit_span(t){const e=(()=>{if(\"h\"==t.direction)return(0,x.range)(0,this.data_size);{const{sx:e}=t,i=this._get_candidates(e);return this._find_strips(i,((t,i)=>t<=e&&e<=i))}})();return new h.Selection({indices:e})}_hit_rect(t){const e=(()=>{const{sx0:e,sx1:i}=t,s=this._get_candidates(e,i);return this._find_strips(s,((t,s)=>e<=t&&t<=i&&e<=s&&s<=i))})();return new h.Selection({indices:e})}draw_legend_for_index(t,e,i){(0,o.generic_area_vector_legend)(this.visuals,t,e,i)}}i.VStripView=u,u.__name__=\"VStripView\";class w extends _.Glyph{constructor(t){super(t)}}i.VStrip=w,n=w,w.__name__=\"VStrip\",n.prototype.default_view=u,n.mixins([c.LineVector,c.FillVector,c.HatchVector]),n.define((()=>({x0:[f.XCoordinateSpec,{field:\"x0\"}],x1:[f.XCoordinateSpec,{field:\"x1\"}]})))},\n function _(e,t,s,i,r){var n;i();const a=e(1),h=e(220),d=e(221),_=e(227),c=e(80),l=e(24),o=e(20),g=a.__importStar(e(18)),u=e(11),p=e(130),x=e(13);class y extends h.XYGlyphView{async load_glglyph(){const{WedgeGL:t}=await Promise.resolve().then((()=>a.__importStar(e(571))));return t}_map_data(){this._define_or_inherit_attr(\"sradius\",(()=>\"data\"==this.model.properties.radius.units?this.inherited_x&&this.inherited_radius?d.inherit:this.sdist(this.renderer.xscale,this.x,this.radius):this.inherited_radius?d.inherit:(0,l.to_screen)(this.radius))),this._define_or_inherit_attr(\"max_sradius\",(()=>(0,x.max)(this.sradius)))}_paint(e,t,s){const{sx:i,sy:r,sradius:n,start_angle:a,end_angle:h}={...this,...s},d=\"anticlock\"==this.model.direction;for(const s of t){const t=i[s],_=r[s],c=n[s],l=a.get(s),o=h.get(s);isFinite(t+_+c+l+o)&&(e.beginPath(),e.arc(t,_,c,l,o,d),e.lineTo(t,_),e.closePath(),this.visuals.fill.apply(e,s),this.visuals.hatch.apply(e,s),this.visuals.line.apply(e,s))}}_hit_point(e){let t,s,i,r,n;const{sx:a,sy:h}=e,d=this.renderer.xscale.invert(a),_=this.renderer.yscale.invert(h);s=a-this.max_sradius,i=a+this.max_sradius;const[c,l]=this.renderer.xscale.r_invert(s,i);r=h-this.max_sradius,n=h+this.max_sradius;const[o,g]=this.renderer.yscale.r_invert(r,n),x=[];for(const e of this.index.indices({x0:c,x1:l,y0:o,y1:g})){const a=this.sradius[e]**2;[s,i]=this.renderer.xscale.r_compute(d,this.x[e]),[r,n]=this.renderer.yscale.r_compute(_,this.y[e]),t=(s-i)**2+(r-n)**2,t<=a&&x.push(e)}const y=\"anticlock\"==this.model.direction,m=[];for(const e of x){const t=Math.atan2(h-this.sy[e],a-this.sx[e]);(Math.abs(this.start_angle.get(e)-this.end_angle.get(e))>=2*Math.PI||(0,u.angle_between)(-t,-this.start_angle.get(e),-this.end_angle.get(e),y))&&m.push(e)}return new p.Selection({indices:m})}draw_legend_for_index(e,t,s){(0,_.generic_area_vector_legend)(this.visuals,e,t,s)}scenterxy(e){const t=this.sradius[e]/2,s=(this.start_angle.get(e)+this.end_angle.get(e))/2;return[this.sx[e]+t*Math.cos(s),this.sy[e]+t*Math.sin(s)]}}s.WedgeView=y,y.__name__=\"WedgeView\";class m extends h.XYGlyph{constructor(e){super(e)}}s.Wedge=m,n=m,m.__name__=\"Wedge\",n.prototype.default_view=y,n.mixins([c.LineVector,c.FillVector,c.HatchVector]),n.define((({})=>({direction:[o.Direction,\"anticlock\"],radius:[g.DistanceSpec,{field:\"radius\"}],start_angle:[g.AngleSpec,{field:\"start_angle\"}],end_angle:[g.AngleSpec,{field:\"end_angle\"}]})))},\n function _(n,i,o,a,r){a(),r(\"Decoration\",n(226).Decoration),r(\"Marking\",n(169).Marking)},\n function _(t,_,r,o,a){o();const e=t(1);e.__exportStar(t(391),r),e.__exportStar(t(392),r),e.__exportStar(t(393),r)},\n function _(e,t,n,d,s){d();const o=e(51),r=e(13),i=e(10),_=e(9),c=e(130);class a extends o.Model{constructor(e){super(e)}_hit_test(e,t,n){if(!t.model.visible)return null;const d=n.glyph.hit_test(e);return null==d?null:n.model.view.convert_selection_from_subset(d)}}n.GraphHitTestPolicy=a,a.__name__=\"GraphHitTestPolicy\";class l extends a{constructor(e){super(e)}hit_test(e,t){return this._hit_test(e,t,t.edge_view)}do_selection(e,t,n,d){if(null==e)return!1;const s=t.edge_renderer.data_source.selected;return s.update(e,n,d),t.edge_renderer.data_source._select.emit(),!s.is_empty()}do_inspection(e,t,n,d,s){if(null==e)return!1;const{edge_renderer:o}=n.model,r=o.get_selection_manager().get_or_create_inspector(n.edge_view.model);return r.update(e,d,s),n.edge_view.model.data_source.setv({inspected:r},{silent:!0}),n.edge_view.model.data_source.inspect.emit([n.edge_view.model,{geometry:t}]),!r.is_empty()}}n.EdgesOnly=l,l.__name__=\"EdgesOnly\";class u extends a{constructor(e){super(e)}hit_test(e,t){return this._hit_test(e,t,t.node_view)}do_selection(e,t,n,d){if(null==e)return!1;const s=t.node_renderer.data_source.selected;return s.update(e,n,d),t.node_renderer.data_source._select.emit(),!s.is_empty()}do_inspection(e,t,n,d,s){if(null==e)return!1;const{node_renderer:o}=n.model,r=o.get_selection_manager().get_or_create_inspector(n.node_view.model);return r.update(e,d,s),n.node_view.model.data_source.setv({inspected:r},{silent:!0}),n.node_view.model.data_source.inspect.emit([n.node_view.model,{geometry:t}]),!r.is_empty()}}n.NodesOnly=u,u.__name__=\"NodesOnly\";class m extends a{constructor(e){super(e)}hit_test(e,t){return this._hit_test(e,t,t.node_view)}get_linked_edges(e,t,n){const d=(0,_.dict)(e.data).get(\"index\")??[],s=(()=>{switch(n){case\"selection\":return(0,r.map)(e.selected.indices,(e=>d[e]));case\"inspection\":return(0,r.map)(e.inspected.indices,(e=>d[e]))}})(),o=(0,_.dict)(t.data),a=o.get(\"start\")??[],l=o.get(\"end\")??[],u=[],m=a.length;for(let e=0;e{switch(n){case\"selection\":return t.selected.indices;case\"inspection\":return t.inspected.indices}})(),s=(0,_.dict)(t.data),o=s.get(\"start\")??[],a=s.get(\"end\")??[],l=[];for(const e of d)l.push(o[e],a[e]);const u=(0,_.dict)(e.data).get(\"index\")??[],m=(0,i.uniq)(l).map((e=>(0,r.index_of)(u,e)));return new c.Selection({indices:m})}do_selection(e,t,n,d){if(null==e)return!1;const s=t.edge_renderer.data_source.selected;s.update(e,n,d);const o=t.node_renderer.data_source.selected,r=this.get_linked_nodes(t.node_renderer.data_source,t.edge_renderer.data_source,\"selection\");return o.update(r,n,d),t.edge_renderer.data_source._select.emit(),!s.is_empty()}do_inspection(e,t,n,d,s){if(null==e)return!1;const o=n.edge_view.model.data_source.selection_manager.get_or_create_inspector(n.edge_view.model);o.update(e,d,s),n.edge_view.model.data_source.setv({inspected:o},{silent:!0});const r=n.node_view.model.data_source.selection_manager.get_or_create_inspector(n.node_view.model),i=this.get_linked_nodes(n.node_view.model.data_source,n.edge_view.model.data_source,\"inspection\");return r.update(i,d,s),n.node_view.model.data_source.setv({inspected:r},{silent:!0}),n.edge_view.model.data_source.inspect.emit([n.edge_view.model,{geometry:t}]),!o.is_empty()}}n.EdgesAndLinkedNodes=p,p.__name__=\"EdgesAndLinkedNodes\";class g extends a{constructor(e){super(e)}hit_test(e,t){return this._hit_test(e,t,t.node_view)}get_adjacent_nodes(e,t,n){const d=(0,_.dict)(e.data).get(\"index\")??[],s=(()=>{switch(n){case\"selection\":return(0,r.map)(e.selected.indices,(e=>d[e]));case\"inspection\":return(0,r.map)(e.inspected.indices,(e=>d[e]))}})(),o=(0,_.dict)(t.data),a=o.get(\"start\")??[],l=o.get(\"end\")??[],u=[],m=[];for(let e=0;e(0,r.index_of)(d,e)));return new c.Selection({indices:p})}do_selection(e,t,n,d){if(null==e)return!1;const s=t.node_renderer.data_source.selected;s.update(e,n,d);const o=this.get_adjacent_nodes(t.node_renderer.data_source,t.edge_renderer.data_source,\"selection\");return o.is_empty()||s.update(o,n,d),t.node_renderer.data_source._select.emit(),!s.is_empty()}do_inspection(e,t,n,d,s){if(null==e)return!1;const o=n.node_view.model.data_source.selection_manager.get_or_create_inspector(n.node_view.model);o.update(e,d,s),n.node_view.model.data_source.setv({inspected:o},{silent:!0});const r=this.get_adjacent_nodes(n.node_view.model.data_source,n.edge_view.model.data_source,\"inspection\");return r.is_empty()||(o.update(r,d,s),n.node_view.model.data_source.setv({inspected:o},{silent:!0})),n.node_view.model.data_source.inspect.emit([n.node_view.model,{geometry:t}]),!o.is_empty()}}n.NodesAndAdjacentNodes=g,g.__name__=\"NodesAndAdjacentNodes\"},\n function _(e,o,t,r,n){var s;r();const a=e(51),d=e(336);class _ extends a.Model{constructor(e){super(e)}get node_coordinates(){return new u({layout:this})}get edge_coordinates(){return new i({layout:this})}}t.LayoutProvider=_,_.__name__=\"LayoutProvider\";class c extends d.CoordinateTransform{constructor(e){super(e)}}t.GraphCoordinates=c,s=c,c.__name__=\"GraphCoordinates\",s.define((({Ref:e})=>({layout:[e(_)]})));class u extends c{constructor(e){super(e)}_v_compute(e){const[o,t]=this.layout.get_node_coordinates(e);return{x:o,y:t}}}t.NodeCoordinates=u,u.__name__=\"NodeCoordinates\";class i extends c{constructor(e){super(e)}_v_compute(e){const[o,t]=this.layout.get_edge_coordinates(e);return{x:o,y:t}}}t.EdgeCoordinates=i,i.__name__=\"EdgeCoordinates\"},\n function _(t,a,e,r,o){var n;r();const s=t(392),i=t(9),l=t(21);e.GraphLayout=(0,l.Or)((0,l.Dict)((0,l.Arrayable)(l.Float)),(0,l.Mapping)((0,l.Or)(l.Int,l.Str),(0,l.Arrayable)(l.Float)));class c extends s.LayoutProvider{constructor(t){super(t)}get_node_coordinates(t){const a=(0,i.dict)(t.data).get(\"index\")??[],e=a.length,r=new Float64Array(e),o=new Float64Array(e),n=(0,i.dict)(this.graph_layout);for(let t=0;t({graph_layout:[e.GraphLayout,new Map]})))},\n function _(i,d,n,r,G){r(),G(\"Grid\",i(395).Grid)},\n function _(i,e,s,t,n){var r;t();const o=i(1),a=i(265),d=i(190),_=i(191),l=o.__importStar(i(80)),h=i(8);class u extends d.GuideRendererView{_paint(){const i=this.layer.ctx;i.save(),this._draw_regions(i),this._draw_minor_grids(i),this._draw_grids(i),i.restore()}connect_signals(){super.connect_signals(),this.connect(this.model.change,(()=>this.request_paint()))}_draw_regions(i){if(!this.visuals.band_fill.doit&&!this.visuals.band_hatch.doit)return;const[e,s]=this.grid_coords(\"major\",!1);for(let t=0;ts[1]&&(n=s[1]);else{[t,n]=s;for(const i of this.plot_view.axis_views)i.dimension==this.model.dimension&&i.model.x_range_name==this.model.x_range_name&&i.model.y_range_name==this.model.y_range_name&&([t,n]=i.computed_bounds)}return[t,n]}grid_coords(i,e=!0){const s=this.model.dimension,t=1-s,[n,r]=this.ranges(),[o,a]=(()=>{const[i,e]=this.computed_bounds();return[Math.min(i,e),Math.max(i,e)]})(),d=[[],[]],_=this.model.get_ticker();if(null==_)return d;const l=_.get_ticks(o,a,n,r.min)[i],h=n.min,u=n.max,[c,m]=(()=>{const{cross_bounds:i}=this.model;return\"auto\"==i?[r.min,r.max]:i})();e||(l[0]!=h&&l.splice(0,0,h),l[l.length-1]!=u&&l.push(u));for(let i=0;i({bounds:[r(n(i,i),e),\"auto\"],cross_bounds:[r(n(i,i),e),\"auto\"],dimension:[s(0,1),0],axis:[o(t(a.Axis)),null],ticker:[o(t(_.Ticker)),null]}))),r.override({level:\"underlay\",band_fill_color:null,band_fill_alpha:0,grid_line_color:\"#e5e5e5\",minor_grid_line_color:null})},\n function _(o,x,B,a,l){a(),l(\"Column\",o(397).Column),l(\"FlexBox\",o(398).FlexBox),l(\"GridBox\",o(404).GridBox),l(\"GroupBox\",o(406).GroupBox),l(\"HBox\",o(408).HBox),l(\"LayoutDOM\",o(399).LayoutDOM),l(\"Row\",o(409).Row),l(\"ScrollBox\",o(410).ScrollBox),l(\"Spacer\",o(411).Spacer),l(\"TabPanel\",o(412).TabPanel),l(\"Tabs\",o(416).Tabs),l(\"VBox\",o(418).VBox)},\n function _(e,o,n,t,s){var u;t();const _=e(398);class c extends _.FlexBoxView{constructor(){super(...arguments),this._direction=\"column\"}}n.ColumnView=c,c.__name__=\"ColumnView\";class l extends _.FlexBox{constructor(e){super(e)}}n.Column=l,u=l,l.__name__=\"Column\",u.prototype.default_view=c},\n function _(t,e,i,o,s){var n;o();const a=t(399),c=t(403),l=t(258),r=t(111),h=t(63);class d extends a.LayoutDOMView{connect_signals(){super.connect_signals();const{children:t}=this.model.properties;this.on_change(t,(()=>this.update_children()))}get child_models(){return this.model.children}_intrinsic_display(){return{inner:this.model.flow_mode,outer:\"flex\"}}_update_layout(){super._update_layout(),this.style.append(\":host\",{flex_direction:this._direction,gap:(0,h.px)(this.model.spacing)});const t=new l.Container;let e=0,i=0;for(const o of this.child_views){if(!(o instanceof a.LayoutDOMView))continue;const s=o.box_sizing(),n=(()=>{const t=\"row\"==this._direction?s.width_policy:s.height_policy,e=\"row\"==this._direction?s.width:s.height,i=null!=e?(0,h.px)(e):\"auto\";switch(t){case\"auto\":case\"fixed\":return`0 0 ${i}`;case\"fit\":return\"1 1 auto\";case\"min\":return\"0 1 auto\";case\"max\":return\"1 0 0px\"}})(),c=(()=>{switch(\"row\"==this._direction?s.height_policy:s.width_policy){case\"auto\":case\"fixed\":case\"fit\":case\"min\":return\"row\"==this._direction?s.valign:s.halign;case\"max\":return\"stretch\"}})();o.style.append(\":host\",{flex:n,align_self:c}),\"row\"==this._direction?\"max\"==s.height_policy&&o.style.append(\":host\",{height:\"auto\"}):\"max\"==s.width_policy&&o.style.append(\":host\",{width:\"auto\"}),null!=o.layout&&(t.add({r0:e,c0:i,r1:e+1,c1:i+1},o),\"row\"==this._direction?i+=1:e+=1)}0!=t.size?(this.layout=new c.GridAlignmentLayout(t),this.layout.set_sizing()):delete this.layout}}i.FlexBoxView=d,d.__name__=\"FlexBoxView\";class _ extends a.LayoutDOM{constructor(t){super(t)}}i.FlexBox=_,n=_,_.__name__=\"FlexBox\",n.define((({Float:t,List:e,Ref:i})=>({children:[e(i(r.UIElement)),[]],spacing:[t,0]})))},\n function _(t,e,i,s,o){var a;s();const l=t(400),n=t(19),r=t(15),h=t(20),u=t(63),_=t(8),c=t(56),d=t(256),p=t(112),f=t(12);class m extends l.PaneView{constructor(){super(...arguments),this._child_views=new Map,this.mouseenter=new r.Signal(this,\"mouseenter\"),this.mouseleave=new r.Signal(this,\"mouseleave\"),this.disabled=new r.Signal(this,\"disabled\"),this._auto_width=\"fit-content\",this._auto_height=\"fit-content\",this._layout_computed=!1}get is_layout_root(){return this.is_root||!(this.parent instanceof m)}_after_resize(){super._after_resize(),this.is_layout_root&&!this._was_built?(n.logger.warn(`${this} wasn't built properly`),this.render(),this.r_after_render()):this.compute_layout()}async lazy_initialize(){await super.lazy_initialize(),await this.build_child_views()}remove(){for(const t of this.child_views)t.remove();this._child_views.clear(),super.remove()}connect_signals(){super.connect_signals(),this.el.addEventListener(\"mouseenter\",(t=>{this.mouseenter.emit(t)})),this.el.addEventListener(\"mouseleave\",(t=>{this.mouseleave.emit(t)})),this.parent instanceof m&&this.connect(this.parent.disabled,(t=>{this.disabled.emit(t||this.model.disabled)}));const t=this.model.properties;this.on_change(t.disabled,(()=>{this.disabled.emit(this.model.disabled)})),this.on_change([t.css_classes,t.stylesheets,t.width,t.height,t.min_width,t.min_height,t.max_width,t.max_height,t.margin,t.width_policy,t.height_policy,t.flow_mode,t.sizing_mode,t.aspect_ratio,t.visible],(()=>this.invalidate_layout()))}*children(){yield*super.children(),yield*this.child_views}get child_views(){return this.child_models.map((t=>this._child_views.get(t))).filter(_.isNotNull)}get layoutable_views(){return this.child_views.filter((t=>t instanceof m))}async build_child_views(){const{created:t,removed:e}=await(0,c.build_views)(this._child_views,this.child_models,{parent:this});for(const t of e)this._resize_observer.unobserve(t.el);for(const e of t)this._resize_observer.observe(e.el,{box:\"border-box\"});return t}render(){super.render();for(const t of this.child_views){const e=t.rendering_target()??this.shadow_el;t.render_to(e)}}_update_children(){}async update_children(){const t=await this.build_child_views(),e=new Set(t);for(const t of this.child_views)t.el.remove();for(const t of this.child_views){const i=e.has(t),s=t.rendering_target()??this.shadow_el;i?t.render_to(s):s.append(t.el)}this.r_after_render(),this._update_children(),this.invalidate_layout()}_intrinsic_display(){return{inner:this.model.flow_mode,outer:\"flow\"}}_update_layout(){function t(t,e,i,s){switch(t){case\"auto\":return null!=e?(0,u.px)(e):i;case\"fixed\":return null!=e?(0,u.px)(e):\"fit-content\";case\"fit\":return\"fit-content\";case\"min\":return\"min-content\";case\"max\":return null==s?\"100%\":`calc(100% - ${s})`}}function e(t){return(0,_.isNumber)(t)?(0,u.px)(t):`${t.percent}%`}const i={},s=this._intrinsic_display();i.display=function(t){const{inner:e,outer:i}=t;switch(`${e} ${i}`){case\"block flow\":return\"block\";case\"inline flow\":return\"inline\";case\"block flow-root\":return\"flow-root\";case\"inline flow-root\":return\"inline-block\";case\"block flex\":return\"flex\";case\"inline flex\":return\"inline-flex\";case\"block grid\":return\"grid\";case\"inline grid\":return\"inline-grid\";case\"block table\":return\"table\";case\"inline table\":return\"inline-table\";default:(0,f.unreachable)()}}(s);const o=this.box_sizing(),{width_policy:a,height_policy:l,width:n,height:r,aspect_ratio:h}=o,c=(()=>{if(\"auto\"==h){if(null!=n&&null!=r)return n/r}else if((0,_.isNumber)(h))return h;return null})();\"auto\"==h?i.aspect_ratio=null!=n&&null!=r?`${n} / ${r}`:\"auto\":(0,_.isNumber)(h)&&(i.aspect_ratio=`${h}`);const{margin:d}=this.model,p=(()=>{if(null!=d){if((0,_.isNumber)(d))return i.margin=(0,u.px)(d),{width:(0,u.px)(2*d),height:(0,u.px)(2*d)};if(2==d.length){const[t,e]=d;return i.margin=`${(0,u.px)(t)} ${(0,u.px)(e)}`,{width:(0,u.px)(2*e),height:(0,u.px)(2*t)}}{const[t,e,s,o]=d;return i.margin=`${(0,u.px)(t)} ${(0,u.px)(e)} ${(0,u.px)(s)} ${(0,u.px)(o)}`,{width:(0,u.px)(o+e),height:(0,u.px)(t+s)}}}return{width:null,height:null}})(),[m,w]=(()=>{const e=t(a,n,this._auto_width,p.width),i=t(l,r,this._auto_height,p.height);if(null!=h){if(a!=l)return\"fixed\"==a?[e,\"auto\"]:\"fixed\"==l?[\"auto\",i]:\"max\"==a?[e,\"auto\"]:\"max\"==l?[\"auto\",i]:[\"auto\",\"auto\"];if(\"fixed\"!=a&&\"fixed\"!=l&&null!=c)return c>=1?[e,\"auto\"]:[\"auto\",i]}return[e,i]})();i.width=m,i.height=w;const{min_width:y,max_width:g}=this.model,{min_height:b,max_height:x}=this.model;i.min_width=null==y?\"0px\":e(y),i.min_height=null==b?\"0px\":e(b),this.is_layout_root?(null!=g&&(i.max_width=e(g)),null!=x&&(i.max_height=e(x))):(null!=g?i.max_width=`min(${e(g)}, 100%)`:\"fixed\"!=a&&(i.max_width=\"100%\"),null!=x?i.max_height=`min(${e(x)}, 100%)`:\"fixed\"!=l&&(i.max_height=\"100%\"));const{resizable:v}=this.model;if(!1!==v){const t=(()=>{switch(v){case\"width\":return\"horizontal\";case\"height\":return\"vertical\";case!0:case\"both\":return\"both\"}})();i.resize=t,i.overflow=\"auto\"}this.style.append(\":host\",i)}update_layout(){this.update_style();for(const t of this.layoutable_views)t.update_layout();this._update_layout()}get is_managed(){return this.parent instanceof m}_measure_layout(){}measure_layout(){for(const t of this.layoutable_views)t.measure_layout();this._measure_layout()}compute_layout(){this.parent instanceof m?this.parent.compute_layout():(this.measure_layout(),this.update_bbox(),this._compute_layout(),this.after_layout()),this._layout_computed=!0}_compute_layout(){if(null!=this.layout){this.layout.compute(this.bbox.size);for(const t of this.layoutable_views)null==t.layout?t._compute_layout():t._propagate_layout()}else for(const t of this.layoutable_views)t._compute_layout()}_propagate_layout(){for(const t of this.layoutable_views)null==t.layout&&t._compute_layout()}update_bbox(){for(const t of this.layoutable_views)t.update_bbox();const t=super.update_bbox();return null!=this.layout&&(this.layout.visible=this.is_displayed),t}_after_layout(){}after_layout(){for(const t of this.layoutable_views)t.after_layout();this._after_layout()}_after_render(){this.is_managed||this.invalidate_layout()}invalidate_layout(){this.parent instanceof m?this.parent.invalidate_layout():(this.update_layout(),this.compute_layout())}invalidate_render(){this.render(),this.invalidate_layout()}has_finished(){if(!super.has_finished())return!1;if(this.is_layout_root&&!this._layout_computed)return!1;for(const t of this.child_views)if(!t.has_finished())return!1;return!0}box_sizing(){let{width_policy:t,height_policy:e,aspect_ratio:i}=this.model;const{sizing_mode:s}=this.model;if(null!=s)if(\"inherit\"==s){if(this.parent instanceof m){const s=this.parent.box_sizing();t=s.width_policy,e=s.height_policy,null==i&&(i=s.aspect_ratio)}}else if(\"fixed\"==s)t=e=\"fixed\";else if(\"stretch_both\"==s)t=e=\"max\";else if(\"stretch_width\"==s)t=\"max\";else if(\"stretch_height\"==s)e=\"max\";else switch(null==i&&(i=\"auto\"),s){case\"scale_width\":t=\"max\",e=\"min\";break;case\"scale_height\":t=\"min\",e=\"max\";break;case\"scale_both\":t=\"max\",e=\"max\"}const[o,a]=(()=>{const{align:t}=this.model;return\"auto\"==t?[void 0,void 0]:(0,_.isArray)(t)?t:[t,t]})(),{width:l,height:n}=this.model;return{width_policy:t,height_policy:e,width:l,height:n,aspect_ratio:i,halign:o,valign:a}}export(t=\"auto\",e=!0){const i=(()=>{switch(t){case\"auto\":case\"png\":return\"canvas\";case\"svg\":return\"svg\"}})(),s=new p.CanvasLayer(i,e),{x:o,y:a,width:l,height:n}=this.bbox;s.resize(l,n);const r=getComputedStyle(this.el).backgroundColor;s.ctx.fillStyle=r,s.ctx.fillRect(o,a,l,n);for(const i of this.child_views){const o=i.export(t,e),{x:a,y:l}=i.bbox.scale(s.pixel_ratio);s.ctx.drawImage(o.canvas,a,l)}return s}}i.LayoutDOMView=m,m.__name__=\"LayoutDOMView\";class w extends l.Pane{constructor(t){super(t)}}i.LayoutDOM=w,a=w,w.__name__=\"LayoutDOM\",a.define((t=>{const{Bool:e,Float:i,Auto:s,Tuple:o,Or:a,Null:l,Nullable:n}=t,r=o(i,i),u=o(i,i,i,i);return{width:[n(i),null],height:[n(i),null],min_width:[n(i),null],min_height:[n(i),null],max_width:[n(i),null],max_height:[n(i),null],margin:[n(a(i,r,u)),null],width_policy:[a(d.SizingPolicy,s),\"auto\"],height_policy:[a(d.SizingPolicy,s),\"auto\"],aspect_ratio:[a(i,s,l),null],flow_mode:[h.FlowMode,\"block\"],sizing_mode:[n(h.SizingMode),null],disabled:[e,!1],align:[a(h.Align,o(h.Align,h.Align),s),\"auto\"],resizable:[a(e,h.Dimensions),!1]}}))},\n function _(e,t,s,n,i){var r;n();const _=e(111),l=e(125),a=e(401),o=e(56),m=e(21);s.ElementLike=(0,m.Or)((0,m.Ref)(_.UIElement),(0,m.Ref)(l.DOMNode),(0,m.Ref)(a.HTML));class h extends _.UIElementView{constructor(){super(...arguments),this._element_views=new Map}get elements(){return this.model.elements}get element_views(){return this.elements.map((e=>this._element_views.get(e)))}*children(){yield*super.children(),yield*this.element_views}async lazy_initialize(){await super.lazy_initialize(),await this._build_elements()}async _build_elements(){return await(0,o.build_views)(this._element_views,this.elements,{parent:this})}async _update_elements(){const{created:e}=await this._build_elements(),t=new Set(e);for(const e of this.element_views)e.el.remove();for(const e of this.element_views){const s=t.has(e),n=e.rendering_target()??this.shadow_el;s?e.render_to(n):n.append(e.el)}this.r_after_render()}remove(){(0,o.remove_views)(this._element_views),super.remove()}connect_signals(){super.connect_signals();const{elements:e}=this.model.properties;this.on_change(e,(async()=>{await this._update_elements()}))}render(){super.render();for(const e of this.element_views){const t=e.rendering_target()??this.shadow_el;e.render_to(t)}}has_finished(){if(!super.has_finished())return!1;for(const e of this.element_views)if(!e.has_finished())return!1;return!0}}s.PaneView=h,h.__name__=\"PaneView\";class d extends _.UIElement{constructor(e){super(e)}}s.Pane=d,r=d,d.__name__=\"Pane\",r.prototype.default_view=h,r.define((({List:e})=>({elements:[e(s.ElementLike),[]]})))},\n function _(e,t,r,i,s){var n;i();const o=e(402),l=e(111),a=e(56),d=e(63),f=e(12),c=e(8),h=e(21),u=(0,h.Or)((0,h.Ref)(o.DOMElement),(0,h.Ref)(l.UIElement)),_=h.Str;class m extends o.DOMElementView{constructor(){super(...arguments),this._refs=new Map}get refs(){const{html:e,refs:t}=this.model;return[...(0,c.isArray)(e)?e.filter((e=>!(0,c.isString)(e))):[],...t]}*children(){yield*super.children(),yield*this._refs.values()}async lazy_initialize(){await super.lazy_initialize(),await(0,a.build_views)(this._refs,this.refs)}remove(){(0,a.remove_views)(this._refs),super.remove()}render(){super.render();const e=(()=>{const{html:e}=this.model;return(0,c.isArray)(e)?e.map((e=>(0,c.isString)(e)?e:``)).join(\"\"):e})(),t=(()=>(0,c.isString)(e)?this.parse_html(e):[e])();this.el.append(...t),this.finish()}parse_html(e){const t=(new DOMParser).parseFromString(e,\"text/html\"),r=t.createNodeIterator(t,NodeFilter.SHOW_ELEMENT,(e=>\"ref\"==e.nodeName.toLowerCase()?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT));let i;e:for(;null!=(i=r.nextNode());){(0,f.assert)(i instanceof Element);const e=i.getAttribute(\"id\");if(null!=e){for(const[t,r]of this._refs)if(t.id==e){r.render(),i.replaceWith(r.el);continue e}i.replaceWith((0,d.span)(``));continue}const t=i.getAttribute(\"name\");if(null==t);else{for(const[e,r]of this._refs)if(e.name==t){r.render(),i.replaceWith(r.el);continue e}i.replaceWith((0,d.span)(``))}}return[...t.body.childNodes]}}r.HTMLView=m,m.__name__=\"HTMLView\";class p extends o.DOMElement{constructor(e){super(e)}}r.HTML=p,n=p,p.__name__=\"HTML\",n.prototype.default_view=m,n.define((({Node:e,List:t,Or:r})=>({html:[r(e,_,t(r(_,u)))],refs:[t(u),[]]})))},\n function _(e,i,s,t,l){var n;t();const r=e(125),d=e(84),h=e(111),c=e(56),o=e(8),a=e(65),_=e(63);class m extends r.DOMNodeView{constructor(){super(...arguments),this.child_views=new Map}*children(){yield*super.children(),yield*this.child_views.values()}async lazy_initialize(){await super.lazy_initialize();const e=this.model.children.filter((e=>!(0,o.isString)(e)));await(0,c.build_views)(this.child_views,e,{parent:this})}remove(){(0,c.remove_views)(this.child_views),super.remove()}render(){(0,_.empty)(this.el),(0,a.apply_styles)(this.el.style,this.model.style);for(const e of this.model.children)if((0,o.isString)(e)){const i=document.createTextNode(e);this.el.appendChild(i)}else{this.child_views.get(e).render_to(this.el)}this.finish()}}s.DOMElementView=m,m.__name__=\"DOMElementView\";class w extends r.DOMNode{constructor(e){super(e)}}s.DOMElement=w,n=w,w.__name__=\"DOMElement\",n.define((({Str:e,List:i,Or:s,Ref:t})=>({style:[d.StylesLike,{}],children:[i(s(e,t(r.DOMNode),t(h.UIElement))),[]]})))},\n function _(t,o,e,r,i){r();const n=t(175),h=t(64),l=t(12),{max:s}=Math;class f extends n.Layoutable{constructor(t){super(),this.children=t}_measure(t){return{width:0,height:0}}compute(t={}){const{width:o,height:e}=t;(0,l.assert)(null!=o&&null!=e);const r={width:o,height:e},i=new h.BBox({left:0,top:0,width:o,height:e});let n;if(null!=r.inner){const{left:t,top:i,right:l,bottom:s}=r.inner;n=new h.BBox({left:t,top:i,right:o-l,bottom:e-s})}this.set_geometry(i,n)}_set_geometry(t,o){super._set_geometry(t,o);const e=this.children.map(((t,o)=>{const{layout:e,bbox:r}=o;(0,l.assert)(null!=e);const i=e.measure(r);return{child:o,layout:e,bbox:r,size_hint:i}})),r=Array(e.nrows).fill(null).map((()=>({top:0,bottom:0}))),i=Array(e.ncols).fill(null).map((()=>({left:0,right:0})));e.foreach((({r0:t,c0:o,r1:e,c1:n},{size_hint:h})=>{const{inner:l}=h;null!=l&&(i[o].left=s(i[o].left,l.left),i[n].right=s(i[n].right,l.right),r[t].top=s(r[t].top,l.top),r[e].bottom=s(r[e].bottom,l.bottom))})),e.foreach((({r0:t,c0:o,r1:e,c1:n},{layout:l,size_hint:s,bbox:f})=>{const g=f,m=null==s.inner?void 0:(()=>{const{inner:l,align:f}=s,m=f?.left??!0,u=f?.right??!0,c=f?.top??!0,b=f?.bottom??!0,p=f?.fixed_width??!1,a=f?.fixed_height??!1,{left:d,right:_}=(()=>{if(p){const t=g.width-l.right-l.left;if(m){const e=i[o].left;return{left:e,right:g.width-(e+t)}}if(u){const o=i[n].right;return{left:g.width-(o+t),right:o}}return{left:l.left,right:l.right}}return{left:m?i[o].left:l.left,right:u?i[n].right:l.right}})(),{top:w,bottom:y}=(()=>{if(a){const o=g.height-l.bottom-l.top;if(c){const e=r[t].top;return{top:e,bottom:g.height-(e+o)}}if(b){const t=r[e].bottom;return{top:g.height-(t+o),bottom:t}}return{top:l.top,bottom:l.bottom}}return{top:c?r[t].top:l.top,bottom:b?r[e].bottom:l.bottom}})(),{width:x,height:B}=g;return h.BBox.from_lrtb({left:d,top:w,right:x-_,bottom:B-y})})();l.set_geometry(g,m)}))}}e.GridAlignmentLayout=f,f.__name__=\"GridAlignmentLayout\"},\n function _(e,i,s,n,r){var t;n();const o=e(405),l=e(185),c=e(111);class d extends o.CSSGridBoxView{connect_signals(){super.connect_signals();const{children:e,rows:i,cols:s}=this.model.properties;this.on_change(e,(()=>this.update_children())),this.on_change([i,s],(()=>this.invalidate_layout()))}get _children(){return this.model.children}get _rows(){return this.model.rows}get _cols(){return this.model.cols}}s.GridBoxView=d,d.__name__=\"GridBoxView\";class _ extends o.CSSGridBox{constructor(e){super(e)}}s.GridBox=_,t=_,_.__name__=\"GridBox\",t.prototype.default_view=d,t.define((({List:e,Nullable:i})=>({children:[e((0,l.GridChild)(c.UIElement)),[]],rows:[i(l.TracksSizing),null],cols:[i(l.TracksSizing),null]})))},\n function _(i,n,s,t,e){var o;t();const a=i(399),r=i(403),l=i(185),c=i(63),_=i(258),d=i(34),u=i(8),{max:p}=Math;class g extends a.LayoutDOMView{connect_signals(){super.connect_signals();const{spacing:i}=this.model.properties;this.on_change(i,(()=>this.invalidate_layout()))}get child_models(){return this._children.map((([i])=>i))}_intrinsic_display(){return{inner:this.model.flow_mode,outer:\"grid\"}}_update_layout(){super._update_layout();const i={},[n,s]=(()=>{const{spacing:i}=this.model;return(0,u.isNumber)(i)?[i,i]:i})();i.row_gap=(0,c.px)(n),i.column_gap=(0,c.px)(s);let t=0,e=0;const o=new _.Container;for(const[[,i,n,s=1,r=1],l]of(0,d.enumerate)(this._children)){const c=this.child_views[l];t=p(t,i+s),e=p(e,n+r);const _={};if(_.grid_row_start=`${i+1}`,_.grid_row_end=`span ${s}`,_.grid_column_start=`${n+1}`,_.grid_column_end=`span ${r}`,c.style.append(\":host\",_),c instanceof a.LayoutDOMView&&null!=c.layout){const t=i,e=n,a=i+s-1,l=n+r-1;o.add({r0:t,c0:e,r1:a,c1:l},c)}}const{_rows:l,_cols:g}=this;function h(i,n){if(i instanceof Map)for(const[s,t]of i.entries())(0,u.isString)(t)?n[s].size=t:n[s]=t;else if((0,u.isArray)(i))for(const[s,t]of(0,d.enumerate)(i))(0,u.isString)(s)?n[t].size=s:n[t]=s;else if(null!=i)for(const s of n)(0,u.isString)(i)?s.size=i:(s.size=i.size,s.align=i.align)}l instanceof Map?t=p(t,...l.keys()):(0,u.isArray)(l)&&(t=p(t,l.length)),g instanceof Map?e=p(e,...g.keys()):(0,u.isArray)(g)&&(e=p(e,g.length));const f=Array(t).fill(null).map((()=>({}))),m=Array(e).fill(null).map((()=>({})));h(l,f),h(g,m);for(const[[,i,n],s]of(0,d.enumerate)(this._children)){const t=this.child_views[s],{halign:e,valign:o}=t.box_sizing();t.style.append(\":host\",{justify_self:e??m[n].align,align_self:o??f[i].align})}const y=\"auto\";i.grid_template_rows=f.map((({size:i})=>i??y)).join(\" \"),i.grid_template_columns=m.map((({size:i})=>i??y)).join(\" \"),this.style.append(\":host\",i),0!=o.size?(this.layout=new r.GridAlignmentLayout(o),this.layout.set_sizing()):delete this.layout}}s.CSSGridBoxView=g,g.__name__=\"CSSGridBoxView\";class h extends a.LayoutDOM{constructor(i){super(i)}}s.CSSGridBox=h,o=h,h.__name__=\"CSSGridBox\",o.define((()=>({spacing:[l.GridSpacing,0]})))},\n function _(e,t,s,l,i){var c;l();const h=e(1),d=e(399),o=e(111),n=e(63),a=h.__importDefault(e(407));class _ extends d.LayoutDOMView{stylesheets(){return[...super.stylesheets(),a.default]}connect_signals(){super.connect_signals();const{child:e}=this.model.properties;this.on_change(e,(()=>this.update_children()));const{checkable:t,disabled:s}=this.model.properties;this.on_change(t,(()=>{(0,n.display)(this.checkbox_el,this.model.checkable)})),this.on_change(s,(()=>{this.checkbox_el.checked=!this.model.disabled}))}get child_models(){return[this.model.child]}render(){super.render();const{checkable:e,disabled:t,title:s}=this.model;this.checkbox_el=(0,n.input)({type:\"checkbox\",checked:!t}),this.checkbox_el.addEventListener(\"change\",(()=>{this.model.disabled=!this.checkbox_el.checked})),(0,n.display)(this.checkbox_el,e);const l=(0,n.legend)({},this.checkbox_el,s),i=this.child_views.map((e=>e.el));this.fieldset_el=(0,n.fieldset)({},l,...i),this.shadow_el.appendChild(this.fieldset_el)}_update_children(){const e=this.child_views.map((e=>e.el));this.fieldset_el.append(...e)}}s.GroupBoxView=_,_.__name__=\"GroupBoxView\";class r extends d.LayoutDOM{constructor(e){super(e)}}s.GroupBox=r,c=r,r.__name__=\"GroupBox\",c.prototype.default_view=_,c.define((({Bool:e,Str:t,Nullable:s,Ref:l})=>({title:[s(t),null],child:[l(o.UIElement)],checkable:[e,!1]})))},\n function _(d,a,e,l,i){l(),e.default=\"legend{display:flex;gap:0.5em;padding:0 calc(var(--padding-horizontal) / 2);}fieldset{border:1px solid #ccc;}\"},\n function _(e,n,t,s,l){var i;s();const o=e(405),c=e(185),r=e(111),a=e(21),d=(0,a.Struct)({child:(0,a.Ref)(r.UIElement),col:(0,a.Opt)(c.Index),span:(0,a.Opt)(c.Span)});class _ extends o.CSSGridBoxView{connect_signals(){super.connect_signals();const{children:e,cols:n}=this.model.properties;this.on_change(e,(()=>this.update_children())),this.on_change(n,(()=>this.invalidate_layout()))}get _children(){return this.model.children.map((({child:e,col:n,span:t},s)=>[e,0,n??s,1,t??1]))}get _rows(){return null}get _cols(){return this.model.cols}}t.HBoxView=_,_.__name__=\"HBoxView\";class h extends o.CSSGridBox{constructor(e){super(e)}}t.HBox=h,i=h,h.__name__=\"HBox\",i.prototype.default_view=_,i.define((({List:e,Nullable:n})=>({children:[e(d),[]],cols:[n(c.TracksSizing),null]})))},\n function _(e,o,t,s,_){var n;s();const r=e(398);class c extends r.FlexBoxView{constructor(){super(...arguments),this._direction=\"row\"}}t.RowView=c,c.__name__=\"RowView\";class w extends r.FlexBox{constructor(e){super(e)}}t.Row=w,n=w,w.__name__=\"Row\",n.prototype.default_view=c},\n function _(e,l,o,t,r){var s;t();const a=e(399),c=e(111),i=e(20);class n extends a.LayoutDOMView{stylesheets(){return[...super.stylesheets()]}connect_signals(){super.connect_signals();const{child:e,horizontal_scrollbar:l,vertical_scrollbar:o}=this.model.properties;this.on_change(e,(()=>this.update_children())),this.on_change([l,o],(()=>this.invalidate_layout()))}get child_models(){return[this.model.child]}_update_layout(){function e(e){switch(e){case\"auto\":return\"auto\";case\"visible\":return\"scroll\";case\"hidden\":return\"hidden\"}}super._update_layout();const{horizontal_scrollbar:l,vertical_scrollbar:o}=this.model;this.style.append(\":host\",{overflow_x:e(l),overflow_y:e(o)})}}o.ScrollBoxView=n,n.__name__=\"ScrollBoxView\";class _ extends a.LayoutDOM{constructor(e){super(e)}}o.ScrollBox=_,s=_,_.__name__=\"ScrollBox\",s.prototype.default_view=n,s.define((({Ref:e})=>({child:[e(c.UIElement)],horizontal_scrollbar:[i.ScrollbarPolicy,\"auto\"],vertical_scrollbar:[i.ScrollbarPolicy,\"auto\"]})))},\n function _(t,e,a,o,_){var r;o();const s=t(399);class c extends s.LayoutDOMView{constructor(){super(...arguments),this._auto_width=\"auto\",this._auto_height=\"auto\"}get child_models(){return[]}}a.SpacerView=c,c.__name__=\"SpacerView\";class u extends s.LayoutDOM{constructor(t){super(t)}}a.Spacer=u,r=u,u.__name__=\"Spacer\",r.prototype.default_view=c},\n function _(l,e,o,t,n){var a;t();const s=l(413),c=l(51),i=l(111);class d extends c.Model{constructor(l){super(l)}}o.TabPanel=d,a=d,d.__name__=\"TabPanel\",a.define((({Bool:l,Str:e,Ref:o,Nullable:t})=>({title:[e,\"\"],tooltip:[t(o(s.Tooltip)),null],child:[o(i.UIElement)],closable:[l,!1],disabled:[l,!1]})))},\n function _(e,t,i,r,s){var n;r();const o=e(1),l=e(111),a=e(125),h=e(60),c=e(414),_=e(20),u=e(63),d=e(57),p=e(8),f=e(12),w=e(64),g=e(19),v=e(56),m=e(51),b=globalThis.Node,y=o.__importStar(e(415)),x=y,z=o.__importDefault(e(123));class E extends l.UIElementView{constructor(){super(...arguments),this._element_view=null,this._has_rendered=!1}get target(){return this._target}set target(e){this._target=e}_init_target(){const{target:e}=this.model,t=(()=>{if(e instanceof l.UIElement)return this.owner.find_one(e)?.el??null;if(e instanceof c.Selector)return e.find_one(document);if(e instanceof b)return e;{const{parent:e}=this;return e instanceof d.DOMElementView?e.el:null}})();t instanceof Element?this._target=t:(g.logger.warn(`unable to resolve target '${e}' for '${this}'`),this._target=document.body)}initialize(){super.initialize(),this._init_target()}*children(){yield*super.children(),null!=this._element_view&&(yield this._element_view)}async lazy_initialize(){await super.lazy_initialize(),await this._build_content()}async _build_content(){null!=this._element_view&&(this._element_view.remove(),this._element_view=null);const{content:e}=this.model;e instanceof m.Model&&(this._element_view=await(0,v.build_view)(e,{parent:this}))}connect_signals(){super.connect_signals(),this._observer=new ResizeObserver((()=>{this._reposition()})),this._observer.observe(this.target);let e=!1;document.addEventListener(\"scroll\",this._scroll_listener=()=>{e||(requestAnimationFrame((()=>{this._reposition(),e=!1})),e=!0)},{capture:!0});const{target:t,content:i,closable:r,interactive:s,position:n,attachment:o,visible:l}=this.model.properties;this.on_change(t,(()=>{this._init_target(),this._observer.disconnect(),this._observer.observe(this.target),this.render(),this.after_render()})),this.on_change(i,(async()=>{await this._build_content(),this.render(),this.after_render()})),this.on_change([r,s],(()=>{this.render(),this.after_render()})),this.on_change([n,o,l],(()=>{this._reposition()}))}disconnect_signals(){null!=this._scroll_listener&&(document.removeEventListener(\"scroll\",this._scroll_listener,{capture:!0}),delete this._scroll_listener),super.disconnect_signals()}remove(){this._element_view?.remove(),this._observer.disconnect(),super.remove()}stylesheets(){return[...super.stylesheets(),y.default,z.default]}get content(){const{content:e}=this.model;return(0,p.isString)(e)?document.createTextNode(e):e instanceof m.Model?((0,f.assert)(null!=this._element_view),this._element_view.el):e}render(){super.render();const{_element_view:e}=this;null!=e&&(e.render(),e.r_after_render()),this.arrow_el=(0,u.div)({class:[x.arrow]}),this.content_el=(0,u.div)({class:x.tooltip_content},this.content),this.shadow_el.append(this.arrow_el,this.content_el),this.class_list.toggle(x.closable,this.model.closable);const t=(0,u.div)({class:x.close});this.shadow_el.append(t),t.addEventListener(\"click\",(()=>{this.model.visible=!1})),this.el.classList.toggle(x.show_arrow,this.model.show_arrow),this.el.classList.toggle(x.non_interactive,!this.model.interactive),this._has_rendered=!0}_after_render(){super._after_render(),this._reposition()}_after_resize(){super._after_resize(),this._reposition()}_anchor_to_align(e){e=(()=>{switch(e){case\"top\":return\"top_center\";case\"bottom\":return\"bottom_center\";case\"left\":return\"center_left\";case\"right\":return\"center_right\";default:return e}})();const[t,i]=e.split(\"_\");return{v:t,h:i}}_reposition(){const e=document.body.shadowRoot??document.body;if(!this._has_rendered)return this.render_to(e),void this.after_render();const{position:t,visible:i}=this.model;if(null==t||!i)return void this.el.remove();e.append(this.el);const r=(0,u.bounding_box)(this.target),[s,n]=(()=>{if((0,p.isString)(t)){const{v:e,h:i}=this._anchor_to_align(t);return[(()=>{switch(i){case\"left\":return r.left;case\"center\":return r.hcenter;case\"right\":return r.right}})(),(()=>{switch(e){case\"top\":return r.top;case\"center\":return r.vcenter;case\"bottom\":return r.bottom}})()]}if((0,p.isArray)(t)){const[e,i]=t;return[r.left+e,r.top+i]}{const{x:e,y:i}=this.resolve_as_xy(t);return[r.left+e,r.top+i]}})(),o=new w.BBox({x:0,y:0,width:window.innerWidth,height:window.innerHeight}),l=(0,u.box_size)(this.arrow_el),a=(()=>{const e=(()=>{const{attachment:e}=this.model;if(\"auto\"==e){if((0,p.isString)(t)){const{v:e,h:i}=this._anchor_to_align(t);if(\"center\"!=i)return\"left\"==i?\"left\":\"right\";if(\"center\"!=e)return\"top\"==e?\"above\":\"below\"}return\"horizontal\"}return e})(),i=(0,u.box_size)(this.el),a=i.width+l.width,h=i.height+l.height;switch(e){case\"horizontal\":return s=o.left?\"left\":\"right\";case\"vertical\":return n=o.top?\"above\":\"below\";default:return e}})();this.class_list.remove(x.right,x.left,x.above,x.below),this.class_list.add((()=>{switch(a){case\"left\":return x.right;case\"right\":return x.left;case\"above\":return x.below;case\"below\":return x.above}})()),this.arrow_el.style.left=`${s}px`,this.arrow_el.style.top=`${n}px`;const{left:h,top:c}=(()=>{const{width:e,height:t}=(0,u.box_size)(this.el);function i(e){return eo.bottom?o.bottom-t:e}function r(t){return to.right?o.right-e:t}switch(a){case\"left\":return{left:s-e-l.width,top:i(n-t/2)};case\"right\":return{left:s+l.width,top:i(n-t/2)};case\"above\":return{left:r(s-e/2),top:n-t-l.height};case\"below\":return{left:r(s-e/2),top:n+l.height}}})();this.el.style.top=`${c}px`,this.el.style.left=`${h}px`}}i.TooltipView=E,E.__name__=\"TooltipView\";class T extends l.UIElement{constructor(e){super(e)}show({x:e,y:t}){this.setv({position:[e,t],visible:!0},{check_eq:!1})}clear(){this.position=null}}i.Tooltip=T,n=T,T.__name__=\"Tooltip\",n.prototype.default_view=E,n.define((({Bool:e,Float:t,Str:i,Tuple:r,Or:s,Ref:n,Nullable:o,Auto:u})=>({target:[s(n(l.UIElement),n(c.Selector),n(b),u),\"auto\"],position:[o(s(_.Anchor,r(t,t),n(h.Coordinate))),null],content:[s(i,n(a.DOMNode),n(l.UIElement),n(b))],attachment:[s(_.TooltipAttachment,u),\"auto\"],show_arrow:[e,!0],closable:[e,!1],interactive:[e,!0]}))),n.override({visible:!1})},\n function _(e,r,t,c,n){var o;c();const s=e(51);class _ extends s.Model{constructor(e){super(e)}}t.Selector=_,o=_,_.__name__=\"Selector\",o.define((({Str:e})=>({query:[e]})))},\n function _(o,t,r,e,a){e(),r.non_interactive=\"bk-non-interactive\",r.arrow=\"bk-arrow\",r.show_arrow=\"bk-show-arrow\",r.left=\"bk-left\",r.right=\"bk-right\",r.above=\"bk-above\",r.below=\"bk-below\",r.tooltip_content=\"bk-tooltip-content\",r.tooltip_row_label=\"bk-tooltip-row-label\",r.tooltip_row_value=\"bk-tooltip-row-value\",r.tooltip_color_block=\"bk-tooltip-color-block\",r.closable=\"bk-closable\",r.close=\"bk-close\",r.default=':host{--tooltip-border:#e5e5e5;--tooltip-color:white;--tooltip-text:#2f2f2f;--tooltip-arrow-color:#909599;--tooltip-arrow-width:10px;--tooltip-arrow-height:10px;--tooltip-arrow-half-width:7px;--tooltip-arrow-half-height:7px;}:host{width:max-content;font-size:var(--font-size);position:fixed;padding:5px;border:1px solid var(--tooltip-border);color:var(--tooltip-text);background-color:var(--tooltip-color);opacity:0.95;z-index:var(--bokeh-top-level);}:host(.bk-non-interactive){pointer-events:none;}.bk-arrow{pointer-events:none;position:fixed;width:0;height:0;content:\" \";border-style:solid;border-color:transparent;}:host(:not(.bk-show-arrow)) .bk-arrow{display:none;}:host(.bk-left) .bk-arrow,:host(.bk-right) .bk-arrow{border-width:var(--tooltip-arrow-half-height) 0 var(--tooltip-arrow-half-height) 0;}:host(.bk-above) .bk-arrow,:host(.bk-below) .bk-arrow{border-width:0 var(--tooltip-arrow-half-width) 0 var(--tooltip-arrow-half-width);}:host(.bk-left) .bk-arrow{transform:translate(0%, -50%);border-right-width:var(--tooltip-arrow-width);border-right-color:var(--tooltip-arrow-color);}:host(.bk-right) .bk-arrow{transform:translate(-100%, -50%);border-left-width:var(--tooltip-arrow-width);border-left-color:var(--tooltip-arrow-color);}:host(.bk-above) .bk-arrow{transform:translate(-50%, 0%);border-bottom-width:var(--tooltip-arrow-height);border-bottom-color:var(--tooltip-arrow-color);}:host(.bk-below) .bk-arrow{transform:translate(-50%, -100%);border-top-width:var(--tooltip-arrow-height);border-top-color:var(--tooltip-arrow-color);}.bk-tooltip-content > div:not(:first-child){margin-top:5px;border-top:var(--tooltip-border) 1px dashed;}.bk-tooltip-row-label{text-align:right;color:#26aae1;}.bk-tooltip-row-value{color:none;}.bk-tooltip-color-block{width:12px;height:12px;margin-left:5px;margin-right:5px;outline:#dddddd solid 1px;display:inline-block;}:host(:not(.bk-closable)) .bk-close{display:none;}.bk-close{position:absolute;top:2px;right:2px;width:12px;height:12px;cursor:pointer;background-color:gray;mask-image:var(--bokeh-icon-x);mask-size:contain;mask-repeat:no-repeat;-webkit-mask-image:var(--bokeh-icon-x);-webkit-mask-size:contain;-webkit-mask-repeat:no-repeat;}.bk-close:hover{background-color:red;}'},\n function _(e,t,s,i,a){var l;i();const o=e(1),n=e(56),d=e(63),c=e(10),r=e(258),h=e(20),_=e(399),u=e(412),p=e(403),v=o.__importStar(e(417)),m=v,b=o.__importDefault(e(123));class f extends _.LayoutDOMView{constructor(){super(...arguments),this.tooltip_views=new Map}connect_signals(){super.connect_signals();const{tabs:e,active:t}=this.model.properties;this.on_change(e,(async()=>{this._update_headers(),await this.update_children()})),this.on_change(t,(()=>{this.update_active()}))}async lazy_initialize(){await super.lazy_initialize();const{tabs:e}=this.model,t=e.map((e=>e.tooltip)).filter((e=>null!=e));await(0,n.build_views)(this.tooltip_views,t,{parent:this})}stylesheets(){return[...super.stylesheets(),v.default,b.default]}get child_models(){return this.model.tabs.map((e=>e.child))}_intrinsic_display(){return{inner:this.model.flow_mode,outer:\"grid\"}}_update_layout(){super._update_layout();const e=this.model.tabs_location;this.class_list.remove([...h.Location].map((e=>m[e]))),this.class_list.add(m[e]);const t=new r.Container;for(const e of this.child_views)e.style.append(\":host\",{grid_area:\"stack\"}),e instanceof _.LayoutDOMView&&null!=e.layout&&t.add({r0:0,c0:0,r1:1,c1:1},e);0!=t.size?(this.layout=new p.GridAlignmentLayout(t),this.layout.set_sizing()):delete this.layout}_after_layout(){super._after_layout();const{child_views:e}=this;for(const t of e)(0,d.hide)(t.el);const{active:t}=this.model;if(t in e){const s=e[t];(0,d.show)(s.el)}}render(){super.render(),this.header_el=(0,d.div)({class:m.header}),this.shadow_el.append(this.header_el),this._update_headers()}_update_headers(){const{active:e}=this.model,t=this.model.tabs.map(((t,s)=>{const i=(0,d.div)({class:[m.tab,s==e?m.active:null],tabIndex:0},t.title);i.addEventListener(\"click\",(e=>{this.model.disabled||e.target==e.currentTarget&&this.change_active(s)}));const a=null!=t.tooltip?this.tooltip_views.get(t.tooltip):null;if(null!=a){a.model.target=i;const e=e=>{a.model.visible=e};i.addEventListener(\"mouseenter\",(()=>{e(!0)})),i.addEventListener(\"mouseleave\",(()=>{e(!1)}))}if(t.closable){const e=(0,d.div)({class:m.close});e.addEventListener(\"click\",(e=>{if(e.target==e.currentTarget){this.model.tabs=(0,c.remove_at)(this.model.tabs,s);const e=this.model.tabs.length;this.model.active>e-1&&(this.model.active=e-1)}})),i.appendChild(e)}return(this.model.disabled||t.disabled)&&i.classList.add(m.disabled),i}));this.header_els=t,(0,d.empty)(this.header_el),this.header_el.append(...t)}change_active(e){e!=this.model.active&&(this.model.active=e)}update_active(){const e=this.model.active,{header_els:t}=this;for(const e of t)e.classList.remove(m.active);e in t&&t[e].classList.add(m.active);const{child_views:s}=this;for(const e of s)(0,d.hide)(e.el);e in s&&(0,d.show)(s[e].el)}}s.TabsView=f,f.__name__=\"TabsView\";class w extends _.LayoutDOM{constructor(e){super(e)}}s.Tabs=w,l=w,w.__name__=\"Tabs\",l.prototype.default_view=f,l.define((({Int:e,List:t,Ref:s})=>({tabs:[t(s(u.TabPanel)),[]],tabs_location:[h.Location,\"above\"],active:[e,0]})))},\n function _(e,r,o,t,a){t(),o.above=\"bk-above\",o.below=\"bk-below\",o.left=\"bk-left\",o.right=\"bk-right\",o.header=\"bk-header\",o.tab=\"bk-tab\",o.active=\"bk-active\",o.close=\"bk-close\",o.disabled=\"bk-disabled\",o.default=':host{display:grid;}:host(.bk-above){grid-template:\"header\" max-content \"stack\" 1fr / 1fr;}:host(.bk-below){grid-template:\"stack\" 1fr \"header\" max-content / 1fr;}:host(.bk-left){grid-template:\"header stack\" 1fr / max-content 1fr;}:host(.bk-right){grid-template:\"stack header\" 1fr / 1fr max-content;}.bk-header{grid-area:\"header\";display:flex;flex-wrap:nowrap;align-items:stretch;user-select:none;-webkit-user-select:none;}:host(.bk-above) .bk-header,:host(.bk-below) .bk-header{flex-direction:row;}:host(.bk-left) .bk-header,:host(.bk-right) .bk-header{flex-direction:column;}:host(.bk-above) .bk-header{border-bottom:1px solid #e6e6e6;}:host(.bk-right) .bk-header{border-left:1px solid #e6e6e6;}:host(.bk-below) .bk-header{border-top:1px solid #e6e6e6;}:host(.bk-left) .bk-header{border-right:1px solid #e6e6e6;}.bk-tab{padding:4px 8px;border:solid transparent;outline:0;outline-offset:-5px;white-space:nowrap;cursor:pointer;text-align:center;}.bk-tab:hover{background-color:#f2f2f2;}.bk-tab:focus,.bk-tab:active{outline:1px dotted #ccc;}.bk-tab.bk-active{color:#4d4d4d;background-color:white;border-color:#e6e6e6;}.bk-tab .bk-close{margin-left:10px;}.bk-tab.bk-disabled{cursor:not-allowed;pointer-events:none;opacity:0.65;}:host(.bk-above) .bk-tab{border-width:3px 1px 0px 1px;border-radius:var(--border-radius) var(--border-radius) 0 0;}:host(.bk-right) .bk-tab{border-width:1px 3px 1px 0px;border-radius:0 var(--border-radius) var(--border-radius) 0;}:host(.bk-below) .bk-tab{border-width:0px 1px 3px 1px;border-radius:0 0 var(--border-radius) var(--border-radius);}:host(.bk-left) .bk-tab{border-width:1px 0px 1px 3px;border-radius:var(--border-radius) 0 0 var(--border-radius);}.bk-close{display:inline-block;vertical-align:middle;width:14px;height:14px;cursor:pointer;background-color:gray;mask-image:var(--bokeh-icon-x);mask-size:contain;mask-repeat:no-repeat;-webkit-mask-image:var(--bokeh-icon-x);-webkit-mask-size:contain;-webkit-mask-repeat:no-repeat;}.bk-close:hover{background-color:red;}'},\n function _(e,n,t,s,i){var o;s();const r=e(405),l=e(185),c=e(111),a=e(21),d=(0,a.Struct)({child:(0,a.Ref)(c.UIElement),row:(0,a.Opt)(l.Index),span:(0,a.Opt)(l.Span)});class _ extends r.CSSGridBoxView{connect_signals(){super.connect_signals();const{children:e,rows:n}=this.model.properties;this.on_change(e,(()=>this.update_children())),this.on_change(n,(()=>this.invalidate_layout()))}get _children(){return this.model.children.map((({child:e,row:n,span:t},s)=>[e,n??s,0,t??1,1]))}get _rows(){return this.model.rows}get _cols(){return null}}t.VBoxView=_,_.__name__=\"VBoxView\";class h extends r.CSSGridBox{constructor(e){super(e)}}t.VBox=h,o=h,h.__name__=\"VBox\",o.prototype.default_view=_,o.define((({List:e,Nullable:n})=>({children:[e(d),[]],rows:[n(l.TracksSizing),null]})))},\n function _(o,r,u,e,p){e();var y=o(420);p(\"GroupByModels\",y.GroupByModels),p(\"GroupByName\",y.GroupByName)},\n function _(o,s,e,n,r){var t;n();const u=o(51),c=o(21);class l extends u.Model{constructor(o){super(o)}}e.GroupBy=l,l.__name__=\"GroupBy\";class a extends l{constructor(o){super(o)}*query_groups(o,s){for(const s of o)for(const o of this.groups)o.includes(s)&&(yield o)}}e.GroupByModels=a,t=a,a.__name__=\"GroupByModels\",t.define({groups:[(0,c.List)((0,c.List)((0,c.Ref)(u.Model)))]});class _ extends l{constructor(o){super(o)}*query_groups(o,s){const e=new Map;for(const o of s){const{name:s}=o;if(null!=s){let n=e.get(s);void 0===n&&(n=new Set,e.set(s,n)),n.add(o)}}for(const s of o)for(const o of e.values())null!=s.name&&o.has(s)&&(yield[...o])}}e.GroupByName=_,_.__name__=\"GroupByName\"},\n function _(t,a,i,e,M){e();var T=t(178);M(\"MathText\",T.MathText),M(\"Ascii\",T.Ascii),M(\"MathML\",T.MathML),M(\"TeX\",T.TeX),M(\"PlainText\",t(184).PlainText)},\n function _(r,o,t,e,n){e(),n(\"CustomJSTransform\",r(423).CustomJSTransform),n(\"Dodge\",r(424).Dodge),n(\"Interpolator\",r(426).Interpolator),n(\"Jitter\",r(427).Jitter),n(\"LinearInterpolator\",r(429).LinearInterpolator),n(\"StepInterpolator\",r(430).StepInterpolator),n(\"Transform\",r(97).Transform)},\n function _(r,t,s,n,e){var a;n();const u=r(97),o=r(9),m=r(40);class _ extends u.Transform{constructor(r){super(r)}get names(){return(0,o.keys)(this.args)}get values(){return(0,o.values)(this.args)}_make_transform(r,t){return new Function(...this.names,r,(0,m.use_strict)(t))}get scalar_transform(){return this._make_transform(\"x\",this.func)}get vector_transform(){return this._make_transform(\"xs\",this.v_func)}compute(r){return this.scalar_transform(...this.values,r)}v_compute(r){return this.vector_transform(...this.values,r)}}s.CustomJSTransform=_,a=_,_.__name__=\"CustomJSTransform\",a.define((({Unknown:r,Str:t,Dict:s})=>({args:[s(r),{}],func:[t,\"\"],v_func:[t,\"\"]})))},\n function _(e,n,o,t,a){var r;t();const s=e(425);class u extends s.RangeTransform{constructor(e){super(e)}_compute(e){return e+this.value}}o.Dodge=u,r=u,u.__name__=\"Dodge\",r.define((({Float:e})=>({value:[e,0]})))},\n function _(e,n,t,r,a){var s;r();const c=e(97),o=e(98),i=e(109),u=e(24),h=e(8),l=e(12);class g extends c.Transform{constructor(e){super(e)}v_compute(e){let n;this.range instanceof i.FactorRange?n=this.range.v_synthetic(e):(0,h.isArrayableOf)(e,h.isNumber)?n=e:(0,l.unreachable)();const t=new((0,u.infer_type)(n))(n.length);for(let e=0;e({range:[n(e(o.Range)),null]})))},\n function _(t,e,r,n,s){var o;n();const i=t(97),a=t(128),h=t(24),l=t(10),d=t(8);class c extends i.Transform{constructor(t){super(t),this._sorted_dirty=!0}connect_signals(){super.connect_signals(),this.connect(this.change,(()=>this._sorted_dirty=!0))}v_compute(t){const e=new((0,h.infer_type)(t))(t.length);for(let r=0;ro*(e[t]-e[r]))),this._x_sorted=new((0,h.infer_type)(e))(n),this._y_sorted=new((0,h.infer_type)(r))(n);for(let t=0;t({x:[o(r,s(e))],y:[o(r,s(e))],data:[i(n(a.ColumnarDataSource)),null],clip:[t,!0]})))},\n function _(t,e,n,r,i){var s;r();const o=t(425),a=t(109),u=t(428),_=t(20),h=t(13),m=t(115);class c extends o.RangeTransform{constructor(t){super(t),this._previous_offsets=null}initialize(){super.initialize(),this._generator=this.random_generator?.generator()??new m.SystemRandom}v_compute(t){const e=(()=>this.range instanceof a.FactorRange?this.range.v_synthetic(t):t)(),n=(()=>{const t=e.length;return this._previous_offsets?.length!=t&&(this._previous_offsets=this._v_compute(t)),this._previous_offsets})();return(0,h.map)(n,((t,n)=>t+e[n]))}_compute(){const{mean:t,width:e}=this;switch(this.distribution){case\"uniform\":return this._generator.uniform(t,e);case\"normal\":return this._generator.normal(t,e)}}_v_compute(t){const{mean:e,width:n}=this;switch(this.distribution){case\"uniform\":return this._generator.uniforms(e,n,t);case\"normal\":return this._generator.normals(e,n,t)}}}n.Jitter=c,s=c,c.__name__=\"Jitter\",s.define((({Float:t})=>({mean:[t,0],width:[t,1],distribution:[_.Distribution,\"uniform\"]}))),s.internal((({Nullable:t,Ref:e})=>({random_generator:[t(e(u.RandomGenerator)),null]})))},\n function _(n,e,o,r,t){r();const a=n(51);class s extends a.Model{constructor(n){super(n)}}o.RandomGenerator=s,s.__name__=\"RandomGenerator\"},\n function _(t,s,_,r,e){r();const i=t(10),o=t(426);class n extends o.Interpolator{constructor(t){super(t)}compute(t){if(this.sort(!1),this.clip){if(tthis._x_sorted[this._x_sorted.length-1])return NaN}else{if(tthis._x_sorted[this._x_sorted.length-1])return this._y_sorted[this._y_sorted.length-1]}if(t==this._x_sorted[0])return this._y_sorted[0];const s=(0,i.find_last_index)(this._x_sorted,(s=>sthis._x_sorted[this._x_sorted.length-1])return NaN}else{if(tthis._x_sorted[this._x_sorted.length-1])return this._y_sorted[this._y_sorted.length-1]}let e;switch(this.mode){case\"after\":e=(0,d.find_last_index)(this._x_sorted,(e=>t>=e));break;case\"before\":e=(0,d.find_index)(this._x_sorted,(e=>t<=e));break;case\"center\":{const s=(0,d.map)(this._x_sorted,(e=>Math.abs(e-t))),r=(0,d.min)(s);e=(0,d.find_index)(s,(t=>r===t));break}default:throw new Error(`unknown mode: ${this.mode}`)}return-1!=e?this._y_sorted[e]:NaN}}s.StepInterpolator=h,_=h,h.__name__=\"StepInterpolator\",_.define((()=>({mode:[n.StepMode,\"after\"]})))},\n function _(p,o,t,i,a){i(),a(\"MapOptions\",p(432).MapOptions),a(\"GMapOptions\",p(432).GMapOptions),a(\"GMapPlot\",p(432).GMapPlot),a(\"GMap\",p(444).GMap),a(\"Plot\",p(433).Plot),a(\"GridPlot\",p(445).GridPlot),a(\"Figure\",p(446).Figure)},\n function _(e,t,a,o,n){var p,l,s;o();const _=e(433),r=e(20),i=e(51),c=e(99),d=e(443);n(\"GMapPlotView\",d.GMapPlotView);class u extends i.Model{constructor(e){super(e)}}a.MapOptions=u,p=u,u.__name__=\"MapOptions\",p.define((({Int:e,Float:t})=>({lat:[t],lng:[t],zoom:[e,12]})));class M extends u{constructor(e){super(e)}}a.GMapOptions=M,l=M,M.__name__=\"GMapOptions\",l.define((({Bool:e,Int:t,Str:a,Nullable:o})=>({map_type:[r.MapType,\"roadmap\"],scale_control:[e,!1],styles:[o(a),null],tilt:[t,45]})));class m extends _.Plot{constructor(e){super(e),this.use_map=!0}}a.GMapPlot=m,s=m,m.__name__=\"GMapPlot\",s.prototype.default_view=d.GMapPlotView,s.define((({Str:e,Bytes:t,Ref:a})=>({map_options:[a(M)],api_key:[t],api_version:[e,\"weekly\"]}))),s.override({x_range:()=>new c.Range1d,y_range:()=>new c.Range1d,background_fill_alpha:0})},\n function _(e,t,o,r,n){var a;r();const l=e(1),i=l.__importStar(e(80)),s=l.__importStar(e(18)),_=e(15),d=e(20),c=e(10),h=e(44),u=e(8),f=e(399),b=e(189),g=e(395),p=e(81),m=e(171),w=e(101),y=e(291),v=e(401),x=e(98),S=e(96),R=e(133),A=e(83),L=e(218),T=e(217),D=e(292),P=e(106),k=e(434);n(\"PlotView\",k.PlotView);class O extends f.LayoutDOM{constructor(e){super(e),this.use_map=!1,this.reset=new _.Signal0(this,\"reset\")}add_layout(e,t=\"center\"){const o=this.properties[t].get_value();this.setv({[t]:[...o,e]})}remove_layout(e){const t=t=>{(0,c.remove_by)(t,(t=>t==e))};t(this.left),t(this.right),t(this.above),t(this.below),t(this.center)}get data_renderers(){return this.renderers.filter((e=>e instanceof L.DataRenderer))}add_renderers(...e){this.renderers=[...this.renderers,...e]}add_glyph(e,t=new R.ColumnDataSource,o={}){const r=new T.GlyphRenderer({...o,data_source:t,glyph:e});return this.add_renderers(r),r}add_tools(...e){const t=e.map((e=>e instanceof D.Tool?e:D.Tool.from_string(e)));this.toolbar.tools=[...this.toolbar.tools,...t]}remove_tools(...e){this.toolbar.tools=[...(0,h.difference)(new Set(this.toolbar.tools),new Set(e))]}get panels(){return[...this.side_panels,...this.center]}get side_panels(){const{above:e,below:t,left:o,right:r}=this;return(0,c.concat)([e,t,o,r])}}o.Plot=O,a=O,O.__name__=\"Plot\",a.prototype.default_view=k.PlotView,a.mixins([[\"outline_\",i.Line],[\"background_\",i.Fill],[\"border_\",i.Fill]]),a.define((({Bool:e,Float:t,Str:o,List:r,Dict:n,Or:a,Ref:l,Null:i,Nullable:_,Struct:c,Opt:h})=>({toolbar:[l(y.Toolbar),()=>new y.Toolbar],toolbar_location:[_(d.Location),\"right\"],toolbar_sticky:[e,!0],toolbar_inner:[e,!1],frame_width:[_(t),null],frame_height:[_(t),null],frame_align:[a(e,c({left:h(e),right:h(e),top:h(e),bottom:h(e)})),!0],title:[a(l(m.Title),o,i),\"\",{convert:e=>(0,u.isString)(e)?new m.Title({text:e}):e}],title_location:[_(d.Location),\"above\"],above:[r(a(l(p.Annotation),l(b.Axis))),[]],below:[r(a(l(p.Annotation),l(b.Axis))),[]],left:[r(a(l(p.Annotation),l(b.Axis))),[]],right:[r(a(l(p.Annotation),l(b.Axis))),[]],center:[r(a(l(p.Annotation),l(g.Grid))),[]],renderers:[r(l(A.Renderer)),[]],x_range:[l(x.Range),()=>new P.DataRange1d],y_range:[l(x.Range),()=>new P.DataRange1d],x_scale:[l(S.Scale),()=>new w.LinearScale],y_scale:[l(S.Scale),()=>new w.LinearScale],extra_x_ranges:[n(l(x.Range)),{}],extra_y_ranges:[n(l(x.Range)),{}],extra_x_scales:[n(l(S.Scale)),{}],extra_y_scales:[n(l(S.Scale)),{}],lod_factor:[t,10],lod_interval:[t,300],lod_threshold:[_(t),2e3],lod_timeout:[t,500],hidpi:[e,!0],output_backend:[d.OutputBackend,\"canvas\"],min_border:[_(t),5],min_border_top:[_(t),null],min_border_left:[_(t),null],min_border_bottom:[_(t),null],min_border_right:[_(t),null],inner_width:[t,s.unset,{readonly:!0}],inner_height:[t,s.unset,{readonly:!0}],outer_width:[t,s.unset,{readonly:!0}],outer_height:[t,s.unset,{readonly:!0}],match_aspect:[e,!1],aspect_scale:[t,1],reset_policy:[d.ResetPolicy,\"standard\"],hold_render:[e,!1],attribution:[r(a(o,l(v.HTML))),[]]}))),a.override({width:600,height:600,outline_line_color:\"#e5e5e5\",border_fill_color:\"#ffffff\",background_fill_color:\"#ffffff\"})},\n function _(e,t,i,s,n){s();const a=e(1),r=e(188),o=e(324),l=e(83),_=e(82),h=e(293),d=e(399),c=e(81),u=e(171),p=e(189),m=e(290),g=e(106),f=e(435),b=e(437),w=e(53),v=e(56),y=e(87),x=e(19),z=e(53),S=e(15),k=e(438),N=e(8),$=e(10),M=e(34),q=e(112),P=e(257),V=e(259),R=e(258),T=e(173),B=e(64),C=e(181),D=e(439),A=e(440),H=e(29),L=e(63),I=e(59),F=a.__importDefault(e(441)),O=a.__importDefault(e(442)),{max:U}=Math;class j extends d.LayoutDOMView{constructor(){super(...arguments),this._render_count=0,this.repainted=new S.Signal0(this,\"repainted\"),this._computed_style=new L.InlineStyleSheet,this._outer_bbox=new B.BBox,this._inner_bbox=new B.BBox,this._needs_paint=!0,this._invalidated_painters=new Set,this._invalidate_all=!0,this.computed_renderers=[],this.renderer_views=new Map,this.tool_views=new Map,this._is_paused=0,this._needs_notify=!1,this._messages=new Map}get frame(){return this.frame_view}get canvas(){return this.canvas_view}stylesheets(){return[...super.stylesheets(),F.default,this._computed_style]}get toolbar_panel(){return null!=this._toolbar?this.views.find_one(this._toolbar):null}get state(){return this._state_manager}set invalidate_dataranges(e){this._range_manager.invalidate_dataranges=e}get computed_renderer_views(){return this.computed_renderers.map((e=>this.renderer_views.get(e))).filter(N.isNotNull)}get all_renderer_views(){const e=[];for(const t of this.computed_renderer_views)e.push(t),t instanceof _.CompositeRendererView&&e.push(...t.computed_renderer_views);return e}get auto_ranged_renderers(){return this.computed_renderer_views.filter(g.is_auto_ranged)}get base_font_size(){const e=getComputedStyle(this.el).fontSize,t=(0,C.parse_css_font_size)(e);if(null!=t){const{value:e,unit:i}=t;if(\"px\"==i)return e}return null}*children(){yield*super.children(),yield*this.renderer_views.values(),yield*this.tool_views.values()}get child_models(){return[]}get is_paused(){return 0!=this._is_paused}pause(){this._is_paused+=1}unpause(e=!1){this._is_paused=U(this._is_paused-1,0),this.is_paused||e||this.request_repaint()}notify_finished_after_paint(){this._needs_notify=!0}request_repaint(){this.request_paint()}request_paint(...e){this.invalidate_painters(...e),this.schedule_paint()}invalidate_painters(...e){if(0!=e.length)for(const t of e){const e=(()=>t instanceof l.RendererView?t:this.views.get_one(t))();this._invalidated_painters.add(e)}else this._invalidate_all=!0}schedule_paint(){if(!this.is_paused){const e=this.throttled_paint();this._ready=this._ready.then((()=>e))}}request_layout(){this.request_repaint()}reset(){\"standard\"==this.model.reset_policy&&(this.state.clear(),this.reset_range(),this.reset_selection()),this.model.trigger_event(new w.Reset)}remove(){(0,v.remove_views)(this.renderer_views),(0,v.remove_views)(this.tool_views),super.remove()}get_context_menu(e){const{x:t,y:i}=e;for(const e of(0,$.reversed)([...this.renderer_views.values()]))if(null!=e.context_menu&&1==e.interactive_hit?.(t,i))return e.context_menu;return super.get_context_menu(e)}initialize(){this.pause(),super.initialize(),this.lod_started=!1,this.visuals=new y.Visuals(this),this._initial_state={selection:new Map},this._frame=new r.CartesianFrame({x_scale:this.model.x_scale,y_scale:this.model.y_scale,x_range:this.model.x_range,y_range:this.model.y_range,extra_x_ranges:this.model.extra_x_ranges,extra_y_ranges:this.model.extra_y_ranges,extra_x_scales:this.model.extra_x_scales,extra_y_scales:this.model.extra_y_scales,aspect_scale:this.model.aspect_scale,match_aspect:this.model.match_aspect}),this._range_manager=new D.RangeManager(this),this._state_manager=new A.StateManager(this,this._initial_state),this.throttled_paint=(0,k.throttle)((()=>{this.is_destroyed||this.repaint()}),1e3/60);const{title_location:e,title:t}=this.model;null!=e&&null!=t&&(this._title=t instanceof u.Title?t:new u.Title({text:t}));const{toolbar_location:i,toolbar_inner:s,toolbar:n}=this.model;null!=i&&(this._toolbar=new m.ToolbarPanel({toolbar:n}),n.location=i,n.inner=s);const{hidpi:a,output_backend:l}=this.model;this._canvas=new o.Canvas({hidpi:a,output_backend:l}),this._attribution=new f.Panel({position:new I.Node({target:\"frame\",symbol:\"bottom_right\"}),anchor:\"bottom_right\",elements:[],css_variables:{\"--max-width\":new I.Node({target:\"frame\",symbol:\"width\"})},stylesheets:[O.default]}),this._notifications=new f.Panel({position:new I.Node({target:this.model,symbol:\"top_center\"}),anchor:\"top_center\",elements:[],stylesheets:[\"\\n :host {\\n display: flex;\\n flex-direction: column;\\n gap: 1em;\\n width: max-content;\\n max-width: 80%;\\n }\\n\\n :host:empty {\\n display: none;\\n }\\n\\n :host > div {\\n padding: 0.5em;\\n border: 1px solid gray;\\n border-radius: 0.5em;\\n opacity: 0.8;\\n }\\n \"]})}get elements(){return[this._canvas,this._frame,this._attribution,this._notifications,...super.elements]}async lazy_initialize(){await super.lazy_initialize(),this.canvas_view=this._element_views.get(this._canvas),this.canvas_view.plot_views=[this],this.frame_view=this._element_views.get(this._frame),await this.build_tool_views(),await this.build_renderer_views(),this._range_manager.update_dataranges()}box_sizing(){const{width_policy:e,height_policy:t,...i}=super.box_sizing(),{frame_width:s,frame_height:n}=this.model;return{...i,width_policy:null!=s&&\"auto\"==e?\"fit\":e,height_policy:null!=n&&\"auto\"==t?\"fit\":t}}_intrinsic_display(){return{inner:this.model.flow_mode,outer:\"grid\"}}_update_layout(){super._update_layout(),this._invalidate_all=!0,this._needs_paint=!0;const e=new V.BorderLayout,{frame_align:t}=this.model;if(e.aligns=(()=>{if((0,N.isBoolean)(t))return{left:t,right:t,top:t,bottom:t};{const{left:e=!0,right:i=!0,top:s=!0,bottom:n=!0}=t;return{left:e,right:i,top:s,bottom:n}}})(),e.set_sizing({width_policy:\"max\",height_policy:\"max\"}),this.visuals.outline_line.doit){const t=this.visuals.outline_line.line_width.get_value();e.center_border_width=t}const i=(0,$.copy)(this.model.above),s=(0,$.copy)(this.model.below),n=(0,$.copy)(this.model.left),a=(0,$.copy)(this.model.right),r=[],o=[],l=[],_=[],h=(e,t=!1)=>{switch(e){case\"above\":return t?r:i;case\"below\":return t?o:s;case\"left\":return t?l:n;case\"right\":return t?_:a}},{title_location:d}=this.model;if(null!=d&&null!=this._title&&h(d).push(this._title),null!=this._toolbar){const{location:e}=this._toolbar.toolbar;if(this.model.toolbar_inner){h(e,!0).push(this._toolbar)}else{const t=h(e);let i=!0;if(this.model.toolbar_sticky)for(let s=0;s{const i=this.views.get_one(t);return i.panel=new T.SidePanel(e),i.update_layout?.(),i.layout},g=(e,t)=>{const i=\"above\"==e||\"below\"==e,s=[];for(const n of t)if((0,N.isArray)(n)){const t=n.map((t=>{const s=p(e,t);if(null!=s){if(t instanceof m.ToolbarPanel){const e=i?\"width_policy\":\"height_policy\";s.set_sizing({...s.sizing,[e]:\"min\"})}return s}})).filter((e=>null!=e));let a;i?(a=new R.Row(t),a.set_sizing({width_policy:\"max\",height_policy:\"min\"})):(a=new R.Column(t),a.set_sizing({width_policy:\"min\",height_policy:\"max\"})),a.absolute=!0,s.push(a)}else{const t=p(e,n);null!=t&&s.push(t)}return s},f=this.model.min_border??0;e.min_border={left:this.model.min_border_left??f,top:this.model.min_border_top??f,right:this.model.min_border_right??f,bottom:this.model.min_border_bottom??f};const b=new P.NodeLayout,w=new P.VStack,v=new P.VStack,y=new P.HStack,x=new P.HStack,z=new P.VStack,S=new P.VStack,k=new P.HStack,M=new P.HStack;b.absolute=!0,w.absolute=!0,v.absolute=!0,y.absolute=!0,x.absolute=!0,z.absolute=!0,S.absolute=!0,k.absolute=!0,M.absolute=!0,b.children=this.model.center.filter((e=>e instanceof c.Annotation)).map((e=>{const t=this.views.get_one(e);return t.update_layout?.(),t.layout})).filter((e=>null!=e));const{frame_width:q,frame_height:B}=this.model;b.set_sizing({...null!=q?{width_policy:\"fixed\",width:q}:{width_policy:\"fit\"},...null!=B?{height_policy:\"fixed\",height:B}:{height_policy:\"fit\"}}),b.on_resize((e=>this.frame.set_geometry(e))),w.children=(0,$.reversed)(g(\"above\",i)),v.children=g(\"below\",s),y.children=(0,$.reversed)(g(\"left\",n)),x.children=g(\"right\",a),z.children=g(\"above\",r),S.children=g(\"below\",o),k.children=g(\"left\",l),M.children=g(\"right\",_),w.set_sizing({width_policy:\"fit\",height_policy:\"min\"}),v.set_sizing({width_policy:\"fit\",height_policy:\"min\"}),y.set_sizing({width_policy:\"min\",height_policy:\"fit\"}),x.set_sizing({width_policy:\"min\",height_policy:\"fit\"}),z.set_sizing({width_policy:\"fit\",height_policy:\"min\"}),S.set_sizing({width_policy:\"fit\",height_policy:\"min\"}),k.set_sizing({width_policy:\"min\",height_policy:\"fit\"}),M.set_sizing({width_policy:\"min\",height_policy:\"fit\"}),e.center_panel=b,e.top_panel=w,e.bottom_panel=v,e.left_panel=y,e.right_panel=x,0!=z.children.length&&(e.inner_top_panel=z),0!=S.children.length&&(e.inner_bottom_panel=S),0!=k.children.length&&(e.inner_left_panel=k),0!=M.children.length&&(e.inner_right_panel=M),this.layout=e}_measure_layout(){const{frame_width:e,frame_height:t}=this.model,i=null==e?\"1fr\":(0,L.px)(e),s=null==t?\"1fr\":(0,L.px)(t),{layout:n}=this,a=n.top_panel.measure({width:1/0,height:1/0}),r=n.bottom_panel.measure({width:1/0,height:1/0}),o=n.left_panel.measure({width:1/0,height:1/0}),l=n.right_panel.measure({width:1/0,height:1/0}),_=U(a.height,n.min_border.top),h=U(r.height,n.min_border.bottom),d=U(o.width,n.min_border.left),c=U(l.width,n.min_border.right);this._computed_style.replace(`\\n :host {\\n grid-template-rows: ${_}px ${s} ${h}px;\\n grid-template-columns: ${d}px ${i} ${c}px;\\n }\\n `)}get axis_views(){const e=[];for(const[,t]of this.renderer_views)t instanceof p.AxisView&&e.push(t);return e}update_range(e,t){this.pause(),this._range_manager.update(e,t),this.unpause()}reset_range(){this.pause(),this._range_manager.reset(),this.unpause(),this.trigger_ranges_update_event()}trigger_ranges_update_event(e=[]){const{x_ranges:t,y_ranges:i}=this._range_manager.ranges(),s=[...t,...i,...e],n=new Set(s.flatMap((e=>[...e.linked_plots])));for(const e of n){const{x_range:t,y_range:i}=e.model,s=new z.RangesUpdate(t.start,t.end,i.start,i.end);e.model.trigger_event(s)}}get_selection(){const e=new Map;for(const t of this.model.data_renderers){const{selected:i}=t.selection_manager.source;e.set(t,i)}return e}update_selection(e){for(const t of this.model.data_renderers){const i=t.selection_manager.source;if(null!=e){const s=e.get(t);null!=s&&i.selected.update(s,!0)}else i.selection_manager.clear()}}reset_selection(){this.update_selection(null)}_invalidate_layout_if_needed(){(()=>{for(const e of this.model.side_panels){const t=this.renderer_views.get(e);if(t.layout?.has_size_changed())return this.invalidate_painters(t),!0}return!1})()&&this.compute_layout()}*_compute_renderers(){const{above:e,below:t,left:i,right:s,center:n,renderers:a}=this.model;yield*a,yield*e,yield*t,yield*i,yield*s,yield*n,null!=this._title&&(yield this._title),null!=this._toolbar&&(yield this._toolbar);for(const[,e]of this.tool_views)yield*e.overlays}_update_attribution(){const e=[...this.model.attribution,...this.computed_renderer_views.map((e=>e.attribution))].filter(N.isNotNull).map((e=>(0,N.isString)(e)?new b.Div({children:[e]}):e));this._attribution.elements=e}async _build_renderers(){this.computed_renderers=[...this._compute_renderers()];const e=await(0,v.build_views)(this.renderer_views,this.computed_renderers,{parent:this});return this._update_attribution(),e}async _update_renderers(){const{created:e}=await this._build_renderers(),t=new Set(e);for(const e of this.renderer_views.values())e.el.remove();for(const e of this.renderer_views.values()){const i=t.has(e),s=e.rendering_target();i?e.render_to(s):s.append(e.el)}this.r_after_render()}async build_renderer_views(){await this._build_renderers()}async build_tool_views(){const e=(0,M.flat_map)(this.model.toolbar.tools,(e=>e instanceof h.ToolProxy?e.tools:[e])),{created:t}=await(0,v.build_views)(this.tool_views,[...e],{parent:this});t.map((e=>this.canvas_view.ui_event_bus.register_tool(e)))}connect_signals(){super.connect_signals();const{x_range:e,y_range:t,x_scale:i,y_scale:s,extra_x_ranges:n,extra_y_ranges:a,extra_x_scales:r,extra_y_scales:o,aspect_scale:l,match_aspect:_}=this.model.properties;this.on_change([e,t,i,s,n,a,r,o,l,_],(()=>{const{x_range:e,y_range:t,x_scale:i,y_scale:s,extra_x_ranges:n,extra_y_ranges:a,extra_x_scales:r,extra_y_scales:o,aspect_scale:l,match_aspect:_}=this.model;this._frame.setv({x_range:e,y_range:t,x_scale:i,y_scale:s,extra_x_ranges:n,extra_y_ranges:a,extra_x_scales:r,extra_y_scales:o,aspect_scale:l,match_aspect:_})}));const{above:h,below:d,left:c,right:u,center:p,renderers:g}=this.model.properties,f=[h,d,c,u,p];this.on_change(g,(async()=>{await this._update_renderers()})),this.on_change(f,(async()=>{await this._update_renderers(),this.invalidate_layout()})),this.connect(this.model.toolbar.properties.tools.change,(async()=>{await this.build_tool_views(),await this._update_renderers()}));const{x_ranges:b,y_ranges:w}=this.frame;for(const[,e]of b)this.connect(e.change,(()=>{this.request_repaint()}));for(const[,e]of w)this.connect(e.change,(()=>{this.request_repaint()}));this.connect(this.model.change,(()=>this.request_repaint())),this.connect(this.model.reset,(()=>this.reset()));const{toolbar_location:v}=this.model.properties;this.on_change(v,(async()=>{const{toolbar_location:e}=this.model;if(null!=this._toolbar)null!=e?this._toolbar.toolbar.location=e:(this._toolbar=void 0,await this._update_renderers());else if(null!=e){const{toolbar:t,toolbar_inner:i}=this.model;this._toolbar=new m.ToolbarPanel({toolbar:t}),t.location=e,t.inner=i,await this._update_renderers()}this.invalidate_layout()}));const{hold_render:y}=this.model.properties;this.on_change(y,(()=>{this.model.hold_render||this.request_repaint()}))}has_finished(){if(!super.has_finished())return!1;if(this.model.visible)for(const[,e]of this.renderer_views)if(!e.has_finished())return!1;return!0}_after_layout(){super._after_layout(),this.unpause(!0);const e=this.layout.left_panel.bbox,t=this.layout.right_panel.bbox,i=this.layout.center_panel.bbox,s=this.layout.top_panel.bbox,n=this.layout.bottom_panel.bbox,{bbox:a}=this,r=s.bottom,o=a.height-n.top,l=e.right,_=a.width-t.left;this.canvas.style.replace(`\\n .bk-layer.bk-events {\\n display: grid;\\n grid-template-areas:\\n \". above . \"\\n \"left center right\"\\n \". below . \";\\n grid-template-rows: ${(0,L.px)(r)} ${(0,L.px)(i.height)} ${(0,L.px)(o)};\\n grid-template-columns: ${(0,L.px)(l)} ${(0,L.px)(i.width)} ${(0,L.px)(_)};\\n }\\n `);for(const[,e]of this.renderer_views)e instanceof c.AnnotationView&&e.after_layout?.();this.model.setv({inner_width:Math.round(this.frame.bbox.width),inner_height:Math.round(this.frame.bbox.height),outer_width:Math.round(this.bbox.width),outer_height:Math.round(this.bbox.height)},{no_change:!0}),this.model.match_aspect&&(this.pause(),this._range_manager.update_dataranges(),this.unpause(!0)),this._outer_bbox.equals(this.bbox)||(this.canvas_view.resize(),this._outer_bbox=this.bbox,this._invalidate_all=!0,this._needs_paint=!0);const{inner_bbox:h}=this.layout;this._inner_bbox.equals(h)||(this._inner_bbox=h,this._invalidate_all=!0,this._needs_paint=!0),this._needs_paint&&this.paint()}render(){super.render();for(const e of this.computed_renderer_views){const t=e.rendering_target();e.render_to(t)}}repaint(){this._invalidate_layout_if_needed(),this.paint()}paint(){if(!this.is_paused&&!this.model.hold_render){if(this.is_displayed)x.logger.trace(`${this.toString()}.paint()`),this._actual_paint();else for(const e of this.computed_renderer_views)e.force_finished();this._needs_notify&&(this._needs_notify=!1,this.notify_finished())}}_actual_paint(){x.logger.trace(`${this.toString()}._actual_paint ${this._render_count} start`);const{document:e}=this.model;if(null!=e){const t=e.interactive_duration();t>=0&&t{e.interactive_duration()>this.model.lod_timeout&&e.interactive_stop(),this.request_repaint()}),this.model.lod_timeout):e.interactive_stop()}this._range_manager.invalidate_dataranges&&(this._range_manager.update_dataranges(),this._invalidate_layout_if_needed());let t=!1,i=!1;if(this._invalidate_all)t=!0,i=!0;else for(const e of this._invalidated_painters){const{level:s}=e.model;if(\"overlay\"!=s?t=!0:i=!0,t&&i)break}this._invalidated_painters.clear(),this._invalidate_all=!1;const s=[this.frame.bbox.left,this.frame.bbox.top,this.frame.bbox.width,this.frame.bbox.height],{primary:n,overlays:a}=this.canvas_view;t&&(n.prepare(),this.canvas_view.prepare_webgl(s),this._paint_empty(n.ctx,s),this._paint_outline(n.ctx,s),this._paint_levels(n.ctx,\"image\",s,!0),this._paint_levels(n.ctx,\"underlay\",s,!0),this._paint_levels(n.ctx,\"glyph\",s,!0),this._paint_levels(n.ctx,\"guide\",s,!1),this._paint_levels(n.ctx,\"annotation\",s,!1),n.finish()),(i||H.settings.wireframe)&&(a.prepare(),this._paint_levels(a.ctx,\"overlay\",s,!1),H.settings.wireframe&&this.paint_layout(a.ctx,this.layout),a.finish()),null==this._initial_state.range&&(this._initial_state.range=this._range_manager.compute_initial()??void 0);for(const e of this.element_views)e.reposition();this._needs_paint=!1,this.repainted.emit(),x.logger.trace(`${this.toString()}._actual_paint ${this._render_count} end`),this._render_count++}_paint_levels(e,t,i,s){for(const n of this.computed_renderer_views)n.model.level==t&&(e.save(),(s||n.needs_clip)&&(e.beginPath(),e.rect(...i),e.clip()),n.paint(),e.restore(),n.has_webgl&&this.canvas_view.blit_webgl(e))}paint_layout(e,t){const{x:i,y:s,width:n,height:a}=t.bbox;e.strokeStyle=\"blue\",e.strokeRect(i,s,n,a);for(const n of t)e.save(),t.absolute||e.translate(i,s),this.paint_layout(e,n),e.restore()}_paint_empty(e,t){const[i,s,n,a]=[0,0,this.bbox.width,this.bbox.height],[r,o,l,_]=t;this.visuals.border_fill.doit&&(e.save(),e.beginPath(),e.rect(i,s,n,a),e.rect(r,o,l,_),e.clip(\"evenodd\"),e.beginPath(),e.rect(i,s,n,a),this.visuals.border_fill.apply(e),e.restore()),this.visuals.background_fill.doit&&(this.visuals.background_fill.set_value(e),e.fillRect(r,o,l,_))}_paint_outline(e,t){if(this.visuals.outline_line.doit){e.save(),this.visuals.outline_line.set_value(e);let[i,s,n,a]=t;i+n==this.bbox.width&&(n-=1),s+a==this.bbox.height&&(a-=1),e.strokeRect(i,s,n,a),e.restore()}}export(e=\"auto\",t=!0){const i=(()=>{switch(e){case\"auto\":return this.canvas_view.model.output_backend;case\"png\":return\"canvas\";case\"svg\":return\"svg\"}})(),s=new q.CanvasLayer(i,t),{width:n,height:a}=this.bbox;if(s.resize(n,a),0!=n&&0!=a){const{canvas:e}=this.canvas_view.compose();s.ctx.drawImage(e,0,0)}return s}resolve_frame(){return this.frame}resolve_canvas(){return this.canvas}resolve_plot(){return this}resolve_xy(e){const{x:t,y:i}=e,s=this.frame.x_scale.compute(t),n=this.frame.y_scale.compute(i);return this.frame.bbox.contains(s,n)?{x:s,y:n}:{x:NaN,y:NaN}}resolve_indexed(e){const{index:t,renderer:i}=e,s=this.views.find_one(i);if(null!=s&&s.has_finished()){const[e,i]=s.glyph.scenterxy(t,NaN,NaN);if(this.frame.bbox.contains(e,i))return{x:e,y:i}}return{x:NaN,y:NaN}}notify_about(e){if(this._messages.has(e))return;const t=new b.Div({children:[e]}),i=setTimeout((()=>{this._messages.delete(e),this._notifications.elements=this._notifications.elements.filter((e=>e!=t))}),2e3);this._messages.set(e,i),this._notifications.elements=[...this._notifications.elements,t],x.logger.info(e)}}i.PlotView=j,j.__name__=\"PlotView\"},\n function _(e,t,s,o,n){var i;o();const r=e(1),a=e(400),l=e(60),h=e(59),c=e(185),p=r.__importStar(e(186)),_=e(63),d=r.__importStar(e(436));class u extends a.PaneView{stylesheets(){return[...super.stylesheets(),d.default]}connect_signals(){super.connect_signals();const{position:e,anchor:t,width:s,height:o,elements:n}=this.model.properties;this.on_change([t,s,o,n],(()=>this.reposition())),this.on_transitive_change(e,(()=>this.reposition()))}reposition(e){super.reposition(e);const{position:t,visible:s,anchor:o,elements:n}=this.model;if(0==e||!s||0==n.length)return void this.el.remove();const{x:i,y:r}=this.resolve_as_xy(t);if(!isFinite(i+r))return void this.el.remove();const a=this.parent?.el??document.body,l=a.shadowRoot??a;this.el.isConnected||l.append(this.el),this.el.style.left=(0,_.px)(i),this.el.style.top=(0,_.px)(r);const h=p.anchor(o);this.el.style.transform=`translate(${-100*h.x}%, ${-100*h.y}%)`}}s.PanelView=u,u.__name__=\"PanelView\";class m extends a.Pane{constructor(e){super(e)}}s.Panel=m,i=m,m.__name__=\"Panel\",i.prototype.default_view=u,i.define((({Ref:e,Or:t,Auto:s,Int:o})=>({position:[e(l.Coordinate)],anchor:[c.Anchor,\"top_left\"],width:[t(s,o,e(h.Node)),\"auto\"],height:[t(s,o,e(h.Node)),\"auto\"]})))},\n function _(o,t,i,n,u){n(),i.default=\":host{position:absolute;background-color:white;}\"},\n function _(e,a,_,n,t){var l,s,i,m;n();const w=e(402);class o extends w.DOMElementView{}_.SpanView=o,o.__name__=\"SpanView\",o.tag_name=\"span\";class d extends w.DOMElement{}_.Span=d,l=d,d.__name__=\"Span\",l.prototype.default_view=o;class p extends w.DOMElementView{}_.DivView=p,p.__name__=\"DivView\",p.tag_name=\"div\";class D extends w.DOMElement{}_.Div=D,s=D,D.__name__=\"Div\",s.prototype.default_view=p;class V extends w.DOMElementView{}_.TableView=V,V.__name__=\"TableView\",V.tag_name=\"table\";class c extends w.DOMElement{}_.Table=c,i=c,c.__name__=\"Table\",i.prototype.default_view=V;class v extends w.DOMElementView{}_.TableRowView=v,v.__name__=\"TableRowView\",v.tag_name=\"tr\";class b extends w.DOMElement{}_.TableRow=b,m=b,b.__name__=\"TableRow\",m.prototype.default_view=v},\n function _(n,t,e,l,u){l(),e.throttle=function(n,t){let e,l=null,u=null,o=0,i=!1;const r=function(){return new Promise(((r,c)=>{e=r;const a=function(){o=Date.now(),l=null,u=null,i=!1;try{n(),r()}catch(n){c(n)}},m=Date.now(),s=t-(m-o);s<=0&&!i?(null!=l&&clearTimeout(l),i=!0,u=requestAnimationFrame(a)):null!=l||i?r():l=setTimeout((()=>u=requestAnimationFrame(a)),s)}))};return r.stop=function(){null!=l&&clearTimeout(l),null!=u&&cancelAnimationFrame(u),e()},r}},\n function _(t,n,e,a,s){a();const o=t(106),r=t(19);class l{constructor(t){this.invalidate_dataranges=!0,this.parent=t}get frame(){return this.parent.frame}update(t,n={}){const e=new Map;for(const[n,a]of t.xrs)e.set(n,a);for(const[n,a]of t.yrs)e.set(n,a);n.scrolling&&this._update_ranges_together(e),this._update_ranges_individually(e,n)}ranges(){const t=new Set,n=new Set;for(const n of this.frame.x_ranges.values())t.add(n);for(const t of this.frame.y_ranges.values())n.add(t);for(const e of this.parent.model.data_renderers){const{coordinates:a}=e;null!=a&&(t.add(a.x_source),n.add(a.y_source))}return{x_ranges:[...t],y_ranges:[...n]}}reset(){const{x_ranges:t,y_ranges:n}=this.ranges();for(const n of t)n.reset();for(const t of n)t.reset();this.update_dataranges()}_update_dataranges(t){const n=new Map,e=new Map;let a=!1;for(const[,n]of t.x_ranges)n instanceof o.DataRange1d&&\"log\"==n.scale_hint&&(a=!0);for(const[,n]of t.y_ranges)n instanceof o.DataRange1d&&\"log\"==n.scale_hint&&(a=!0);for(const t of this.parent.auto_ranged_renderers){const s=t.bounds();if(n.set(t.model,s),a){const n=t.log_bounds();e.set(t.model,n)}}let s=!1,l=!1;const i=t.x_target.span,d=t.y_target.span;let _;!1!==this.parent.model.match_aspect&&0!=i&&0!=d&&(_=1/this.parent.model.aspect_scale*(i/d));for(const[,a]of t.x_ranges){if(a instanceof o.DataRange1d){const t=\"log\"==a.scale_hint?e:n;a.update(t,0,this.parent,_),null!=a.follow&&(s=!0)}null!=a.bounds&&(l=!0)}for(const[,a]of t.y_ranges){if(a instanceof o.DataRange1d){const t=\"log\"==a.scale_hint?e:n;a.update(t,1,this.parent,_),null!=a.follow&&(s=!0)}null!=a.bounds&&(l=!0)}if(s&&l){r.logger.warn(\"Follow enabled so bounds are unset.\");for(const[,n]of t.x_ranges)n.bounds=null;for(const[,n]of t.y_ranges)n.bounds=null}}update_dataranges(){this._update_dataranges(this.frame);for(const t of this.parent.auto_ranged_renderers){const{coordinates:n}=t.model;null!=n&&this._update_dataranges(n)}null!=this.compute_initial()&&(this.invalidate_dataranges=!1)}compute_initial(){let t=!0;const{x_ranges:n,y_ranges:e}=this.frame,a=new Map,s=new Map;for(const[,e]of n){const{start:n,end:s}=e;if(isNaN(n+s)){t=!1;break}a.set(e,{start:n,end:s})}if(t)for(const[,n]of e){const{start:e,end:a}=n;if(isNaN(e+a)){t=!1;break}s.set(n,{start:e,end:a})}return t?{xrs:a,yrs:s}:(r.logger.warn(\"could not set initial ranges\"),null)}_update_ranges_together(t){let n=1;for(const[e,a]of t)n=Math.min(n,this._get_weight_to_constrain_interval(e,a));if(n<1)for(const[e,a]of t)a.start=n*a.start+(1-n)*e.start,a.end=n*a.end+(1-n)*e.end}_update_ranges_individually(t,n={}){const e=n.panning??!1,a=n.scrolling??!1,s=n.maintain_focus??!1;let o=!1;for(const[n,s]of t){if(!a){const t=this._get_weight_to_constrain_interval(n,s);t<1&&(s.start=t*s.start+(1-t)*n.start,s.end=t*s.end+(1-t)*n.end)}if(null!=n.bounds){const[t,r]=n.computed_bounds,l=Math.abs(s.end-s.start);n.is_reversed?(t>s.end&&(o=!0,s.end=t,(e||a)&&(s.start=t+l)),rs.start&&(o=!0,s.start=t,(e||a)&&(s.end=t+l)),r0&&r0&&r>a&&(s=(a-o)/(r-o)),s=Math.max(0,Math.min(1,s))}return s}}e.RangeManager=l,l.__name__=\"RangeManager\"},\n function _(t,i,e,s,n){s();const h=t(15);class a{constructor(t,i){this.history=[],this.index=-1,this.parent=t,this.initial_state=i,this.changed=new h.Signal0(this.parent,\"state_changed\")}_do_state_change(t){const i=t in this.history?this.history[t].state:this.initial_state;return null!=i.range&&this.parent.update_range(i.range),null!=i.selection&&this.parent.update_selection(i.selection),i}peek(){return this.can_undo?this.history[this.index]:null}push(t,i){const{history:e,index:s}=this,n=s in e?e[s].state:{},h={...this.initial_state,...n,...i};this.history=this.history.slice(0,this.index+1),this.history.push({type:t,state:h}),this.index=this.history.length-1,this.changed.emit()}clear(){this.history=[],this.index=-1,this.changed.emit()}undo(){if(this.can_undo){this.index-=1;const t=this._do_state_change(this.index);return this.changed.emit(),t}return null}redo(){if(this.can_redo){this.index+=1;const t=this._do_state_change(this.index);return this.changed.emit(),t}return null}get can_undo(){return this.index>=0}get can_redo(){return this.index *{overflow:hidden;text-overflow:ellipsis;text-align:right;}a{color:black;}\"},\n function _(t,e,s,i,o){i();const a=t(19),n=t(15),p=t(63),_=t(134),l=t(434);const h=new n.Signal0({},\"gmaps_ready\");class m extends l.PlotView{initialize(){super.initialize(),this._tiles_loaded=!1,this.zoom_count=0;const{zoom:t,lat:e,lng:s}=this.model.map_options;this.initial_zoom=t,this.initial_lat=e,this.initial_lng=s;const i=new TextDecoder(\"utf-8\");if(this._api_key=i.decode(this.model.api_key),\"\"==this._api_key){const t=\"https://developers.google.com/maps/documentation/javascript/get-api-key\";a.logger.error(`api_key is required. See ${t} for more information on how to obtain your own.`)}}async lazy_initialize(){if(await super.lazy_initialize(),this.map_el=(0,p.div)({style:{position:\"absolute\"}}),this.canvas_view.underlays_el.append(this.map_el),\"undefined\"==typeof google||void 0===google.maps){if(void 0===window._bokeh_gmaps_callback){const{api_version:t}=this.model;!function(t,e){window._bokeh_gmaps_callback=()=>h.emit();const s=encodeURIComponent,i=document.createElement(\"script\");i.type=\"text/javascript\",i.src=`https://maps.googleapis.com/maps/api/js?v=${s(e)}&key=${s(t)}&callback=_bokeh_gmaps_callback&loading=async`,document.body.appendChild(i)}(this._api_key,t)}h.connect((()=>{this._build_map(),this.request_repaint()}))}else this._build_map()}remove(){this.map_el.remove(),super.remove()}update_range(t,e){if(null==t)this.map.setCenter({lat:this.initial_lat,lng:this.initial_lng}),this.map.setOptions({zoom:this.initial_zoom}),super.reset_range();else if(null!=t.sdx||null!=t.sdy)this.map.panBy(t.sdx??0,t.sdy??0),super.update_range(t,e);else if(null!=t.factor){if(10!==this.zoom_count)return void(this.zoom_count+=1);this.zoom_count=0,this.pause(),super.update_range(t,e);const s=t.factor<0?-1:1,i=this.map.getZoom(),o=this.map.getBounds();if(null!=i&&null!=o){const t=i+s;if(t>=2){this.map.setZoom(t);const[e,s]=this._get_projected_bounds(o);s-e<0&&this.map.setZoom(i)}}this.unpause()}this._set_bokeh_ranges()}_build_map(){const{maps:t}=google;this.map_types={satellite:t.MapTypeId.SATELLITE,terrain:t.MapTypeId.TERRAIN,roadmap:t.MapTypeId.ROADMAP,hybrid:t.MapTypeId.HYBRID};const e=this.model.map_options,s={center:new t.LatLng(e.lat,e.lng),zoom:e.zoom,disableDefaultUI:!0,mapTypeId:this.map_types[e.map_type],scaleControl:e.scale_control,tilt:e.tilt};null!=e.styles&&(s.styles=JSON.parse(e.styles)),this.map=new t.Map(this.map_el,s),t.event.addListener(this.map,\"idle\",(()=>this._set_bokeh_ranges())),t.event.addListener(this.map,\"bounds_changed\",(()=>this._set_bokeh_ranges())),t.event.addListenerOnce(this.map,\"tilesloaded\",(()=>this._render_finished())),this.connect(this.model.properties.map_options.change,(()=>this._update_options())),this.connect(this.model.map_options.properties.styles.change,(()=>this._update_styling())),this.connect(this.model.map_options.properties.lat.change,(()=>this._update_center(\"lat\"))),this.connect(this.model.map_options.properties.lng.change,(()=>this._update_center(\"lng\"))),this.connect(this.model.map_options.properties.zoom.change,(()=>this._update_zoom())),this.connect(this.model.map_options.properties.map_type.change,(()=>this._update_map_type())),this.connect(this.model.map_options.properties.scale_control.change,(()=>this._update_scale_control())),this.connect(this.model.map_options.properties.tilt.change,(()=>this._update_tilt()))}_render_finished(){this._tiles_loaded=!0,this.notify_finished()}has_finished(){return super.has_finished()&&!0===this._tiles_loaded}_get_latlon_bounds(t){const e=t.getNorthEast(),s=t.getSouthWest();return[s.lng(),e.lng(),s.lat(),e.lat()]}_get_projected_bounds(t){const[e,s,i,o]=this._get_latlon_bounds(t),[a,n]=_.wgs84_mercator.compute(e,i),[p,l]=_.wgs84_mercator.compute(s,o);return[a,p,n,l]}_set_bokeh_ranges(){const t=this.map.getBounds();if(null!=t){const[e,s,i,o]=this._get_projected_bounds(t);this.frame.x_range.setv({start:e,end:s}),this.frame.y_range.setv({start:i,end:o})}}_update_center(t){const e=this.map.getCenter()?.toJSON();null!=e&&(e[t]=this.model.map_options[t],this.map.setCenter(e),this._set_bokeh_ranges())}_update_map_type(){this.map.setOptions({mapTypeId:this.map_types[this.model.map_options.map_type]})}_update_scale_control(){this.map.setOptions({scaleControl:this.model.map_options.scale_control})}_update_tilt(){this.map.setOptions({tilt:this.model.map_options.tilt})}_update_options(){this._update_styling(),this._update_center(\"lat\"),this._update_center(\"lng\"),this._update_zoom(),this._update_map_type()}_update_styling(){const{styles:t}=this.model.map_options;this.map.setOptions({styles:null!=t?JSON.parse(t):null})}_update_zoom(){this.map.setOptions({zoom:this.model.map_options.zoom}),this._set_bokeh_ranges()}_after_layout(){super._after_layout();const{left:t,top:e,width:s,height:i}=this.frame.bbox;this.map_el.style.top=`${e}px`,this.map_el.style.left=`${t}px`,this.map_el.style.width=`${s}px`,this.map_el.style.height=`${i}px`}}s.GMapPlotView=m,m.__name__=\"GMapPlotView\"},\n function _(e,a,t,_,p){var s;_();const n=e(432);class o extends n.GMapPlotView{}t.GMapView=o,o.__name__=\"GMapView\";class c extends n.GMapPlot{constructor(e){super(e)}}t.GMap=c,s=c,c.__name__=\"GMap\",s.prototype.default_view=o},\n function _(i,o,t,e,s){var l;e();const n=i(399),a=i(404),r=i(185),c=i(291),_=i(302),d=i(56),h=i(20);class u extends n.LayoutDOMView{constructor(){super(...arguments),this._tool_views=new Map}get toolbar_view(){return this.child_views.find((i=>i.model==this.model.toolbar))}get grid_box_view(){return this.child_views.find((i=>i.model==this._grid_box))}_update_location(){const i=this.model.toolbar_location;null==i?this.model.toolbar.visible=!1:this.model.toolbar.setv({visible:!0,location:i})}initialize(){super.initialize(),this._update_location();const{children:i,rows:o,cols:t,spacing:e}=this.model;this._grid_box=new a.GridBox({children:i,rows:o,cols:t,spacing:e,sizing_mode:\"inherit\"})}async lazy_initialize(){await super.lazy_initialize(),await this.build_tool_views()}connect_signals(){super.connect_signals();const{toolbar:i,toolbar_location:o,children:t,rows:e,cols:s,spacing:l}=this.model.properties;this.on_change(o,(async()=>{this._update_location(),this.invalidate_layout()})),this.on_change([i,t,e,s,l],(async()=>{await this.update_children()})),this.on_change(this.model.toolbar.properties.tools,(async()=>{await this.build_tool_views()})),this.mouseenter.connect((()=>{this.toolbar_view.set_visibility(!0)})),this.mouseleave.connect((()=>{this.toolbar_view.set_visibility(!1)}))}remove(){(0,d.remove_views)(this._tool_views),super.remove()}async build_tool_views(){const i=this.model.toolbar.tools.filter((i=>i instanceof _.ActionTool));await(0,d.build_views)(this._tool_views,i,{parent:this})}*children(){yield*super.children(),yield*this._tool_views.values()}get child_models(){return[this.model.toolbar,this._grid_box]}_intrinsic_display(){return{inner:this.model.flow_mode,outer:\"flex\"}}_update_layout(){super._update_layout();const{location:i}=this.model.toolbar,o=(()=>{switch(i){case\"above\":return\"column\";case\"below\":return\"column-reverse\";case\"left\":return\"row\";case\"right\":return\"row-reverse\"}})();this.style.append(\":host\",{flex_direction:o})}}t.GridPlotView=u,u.__name__=\"GridPlotView\";class w extends n.LayoutDOM{constructor(i){super(i)}}t.GridPlot=w,l=w,w.__name__=\"GridPlot\",l.prototype.default_view=u,l.define((({List:i,Ref:o,Nullable:t})=>({toolbar:[o(c.Toolbar),()=>new c.Toolbar],toolbar_location:[t(h.Location),\"above\"],children:[i((0,r.GridChild)(n.LayoutDOM)),[]],rows:[t(r.TracksSizing),null],cols:[t(r.TracksSizing),null],spacing:[r.GridSpacing,0]})))},\n function _(e,t,_,i,r){var s;i();const n=e(433);class o extends n.PlotView{}_.FigureView=o,o.__name__=\"FigureView\";class u extends n.Plot{constructor(e){super(e)}}_.Figure=u,s=u,u.__name__=\"Figure\",s.prototype.default_view=o},\n function _(t,_,n,o,r){o();t(1).__exportStar(t(193),n)},\n function _(l,r,i,a,e){a(),e(\"ParkMillerLCG\",l(449).ParkMillerLCG)},\n function _(e,n,r,a,t){var l;a();const o=e(428),s=e(115);class d extends o.RandomGenerator{constructor(e){super(e)}generator(){return new s.LCGRandom(this.seed??Date.now())}}r.ParkMillerLCG=d,l=d,d.__name__=\"ParkMillerLCG\",l.define((({Int:e,Nullable:n})=>({seed:[n(e),null]})))},\n function _(e,r,n,d,R){d(),R(\"ContourRenderer\",e(451).ContourRenderer),R(\"GlyphRenderer\",e(217).GlyphRenderer),R(\"GraphRenderer\",e(452).GraphRenderer),R(\"GuideRenderer\",e(190).GuideRenderer),R(\"Renderer\",e(83).Renderer),R(\"RendererGroup\",e(124).RendererGroup)},\n function _(e,i,r,t,l){var n;t();const _=e(218),s=e(217),a=e(56);class d extends _.DataRendererView{*children(){yield*super.children(),yield this.fill_view,yield this.line_view}get glyph_view(){return this.fill_view.glyph.data_size>0?this.fill_view.glyph:this.line_view.glyph}async lazy_initialize(){await super.lazy_initialize();const{parent:e}=this,{fill_renderer:i,line_renderer:r}=this.model;this.fill_view=await(0,a.build_view)(i,{parent:e}),this.line_view=await(0,a.build_view)(r,{parent:e})}remove(){this.fill_view.remove(),this.line_view.remove(),super.remove()}_paint(){this.fill_view.paint(),this.line_view.paint()}hit_test(e){return this.fill_view.hit_test(e)}}r.ContourRendererView=d,d.__name__=\"ContourRendererView\";class h extends _.DataRenderer{constructor(e){super(e)}get_selection_manager(){return this.fill_renderer.data_source.selection_manager}}r.ContourRenderer=h,n=h,h.__name__=\"ContourRenderer\",n.prototype.default_view=d,n.define((({List:e,Float:i,Ref:r})=>({fill_renderer:[r(s.GlyphRenderer)],line_renderer:[r(s.GlyphRenderer)],levels:[e(i),[]]})))},\n function _(e,t,i,r,n){var s;r();const o=e(218),a=e(217),l=e(392),d=e(391),_=e(56),p=e(19),h=e(220),y=e(370),c=e(372);class g extends o.DataRendererView{get glyph_view(){return this.node_view.glyph}*children(){yield*super.children(),yield this.edge_view,yield this.node_view}async lazy_initialize(){await super.lazy_initialize(),this.apply_coordinates();const{parent:e}=this,{edge_renderer:t,node_renderer:i}=this.model;this.edge_view=await(0,_.build_view)(t,{parent:e}),this.node_view=await(0,_.build_view)(i,{parent:e})}connect_signals(){super.connect_signals(),this.connect(this.model.layout_provider.change,(async()=>{this.apply_coordinates(),await this.edge_view.set_data(),await this.node_view.set_data(),this.request_paint()}))}apply_coordinates(){const{edge_renderer:e,node_renderer:t}=this.model,i=this.model.layout_provider.edge_coordinates,r=this.model.layout_provider.node_coordinates,n={expr:i.x},s={expr:i.y},o={expr:r.x},a={expr:r.y},l=[e.glyph,e.hover_glyph,e.muted_glyph,e.selection_glyph,e.nonselection_glyph],d=[t.glyph,t.hover_glyph,t.muted_glyph,t.selection_glyph,t.nonselection_glyph];for(const e of l)null!=e&&\"auto\"!=e&&(e instanceof y.MultiLine||e instanceof c.Patches?(e.properties.xs.internal=!0,e.properties.ys.internal=!0,e.xs=n,e.ys=s):p.logger.warn(`${this}.edge_renderer only supports MultiLine and Patches glyphs`));for(const e of d)null!=e&&\"auto\"!=e&&(e instanceof h.XYGlyph?(e.properties.x.internal=!0,e.properties.y.internal=!0,e.x=o,e.y=a):p.logger.warn(`${this}.node_renderer only supports XY glyphs`))}remove(){this.edge_view.remove(),this.node_view.remove(),super.remove()}_paint(){this.edge_view.paint(),this.node_view.paint()}get has_webgl(){return this.edge_view.has_webgl||this.node_view.has_webgl}hit_test(e){return this.model.inspection_policy.hit_test(e,this)}}i.GraphRendererView=g,g.__name__=\"GraphRendererView\";class w extends o.DataRenderer{constructor(e){super(e)}get_selection_manager(){return this.node_renderer.data_source.selection_manager}}i.GraphRenderer=w,s=w,w.__name__=\"GraphRenderer\",s.prototype.default_view=g,s.define((({Ref:e})=>({layout_provider:[e(l.LayoutProvider)],node_renderer:[e(a.GlyphRenderer)],edge_renderer:[e(a.GlyphRenderer)],selection_policy:[e(d.GraphHitTestPolicy),()=>new d.NodesOnly],inspection_policy:[e(d.GraphHitTestPolicy),()=>new d.NodesOnly]})))},\n function _(e,t,n,o,c){o();e(1).__exportStar(e(131),n),c(\"Selection\",e(130).Selection)},\n function _(y,B,a,s,C){s(),C(\"ByID\",y(455).ByID),C(\"ByClass\",y(456).ByClass),C(\"ByCSS\",y(457).ByCSS),C(\"ByXPath\",y(458).ByXPath)},\n function _(e,n,r,t,c){t();const o=e(414);class s extends o.Selector{constructor(e){super(e)}find_one(e){return e.querySelector(`#${this.query}`)}}r.ByID=s,s.__name__=\"ByID\"},\n function _(e,s,n,r,t){r();const c=e(414);class o extends c.Selector{constructor(e){super(e)}find_one(e){return e.querySelector(`.${this.query}`)}}n.ByClass=o,o.__name__=\"ByClass\"},\n function _(e,n,r,t,c){t();const o=e(414);class s extends o.Selector{constructor(e){super(e)}find_one(e){return e.querySelector(this.query)}}r.ByCSS=s,s.__name__=\"ByCSS\"},\n function _(e,t,n,r,a){r();const c=e(414);class o extends c.Selector{constructor(e){super(e)}find_one(e){return document.evaluate(this.query,e).iterateNext()}}n.ByXPath=o,o.__name__=\"ByXPath\"},\n function _(a,e,S,o,r){o(),r(\"ServerSentDataSource\",a(460).ServerSentDataSource),r(\"AjaxDataSource\",a(462).AjaxDataSource),r(\"ColumnDataSource\",a(133).ColumnDataSource),r(\"ColumnarDataSource\",a(128).ColumnarDataSource),r(\"CDSView\",a(236).CDSView),r(\"DataSource\",a(132).DataSource),r(\"GeoJSONDataSource\",a(463).GeoJSONDataSource),r(\"WebDataSource\",a(461).WebDataSource)},\n function _(e,t,a,i,s){i();const n=e(461);class r extends n.WebDataSource{constructor(e){super(e),this.initialized=!1}setup(){if(!this.initialized){this.initialized=!0;new EventSource(this.data_url).onmessage=async e=>{await this.load_data(JSON.parse(e.data),this.mode,this.max_size??void 0)}}}}a.ServerSentDataSource=r,r.__name__=\"ServerSentDataSource\"},\n function _(t,e,a,n,s){var r;n();const c=t(133),l=t(20),i=t(50),o=t(9);class u extends c.ColumnDataSource{constructor(t){super(t)}get_column(t){return(0,o.dict)(this.data).get(t)??[]}get_length(){return super.get_length()??0}initialize(){super.initialize(),this.setup()}async load_data(t,e,a){const{adapter:n}=this;let s;switch(s=null!=n?await(0,i.execute)(n,this,{response:t}):t,e){case\"replace\":break;case\"append\":{const t=(0,o.dict)(this.data),e=(0,o.dict)(s);for(const n of this.columns()){const s=Array.from(t.get(n)??[]),r=Array.from(e.get(n)??[]),c=s.concat(r);e.set(n,null!=a?c.slice(-a):c)}break}}this.data=s}}a.WebDataSource=u,r=u,u.__name__=\"WebDataSource\",r.define((({Any:t,Int:e,Str:a,Nullable:n})=>({max_size:[n(e),null],mode:[l.UpdateMode,\"replace\"],adapter:[n(t),null],data_url:[a]})))},\n function _(t,e,i,s,a){var r;s();const n=t(461),o=t(20),d=t(19),l=t(9);class h extends n.WebDataSource{constructor(t){super(t)}destroy(){null!=this.interval&&clearInterval(this.interval),super.destroy()}setup(){if(!0!==this.initialized&&(this.initialized=!0,this.get_data(this.mode),null!=this.polling_interval)){const t=()=>this.get_data(this.mode,this.max_size,this.if_modified);this.interval=setInterval(t,this.polling_interval)}}get_data(t,e=null,i=!1){const s=this.prepare_request();s.addEventListener(\"load\",(()=>this.do_load(s,t,e??void 0))),s.addEventListener(\"error\",(()=>this.do_error(s))),i&&null!=this.last_fetch_time&&s.setRequestHeader(\"If-Modified-Since\",this.last_fetch_time.toUTCString()),s.send()}prepare_request(){const t=new XMLHttpRequest;t.open(this.method,this.data_url,!0),t.withCredentials=!1,t.setRequestHeader(\"Content-Type\",this.content_type);for(const[e,i]of(0,l.entries)(this.http_headers))t.setRequestHeader(e,i);return t}async do_load(t,e,i){if(200==t.status){const s=JSON.parse(t.responseText);this.last_fetch_time=new Date,await this.load_data(s,e,i)}}do_error(t){d.logger.error(`Failed to fetch JSON from ${this.data_url} with code ${t.status}`)}}i.AjaxDataSource=h,r=h,h.__name__=\"AjaxDataSource\",r.define((({Bool:t,Int:e,Str:i,Dict:s,Nullable:a})=>({polling_interval:[a(e),null],content_type:[i,\"application/json\"],http_headers:[s(i),{}],method:[o.HTTPMethod,\"POST\"],if_modified:[t,!1]})))},\n function _(e,t,o,r,n){var s;r();const a=e(128),i=e(19),c=e(8),l=e(10),_=e(9);function g(e){return null!=e?e:NaN}class u extends a.ColumnarDataSource{constructor(e){super(e)}initialize(){super.initialize(),this._update_data()}connect_signals(){super.connect_signals(),this.connect(this.properties.geojson.change,(()=>this._update_data()))}_update_data(){this.data=this.geojson_to_column_data()}_get_new_list_array(e){return(0,l.range)(0,e).map((e=>[]))}_get_new_nan_array(e){return(0,l.range)(0,e).map((e=>NaN))}_add_properties(e,t,o,r){const n=e.properties??{},s=(0,_.dict)(t);for(const[e,a]of(0,_.dict)(n))s.has(e)||(t[e]=this._get_new_nan_array(r)),t[e][o]=g(a)}_add_geometry(e,t,o){function r(e,t){return e.concat([[NaN,NaN,NaN]]).concat(t)}switch(e.type){case\"Point\":{const[r,n,s]=e.coordinates;t.x[o]=r,t.y[o]=n,t.z[o]=g(s);break}case\"LineString\":{const{coordinates:r}=e;for(let e=0;e1&&i.logger.warn(\"Bokeh does not support Polygons with holes in, only exterior ring used.\");const r=e.coordinates[0];for(let e=0;e1&&i.logger.warn(\"Bokeh does not support Polygons with holes in, only exterior ring used.\"),n.push(t[0]);const s=n.reduce(r);for(let e=0;e({geojson:[e]}))),s.internal((({Unknown:e,Dict:t,Arrayable:o})=>({data:[t(o(e)),{}]})))},\n function _(e,r,T,o,S){o(),S(\"BBoxTileSource\",e(465).BBoxTileSource),S(\"MercatorTileSource\",e(466).MercatorTileSource),S(\"QUADKEYTileSource\",e(469).QUADKEYTileSource),S(\"TileRenderer\",e(470).TileRenderer),S(\"TileSource\",e(467).TileSource),S(\"TMSTileSource\",e(472).TMSTileSource),S(\"WMTSTileSource\",e(471).WMTSTileSource)},\n function _(e,t,r,o,l){var i;o();const s=e(466);class _ extends s.MercatorTileSource{constructor(e){super(e)}get_image_url(e,t,r){const o=this.string_lookup_replace(this.url,this.extra_url_vars);let l,i,s,_;return this.use_latlon?[i,_,l,s]=this.get_tile_geographic_bounds(e,t,r):[i,_,l,s]=this.get_tile_meter_bounds(e,t,r),o.replace(\"{XMIN}\",i.toString()).replace(\"{YMIN}\",_.toString()).replace(\"{XMAX}\",l.toString()).replace(\"{YMAX}\",s.toString())}}r.BBoxTileSource=_,i=_,_.__name__=\"BBoxTileSource\",i.define((({Bool:e})=>({use_latlon:[e,!1]})))},\n function _(t,e,i,_,s){var r;_();const o=t(467),n=t(10),l=t(468);class u extends o.TileSource{constructor(t){super(t)}initialize(){super.initialize(),this._resolutions=(0,n.range)(this.min_zoom,this.max_zoom+1).map((t=>this.get_resolution(t)))}_computed_initial_resolution(){return null!=this.initial_resolution?this.initial_resolution:2*Math.PI*6378137/this.tile_size}is_valid_tile(t,e,i){return!(!this.wrap_around&&(t<0||t>=2**i))&&!(e<0||e>=2**i)}parent_by_tile_xyz(t,e,i){const _=this.tile_xyz_to_quadkey(t,e,i),s=_.substring(0,_.length-1);return this.quadkey_to_tile_xyz(s)}get_resolution(t){return this._computed_initial_resolution()/2**t}get_resolution_by_extent(t,e,i){return[(t[2]-t[0])/i,(t[3]-t[1])/e]}get_level_by_extent(t,e,i){const _=(t[2]-t[0])/i,s=(t[3]-t[1])/e,r=Math.max(_,s);let o=0;for(const t of this._resolutions){if(r>t){if(0==o)return 0;if(o>0)return o-1}o+=1}return o-1}get_closest_level_by_extent(t,e,i){const _=(t[2]-t[0])/i,s=(t[3]-t[1])/e,r=Math.max(_,s),o=this._resolutions.reduce((function(t,e){return Math.abs(e-r)e?(u=o-s,a*=t):(u*=e,a=n-r)}const h=(u-(o-s))/2,c=(a-(n-r))/2;return[s-h,r-c,o+h,n+c]}tms_to_wmts(t,e,i){return[t,2**i-1-e,i]}wmts_to_tms(t,e,i){return[t,2**i-1-e,i]}pixels_to_meters(t,e,i){const _=this.get_resolution(i);return[t*_-this.x_origin_offset,e*_-this.y_origin_offset]}meters_to_pixels(t,e,i){const _=this.get_resolution(i);return[(t+this.x_origin_offset)/_,(e+this.y_origin_offset)/_]}pixels_to_tile(t,e){let i=Math.ceil(t/this.tile_size);i=0===i?i:i-1;return[i,Math.max(Math.ceil(e/this.tile_size)-1,0)]}pixels_to_raster(t,e,i){return[t,(this.tile_size<=l;t--)for(let i=n;i<=u;i++)this.is_valid_tile(i,t,e)&&h.push([i,t,e,this.get_tile_meter_bounds(i,t,e)]);return this.sort_tiles_from_center(h,[n,l,u,a]),h}quadkey_to_tile_xyz(t){let e=0,i=0;const _=t.length;for(let s=_;s>0;s--){const r=1<0;s--){const i=1<0;)if(s=s.substring(0,s.length-1),[t,e,i]=this.quadkey_to_tile_xyz(s),[t,e,i]=this.denormalize_xyz(t,e,i,_),this.tiles.has(this.tile_xyz_to_key(t,e,i)))return[t,e,i];return[0,0,0]}normalize_xyz(t,e,i){if(this.wrap_around){const _=2**i;return[(t%_+_)%_,e,i]}return[t,e,i]}denormalize_xyz(t,e,i,_){return[t+_*2**i,e,i]}denormalize_meters(t,e,i,_){return[t+2*_*Math.PI*6378137,e]}calculate_world_x_by_tile_xyz(t,e,i){return Math.floor(t/2**i)}}i.MercatorTileSource=u,r=u,u.__name__=\"MercatorTileSource\",r.define((({Bool:t})=>({snap_to_zoom:[t,!1],wrap_around:[t,!0]}))),r.override({x_origin_offset:20037508.34,y_origin_offset:20037508.34,initial_resolution:156543.03392804097})},\n function _(e,t,r,i,l){var n;i();const a=e(51),s=e(9);class c extends a.Model{constructor(e){super(e)}initialize(){super.initialize(),this.tiles=new Map,this._normalize_case()}connect_signals(){super.connect_signals(),this.connect(this.change,(()=>this._clear_cache()))}string_lookup_replace(e,t){let r=e;for(const[e,i]of(0,s.entries)(t))r=r.replace(`{${e}}`,i);return r}_normalize_case(){const e=this.url.replace(\"{x}\",\"{X}\").replace(\"{y}\",\"{Y}\").replace(\"{z}\",\"{Z}\").replace(\"{q}\",\"{Q}\").replace(\"{xmin}\",\"{XMIN}\").replace(\"{ymin}\",\"{YMIN}\").replace(\"{xmax}\",\"{XMAX}\").replace(\"{ymax}\",\"{YMAX}\");this.url=e}_clear_cache(){this.tiles=new Map}tile_xyz_to_key(e,t,r){return`${e}:${t}:${r}`}key_to_tile_xyz(e){const[t,r,i]=e.split(\":\").map((e=>parseInt(e)));return[t,r,i]}sort_tiles_from_center(e,t){const[r,i,l,n]=t,a=(l-r)/2+r,s=(n-i)/2+i;e.sort((function(e,t){return Math.sqrt((a-e[0])**2+(s-e[1])**2)-Math.sqrt((a-t[0])**2+(s-t[1])**2)}))}get_image_url(e,t,r){return this.string_lookup_replace(this.url,this.extra_url_vars).replace(\"{X}\",e.toString()).replace(\"{Y}\",t.toString()).replace(\"{Z}\",r.toString())}}r.TileSource=c,n=c,c.__name__=\"TileSource\",n.define((({Float:e,Str:t,Dict:r,Nullable:i})=>({url:[t,\"\"],tile_size:[e,256],max_zoom:[e,30],min_zoom:[e,0],extra_url_vars:[r(t),{}],attribution:[t,\"\"],x_origin_offset:[e],y_origin_offset:[e],initial_resolution:[i(e),null]})))},\n function _(t,e,r,n,o){n();const c=t(134);function _(t,e){return c.wgs84_mercator.compute(t,e)}function g(t,e){return c.wgs84_mercator.invert(t,e)}r.geographic_to_meters=_,r.meters_to_geographic=g,r.geographic_extent_to_meters=function(t){const[e,r,n,o]=t,[c,g]=_(e,r),[i,u]=_(n,o);return[c,g,i,u]},r.meters_extent_to_geographic=function(t){const[e,r,n,o]=t,[c,_]=g(e,r),[i,u]=g(n,o);return[c,_,i,u]}},\n function _(e,t,r,s,_){s();const o=e(466);class c extends o.MercatorTileSource{constructor(e){super(e)}get_image_url(e,t,r){const s=this.string_lookup_replace(this.url,this.extra_url_vars),[_,o,c]=this.tms_to_wmts(e,t,r),i=this.tile_xyz_to_quadkey(_,o,c);return s.replace(\"{Q}\",i)}}r.QUADKEYTileSource=c,c.__name__=\"QUADKEYTileSource\"},\n function _(e,t,i,s,_){var n;s();const a=e(467),h=e(471),r=e(83),o=e(99),l=e(401),d=e(179),m=e(10),c=e(12);class p extends r.RendererView{constructor(){super(...arguments),this._tiles=null,this.map_initialized=!1}connect_signals(){super.connect_signals(),this.connect(this.model.change,(()=>this.request_paint())),this.connect(this.model.tile_source.change,(()=>this.request_paint()))}force_finished(){super.force_finished(),null==this._tiles&&(this._tiles=[])}get_extent(){const{x_range:e,y_range:t}=this,i=e.start,s=t.start,_=e.end,n=t.end;return(0,c.assert)(isFinite(i)),(0,c.assert)(isFinite(s)),(0,c.assert)(isFinite(_)),(0,c.assert)(isFinite(n)),[i,s,_,n]}get map_plot(){return this.plot_model}get map_canvas(){return this.layer.ctx}get map_frame(){return this.plot_view.frame}get x_range(){return this.map_plot.x_range}get y_range(){return this.map_plot.y_range}_set_data(){this.extent=this.get_extent(),this._last_height=void 0,this._last_width=void 0}get attribution(){return new l.HTML({html:[this.model.tile_source.attribution]})}_map_data(){this.initial_extent=this.get_extent();const e=this.model.tile_source.get_level_by_extent(this.initial_extent,this.map_frame.bbox.height,this.map_frame.bbox.width),t=this.model.tile_source.snap_to_zoom_level(this.initial_extent,this.map_frame.bbox.height,this.map_frame.bbox.width,e);this.x_range.start=t[0],this.y_range.start=t[1],this.x_range.end=t[2],this.y_range.end=t[3],this.x_range instanceof o.Range1d&&(this.x_range.reset_start=t[0],this.x_range.reset_end=t[2]),this.y_range instanceof o.Range1d&&(this.y_range.reset_start=t[1],this.y_range.reset_end=t[3])}_create_tile(e,t,i,s,_=!1){const n=this.model.tile_source.tile_xyz_to_quadkey(e,t,i),a=this.model.tile_source.tile_xyz_to_key(e,t,i);if(this.model.tile_source.tiles.has(a))return;const[h,r,o]=this.model.tile_source.normalize_xyz(e,t,i),l=this.model.tile_source.get_image_url(h,r,o),m={img:void 0,tile_coords:[e,t,i],normalized_coords:[h,r,o],quadkey:n,cache_key:a,bounds:s,loaded:!1,finished:!1,x_coord:s[0],y_coord:s[3]};this.model.tile_source.tiles.set(a,m),null==this._tiles&&(this._tiles=[]),this._tiles.push(m),new d.ImageLoader(l,{loaded:e=>{Object.assign(m,{img:e,loaded:!0}),_?(m.finished=!0,this.notify_finished()):this.request_paint()},failed(){m.finished=!0}})}_enforce_aspect_ratio(){if(this._last_height!==this.map_frame.bbox.height||this._last_width!==this.map_frame.bbox.width){const e=this.get_extent(),t=this.model.tile_source.get_level_by_extent(e,this.map_frame.bbox.height,this.map_frame.bbox.width),i=this.model.tile_source.snap_to_zoom_level(e,this.map_frame.bbox.height,this.map_frame.bbox.width,t);this.x_range.setv({start:i[0],end:i[2]}),this.y_range.setv({start:i[1],end:i[3]}),this.extent=i,this._last_height=this.map_frame.bbox.height,this._last_width=this.map_frame.bbox.width}}has_finished(){if(!super.has_finished())return!1;if(null==this._tiles)return!1;for(const e of this._tiles)if(!e.finished)return!1;return!0}_paint(){this.map_initialized||(this._set_data(),this._map_data(),this.map_initialized=!0),this._enforce_aspect_ratio(),this._update(),null!=this.prefetch_timer&&clearTimeout(this.prefetch_timer),this.prefetch_timer=setTimeout(this._prefetch_tiles.bind(this),500),this.has_finished()&&this.notify_finished()}_draw_tile(e){const t=this.model.tile_source.tiles.get(e);if(null!=t&&t.loaded){const[[e],[i]]=this.coordinates.map_to_screen([t.bounds[0]],[t.bounds[3]]),[[s],[_]]=this.coordinates.map_to_screen([t.bounds[2]],[t.bounds[1]]),n=s-e,a=_-i,h=e,r=i,o=this.map_canvas.imageSmoothingEnabled;this.map_canvas.imageSmoothingEnabled=this.model.smoothing,this.map_canvas.drawImage(t.img,h,r,n,a),this.map_canvas.imageSmoothingEnabled=o,t.finished=!0}}_set_rect(){const e=this.plot_model.outline_line_width,t=this.map_frame.bbox.left+e/2,i=this.map_frame.bbox.top+e/2,s=this.map_frame.bbox.width-e,_=this.map_frame.bbox.height-e;this.map_canvas.rect(t,i,s,_),this.map_canvas.clip()}_render_tiles(e){this.map_canvas.save(),this._set_rect(),this.map_canvas.globalAlpha=this.model.alpha;for(const t of e)this._draw_tile(t);this.map_canvas.restore()}_prefetch_tiles(){const{tile_source:e}=this.model,t=this.get_extent(),i=this.map_frame.bbox.height,s=this.map_frame.bbox.width,_=this.model.tile_source.get_level_by_extent(t,i,s),n=this.model.tile_source.get_tiles_by_extent(t,_);for(let t=0,i=Math.min(10,n.length);ti&&(s=this.extent,h=i,r=!0),r&&(this.x_range.setv({start:s[0],end:s[2]}),this.y_range.setv({start:s[1],end:s[3]})),this.extent=s;const o=e.get_tiles_by_extent(s,h),l=[],d=[],c=[],p=[];for(const t of o){const[i,s,n]=t,a=e.tile_xyz_to_key(i,s,n),h=e.tiles.get(a);if(null!=h&&h.loaded)d.push(a);else if(this.model.render_parents){const[t,a,h]=e.get_closest_parent_by_tile_xyz(i,s,n),r=e.tile_xyz_to_key(t,a,h),o=e.tiles.get(r);if(null!=o&&o.loaded&&!(0,m.includes)(c,r)&&c.push(r),_){const t=e.children_by_tile_xyz(i,s,n);for(const[i,s,_]of t){const t=e.tile_xyz_to_key(i,s,_);e.tiles.has(t)&&p.push(t)}}}null==h&&l.push(t)}this._render_tiles(c),this._render_tiles(p),this._render_tiles(d),null!=this.render_timer&&clearTimeout(this.render_timer),this.render_timer=setTimeout((()=>this._fetch_tiles(l)),65)}}i.TileRendererView=p,p.__name__=\"TileRendererView\";class g extends r.Renderer{constructor(e){super(e)}}i.TileRenderer=g,n=g,g.__name__=\"TileRenderer\",n.prototype.default_view=p,n.define((({Bool:e,Float:t,Ref:i})=>({alpha:[t,1],smoothing:[e,!0],tile_source:[i(a.TileSource),()=>new h.WMTSTileSource],render_parents:[e,!0]}))),n.override({level:\"image\"})},\n function _(t,e,r,o,s){o();const c=t(466);class i extends c.MercatorTileSource{constructor(t){super(t)}get_image_url(t,e,r){const o=this.string_lookup_replace(this.url,this.extra_url_vars),[s,c,i]=this.tms_to_wmts(t,e,r);return o.replace(\"{X}\",s.toString()).replace(\"{Y}\",c.toString()).replace(\"{Z}\",i.toString())}}r.WMTSTileSource=i,i.__name__=\"WMTSTileSource\"},\n function _(e,r,t,c,o){c();const i=e(466);class l extends i.MercatorTileSource{constructor(e){super(e)}get_image_url(e,r,t){return this.string_lookup_replace(this.url,this.extra_url_vars).replace(\"{X}\",e.toString()).replace(\"{Y}\",r.toString()).replace(\"{Z}\",t.toString())}}t.TMSTileSource=l,l.__name__=\"TMSTileSource\"},\n function _(e,t,u,a,r){a(),r(\"CanvasTexture\",e(474).CanvasTexture),r(\"ImageURLTexture\",e(476).ImageURLTexture),r(\"Texture\",e(475).Texture)},\n function _(t,e,n,c,s){var r;c();const o=t(475),a=t(40);class u extends o.Texture{constructor(t){super(t)}get func(){const t=(0,a.use_strict)(this.code);return new Function(\"ctx\",\"color\",\"scale\",\"weight\",t)}get_pattern(t,e,n){const c=document.createElement(\"canvas\");c.width=e,c.height=e;const s=c.getContext(\"2d\");return this.func.call(this,s,t,e,n),c}}n.CanvasTexture=u,r=u,u.__name__=\"CanvasTexture\",r.define((({Str:t})=>({code:[t]})))},\n function _(e,t,n,r,o){var i;r();const s=e(51),u=e(20);class c extends s.Model{constructor(e){super(e)}}n.Texture=c,i=c,c.__name__=\"Texture\",i.define((()=>({repetition:[u.TextureRepetition,\"repeat\"]})))},\n function _(e,t,r,i,a){var n;i();const s=e(475),o=e(179);class u extends s.Texture{constructor(e){super(e)}initialize(){super.initialize(),this._loader=new o.ImageLoader(this.url)}get_pattern(e,t,r){const{_loader:i}=this;return this._loader.finished?i.image:i.promise}}r.ImageURLTexture=u,n=u,u.__name__=\"ImageURLTexture\",n.define((({Str:e})=>({url:[e]})))},\n function _(e,n,a,o,t){o();const l=e(1);l.__exportStar(e(478),a),l.__exportStar(e(483),a),t(\"Panel\",e(435).Panel),t(\"Dialog\",e(319).Dialog),t(\"Examiner\",e(484).Examiner),t(\"Pane\",e(400).Pane),t(\"Tooltip\",e(413).Tooltip),t(\"UIElement\",e(111).UIElement)},\n function _(n,c,o,I,i){I(),i(\"BuiltinIcon\",n(479).BuiltinIcon),i(\"SVGIcon\",n(481).SVGIcon),i(\"TablerIcon\",n(482).TablerIcon)},\n function _(e,n,t,i,s){var o;i();const r=e(1),a=e(480),l=e(63),c=e(22),m=e(8),u=r.__importDefault(e(123));class _ extends a.IconView{constructor(){super(...arguments),this._style=new l.InlineStyleSheet}stylesheets(){return[...super.stylesheets(),u.default,this._style]}render(){super.render();const e=`var(--bokeh-icon-${this.model.icon_name})`,n=(0,c.color2css)(this.model.color),t=(()=>{const{size:e}=this.model;return(0,m.isNumber)(e)?`${e}px`:e})();this._style.replace(`\\n :host {\\n display: inline-block;\\n vertical-align: middle;\\n width: ${t};\\n height: ${t};\\n background-color: ${n};\\n mask-image: ${e};\\n mask-size: contain;\\n mask-repeat: no-repeat;\\n -webkit-mask-image: ${e};\\n -webkit-mask-size: contain;\\n -webkit-mask-repeat: no-repeat;\\n }\\n `)}}t.BuiltinIconView=_,_.__name__=\"BuiltinIconView\";class d extends a.Icon{constructor(e){super(e)}}t.BuiltinIcon=d,o=d,d.__name__=\"BuiltinIcon\",o.prototype.default_view=_,o.define((({Str:e,Color:n})=>({icon_name:[e],color:[n,\"gray\"]})))},\n function _(e,n,s,c,t){var o;c();const i=e(111);class _ extends i.UIElementView{connect_signals(){super.connect_signals(),this.connect(this.model.change,(()=>this.render()))}}s.IconView=_,_.__name__=\"IconView\";class a extends i.UIElement{constructor(e){super(e)}}s.Icon=a,o=a,a.__name__=\"Icon\",o.define((({Float:e,Or:n,CSSLength:s})=>({size:[n(e,s),\"1em\"]})))},\n function _(e,n,s,t,r){var i;t();const o=e(480),l=e(63),c=e(8);class a extends o.IconView{constructor(){super(...arguments),this._style=new l.InlineStyleSheet}stylesheets(){return[...super.stylesheets(),this._style]}render(){super.render();const e=(()=>{const{size:e}=this.model;return(0,c.isNumber)(e)?`${e}px`:e})();this._style.replace(`\\n :host {\\n display: inline-block;\\n vertical-align: middle;\\n }\\n :host svg {\\n width: ${e};\\n height: ${e};\\n }\\n `);const n=(new DOMParser).parseFromString(this.model.svg,\"image/svg+xml\");this.shadow_el.append(n.documentElement)}}s.SVGIconView=a,a.__name__=\"SVGIconView\";class d extends o.Icon{constructor(e){super(e)}}s.SVGIcon=d,i=d,d.__name__=\"SVGIcon\",i.prototype.default_view=a,i.define((({Str:e})=>({svg:[e]})))},\n function _(e,n,t,s,r){var o,l;s();const i=e(480),a=e(63),c=e(8);class f extends i.IconView{constructor(){super(...arguments),this._tabler=new a.ImportedStyleSheet(`${o._url}/tabler-icons.min.css`),this._style=new a.InlineStyleSheet}stylesheets(){return[...super.stylesheets(),o._fonts,this._tabler,this._style]}render(){super.render();const e=(()=>{const{size:e}=this.model;return(0,c.isNumber)(e)?`${e}px`:e})();this._style.replace(`\\n :host {\\n display: inline-block;\\n vertical-align: middle;\\n font-size: ${e};\\n }\\n `);const n=(0,a.span)({class:[\"ti\",`ti-${this.model.icon_name}`]});this.shadow_el.appendChild(n)}}t.TablerIconView=f,o=f,f.__name__=\"TablerIconView\",f._url=\"https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest\",f._fonts=new a.GlobalInlineStyleSheet(` /*!\\n * Tabler Icons 1.68.0 by tabler - https://tabler.io\\n * License - https://github.com/tabler/tabler-icons/blob/master/LICENSE\\n */\\n @font-face {\\n font-family: \"tabler-icons\";\\n font-style: normal;\\n font-weight: 400;\\n src: url(\"${o._url}/fonts/tabler-icons.eot\");\\n src: url(\"${o._url}/fonts/tabler-icons.eot?#iefix\") format(\"embedded-opentype\"),\\n url(\"${o._url}/fonts/tabler-icons.woff2\") format(\"woff2\"),\\n url(\"${o._url}/fonts/tabler-icons.woff\") format(\"woff\"),\\n url(\"${o._url}/fonts/tabler-icons.ttf\") format(\"truetype\"),\\n url(\"${o._url}/fonts/tabler-icons.svg#tabler-icons\") format(\"svg\");\\n }\\n\\n @media screen and (-webkit-min-device-pixel-ratio: 0) {\\n @font-face {\\n font-family: \"tabler-icons\";\\n src: url(\"${o._url}/fonts/tabler-icons.svg#tabler-icons\") format(\"svg\");\\n }\\n }\\n`);class b extends i.Icon{constructor(e){super(e)}}t.TablerIcon=b,l=b,b.__name__=\"TablerIcon\",l.prototype.default_view=f,l.define((({Str:e})=>({icon_name:[e]})))},\n function _(e,t,i,m,n){m(),n(\"Menu\",e(110).Menu),n(\"ActionItem\",e(118).ActionItem),n(\"CheckableItem\",e(120).CheckableItem),n(\"DividerItem\",e(121).DividerItem)},\n function _(t,s,e,n,o){var i;n();const a=t(1),l=t(111),r=a.__importStar(t(18)),c=t(14),d=t(63),p=t(42),h=t(51),u=t(8),f=t(9),_=t(10),m=t(34),g=t(15),v=t(38),b=a.__importDefault(t(485)),y=a.__importStar(t(486)),k=y;class x{constructor(t,s=5,e=3){this.visited=new WeakSet,this.depth=0,this.click=t,this.max_items=s,this.max_depth=e}to_html(t){if((0,u.isObject)(t)){if(this.visited.has(t))return(0,d.span)(\"\");this.visited.add(t)}return null==t?this.null():(0,u.isBoolean)(t)?this.boolean(t):(0,u.isNumber)(t)?this.number(t):(0,u.isString)(t)?this.string(t):(0,u.isSymbol)(t)?this.symbol(t):t instanceof h.Model?this.model(t):t instanceof r.Property?this.property(t):(0,u.isPlainObject)(t)?this.object(t):(0,u.isArray)(t)?this.array(t):(0,u.isIterable)(t)?this.iterable(t):(0,d.span)((0,p.to_string)(t))}null(){return(0,d.span)({class:k.nullish},\"null\")}token(t){return(0,d.span)({class:k.token},t)}boolean(t){return(0,d.span)({class:k.boolean},`${t}`)}number(t){return(0,d.span)({class:k.number},`${t}`)}string(t){const s=t.includes(\"'\"),e=t.includes('\"'),n=s&&e?`\\`${t.replace(/`/g,\"\\\\`\")}\\``:e?`'${t}'`:`\"${t}\"`;return(0,d.span)({class:k.string},n)}symbol(t){return(0,d.span)({class:k.symbol},t.toString())}array(t){const s=this.token,e=[];let n=0;for(const s of t)if(e.push(this.to_html(s)),n++>this.max_items){e.push((0,d.span)(\"\\u2026\"));break}return(0,d.span)({class:k.array},s(\"[\"),...(0,m.interleave)(e,(()=>s(\", \"))),s(\"]\"))}iterable(t){const s=this.token,e=Object(t)[Symbol.toStringTag]??\"Object\",n=this.array([...t]);return(0,d.span)({class:k.iterable},`${e}`,s(\"(\"),n,s(\")\"))}object(t){const s=this.token,e=[];let n=0;for(const[o,i]of(0,f.entries)(t))if(e.push((0,d.span)(`${o}`,s(\": \"),this.to_html(i))),n++>this.max_items){e.push((0,d.span)(\"\\u2026\"));break}return(0,d.span)({class:k.object},s(\"{\"),...(0,m.interleave)(e,(()=>s(\", \"))),s(\"}\"))}model(t){const s=this.token,e=(0,d.span)({class:k.model},t.constructor.__qualified__,s(\"(\"),this.to_html(t.id),s(\")\")),{click:n}=this;return null!=n&&(e.classList.add(\"ref\"),e.addEventListener(\"click\",(()=>n(t)))),e}property(t){const s=this.model(t.obj),e=(0,d.span)({class:k.attr},t.attr);return(0,d.span)(s,this.token(\".\"),e)}}e.HTMLPrinter=x,x.__name__=\"HTMLPrinter\";class w extends l.UIElementView{constructor(){super(...arguments),this.prev_listener=null,this.watched_props=new Set}stylesheets(){return[...super.stylesheets(),y.default,b.default]}render(){super.render(),null!=this.prev_listener&&v.diagnostics.disconnect(this.prev_listener);const t=[],s=[],e=[],n=new WeakMap;v.diagnostics.connect((o=>{if(o instanceof r.Property){for(const[s,e]of t)s==o.obj&&i(e);for(const[t,e]of s)if(t==o){const[,,,s]=e.children;a(t,e,s);break}for(const[t,s]of e)if(t==o){const[,e]=s.children;a(t,s,e);break}}function i(t){const s=n.get(t);null!=s&&s.cancel();const e=t.animate([{backgroundColor:\"#def189\"},{backgroundColor:\"initial\"}],{duration:2e3});n.set(t,e)}function a(t,s,e){s.classList.toggle(\"dirty\",t.dirty),(0,d.empty)(e);const n=t.is_unset?(0,d.span)(\"unset\"):C(t.get_value());e.appendChild(n),i(e)}}));const o=(()=>{const s=(0,d.input)({class:\"filter\",type:\"text\",placeholder:\"Filter\"});return s.addEventListener(\"keyup\",(()=>{const e=s.value;for(const[s,n]of t){const t=s.constructor.__qualified__.includes(e);n.classList.toggle(\"hidden\",!t)}})),(0,d.div)({class:\"toolbar\"},s)})(),i=(0,d.input)({type:\"checkbox\",checked:!0}),a=(0,d.input)({type:\"checkbox\",checked:!0}),l=()=>{for(const[t,e]of s){const s=i.checked,n=a.checked,o=!t.dirty&&!s||t.internal&&!n;e.classList.toggle(\"hidden\",o)}};i.addEventListener(\"change\",(()=>l())),a.addEventListener(\"change\",(()=>l()));const p=(()=>{const t=(0,d.input)({class:\"filter\",type:\"text\",placeholder:\"Filter\"}),e=(0,d.span)({class:\"checkbox\"},(0,d.input)({type:\"checkbox\",checked:!0}),(0,d.span)(\"Group\")),n=(0,d.span)({class:\"checkbox\"},i,(0,d.span)(\"Initial?\")),o=(0,d.span)({class:\"checkbox\"},a,(0,d.span)(\"Internal?\"));return t.addEventListener(\"keyup\",(()=>{const e=t.value;for(const[t,n]of s){const s=t.attr.includes(e);n.classList.toggle(\"hidden\",!s)}})),(0,d.div)({class:\"toolbar\"},t,e,n,o)})(),u=(()=>{const t=(0,d.input)({class:\"filter\",type:\"text\",placeholder:\"Filter\"});return t.addEventListener(\"keyup\",(()=>{const s=t.value;for(const[t,n]of e){const e=t.attr.includes(s);n.classList.toggle(\"hidden\",!e)}})),(0,d.div)({class:\"toolbar\"},t)})(),m=(0,d.div)({class:\"models-list\"}),b=(0,d.div)({class:\"props-list\"}),y=(0,d.div)({class:\"watches-list\"}),k=(0,d.div)({class:\"models-panel\"},o,m),w=(0,d.div)({class:\"props-panel\"},p,b),L=(0,d.div)({class:\"watches-panel\"},u,y),E=(0,d.div)({class:\"col\",style:{width:\"100%\"}},L,w),S=(0,d.div)({class:\"examiner\"},k,E);function j(t){t instanceof h.Model&&O(t)}function C(t){return new x(j).to_html(t)}const P=(s,e)=>{(0,_.clear)(t),(0,d.empty)(m);const n=null!=e?new Set(e.roots()):new Set;for(const e of s){const s=n.has(e)?(0,d.span)({class:\"tag\"},\"root\"):null,o=(0,d.span)({class:\"model-ref\",tabIndex:0},C(e),s);o.addEventListener(\"keydown\",(t=>{\"Enter\"==t.key&&O(e)})),t.push([e,o]),m.appendChild(o)}},O=e=>{(0,_.clear)(s),(0,d.empty)(b);for(const[s,n]of t)n.classList.toggle(\"active\",e==s);const n=(()=>{const t=[];let s=Object.getPrototypeOf(e);do{t.push([s.constructor,(0,f.keys)(s._props)]),s=Object.getPrototypeOf(s)}while(s.constructor!=c.HasProps);t.reverse();const n=[];for(const[,s]of t)s.splice(0,n.length),n.push(...s);return t})(),o=g.receivers_for_sender.get(e)??[];for(const[t,l]of n){if(0==l.length)continue;const n=(0,d.span)({class:[\"expander\"]}),r=(0,d.div)({class:\"base\"},n,\"inherited from\",\" \",(0,d.span)({class:\"monospace\"},t.__qualified__));b.appendChild(r);const c=[];for(const t of l){const n=e.property(t),l=n.kind.toString(),r=n.is_unset?(0,d.span)(\"unset\"):C(n.get_value()),p=n.internal?(0,d.span)({class:\"tag\"},\"internal\"):null,h=o.filter((t=>t.signal==n.change)).length,u=0!=h?(0,d.span)({class:\"tag\"},`${h}`):null,f=this.watched_props.has(n),_=(0,d.input)({type:\"checkbox\",checked:f}),m=(0,d.div)({class:\"prop-attr\",tabIndex:0},_,(0,d.span)({class:\"attr\"},t),p),g=(0,d.div)({class:\"prop-conns\"},u),v=(0,d.div)({class:\"prop-kind\"},l),y=(0,d.div)({class:\"prop-value\"},r),k=n.dirty?\"dirty\":null,x=n.internal?\"internal\":null,w=i.checked,L=a.checked,E=!n.dirty&&!w||n.internal&&!L?\"hidden\":null,S=(0,d.div)({class:[\"prop\",k,x,E]},m,g,v,y);c.push(S),s.push([n,S]),b.appendChild(S),_.addEventListener(\"change\",(()=>{this.watched_props[_.checked?\"add\":\"delete\"](n),$()}))}r.addEventListener(\"click\",(()=>{n.classList.toggle(\"closed\");for(const t of c)t.classList.toggle(\"closed\")}))}},$=()=>{if((0,_.clear)(e),(0,d.empty)(y),0==this.watched_props.size){const t=(0,d.div)({class:\"nothing\"},\"No watched properties\");y.appendChild(t)}else for(const t of this.watched_props){const s=(0,d.span)(C(t)),n=(0,d.span)(t.is_unset?(0,d.span)(\"unset\"):C(t.get_value())),o=(0,d.div)({class:[\"prop\",t.dirty?\"dirty\":null]},s,n);e.push([t,o]),y.appendChild(o)}};this.shadow_el.appendChild(S);const{target:I}=this.model;if(null!=I){const t=I.references(),{document:s}=I;P(t,s),O(I)}else{const{document:t}=this.model;if(null!=t){P(t._all_models.values(),t);const s=t.roots();if(0!=s.length){const[t]=s;O(t)}}}$()}}e.ExaminerView=w,w.__name__=\"ExaminerView\";class L extends l.UIElement{constructor(t){super(t)}}e.Examiner=L,i=L,L.__name__=\"Examiner\",i.prototype.default_view=w,i.define((({Ref:t,Nullable:s})=>({target:[s(t(c.HasProps)),null]})))},\n function _(o,e,i,r,l){r(),i.default=':root{--common-padding:3px;--common-outline:#1a73e8 solid 1px;--panel-bg-color:#f1f3f4;--panel-border-color:#cacdd1;}.ref{cursor:pointer;}.monospace{font-family:var(--mono-font);}.hidden{display:none !important;}.col{display:flex;flex-direction:column;}.row{display:flex;flex-direction:row;}.toolbar{display:flex;flex-direction:row;gap:1em;background-color:var(--panel-bg-color);border-bottom:1px solid var(--panel-border-color);padding:var(--common-padding);}.checkbox{display:flex;flex-direction:row;align-items:center;gap:0.25em;}.filter:focus{outline:var(--common-outline);}.examiner{height:100%;display:flex;border:1px solid var(--panel-border-color);}.models-panel{display:flex;flex-direction:column;border-right:1px solid var(--panel-border-color);}.props-panel{display:flex;flex-direction:column;height:100%;overflow:auto;}.watches-panel{display:flex;flex-direction:column;border-bottom:1px solid var(--panel-border-color);}.models-list{display:flex;flex-direction:column;height:min-content;padding:var(--common-padding);overflow-x:hidden;overflow-y:auto;}.props-list{display:grid;grid-template-columns:min-content min-content 1fr 1fr;column-gap:1em;padding:var(--common-padding);}.watches-list{display:grid;grid-template-columns:1fr 1fr;column-gap:1em;padding:var(--common-padding);}.nothing{grid-column:1 / span 2;font-style:italic;text-align:center;}.prop{display:contents;}.prop.closed{display:none;}.prop > *{opacity:0.6;}.prop.dirty > *{opacity:1;}.model-ref{display:flex;align-items:center;flex-direction:row;font-family:var(--mono-font);}.model-ref:focus-visible{outline:var(--common-outline);}.model-ref:hover{background-color:#e2e2e2;}.model-ref.active{background-color:#c8e0ee;}.tag{margin-left:1em;padding:0 4px;font-size:60%;border-width:1px;border-style:solid;border-radius:4px;color:#202124;background-color:#f1f3f4;border-color:#cacdd1;}.expander{margin:0 2px;display:inline-block;vertical-align:middle;background-color:#5f6368;--open-image:url(\\'data:image/svg+xml;utf8, \\');--closed-image:url(\\'data:image/svg+xml;utf8, \\');}.expander{width:6px;height:6px;mask-image:var(--open-image);-webkit-mask-image:var(--open-image);}.expander.closed{width:6px;height:6px;mask-image:var(--closed-image);-webkit-mask-image:var(--closed-image);}.base{grid-column:1 / span 4;color:#5f6368;cursor:pointer;}.prop-attr,.prop-conns,.prop-kind,.prop-value{display:flex;flex-direction:row;align-items:center;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-family:var(--mono-font);}.prop-attr > input[type=\"checkbox\"]{visibility:hidden;margin-right:0.25em;}.prop-attr > input[type=\"checkbox\"]:checked,.prop-attr:hover > input{visibility:visible;}.prop-attr:focus-visible{outline:var(--common-outline);}'},\n function _(o,b,l,r,k){r(),l.nullish=\"bk-nullish\",l.token=\"bk-token\",l.boolean=\"bk-boolean\",l.number=\"bk-number\",l.string=\"bk-string\",l.symbol=\"bk-symbol\",l.model=\"bk-model\",l.attr=\"bk-attr\",l.array=\"bk-array\",l.object=\"bk-object\",l.iterable=\"bk-iterable\",l.default=\".bk-nullish{color:#7724c1;}.bk-token{color:#881280;}.bk-boolean{color:#007500;}.bk-number{color:#1a1aa6;}.bk-string{color:#994500;}.bk-symbol{color:#c80000;}.bk-model{color:initial;}.bk-attr{color:#c80000;}.bk-array{color:initial;}.bk-object{color:initial;}.bk-iterable{color:initial;}\"},\n function _(o,t,r,n,l){n();const _=o(1);_.__exportStar(o(488),r),_.__exportStar(o(502),r),_.__exportStar(o(512),r),_.__exportStar(o(525),r),l(\"Tool\",o(292).Tool),l(\"ToolProxy\",o(293).ToolProxy),l(\"Toolbar\",o(291).Toolbar),l(\"ToolButton\",o(294).ToolButton),l(\"OnOffButton\",o(300).OnOffButton),l(\"ClickButton\",o(303).ClickButton)},\n function _(o,l,T,e,n){e(),n(\"ActionTool\",o(302).ActionTool),n(\"CopyTool\",o(489).CopyTool),n(\"CustomAction\",o(490).CustomAction),n(\"FullscreenTool\",o(491).FullscreenTool),n(\"HelpTool\",o(304).HelpTool),n(\"ExamineTool\",o(492).ExamineTool),n(\"RedoTool\",o(493).RedoTool),n(\"ResetTool\",o(495).ResetTool),n(\"SaveTool\",o(496).SaveTool),n(\"UndoTool\",o(497).UndoTool),n(\"ZoomInTool\",o(498).ZoomInTool),n(\"ZoomOutTool\",o(501).ZoomOutTool)},\n function _(o,t,e,i,a){var n;i();const c=o(302),p=o(123);class s extends c.ActionToolView{async copy(){const o=await this.parent.export().to_blob(),t=new ClipboardItem({[o.type]:o});await navigator.clipboard.write([t])}doit(){this.copy()}}e.CopyToolView=s,s.__name__=\"CopyToolView\";class l extends c.ActionTool{constructor(o){super(o),this.tool_name=\"Copy\",this.tool_icon=p.tool_icon_copy}}e.CopyTool=l,n=l,l.__name__=\"CopyTool\",n.prototype.default_view=s,n.register_alias(\"copy\",(()=>new n))},\n function _(o,t,n,e,i){var c;e();const l=o(1),s=o(302),_=o(50),u=l.__importStar(o(123));class a extends s.ActionToolView{doit(){const{callback:o}=this.model;null!=o&&(0,_.execute)(o,this.model)}}n.CustomActionView=a,a.__name__=\"CustomActionView\";class m extends s.ActionTool{constructor(o){super(o),this.tool_name=\"Custom Action\",this.tool_icon=u.tool_icon_unknown}}n.CustomAction=m,c=m,m.__name__=\"CustomAction\",c.prototype.default_view=a,c.define((({Any:o,Nullable:t})=>({callback:[t(o),null]}))),c.override({description:\"Perform a Custom Action\"})},\n function _(e,l,t,n,o){var s;n();const c=e(1),r=e(302),u=c.__importStar(e(123)),i=void 0!==Element.prototype.webkitRequestFullscreen?(e,l)=>e.webkitRequestFullscreen(l):(e,l)=>e.requestFullscreen(l);class _ extends r.ActionToolView{async fullscreen(){null!=document.fullscreenElement?await document.exitFullscreen():await i(this.parent.el)}doit(){this.fullscreen()}}t.FullscreenToolView=_,_.__name__=\"FullscreenToolView\";class a extends r.ActionTool{constructor(e){super(e),this.tool_name=\"Fullscreen\",this.tool_icon=u.tool_icon_fullscreen}}t.FullscreenTool=a,s=a,a.__name__=\"FullscreenTool\",s.prototype.default_view=_,s.register_alias(\"fullscreen\",(()=>new s))},\n function _(e,i,t,o,n){var a;o();const l=e(1),s=e(302),_=l.__importStar(e(123)),r=e(319),c=e(484),d=e(401),m=e(56),h=e(63),w=l.__importDefault(e(486));class p extends s.ActionToolView{*children(){yield*super.children(),yield this._dialog}async lazy_initialize(){await super.lazy_initialize();const e=this.parent.model,i=new c.HTMLPrinter,t=new r.Dialog({stylesheets:[w.default],title:new d.HTML({html:(0,h.div)(\"Examine \",i.to_html(e))}),content:new c.Examiner({target:e}),visible:!1,close_action:\"hide\"});this._dialog=await(0,m.build_view)(t,{parent:this.parent})}doit(){this._dialog.open()}}t.ExamineToolView=p,p.__name__=\"ExamineToolView\";class x extends s.ActionTool{constructor(e){super(e),this.tool_name=\"Examine\",this.tool_icon=_.tool_icon_settings}}t.ExamineTool=x,a=x,x.__name__=\"ExamineTool\",a.prototype.default_view=p,a.register_alias(\"examine\",(()=>new a))},\n function _(o,e,t,s,i){var n;s();const l=o(494),_=o(123);class a extends l.PlotActionToolView{connect_signals(){super.connect_signals(),this.connect(this.plot_view.state.changed,(()=>this.model.disabled=!this.plot_view.state.can_redo))}doit(){const o=this.plot_view.state.redo();null!=o?.range&&this.plot_view.trigger_ranges_update_event()}}t.RedoToolView=a,a.__name__=\"RedoToolView\";class d extends l.PlotActionTool{constructor(o){super(o),this.tool_name=\"Redo\",this.tool_icon=_.tool_icon_redo}}t.RedoTool=d,n=d,d.__name__=\"RedoTool\",n.prototype.default_view=a,n.override({disabled:!0}),n.register_alias(\"redo\",(()=>new n))},\n function _(o,t,n,e,l){e();const c=o(302);class i extends c.ActionToolView{get plot_view(){return this.parent}}n.PlotActionToolView=i,i.__name__=\"PlotActionToolView\";class s extends c.ActionTool{constructor(o){super(o)}}n.PlotActionTool=s,s.__name__=\"PlotActionTool\"},\n function _(e,o,t,s,i){var l;s();const _=e(494),n=e(123);class c extends _.PlotActionToolView{doit(){this.plot_view.reset()}}t.ResetToolView=c,c.__name__=\"ResetToolView\";class r extends _.PlotActionTool{constructor(e){super(e),this.tool_name=\"Reset\",this.tool_icon=n.tool_icon_reset}}t.ResetTool=r,l=r,r.__name__=\"ResetTool\",l.prototype.default_view=c,l.register_alias(\"reset\",(()=>new l))},\n function _(e,o,t,a,n){var i;a();const l=e(302),s=e(123);class c extends l.ActionToolView{async copy(){const e=await this.parent.export().to_blob(),o=new ClipboardItem({[e.type]:e});await navigator.clipboard.write([o])}async save(e){const o=await this.parent.export().to_blob(),t=document.createElement(\"a\");t.href=URL.createObjectURL(o),t.download=e,t.target=\"_blank\",t.dispatchEvent(new MouseEvent(\"click\"))}doit(e=\"save\"){switch(e){case\"save\":{const e=this.model.filename??prompt(\"Enter filename\",\"bokeh_plot\");null!=e&&this.save(e);break}case\"copy\":this.copy()}}}t.SaveToolView=c,c.__name__=\"SaveToolView\";class r extends l.ActionTool{constructor(e){super(e),this.tool_name=\"Save\",this.tool_icon=s.tool_icon_save}get menu(){return[{icon:\"bk-tool-icon-copy\",tooltip:\"Copy image to clipboard\",if:()=>\"undefined\"!=typeof ClipboardItem,handler:()=>{this.do.emit(\"copy\")}}]}}t.SaveTool=r,i=r,r.__name__=\"SaveTool\",i.prototype.default_view=c,i.define((({Str:e,Nullable:o})=>({filename:[o(e),null]}))),i.register_alias(\"save\",(()=>new i))},\n function _(o,t,e,n,s){var i;n();const l=o(494),_=o(123);class a extends l.PlotActionToolView{connect_signals(){super.connect_signals(),this.connect(this.plot_view.state.changed,(()=>this.model.disabled=!this.plot_view.state.can_undo))}doit(){const o=this.plot_view.state.undo();null!=o?.range&&this.plot_view.trigger_ranges_update_event()}}e.UndoToolView=a,a.__name__=\"UndoToolView\";class d extends l.PlotActionTool{constructor(o){super(o),this.tool_name=\"Undo\",this.tool_icon=_.tool_icon_undo}}e.UndoTool=d,i=d,d.__name__=\"UndoTool\",i.prototype.default_view=a,i.override({disabled:!0}),i.register_alias(\"undo\",(()=>new i))},\n function _(o,e,n,i,s){var t;i();const _=o(499),a=o(123);class m extends _.ZoomBaseToolView{get factor(){return this.model.factor}}n.ZoomInToolView=m,m.__name__=\"ZoomInToolView\";class l extends _.ZoomBaseTool{constructor(o){super(o),this.maintain_focus=!0,this.tool_name=\"Zoom In\",this.tool_icon=a.tool_icon_zoom_in}}n.ZoomInTool=l,t=l,l.__name__=\"ZoomInTool\",t.prototype.default_view=m,t.register_alias(\"zoom_in\",(()=>new t({dimensions:\"both\"}))),t.register_alias(\"xzoom_in\",(()=>new t({dimensions:\"width\"}))),t.register_alias(\"yzoom_in\",(()=>new t({dimensions:\"height\"})))},\n function _(e,o,t,s,n){var i;s();const a=e(494),l=e(218),r=e(105),c=e(20),_=e(500),d=e(19);class m extends a.PlotActionToolView{doit(){const{dimensions:e}=this.model,o=\"width\"==e||\"both\"==e,t=\"height\"==e||\"both\"==e,{frame:s}=this.plot_view,{x_target:n,y_target:i}=s,a=new Map(s.x_scales),l=new Map(s.y_scales),{renderers:c}=this.model;if(\"auto\"!=c){const e=new Set,o=new Set;for(const t of c)null==t.coordinates&&(e.add(t.x_range_name),o.add(t.y_range_name));for(const o of a.keys())e.has(o)||a.delete(o);for(const e of l.keys())o.has(e)||l.delete(e)}const m=[...a.values()],h=[...l.values()],u=\"auto\"!=c?c:this.plot_view.model.data_renderers;for(const e of u){if(null==e.coordinates)continue;const o=this.plot_view.views.get_one(e),t=(e,o)=>{const{level:t}=this.model;for(let s=0;s({factor:[e,.1],dimensions:[c.Dimensions,\"both\"],renderers:[o(t(s(l.DataRenderer)),n),\"auto\"],level:[i(a),0]})))},\n function _(n,t,r,e,o){e();const s=n(11);function c(n,t,r){const[e,o]=(0,s.minmax)(n.start,n.end),c=r??(o+e)/2;return[e-(e-c)*t,o-(o-c)*t]}function a(n,t,r){const e=new Map;for(const o of n){const[n,s]=c(o.target_range,t,r),[a,f]=o.r_invert(n,s);e.set(o.source_range,{start:a,end:f})}return e}r.scale_interval=c,r.get_info=function(n,[t,r]){const e=new Map;for(const o of n){const[n,s]=o.r_invert(t,r);e.set(o.source_range,{start:n,end:s})}return e},r.rescale=a,r.scale_range=function(n,t,r,e,o,s=!0,c=!0,f){return{xrs:a(n,s?o:0,f?.x),yrs:a(t,c?o:0,f?.y),factor:o}}},\n function _(o,t,e,s,i){var n;s();const _=o(499),a=o(123);class m extends _.ZoomBaseToolView{get factor(){const{factor:o}=this.model;return-o/(1-o)}}e.ZoomOutToolView=m,m.__name__=\"ZoomOutToolView\";class l extends _.ZoomBaseTool{constructor(o){super(o),this.tool_name=\"Zoom Out\",this.tool_icon=a.tool_icon_zoom_out}}e.ZoomOutTool=l,n=l,l.__name__=\"ZoomOutTool\",n.prototype.default_view=m,n.define((({Bool:o})=>({maintain_focus:[o,!0]}))),n.register_alias(\"zoom_out\",(()=>new n({dimensions:\"both\"}))),n.register_alias(\"xzoom_out\",(()=>new n({dimensions:\"width\"}))),n.register_alias(\"yzoom_out\",(()=>new n({dimensions:\"height\"})))},\n function _(o,l,T,i,t){i(),t(\"EditTool\",o(503).EditTool),t(\"BoxEditTool\",o(504).BoxEditTool),t(\"FreehandDrawTool\",o(505).FreehandDrawTool),t(\"LineEditTool\",o(506).LineEditTool),t(\"PointDrawTool\",o(508).PointDrawTool),t(\"PolyDrawTool\",o(509).PolyDrawTool),t(\"PolyTool\",o(510).PolyTool),t(\"PolyEditTool\",o(511).PolyEditTool)},\n function _(e,t,s,o,n){var i;o();const c=e(10),r=e(9),a=e(8),_=e(12),l=e(299);class d extends l.GestureToolView{constructor(){super(...arguments),this._mouse_in_frame=!0}_select_mode(e){const{shift:t,ctrl:s}=e.modifiers;return t||s?t&&!s?\"append\":!t&&s?\"intersect\":t&&s?\"subtract\":void(0,_.unreachable)():\"replace\"}_move_enter(e){this._mouse_in_frame=!0}_move_exit(e){this._mouse_in_frame=!1}_map_drag(e,t,s){if(!this.plot_view.frame.bbox.contains(e,t))return null;const o=this.plot_view.views.find_one(s);if(null==o)return null;return[o.coordinates.x_scale.invert(e),o.coordinates.y_scale.invert(t)]}_delete_selected(e){const t=e.data_source,s=t.selected.indices;s.sort();for(const e of t.columns()){const o=t.get_array(e);for(let e=0;en.has(i)?n.get(i):o.has(i)?o.get(i):s.has(i)?s.get(i):this.model.empty_value)();e.get_array(i).push(t)}}_select_event(e,t,s){const o=this.plot_view.frame,{sx:n,sy:i}=e;if(!o.bbox.contains(n,i))return[];const c={type:\"point\",sx:n,sy:i},r=[];for(const e of s){const s=e.get_selection_manager(),o=e.data_source,n=this.plot_view.views.find_one(e);if(null!=n){s.select([n],c,!0,t)&&r.push(e),o.properties.selected.change.emit()}}return r}}s.EditToolView=d,d.__name__=\"EditToolView\";class u extends l.GestureTool{constructor(e){super(e)}}s.EditTool=u,i=u,u.__name__=\"EditTool\",i.define((({Unknown:e,Dict:t})=>({default_overrides:[t(e),{}],empty_value:[e,0]})))},\n function _(e,i,s,t,l){var n;t();const o=e(20),d=e(28),r=e(220),_=e(376),f=e(353),a=e(373),c=e(358),h=e(385),p=e(361),u=e(387),m=e(217),F=e(503),b=e(123),x=e(12),y=e(9);class g extends F.EditToolView{constructor(){super(...arguments),this._recent_renderers=[]}_tap(e){null==this._draw_basepoint&&null==this._basepoint&&(this._recent_renderers=this._select_event(e,this._select_mode(e),this.model.renderers))}_keyup(e){if(this.model.active&&this._mouse_in_frame)for(const i of this.model.renderers)if(\"Backspace\"==e.key)this._delete_selected(i);else if(\"Escape\"==e.key){i.data_source.selection_manager.clear()}}_set_extent([e,i],[s,t],l,n=!1){const o=this._recent_renderers[0]??this.model.renderers[0],r=this.plot_view.views.find_one(o);if(null==r)return;const{glyph:m}=o,F=o.data_source,b=(0,y.dict)(F.data),[g,w]=r.coordinates.x_scale.r_invert(e,i),[v,B]=r.coordinates.y_scale.r_invert(s,t),E=(()=>{if(m instanceof _.Rect){const{x:e,y:i,width:s,height:t}=m;if((0,d.isField)(e)&&(0,d.isField)(i)&&(0,d.isField)(s)&&(0,d.isField)(t))return{[e.field]:(g+w)/2,[i.field]:(v+B)/2,[s.field]:w-g,[t.field]:B-v}}else if(m instanceof f.Block){const{x:e,y:i,width:s,height:t}=m;if((0,d.isField)(e)&&(0,d.isField)(i)&&(0,d.isField)(s)&&(0,d.isField)(t))return{[e.field]:g,[i.field]:v,[s.field]:w-g,[t.field]:B-v}}else if(m instanceof a.Quad){const{right:e,bottom:i,left:s,top:t}=m;if((0,d.isField)(e)&&(0,d.isField)(i)&&(0,d.isField)(s)&&(0,d.isField)(t))return{[e.field]:w,[i.field]:v,[s.field]:g,[t.field]:B}}else if(m instanceof c.HBar){const{left:e,y:i,height:s,right:t}=m;if((0,d.isField)(e)&&(0,d.isField)(i)&&(0,d.isField)(s)&&(0,d.isField)(t))return{[e.field]:g,[i.field]:(v+B)/2,[s.field]:B-v,[t.field]:w}}else if(m instanceof h.VBar){const{x:e,bottom:i,width:s,top:t}=m;if((0,d.isField)(e)&&(0,d.isField)(i)&&(0,d.isField)(s)&&(0,d.isField)(t))return{[e.field]:(g+w)/2,[i.field]:v,[s.field]:w-g,[t.field]:B}}else if(m instanceof p.HStrip){const{y0:e,y1:i}=m;if((0,d.isField)(e)&&(0,d.isField)(i))return{[e.field]:v,[i.field]:B}}else if(m instanceof u.VStrip){const{x0:e,x1:i}=m;if((0,d.isField)(e)&&(0,d.isField)(i))return{[e.field]:g,[i.field]:w}}else(0,x.unreachable)(`'${m.type}' is not supported\"`);return null})();if(null!=E){if(l){this._pop_glyphs(F,this.model.num_objects);for(const[e,i]of(0,y.entries)(E))F.get_array(e).push(i);this._pad_empty_columns(F,(0,y.keys)(E))}else{const e=F.get_length();if(null==e)return;const i=e-1;for(const[e,s]of(0,y.entries)(E))b.get(e)[i]=s}this._emit_cds_changes(F,!0,!1,n)}}_update_box(e,i=!1,s=!1){if(null==this._draw_basepoint)return;const t=[e.sx,e.sy],l=this.plot_view.frame,n=this.model.dimensions,[o,d]=this.model._get_dim_limits(this._draw_basepoint,t,l,n);this._set_extent(o,d,i,s)}_press(e){this.model.active&&(null!=this._draw_basepoint?(this._update_box(e,!1,!0),this._draw_basepoint=null):(this._draw_basepoint=[e.sx,e.sy],this._select_event(e,\"append\",this.model.renderers),this._update_box(e,!0,!1)))}_move(e){this._update_box(e,!1,!1)}_pan_start(e){if(e.modifiers.shift){if(null!=this._draw_basepoint)return;this._draw_basepoint=[e.sx,e.sy],this._update_box(e,!0,!1)}else{if(null!=this._basepoint)return;this._recent_renderers=this._select_event(e,\"append\",this.model.renderers),this._basepoint=[e.sx,e.sy]}}_pan(e,i=!1,s=!1){if(e.modifiers.shift){if(null==this._draw_basepoint)return;this._update_box(e,i,s)}else{if(null==this._basepoint)return;this._drag_points(e,this.model.renderers)}}_drag_points(e,i,s=\"both\"){if(null==this._basepoint)return;const[t,l]=this._basepoint;for(const n of i){const i=this._map_drag(t,l,n),o=this._map_drag(e.sx,e.sy,n);if(null==o||null==i)continue;const[_,m]=o,[F,b]=i,g=\"width\"==s||\"both\"==s?_-F:0,w=\"height\"==s||\"both\"==s?m-b:0,{glyph:v}=n,B=n.data_source,E=(0,y.dict)(B.data),k={};if(v instanceof r.XYGlyph){const{x:e,y:i}=v;(0,d.isField)(e)&&(k[e.field]=g),(0,d.isField)(i)&&(k[i.field]=w)}else if(v instanceof f.Block){const{x:e,y:i}=v;(0,d.isField)(e)&&(k[e.field]=g),(0,d.isField)(i)&&(k[i.field]=w)}else if(v instanceof a.Quad){const{right:e,bottom:i,left:s,top:t}=v;(0,d.isField)(s)&&(0,d.isField)(e)&&(k[s.field]=g,k[e.field]=g),(0,d.isField)(t)&&(0,d.isField)(i)&&(k[t.field]=w,k[i.field]=w)}else if(v instanceof c.HBar){const{left:e,right:i,y:s}=v;(0,d.isField)(e)&&(0,d.isField)(i)&&(k[e.field]=g,k[i.field]=g),(0,d.isField)(s)&&(k[s.field]=w)}else if(v instanceof h.VBar){const{x:e,top:i,bottom:s}=v;(0,d.isField)(e)&&(k[e.field]=g),(0,d.isField)(i)&&(0,d.isField)(s)&&(k[i.field]=w,k[s.field]=w)}else if(v instanceof p.HStrip){const{y0:e,y1:i}=v;(0,d.isField)(e)&&(0,d.isField)(i)&&(k[e.field]=w,k[i.field]=w)}else if(v instanceof u.VStrip){const{x0:e,x1:i}=v;(0,d.isField)(e)&&(0,d.isField)(i)&&(k[e.field]=g,k[i.field]=g)}else(0,x.unreachable)(`'${v.type}' is not supported\"`);for(const e of B.selected.indices)for(const[i,s]of(0,y.entries)(k)){(E.get(i)??[])[e]+=s}B.change.emit()}this._basepoint=[e.sx,e.sy]}_pan_end(e){if(this._pan(e,!1,!0),e.modifiers.shift)this._draw_basepoint=null;else{this._basepoint=null;for(const e of this.model.renderers)this._emit_cds_changes(e.data_source,!1,!0,!0)}}}s.BoxEditToolView=g,g.__name__=\"BoxEditToolView\";class w extends F.EditTool{constructor(e){super(e),this.tool_name=\"Box Edit Tool\",this.tool_icon=b.tool_icon_box_edit,this.event_type=[\"tap\",\"press\",\"pan\",\"move\"],this.default_order=1}}s.BoxEditTool=w,n=w,w.__name__=\"BoxEditTool\",n.prototype.default_view=g,n.define((({Int:e,List:i,Ref:s})=>({dimensions:[o.Dimensions,\"both\"],num_objects:[e,0],renderers:[i(s(m.GlyphRenderer)),[]]})))},\n function _(e,t,r,a,s){var _;a();const o=e(9),n=e(8),d=e(503),i=e(217),l=e(123);class h extends d.EditToolView{_draw(e,t,r=!1){if(!this.model.active)return;const a=this.model.renderers[0],s=this._map_drag(e.sx,e.sy,a);if(null==s)return;const[_,d]=s,i=a.data_source,l=(0,o.dict)(i.data),h=a.glyph,[c,p]=[h.xs.field,h.ys.field];if(\"new\"==t)this._pop_glyphs(i,this.model.num_objects),c&&i.get_array(c).push([_]),p&&i.get_array(p).push([d]),this._pad_empty_columns(i,[c,p]);else if(\"add\"==t){if(c){const e=l.get(c)??[],t=e.length-1;let r=i.get_array(c)[t];(0,n.isArray)(r)||(r=Array.from(r),e[t]=r),r.push(_)}if(p){const e=l.get(p)??[],t=e.length-1;let r=i.get_array(p)[t];(0,n.isArray)(r)||(r=Array.from(r),e[t]=r),r.push(d)}}this._emit_cds_changes(i,!0,!0,r)}_pan_start(e){this._draw(e,\"new\")}_pan(e){this._draw(e,\"add\")}_pan_end(e){this._draw(e,\"add\",!0)}_tap(e){this._select_event(e,this._select_mode(e),this.model.renderers)}_keyup(e){if(this.model.active&&this._mouse_in_frame)for(const t of this.model.renderers)\"Escape\"==e.key?t.data_source.selection_manager.clear():\"Backspace\"==e.key&&this._delete_selected(t)}}r.FreehandDrawToolView=h,h.__name__=\"FreehandDrawToolView\";class c extends d.EditTool{constructor(e){super(e),this.tool_name=\"Freehand Draw Tool\",this.tool_icon=l.tool_icon_freehand_draw,this.event_type=[\"pan\",\"tap\"],this.default_order=3}}r.FreehandDrawTool=c,_=c,c.__name__=\"FreehandDrawTool\",_.prototype.default_view=h,_.define((({Int:e,List:t,Ref:r})=>({num_objects:[e,0],renderers:[t(r(i.GlyphRenderer)),[]]}))),_.register_alias(\"freehand_draw\",(()=>new _))},\n function _(e,t,s,i,r){var n;i();const _=e(20),d=e(9),o=e(217),l=e(507),c=e(123);class h extends l.LineToolView{constructor(){super(...arguments),this._drawing=!1}_press(e){if(!this.model.active)return;const t=this.model.renderers;for(const s of t){1==this._select_event(e,\"replace\",[s]).length&&(this._selected_renderer=s)}this._show_intersections(),this._update_line_cds()}_show_intersections(){if(!this.model.active)return;if(null==this._selected_renderer)return;if(0==this.model.renderers.length)return this._set_intersection([],[]),this._selected_renderer=null,void(this._drawing=!1);const e=this._selected_renderer.data_source,t=this._selected_renderer.glyph,[s,i]=[t.x.field,t.y.field],r=e.get_array(s),n=e.get_array(i);this._set_intersection(r,n)}_tap(e){const t=this.model.intersection_renderer;if(null==this._map_drag(e.sx,e.sy,t))return;if(this._drawing&&null!=this._selected_renderer){const s=this._select_mode(e);if(0==this._select_event(e,s,[t]).length)return}const s=this._select_mode(e);this._select_event(e,s,[t]),this._select_event(e,s,this.model.renderers)}_update_line_cds(){if(null==this._selected_renderer)return;const e=this.model.intersection_renderer.glyph,t=this.model.intersection_renderer.data_source,s=(0,d.dict)(t.data),[i,r]=[e.x.field,e.y.field];if(i&&r){const e=s.get(i),t=s.get(r);null!=e&&(0,d.dict)(this._selected_renderer.data_source.data).set(i,e),null!=t&&(0,d.dict)(this._selected_renderer.data_source.data).set(r,t)}this._emit_cds_changes(this._selected_renderer.data_source,!0,!0,!1)}_pan_start(e){this._select_event(e,\"append\",[this.model.intersection_renderer]),this._basepoint=[e.sx,e.sy]}_pan(e){null!=this._basepoint&&(this._drag_points(e,[this.model.intersection_renderer],this.model.dimensions),null!=this._selected_renderer&&this._selected_renderer.data_source.change.emit())}_pan_end(e){null!=this._basepoint&&(this._drag_points(e,[this.model.intersection_renderer]),this._emit_cds_changes(this.model.intersection_renderer.data_source,!1,!0,!0),null!=this._selected_renderer&&this._emit_cds_changes(this._selected_renderer.data_source),this._basepoint=null)}activate(){this._drawing=!0}deactivate(){null!=this._selected_renderer&&(this._drawing&&(this._drawing=!1),this._hide_intersections())}}s.LineEditToolView=h,h.__name__=\"LineEditToolView\";class a extends l.LineTool{constructor(e){super(e),this.tool_name=\"Line Edit Tool\",this.tool_icon=c.tool_icon_line_edit,this.event_type=[\"tap\",\"press\",\"pan\",\"move\"],this.default_order=4}get tooltip(){return this._get_dim_tooltip(this.dimensions)}}s.LineEditTool=a,n=a,a.__name__=\"LineEditTool\",n.prototype.default_view=h,n.define((({List:e,Ref:t})=>({dimensions:[_.Dimensions,\"both\"],renderers:[e(t(o.GlyphRenderer)),[]]})))},\n function _(e,i,t,n,s){var o;n();const r=e(9),_=e(8),c=e(503);class d extends c.EditToolView{_set_intersection(e,i){const t=this.model.intersection_renderer.glyph,n=this.model.intersection_renderer.data_source,s=(0,r.dict)(n.data),[o,c]=[t.x.field,t.y.field];o&&((0,_.isArray)(e)?s.set(o,e):t.x={value:e}),c&&((0,_.isArray)(i)?s.set(c,i):t.y={value:i}),this._emit_cds_changes(n,!0,!0,!1)}_hide_intersections(){this._set_intersection([],[])}}t.LineToolView=d,d.__name__=\"LineToolView\";class l extends c.EditTool{constructor(e){super(e)}}t.LineTool=l,o=l,l.__name__=\"LineTool\",o.define((({AnyRef:e})=>({intersection_renderer:[e()]})))},\n function _(e,t,s,o,i){var _;o();const a=e(217),n=e(503),r=e(123);class d extends n.EditToolView{_tap(e){if(0!=this._select_event(e,this._select_mode(e),this.model.renderers).length||!this.model.add)return;const t=this.model.renderers[0],s=this._map_drag(e.sx,e.sy,t);if(null==s)return;const o=t.glyph,i=t.data_source,[_,a]=[o.x.field,o.y.field],[n,r]=s;this._pop_glyphs(i,this.model.num_objects),_&&i.get_array(_).push(n),a&&i.get_array(a).push(r),this._pad_empty_columns(i,[_,a]);const{data:d}=i;i.setv({data:d},{check_eq:!1})}_keyup(e){if(this.model.active&&this._mouse_in_frame)for(const t of this.model.renderers)\"Backspace\"==e.key?this._delete_selected(t):\"Escape\"==e.key&&t.data_source.selection_manager.clear()}_pan_start(e){this.model.drag&&(this._select_event(e,\"append\",this.model.renderers),this._basepoint=[e.sx,e.sy])}_pan(e){this.model.drag&&null!=this._basepoint&&this._drag_points(e,this.model.renderers)}_pan_end(e){if(this.model.drag){this._pan(e);for(const e of this.model.renderers)this._emit_cds_changes(e.data_source,!1,!0,!0);this._basepoint=null}}}s.PointDrawToolView=d,d.__name__=\"PointDrawToolView\";class l extends n.EditTool{constructor(e){super(e),this.tool_name=\"Point Draw Tool\",this.tool_icon=r.tool_icon_point_draw,this.event_type=[\"tap\",\"pan\",\"move\"],this.default_order=2}}s.PointDrawTool=l,_=l,l.__name__=\"PointDrawTool\",_.prototype.default_view=d,_.define((({Bool:e,Int:t,List:s,Ref:o})=>({add:[e,!0],drag:[e,!0],num_objects:[t,0],renderers:[s(o(a.GlyphRenderer)),[]]})))},\n function _(e,t,s,i,r){var o;i();const n=e(9),a=e(8),_=e(217),l=e(510),d=e(123);class h extends l.PolyToolView{constructor(){super(...arguments),this._drawing=!1,this._initialized=!1}_tap(e){this._drawing?this._draw(e,\"add\",!0):this._select_event(e,this._select_mode(e),this.model.renderers)}_draw(e,t,s=!1){const i=this.model.renderers[0],r=this._map_drag(e.sx,e.sy,i);if(this._initialized||this.activate(),null==r)return;const[o,_]=this._snap_to_vertex(e,...r),l=i.data_source,d=(0,n.dict)(l.data),h=i.glyph,[c,g]=[h.xs.field,h.ys.field];if(\"new\"==t)this._pop_glyphs(l,this.model.num_objects),c&&l.get_array(c).push([o,o]),g&&l.get_array(g).push([_,_]),this._pad_empty_columns(l,[c,g]);else if(\"edit\"==t){if(c){const e=d.get(c)??[],t=e[e.length-1];t[t.length-1]=o}if(g){const e=d.get(g)??[],t=e[e.length-1];t[t.length-1]=_}}else if(\"add\"==t){if(c){const e=d.get(c)??[],t=e.length-1;let s=l.get_array(c)[t];const i=s[s.length-1];s[s.length-1]=o,(0,a.isArray)(s)||(s=Array.from(s),e[t]=s),s.push(i)}if(g){const e=d.get(g)??[],t=e.length-1;let s=l.get_array(g)[t];const i=s[s.length-1];s[s.length-1]=_,(0,a.isArray)(s)||(s=Array.from(s),e[t]=s),s.push(i)}}this._emit_cds_changes(l,!0,!1,s)}_show_vertices(){if(!this.model.active)return;const e=[],t=[];for(let s=0;sthis._show_vertices()))}this._initialized=!0}}deactivate(){this._drawing&&(this._remove(),this._drawing=!1),null!=this.model.vertex_renderer&&this._hide_vertices()}}s.PolyDrawToolView=h,h.__name__=\"PolyDrawToolView\";class c extends l.PolyTool{constructor(e){super(e),this.tool_name=\"Polygon Draw Tool\",this.tool_icon=d.tool_icon_poly_draw,this.event_type=[\"pan\",\"tap\",\"press\",\"move\"],this.default_order=3}}s.PolyDrawTool=c,o=c,c.__name__=\"PolyDrawTool\",o.prototype.default_view=h,o.define((({Bool:e,Int:t,List:s,Ref:i})=>({drag:[e,!0],num_objects:[t,0],renderers:[s(i(_.GlyphRenderer)),[]]})))},\n function _(e,t,r,s,l){var i;s();const o=e(9),_=e(8),n=e(12),d=e(503);class a extends d.EditToolView{_set_vertices(e,t){const{vertex_renderer:r}=this.model;(0,n.assert)(null!=r);const s=r.glyph,l=r.data_source,[i,d]=[s.x.field,s.y.field],a=(0,o.dict)(l.data);i&&((0,_.isArray)(e)?a.set(i,e):s.x={value:e}),d&&((0,_.isArray)(t)?a.set(d,t):s.y={value:t}),this._emit_cds_changes(l,!0,!0,!1)}_hide_vertices(){this._set_vertices([],[])}_snap_to_vertex(e,t,r){if(null!=this.model.vertex_renderer){const s=this._select_event(e,\"replace\",[this.model.vertex_renderer]),l=this.model.vertex_renderer.data_source,i=this.model.vertex_renderer.glyph,[_,n]=[i.x.field,i.y.field];if(0!=s.length){const e=l.selected.indices[0],s=(0,o.dict)(l.data);_&&(t=s.get(_)[e]),n&&(r=s.get(n)[e]),l.selection_manager.clear()}}return[t,r]}}r.PolyToolView=a,a.__name__=\"PolyToolView\";class c extends d.EditTool{constructor(e){super(e)}}r.PolyTool=c,i=c,c.__name__=\"PolyTool\",i.define((({AnyRef:e,Nullable:t})=>({vertex_renderer:[t(e()),null]})))},\n function _(e,t,r,s,i){var _;s();const n=e(8),d=e(9),l=e(217),c=e(510),a=e(123);class o extends c.PolyToolView{constructor(){super(...arguments),this._drawing=!1,this._cur_index=null}_press(e){if(null==this.model.vertex_renderer||!this.model.active)return;const t=this._map_drag(e.sx,e.sy,this.model.vertex_renderer);if(null==t)return;const[r,s]=t,i=this._select_event(e,\"replace\",[this.model.vertex_renderer]),_=this.model.vertex_renderer.data_source,n=this.model.vertex_renderer.glyph,[d,l]=[n.x.field,n.y.field];if(0!=i.length&&null!=this._selected_renderer){const e=_.selected.indices[0];this._drawing?(this._drawing=!1,_.selection_manager.clear()):(_.selected.indices=[e+1],d&&_.get_array(d).splice(e+1,0,r),l&&_.get_array(l).splice(e+1,0,s),this._drawing=!0),_.change.emit(),this._emit_cds_changes(this._selected_renderer.data_source)}else this._show_vertices(e)}_show_vertices(e){if(!this.model.active)return;if(0==this.model.renderers.length)return;const t=this.model.renderers[0],r=()=>this._update_vertices(t),s=t.data_source,i=this._select_event(e,\"replace\",this.model.renderers);if(0==i.length)return this._set_vertices([],[]),this._selected_renderer=null,this._drawing=!1,this._cur_index=null,void s.disconnect(s.properties.data.change,r);s.connect(s.properties.data.change,r),this._cur_index=i[0].data_source.selected.indices[0],this._update_vertices(i[0])}_update_vertices(e){const t=e.glyph,r=(0,d.dict)(e.data_source.data),s=this._cur_index,[i,_]=[t.xs.field,t.ys.field];if(this._drawing)return;if(null==s&&(i||_))return;let l,c;if(i&&null!=s){const e=r.get(i)??[];l=e[s],(0,n.isArray)(l)||(e[s]=l=Array.from(l))}else l=t.xs.value;if(_&&null!=s){const e=r.get(_)??[];c=e[s],(0,n.isArray)(c)||(e[s]=c=Array.from(c))}else c=t.ys.value;this._selected_renderer=e,this._set_vertices(l,c)}_move(e){if(this._drawing&&null!=this._selected_renderer){const t=this.model.vertex_renderer;if(null==t)return;const r=t.data_source,s=(0,d.dict)(r.data),i=t.glyph,_=this._map_drag(e.sx,e.sy,t);if(null==_)return;let[n,l]=_;const c=r.selected.indices;[n,l]=this._snap_to_vertex(e,n,l),r.selected.indices=c;const[a,o]=[i.x.field,i.y.field],h=c[0];a&&(s.get(a)[h]=n),o&&(s.get(o)[h]=l),r.change.emit(),this._selected_renderer.data_source.change.emit()}}_tap(e){const t=this.model.vertex_renderer;if(null==t)return;const r=this._map_drag(e.sx,e.sy,t);if(null==r)return;if(this._drawing&&null!=this._selected_renderer){let[s,i]=r;const _=t.data_source,n=t.glyph,[d,l]=[n.x.field,n.y.field],c=_.selected.indices;[s,i]=this._snap_to_vertex(e,s,i);const a=c[0];if(_.selected.indices=[a+1],d){const e=_.get_array(d),t=e[a];e[a]=s,e.splice(a+1,0,t)}if(l){const e=_.get_array(l),t=e[a];e[a]=i,e.splice(a+1,0,t)}return _.change.emit(),void this._emit_cds_changes(this._selected_renderer.data_source,!0,!1,!0)}const s=this._select_mode(e);this._select_event(e,s,[t]),this._select_event(e,s,this.model.renderers)}_remove_vertex(){if(!this._drawing||null==this._selected_renderer)return;const e=this.model.vertex_renderer;if(null==e)return;const t=e.data_source,r=e.glyph,s=t.selected.indices[0],[i,_]=[r.x.field,r.y.field];i&&t.get_array(i).splice(s,1),_&&t.get_array(_).splice(s,1),t.change.emit(),this._emit_cds_changes(this._selected_renderer.data_source)}_pan_start(e){null!=this.model.vertex_renderer&&(this._select_event(e,\"append\",[this.model.vertex_renderer]),this._basepoint=[e.sx,e.sy])}_pan(e){null!=this._basepoint&&null!=this.model.vertex_renderer&&(this._drag_points(e,[this.model.vertex_renderer]),null!=this._selected_renderer&&this._selected_renderer.data_source.change.emit())}_pan_end(e){null!=this._basepoint&&null!=this.model.vertex_renderer&&(this._drag_points(e,[this.model.vertex_renderer]),this._emit_cds_changes(this.model.vertex_renderer.data_source,!1,!0,!0),null!=this._selected_renderer&&this._emit_cds_changes(this._selected_renderer.data_source),this._basepoint=null)}_keyup(e){if(!this.model.active||!this._mouse_in_frame)return;let t;if(null!=this._selected_renderer){const{vertex_renderer:e}=this.model;t=null!=e?[e]:[]}else t=this.model.renderers;for(const r of t)\"Backspace\"==e.key?(this._delete_selected(r),null!=this._selected_renderer&&this._emit_cds_changes(this._selected_renderer.data_source)):\"Escape\"==e.key&&(this._drawing?(this._remove_vertex(),this._drawing=!1):null!=this._selected_renderer&&this._hide_vertices(),r.data_source.selection_manager.clear())}deactivate(){null!=this._selected_renderer&&(this._drawing&&(this._remove_vertex(),this._drawing=!1),this._hide_vertices())}}r.PolyEditToolView=o,o.__name__=\"PolyEditToolView\";class h extends c.PolyTool{constructor(e){super(e),this.tool_name=\"Poly Edit Tool\",this.tool_icon=a.tool_icon_poly_edit,this.event_type=[\"tap\",\"press\",\"pan\",\"move\"],this.default_order=4}}r.PolyEditTool=h,_=h,h.__name__=\"PolyEditTool\",_.prototype.default_view=o,_.define((({List:e,Ref:t})=>({renderers:[e(t(l.GlyphRenderer)),[]]})))},\n function _(o,l,e,T,t){T(),t(\"BoxSelectTool\",o(513).BoxSelectTool),t(\"BoxZoomTool\",o(516).BoxZoomTool),t(\"GestureTool\",o(299).GestureTool),t(\"LassoSelectTool\",o(517).LassoSelectTool),t(\"PanTool\",o(519).PanTool),t(\"PolySelectTool\",o(518).PolySelectTool),t(\"RangeTool\",o(520).RangeTool),t(\"SelectTool\",o(515).SelectTool),t(\"TapTool\",o(521).TapTool),t(\"WheelPanTool\",o(523).WheelPanTool),t(\"WheelZoomTool\",o(524).WheelZoomTool)},\n function _(t,e,i,o,s){var n;o();const l=t(1),_=t(514),r=t(262),a=t(60),c=t(20),h=l.__importStar(t(123));class p extends _.RegionSelectToolView{connect_signals(){super.connect_signals();const{pan:t}=this.model.overlay;this.connect(t,(([t,e])=>{if(\"pan\"==t&&this._is_continuous(e)||\"pan:end\"==t){const{left:t,top:i,right:o,bottom:s}=this.model.overlay;if(!(t instanceof a.Coordinate||i instanceof a.Coordinate||o instanceof a.Coordinate||s instanceof a.Coordinate)){const n=this._compute_lrtb({left:t,right:o,top:i,bottom:s});this._do_select([n.left,n.right],[n.top,n.bottom],!1,this._select_mode(e))}}}));const{active:e}=this.model.properties;this.on_change(e,(()=>{this.model.active||this.model.persistent||this._clear_overlay()}))}_compute_limits(t){const e=this.plot_view.frame,i=this.model.dimensions;let o=this._base_point;if(\"center\"==this.model.origin){const[e,i]=o,[s,n]=t;o=[e-(s-e),i-(n-i)]}return this.model._get_dim_limits(o,t,e,i)}_mappers(){const t=(t,e,i,o)=>{switch(t){case\"canvas\":return o;case\"screen\":return i;case\"data\":return e}},{overlay:e}=this.model,{frame:i,canvas:o}=this.plot_view,{x_scale:s,y_scale:n}=i,{x_view:l,y_view:_}=i.bbox,{x_screen:r,y_screen:a}=o.bbox;return{left:t(e.left_units,s,l,r),right:t(e.right_units,s,l,r),top:t(e.top_units,n,_,a),bottom:t(e.bottom_units,n,_,a)}}_compute_lrtb({left:t,right:e,top:i,bottom:o}){const s=this._mappers();return{left:s.left.compute(t),right:s.right.compute(e),top:s.top.compute(i),bottom:s.bottom.compute(o)}}_invert_lrtb({left:t,right:e,top:i,bottom:o}){const s=this._mappers();return{left:s.left.invert(t),right:s.right.invert(e),top:s.top.invert(i),bottom:s.bottom.invert(o)}}_pan_start(t){const{sx:e,sy:i}=t,{frame:o}=this.plot_view;o.bbox.contains(e,i)&&(this._clear_other_overlays(),this._base_point=[e,i])}_pan(t){if(null==this._base_point)return;const{sx:e,sy:i}=t,[o,s]=this._compute_limits([e,i]),[[n,l],[_,r]]=[o,s];this.model.overlay.update(this._invert_lrtb({left:n,right:l,top:_,bottom:r})),this._is_continuous(t.modifiers)&&this._do_select(o,s,!1,this._select_mode(t.modifiers))}_pan_end(t){if(null==this._base_point)return;const{sx:e,sy:i}=t,[o,s]=this._compute_limits([e,i]);this._do_select(o,s,!0,this._select_mode(t.modifiers)),this.model.persistent||this._clear_overlay(),this._base_point=null,this.plot_view.state.push(\"box_select\",{selection:this.plot_view.get_selection()})}get _is_selecting(){return null!=this._base_point}_stop(){this._clear_overlay(),this._base_point=null}_keyup(t){if(this.model.active){if(\"Escape\"==t.key){if(this._is_selecting)return void this._stop();if(this.model.overlay.visible)return void this._clear_overlay()}super._keyup(t)}}_clear_selection(){this.model.overlay.visible?this._clear_overlay():super._clear_selection()}_do_select([t,e],[i,o],s,n=\"replace\"){const{greedy:l}=this.model,_={type:\"rect\",sx0:t,sx1:e,sy0:i,sy1:o,greedy:l};this._select(_,s,n)}}i.BoxSelectToolView=p,p.__name__=\"BoxSelectToolView\";const m=()=>new r.BoxAnnotation({syncable:!1,level:\"overlay\",visible:!1,editable:!0,left:NaN,right:NaN,top:NaN,bottom:NaN,top_units:\"data\",left_units:\"data\",bottom_units:\"data\",right_units:\"data\",fill_color:\"lightgrey\",fill_alpha:.5,line_color:\"black\",line_alpha:1,line_width:2,line_dash:[4,4]});class d extends _.RegionSelectTool{constructor(t){super(t),this.tool_name=\"Box Select\",this.event_type=\"pan\",this.default_order=30}initialize(){super.initialize();const[t,e]=(()=>{switch(this.dimensions){case\"width\":return[\"x\",\"x\"];case\"height\":return[\"y\",\"y\"];case\"both\":return[\"all\",\"both\"]}})(),i=\"center\"==this.origin;this.overlay.setv({resizable:t,movable:e,symmetric:i})}get computed_icon(){const t=super.computed_icon;if(null!=t)return t;switch(this.dimensions){case\"both\":return`.${h.tool_icon_box_select}`;case\"width\":return`.${h.tool_icon_x_box_select}`;case\"height\":return`.${h.tool_icon_y_box_select}`}}get tooltip(){return this._get_dim_tooltip(this.dimensions)}}i.BoxSelectTool=d,n=d,d.__name__=\"BoxSelectTool\",n.prototype.default_view=p,n.define((({Ref:t})=>({dimensions:[c.Dimensions,\"both\"],overlay:[t(r.BoxAnnotation),m],origin:[c.BoxOrigin,\"corner\"]}))),n.register_alias(\"box_select\",(()=>new n)),n.register_alias(\"xbox_select\",(()=>new n({dimensions:\"width\"}))),n.register_alias(\"ybox_select\",(()=>new n({dimensions:\"height\"})))},\n function _(e,o,t,s,l){var n;s();const c=e(515),r=e(20);class i extends c.SelectToolView{get overlays(){return[...super.overlays,this.model.overlay]}_is_continuous(e){return this.model.continuous!=e.alt}_select(e,o,t){const s=this._computed_renderers_by_data_source();for(const[,l]of s){const s=l[0].get_selection_manager(),n=[];for(const e of l){const o=this.plot_view.views.find_one(e);null!=o&&n.push(o)}s.select(n,e,o,t)}this._emit_selection_event(e,o)}_clear_overlay(){super._clear_overlay(),this.model.overlay.clear()}}t.RegionSelectToolView=i,i.__name__=\"RegionSelectToolView\";class _ extends c.SelectTool{constructor(e){super(e)}}t.RegionSelectTool=_,n=_,_.__name__=\"RegionSelectTool\",n.define((({Bool:e})=>({mode:[r.RegionSelectionMode,\"replace\"],continuous:[e,!1],persistent:[e,!1],greedy:[e,!1]})))},\n function _(e,t,o,r,n){var i;r();const s=e(1),c=e(299),l=e(217),a=e(452),_=e(218),d=e(108),h=e(53),p=e(15),u=e(12),m=e(10),v=s.__importStar(e(123));class f extends c.GestureToolView{connect_signals(){super.connect_signals(),this.model.invert.connect((()=>this._invert_selection())),this.model.clear.connect((()=>this._clear_selection()))}get computed_renderers(){const{renderers:e}=this.model,t=this.plot_view.model.data_renderers;return(0,d.compute_renderers)(e,t)}_computed_renderers_by_data_source(){const e=new Map;for(const t of this.computed_renderers){let o;if(t instanceof l.GlyphRenderer)o=t.data_source;else{if(!(t instanceof a.GraphRenderer))continue;o=t.node_renderer.data_source}const r=e.get(o)??[];e.set(o,[...r,t])}return e}_clear_overlay(){}_clear_other_overlays(){for(const e of this.plot_view.tool_views.values())e instanceof f&&e!=this&&e._clear_overlay()}_clear_selection(){const{computed_renderers:e}=this,t=(0,m.uniq)(e.map((e=>e.selection_manager)));for(const e of t)e.clear();this.plot_view.request_paint(...e)}_invert_selection(){const{computed_renderers:e}=this,t=(0,m.uniq)(e.map((e=>e.selection_manager)));for(const e of t)e.invert();this.plot_view.request_paint(...e)}_select_mode(e){const{shift:t,ctrl:o}=e;return t||o?t&&!o?\"append\":!t&&o?\"intersect\":t&&o?\"subtract\":void(0,u.unreachable)():this.model.mode}_keyup(e){this.model.active&&\"Escape\"==e.key&&this._clear_selection()}_emit_selection_event(e,t=!0){const{x_scale:o,y_scale:r}=this.plot_view.frame,n=(()=>{switch(e.type){case\"point\":{const{sx:t,sy:n}=e,i=o.invert(t),s=r.invert(n);return{...e,x:i,y:s}}case\"span\":{const{sx:t,sy:n}=e,i=o.invert(t),s=r.invert(n);return{...e,x:i,y:s}}case\"rect\":{const{sx0:t,sx1:n,sy0:i,sy1:s}=e,[c,l]=o.r_invert(t,n),[a,_]=r.r_invert(i,s);return{...e,x0:c,y0:a,x1:l,y1:_}}case\"poly\":{const{sx:t,sy:n}=e,i=o.v_invert(t),s=r.v_invert(n);return{...e,x:i,y:s}}}})();this.plot_view.model.trigger_event(new h.SelectionGeometry(n,t))}}o.SelectToolView=f,f.__name__=\"SelectToolView\";class y extends c.GestureTool{constructor(e){super(e),this.invert=new p.Signal0(this,\"invert\"),this.clear=new p.Signal0(this,\"clear\")}get menu(){return[{icon:v.tool_icon_replace_mode,tooltip:\"Replace the current selection\",active:()=>\"replace\"==this.mode,handler:()=>{this.mode=\"replace\",this.active=!0}},{icon:v.tool_icon_append_mode,tooltip:\"Append to the current selection (Shift)\",active:()=>\"append\"==this.mode,handler:()=>{this.mode=\"append\",this.active=!0}},{icon:v.tool_icon_intersect_mode,tooltip:\"Intersect with the current selection (Ctrl)\",active:()=>\"intersect\"==this.mode,handler:()=>{this.mode=\"intersect\",this.active=!0}},{icon:v.tool_icon_subtract_mode,tooltip:\"Subtract from the current selection (Shift+Ctrl)\",active:()=>\"subtract\"==this.mode,handler:()=>{this.mode=\"subtract\",this.active=!0}},{icon:v.tool_icon_xor_mode,tooltip:\"Symmetric difference with the current selection\",active:()=>\"xor\"==this.mode,handler:()=>{this.mode=\"xor\",this.active=!0}},null,{icon:v.tool_icon_invert_selection,tooltip:\"Invert the current selection\",handler:()=>{this.invert.emit()}},{icon:v.tool_icon_clear_selection,tooltip:\"Clear the current selection and/or selection overlay (Esc)\",handler:()=>{this.clear.emit()}}]}}o.SelectTool=y,i=y,y.__name__=\"SelectTool\",i.define((({List:e,Ref:t,Or:o,Auto:r})=>({renderers:[o(e(t(_.DataRenderer)),r),\"auto\"]})))},\n function _(o,t,e,i,s){var n;i();const a=o(1),_=o(299),r=o(262),l=o(20),h=a.__importStar(o(123));class c extends _.GestureToolView{constructor(){super(...arguments),this._base_point=null}get overlays(){return[...super.overlays,this.model.overlay]}_match_aspect([o,t],[e,i],s){const n=s.bbox.aspect,a=s.bbox.h_range.end,_=s.bbox.h_range.start,r=s.bbox.v_range.end,l=s.bbox.v_range.start;let h=Math.abs(o-e),c=Math.abs(t-i);const m=0==c?0:h/c,[d]=m>=n?[1,m/n]:[n/m,1];let p,u,b,x;return o<=e?(p=o,u=o+h*d,u>a&&(u=a)):(u=o,p=o-h*d,p<_&&(p=_)),h=Math.abs(u-p),t<=i?(x=t,b=t+h/n,b>r&&(b=r)):(b=t,x=t-h/n,x{const{dimensions:e}=this.model;if(\"auto\"==e){const[e,i]=o,[s,n]=t,a=Math.abs(e-s),_=Math.abs(i-n),r=5;return ar?\"height\":a>r&&_new r.BoxAnnotation({syncable:!1,level:\"overlay\",visible:!1,editable:!1,left:NaN,right:NaN,top:NaN,bottom:NaN,top_units:\"canvas\",left_units:\"canvas\",bottom_units:\"canvas\",right_units:\"canvas\",fill_color:\"lightgrey\",fill_alpha:.5,line_color:\"black\",line_alpha:1,line_width:2,line_dash:[4,4]});class d extends _.GestureTool{constructor(o){super(o),this.tool_name=\"Box Zoom\",this.event_type=[\"pan\",\"doubletap\"],this.default_order=20}get event_role(){return\"pan\"}get computed_icon(){const o=super.computed_icon;if(null!=o)return o;switch(this.dimensions){case\"both\":return`.${h.tool_icon_box_zoom}`;case\"width\":return`.${h.tool_icon_x_box_zoom}`;case\"height\":return`.${h.tool_icon_y_box_zoom}`;case\"auto\":return`.${h.tool_icon_auto_box_zoom}`}}get tooltip(){return this._get_dim_tooltip(this.dimensions)}get menu(){return[{icon:h.tool_icon_box_zoom,tooltip:\"Box zoom in both dimensions\",active:()=>\"both\"==this.dimensions,handler:()=>{this.dimensions=\"both\",this.active=!0}},{icon:h.tool_icon_x_box_zoom,tooltip:\"Box zoom in x-dimension\",active:()=>\"width\"==this.dimensions,handler:()=>{this.dimensions=\"width\",this.active=!0}},{icon:h.tool_icon_y_box_zoom,tooltip:\"Box zoom in y-dimension\",active:()=>\"height\"==this.dimensions,handler:()=>{this.dimensions=\"height\",this.active=!0}},{icon:h.tool_icon_auto_box_zoom,tooltip:\"Automatic mode (box zoom in x, y or both dimensions, depending on the mouse gesture)\",active:()=>\"auto\"==this.dimensions,handler:()=>{this.dimensions=\"auto\",this.active=!0}}]}}e.BoxZoomTool=d,n=d,d.__name__=\"BoxZoomTool\",n.prototype.default_view=c,n.define((({Bool:o,Ref:t,Or:e,Auto:i})=>({dimensions:[e(l.Dimensions,i),\"both\"],overlay:[t(r.BoxAnnotation),m],match_aspect:[o,!1],origin:[l.BoxOrigin,\"corner\"]}))),n.register_alias(\"box_zoom\",(()=>new n({dimensions:\"both\"}))),n.register_alias(\"xbox_zoom\",(()=>new n({dimensions:\"width\"}))),n.register_alias(\"ybox_zoom\",(()=>new n({dimensions:\"height\"}))),n.register_alias(\"auto_box_zoom\",(()=>new n({dimensions:\"auto\"})))},\n function _(e,s,t,o,i){var _;o();const l=e(514),n=e(285),c=e(518),r=e(12),a=e(123);class h extends l.RegionSelectToolView{constructor(){super(...arguments),this._is_selecting=!1}_mappers(){const e=(e,s,t,o)=>{switch(e){case\"canvas\":return o;case\"screen\":return t;case\"data\":return s}},{overlay:s}=this.model,{frame:t,canvas:o}=this.plot_view,{x_scale:i,y_scale:_}=t,{x_view:l,y_view:n}=t.bbox,{x_screen:c,y_screen:r}=o.bbox;return{x:e(s.xs_units,i,l,c),y:e(s.ys_units,_,n,r)}}_v_compute(e,s){const{x:t,y:o}=this._mappers();return[t.v_compute(e),o.v_compute(s)]}_v_invert(e,s){const{x:t,y:o}=this._mappers();return[t.v_invert(e),o.v_invert(s)]}connect_signals(){super.connect_signals();const{pan:e}=this.model.overlay;this.connect(e,(([e,s])=>{if(\"pan\"==e&&this._is_continuous(s)||\"pan:end\"==e){const{xs:e,ys:t}=this.model.overlay,[o,i]=this._v_compute(e,t);this._do_select(o,i,!1,this._select_mode(s))}}));const{active:s}=this.model.properties;this.on_change(s,(()=>{this.model.active||this.model.persistent||this._clear_overlay()}))}_pan_start(e){const{sx:s,sy:t}=e,{frame:o}=this.plot_view;if(!o.bbox.contains(s,t))return;this._clear_other_overlays(),this._is_selecting=!0;const[i,_]=this._v_invert([s],[t]);this.model.overlay.update({xs:i,ys:_})}_pan(e){(0,r.assert)(this._is_selecting);const[s,t]=(()=>{const{xs:e,ys:s}=this.model.overlay,[t,o]=this._v_compute(e,s);return[[...t],[...o]]})(),[o,i]=this.plot_view.frame.bbox.clip(e.sx,e.sy);s.push(o),t.push(i);const[_,l]=this._v_invert(s,t);this.model.overlay.update({xs:_,ys:l}),this._is_continuous(e.modifiers)&&this._do_select(s,t,!1,this._select_mode(e.modifiers))}_pan_end(e){(0,r.assert)(this._is_selecting),this._is_selecting=!1;const{xs:s,ys:t}=this.model.overlay,[o,i]=this._v_compute(s,t);this._do_select(o,i,!0,this._select_mode(e.modifiers)),this.plot_view.state.push(\"lasso_select\",{selection:this.plot_view.get_selection()}),this.model.persistent||this._clear_overlay()}_keyup(e){this.model.active&&(\"Escape\"==e.key&&this.model.overlay.visible?this._clear_overlay():super._keyup(e))}_clear_selection(){this.model.overlay.visible?this._clear_overlay():super._clear_selection()}_do_select(e,s,t,o){const{greedy:i}=this.model,_={type:\"poly\",sx:e,sy:s,greedy:i};this._select(_,t,o)}}t.LassoSelectToolView=h,h.__name__=\"LassoSelectToolView\";class v extends l.RegionSelectTool{constructor(e){super(e),this.tool_name=\"Lasso Select\",this.tool_icon=a.tool_icon_lasso_select,this.event_type=\"pan\",this.default_order=12}}t.LassoSelectTool=v,_=v,v.__name__=\"LassoSelectTool\",_.prototype.default_view=h,_.define((({Ref:e})=>({overlay:[e(n.PolyAnnotation),c.DEFAULT_POLY_OVERLAY]}))),_.override({continuous:!0}),_.register_alias(\"lasso_select\",(()=>new _))},\n function _(e,s,t,i,o){var _;i();const l=e(514),n=e(285),c=e(123);class r extends l.RegionSelectToolView{constructor(){super(...arguments),this._is_selecting=!1}_mappers(){const e=(e,s,t,i)=>{switch(e){case\"canvas\":return i;case\"screen\":return t;case\"data\":return s}},{overlay:s}=this.model,{frame:t,canvas:i}=this.plot_view,{x_scale:o,y_scale:_}=t,{x_view:l,y_view:n}=t.bbox,{x_screen:c,y_screen:r}=i.bbox;return{x:e(s.xs_units,o,l,c),y:e(s.ys_units,_,n,r)}}_v_compute(e,s){const{x:t,y:i}=this._mappers();return[t.v_compute(e),i.v_compute(s)]}_v_invert(e,s){const{x:t,y:i}=this._mappers();return[t.v_invert(e),i.v_invert(s)]}connect_signals(){super.connect_signals();const{pan:e}=this.model.overlay;this.connect(e,(([e,s])=>{if(\"pan\"==e&&this._is_continuous(s)||\"pan:end\"==e&&!this._is_selecting){const{xs:e,ys:t}=this.model.overlay,[i,o]=this._v_compute(e,t);this._do_select(i,o,!1,this._select_mode(s))}}));const{active:s}=this.model.properties;this.on_change(s,(()=>{this.model.active||this.model.persistent||this._clear_overlay()}))}_tap(e){const{sx:s,sy:t}=e,{frame:i}=this.plot_view;if(!i.bbox.contains(s,t))return;this._clear_other_overlays();const[o,_]=(()=>{if(this._is_selecting){const{xs:e,ys:s}=this.model.overlay,[t,i]=this._v_compute(e,s);return[[...t],[...i]]}return this._is_selecting=!0,[[],[]]})();o.push(s),_.push(t);const[l,n]=this._v_invert(o,_);this.model.overlay.update({xs:l,ys:n}),this._is_continuous(e.modifiers)&&this._do_select(o,_,!0,this._select_mode(e.modifiers))}_finish_selection(e){this._is_selecting=!1;const{xs:s,ys:t}=this.model.overlay,[i,o]=this._v_compute(s,t);this._do_select(i,o,!0,this._select_mode(e)),this.plot_view.state.push(\"poly_select\",{selection:this.plot_view.get_selection()}),this.model.persistent||this._clear_overlay()}_press(e){this._finish_selection(e.modifiers)}_keyup(e){this.model.active&&(\"Enter\"!=e.key?\"Escape\"==e.key&&this.model.overlay.visible?this._clear_overlay():super._keyup(e):this._finish_selection(e.modifiers))}_clear_selection(){this.model.overlay.visible?this._clear_overlay():(this._is_selecting=!1,super._clear_selection())}_clear_overlay(){this._is_selecting=!1,super._clear_overlay()}_do_select(e,s,t,i){const{greedy:o}=this.model,_={type:\"poly\",sx:e,sy:s,greedy:o};this._select(_,t,i)}}t.PolySelectToolView=r,r.__name__=\"PolySelectToolView\";t.DEFAULT_POLY_OVERLAY=()=>new n.PolyAnnotation({syncable:!1,level:\"overlay\",visible:!1,editable:!0,xs_units:\"data\",ys_units:\"data\",fill_color:\"lightgrey\",fill_alpha:.5,line_color:\"black\",line_alpha:1,line_width:2,line_dash:[4,4]});class a extends l.RegionSelectTool{constructor(e){super(e),this.tool_name=\"Poly Select\",this.tool_icon=c.tool_icon_polygon_select,this.event_type=\"tap\",this.default_order=11}}t.PolySelectTool=a,_=a,a.__name__=\"PolySelectTool\",_.prototype.default_view=r,_.define((({Ref:e})=>({overlay:[e(n.PolyAnnotation),t.DEFAULT_POLY_OVERLAY]}))),_.register_alias(\"poly_select\",(()=>new _))},\n function _(t,i,n,s,e){var o;s();const a=t(1),_=t(299),r=t(20),h=a.__importStar(t(123));function d(t,i,n){const s=new Map;for(const[,e]of t){const[t,o]=e.r_invert(i,n);s.set(e.source_range,{start:t,end:o})}return s}n.update_ranges=d;class l extends _.GestureToolView{cursor(t,i){const n=this.plot_view.axis_views.find((n=>n.bbox.contains(t,i)));if(null==n)return this.plot_view.frame.bbox.contains(t,i)?\"move\":super.cursor(t,i);switch(n.dimension){case 0:return\"ew-resize\";case 1:return\"ns-resize\"}}_pan_start(t){this.last_dx=0,this.last_dy=0;const{sx:i,sy:n}=t,s=this.plot_view.frame.bbox;if(!s.contains(i,n)){const t=s.h_range,e=s.v_range;(it.end)&&(this.v_axis_only=!0),(ne.end)&&(this.h_axis_only=!0)}this.model.document?.interactive_start(this.plot_view.model)}_pan(t){this._update(t.dx,t.dy),this.model.document?.interactive_start(this.plot_view.model)}_pan_end(t){this.h_axis_only=!1,this.v_axis_only=!1,null!=this.pan_info&&this.plot_view.state.push(\"pan\",{range:this.pan_info}),this.plot_view.trigger_ranges_update_event()}_update(t,i){const n=this.plot_view.frame,s=t-this.last_dx,e=i-this.last_dy,o=n.bbox.h_range,a=o.start-s,_=o.end-s,r=n.bbox.v_range,h=r.start-e,l=r.end-e,c=this.model.dimensions;let p,m,u,v,w,x;\"width\"!=c&&\"both\"!=c||this.v_axis_only?(p=o.start,m=o.end,u=0):(p=a,m=_,u=-s),\"height\"!=c&&\"both\"!=c||this.h_axis_only?(v=r.start,w=r.end,x=0):(v=h,w=l,x=-e),this.last_dx=t,this.last_dy=i;const{x_scales:g,y_scales:y}=n,b=d(g,p,m),f=d(y,v,w);this.pan_info={xrs:b,yrs:f,sdx:u,sdy:x},this.plot_view.update_range(this.pan_info,{panning:!0})}}n.PanToolView=l,l.__name__=\"PanToolView\";class c extends _.GestureTool{constructor(t){super(t),this.tool_name=\"Pan\",this.event_type=\"pan\",this.default_order=10}get tooltip(){return this._get_dim_tooltip(this.dimensions)}get computed_icon(){const t=super.computed_icon;if(null!=t)return t;switch(this.dimensions){case\"both\":return`.${h.tool_icon_pan}`;case\"width\":return`.${h.tool_icon_x_pan}`;case\"height\":return`.${h.tool_icon_y_pan}`}}get menu(){return[{icon:h.tool_icon_pan,tooltip:\"Pan in both dimensions\",active:()=>\"both\"==this.dimensions,handler:()=>{this.dimensions=\"both\",this.active=!0}},{icon:h.tool_icon_x_pan,tooltip:\"Pan in x-dimension\",active:()=>\"width\"==this.dimensions,handler:()=>{this.dimensions=\"width\",this.active=!0}},{icon:h.tool_icon_y_pan,tooltip:\"Pan in y-dimension\",active:()=>\"height\"==this.dimensions,handler:()=>{this.dimensions=\"height\",this.active=!0}}]}}n.PanTool=c,o=c,c.__name__=\"PanTool\",o.prototype.default_view=l,o.define((()=>({dimensions:[r.Dimensions,\"both\"]}))),o.register_alias(\"pan\",(()=>new o({dimensions:\"both\"}))),o.register_alias(\"xpan\",(()=>new o({dimensions:\"width\"}))),o.register_alias(\"ypan\",(()=>new o({dimensions:\"height\"})))},\n function _(t,e,o,s,i){var n;s();const a=t(299),r=t(300),l=t(262),_=t(98),h=t(19),p=t(12),u=t(8),m=t(123),d=t(59),c=(0,t(21).Enum)(\"pan\",\"tap\",\"none\");class g extends a.GestureToolView{get overlays(){return[...super.overlays,this.model.overlay]}initialize(){super.initialize(),this.model.update_overlay_from_ranges()}connect_signals(){super.connect_signals();const t=()=>this.model.update_overlay_from_ranges();this.on_transitive_change(this.model.properties.x_range,t),this.on_transitive_change(this.model.properties.y_range,t),this.model.overlay.pan.connect((([t,e])=>{if(\"pan\"==t)this.model.update_ranges_from_overlay();else if(\"pan:end\"==t){const t=[this.model.x_range,this.model.y_range].filter(u.non_null);this.parent.trigger_ranges_update_event(t)}}));const{active:e,x_interaction:o,y_interaction:s}=this.model.properties;this.on_change([e,o,s],(()=>{this.model.update_constraints()}))}_mappers(){const t=(t,e,o,s)=>{switch(t){case\"canvas\":return s;case\"screen\":return o;case\"data\":return e}},{overlay:e}=this.model,{frame:o,canvas:s}=this.plot_view,{x_scale:i,y_scale:n}=o,{x_view:a,y_view:r}=o.bbox,{x_screen:l,y_screen:_}=s.bbox;return{left:t(e.left_units,i,a,l),right:t(e.right_units,i,a,l),top:t(e.top_units,n,r,_),bottom:t(e.bottom_units,n,r,_)}}_invert_lrtb({left:t,right:e,top:o,bottom:s}){const i=this._mappers(),{x_range:n,y_range:a}=this.model,r=null!=n,l=null!=a;return{left:r?i.left.invert(t):this.model.nodes.left,right:r?i.right.invert(e):this.model.nodes.right,top:l?i.top.invert(o):this.model.nodes.top,bottom:l?i.bottom.invert(s):this.model.nodes.bottom}}_compute_limits(t){const e=(()=>{const{x_range:t,y_range:e}=this.model,o=null!=t,s=null!=e;return o&&s?\"both\":o?\"width\":s?\"height\":void(0,p.unreachable)()})();(0,p.assert)(null!=this._base_point);let o=this._base_point;if(this.model.overlay.symmetric){const[e,s]=o,[i,n]=t;o=[e-(i-e),s-(n-s)]}const{frame:s}=this.plot_view;return this.model._get_dim_limits(o,t,s,e)}_tap(t){(0,p.assert)(\"tap\"==this.model.start_gesture);const{sx:e,sy:o}=t,{frame:s}=this.plot_view;s.bbox.contains(e,o)&&(null==this._base_point?this._base_point=[e,o]:(this._update_overlay(e,o),this._base_point=null))}_move(t){if(null!=this._base_point&&\"tap\"==this.model.start_gesture){const{sx:e,sy:o}=t;this._update_overlay(e,o)}}_pan_start(t){(0,p.assert)(\"pan\"==this.model.start_gesture),(0,p.assert)(null==this._base_point);const{sx:e,sy:o}=t,{frame:s}=this.plot_view;s.bbox.contains(e,o)&&(this._base_point=[e,o])}_update_overlay(t,e){const[o,s]=this._compute_limits([t,e]),[[i,n],[a,r]]=[o,s];this.model.overlay.update(this._invert_lrtb({left:i,right:n,top:a,bottom:r})),this.model.update_ranges_from_overlay()}_pan(t){if(null==this._base_point)return;const{sx:e,sy:o}=t;this._update_overlay(e,o)}_pan_end(t){if(null==this._base_point)return;const{sx:e,sy:o}=t;this._update_overlay(e,o),this._base_point=null}get _is_selecting(){return null!=this._base_point}_stop(){this._base_point=null}_keyup(t){this.model.active&&\"Escape\"==t.key&&this._is_selecting&&this._stop()}}o.RangeToolView=g,g.__name__=\"RangeToolView\";const v=()=>new l.BoxAnnotation({syncable:!1,level:\"overlay\",visible:!0,editable:!0,propagate_hover:!0,left:NaN,right:NaN,top:NaN,bottom:NaN,left_limit:d.Node.frame.left,right_limit:d.Node.frame.right,top_limit:d.Node.frame.top,bottom_limit:d.Node.frame.bottom,fill_color:\"lightgrey\",fill_alpha:.5,line_color:\"black\",line_alpha:1,line_width:.5,line_dash:[2,2]});class y extends a.GestureTool{constructor(t){super(t),this.nodes=d.Node.frame.freeze(),this.tool_name=\"Range Tool\",this.tool_icon=m.tool_icon_range,this.default_order=40}initialize(){super.initialize(),this.update_constraints()}update_constraints(){this.overlay.editable=this.active;const t=null!=this.x_range&&this.x_interaction,e=null!=this.y_range&&this.y_interaction;t&&e?(this.overlay.movable=\"both\",this.overlay.resizable=\"all\"):t?(this.overlay.movable=\"x\",this.overlay.resizable=\"x\"):e?(this.overlay.movable=\"y\",this.overlay.resizable=\"y\"):(this.overlay.movable=\"none\",this.overlay.resizable=\"none\");const{x_range:o,y_range:s}=this;null!=o&&(this.overlay.min_width=o.min_interval??0,this.overlay.max_width=o.max_interval??1/0),null!=s&&(this.overlay.min_height=s.min_interval??0,this.overlay.max_height=s.max_interval??1/0)}update_ranges_from_overlay(){const{left:t,right:e,top:o,bottom:s}=this.overlay,{x_range:i,y_range:n}=this,a=new Set,r=new Map,l=new Map;if(null!=i&&this.x_interaction){(0,p.assert)((0,u.isNumber)(t)&&(0,u.isNumber)(e)),r.set(i,{start:t,end:e});for(const t of i.linked_plots)a.add(t)}if(null!=n&&this.y_interaction){(0,p.assert)((0,u.isNumber)(s)&&(0,u.isNumber)(o)),l.set(n,{start:s,end:o});for(const t of n.linked_plots)a.add(t)}for(const t of a)t.update_range({xrs:r,yrs:l},{panning:!0,scrolling:!0})}update_overlay_from_ranges(){const{x_range:t,y_range:e}=this,o=null!=t,s=null!=e;this.overlay.update({left:o?t.start:this.nodes.left,right:o?t.end:this.nodes.right,top:s?e.end:this.nodes.top,bottom:s?e.start:this.nodes.bottom}),o||s||(h.logger.warn(\"RangeTool not configured with any Ranges.\"),this.overlay.clear())}get event_type(){switch(this.start_gesture){case\"pan\":return\"pan\";case\"tap\":return[\"tap\",\"move\"];case\"none\":return[]}}supports_auto(){return!0}tool_button(){return new r.OnOffButton({tool:this})}}o.RangeTool=y,n=y,y.__name__=\"RangeTool\",n.prototype.default_view=g,n.define((({Bool:t,Ref:e,Nullable:o})=>({x_range:[o(e(_.Range)),null],y_range:[o(e(_.Range)),null],x_interaction:[t,!0],y_interaction:[t,!0],overlay:[e(l.BoxAnnotation),v],start_gesture:[c,\"none\"]}))),n.override({active:!0})},\n function _(e,t,o,s,i){var l;s();const n=e(515),a=e(522),c=e(50),_=e(20),r=e(20),d=e(8),p=e(13),h=e(123);class u extends n.SelectToolView{_tap(e){const t=\"tap\"==this.model.gesture;return t&&this._handle_tap(e),t}_doubletap(e){const t=\"doubletap\"==this.model.gesture;return t&&this._handle_tap(e),t}_handle_tap(e){if(!(0,a.satisfies_modifiers)(this.model.modifiers,e.modifiers))return;const{sx:t,sy:o}=e,{frame:s}=this.plot_view;if(!s.bbox.contains(t,o))return;this._clear_other_overlays();const i={type:\"point\",sx:t,sy:o};\"select\"==this.model.behavior?this._select(i,!0,this._select_mode(e.modifiers)):this._inspect(i,e.modifiers)}_select(e,t,o){const s=this._computed_renderers_by_data_source();for(const[,i]of s){const s=i[0].get_selection_manager(),l=i.map((e=>this.plot_view.views.find_one(e))).filter(d.non_null);if(s.select(l,e,t,o)){const[t]=l;this._emit_callback(t,e,s.source)}}this._emit_selection_event(e),this.plot_view.state.push(\"tap\",{selection:this.plot_view.get_selection()})}_inspect(e,t){for(const o of this.computed_renderers){const s=this.plot_view.views.find_one(o);if(null==s)continue;const i=o.get_selection_manager();i.inspect(s,e)&&this._emit_callback(s,e,i.source,t)}}_emit_callback(e,t,o,s){const{callback:i}=this.model;if(null!=i){const l=e.coordinates.x_scale.invert(t.sx),n=e.coordinates.y_scale.invert(t.sy),a={geometries:{...t,x:l,y:n},source:o,event:{modifiers:s}};(0,c.execute)(i,this.model,a)}}}o.TapToolView=u,u.__name__=\"TapToolView\";class m extends n.SelectTool{constructor(e){super(e),this.tool_name=\"Tap\",this.tool_icon=h.tool_icon_tap_select,this.event_type=\"tap\",this.default_order=10}get menu(){const e=super.menu;return null==e?null:(0,p.prepend)(e,{icon:h.tool_icon_toggle_mode,tooltip:\"Toggle the current selection\",active:()=>\"toggle\"==this.mode,handler:()=>{this.mode=\"toggle\",this.active=!0}})}}o.TapTool=m,l=m,m.__name__=\"TapTool\",l.prototype.default_view=u,l.define((({Any:e,Nullable:t})=>({mode:[_.SelectionMode,\"toggle\"],behavior:[r.TapBehavior,\"select\"],gesture:[r.TapGesture,\"tap\"],modifiers:[a.Modifiers,{}],callback:[t(e),null]}))),l.register_alias(\"click\",(()=>new l({behavior:\"inspect\"}))),l.register_alias(\"tap\",(()=>new l)),l.register_alias(\"doubletap\",(()=>new l({gesture:\"doubletap\"})))},\n function _(t,l,i,s,n){s();const o=t(21);i.Modifiers=(0,o.PartialStruct)({shift:o.Bool,ctrl:o.Bool,alt:o.Bool}),i.satisfies_modifiers=function(t,l){const{alt:i,ctrl:s,shift:n}=t;return(null==n||n==l.shift)&&((null==s||s==l.ctrl)&&(null==i||i==l.alt))},i.print_modifiers=function(t){const{alt:l,ctrl:i,shift:s}=t,n=[];return!0===l&&n.push(\"alt\"),!0===i&&n.push(\"ctrl\"),!0===s&&n.push(\"shift\"),n.join(\" + \")}},\n function _(e,t,s,i,o){var n;i();const a=e(299),l=e(522),r=e(20),_=e(11),h=e(123),d=e(519);class p extends a.GestureToolView{_scroll(e){const{modifiers:t}=this.model;if(!(0,l.satisfies_modifiers)(t,e.modifiers))return this.plot_view.notify_about(`use ${(0,l.print_modifiers)(t)} + scroll to pan`),!1;const s=(0,_.clamp)(this.model.speed*e.delta,-.9,.9);return this._update_ranges(s),!0}_update_ranges(e){const{frame:t}=this.plot_view,s=t.bbox.h_range,i=t.bbox.v_range,[o,n]=[s.start,s.end],[a,l]=[i.start,i.end];let r,_,h,p;switch(this.model.dimension){case\"height\":{const t=Math.abs(l-a);r=o,_=n,h=a-t*e,p=l-t*e;break}case\"width\":{const t=Math.abs(n-o);r=o-t*e,_=n-t*e,h=a,p=l;break}}const{x_scales:c,y_scales:u}=t,m={xrs:(0,d.update_ranges)(c,r,_),yrs:(0,d.update_ranges)(u,h,p),factor:e};this.plot_view.state.push(\"wheel_pan\",{range:m}),this.plot_view.update_range(m,{scrolling:!0}),this.model.document?.interactive_start(this.plot_view.model,(()=>this.plot_view.trigger_ranges_update_event()))}}s.WheelPanToolView=p,p.__name__=\"WheelPanToolView\";class c extends a.GestureTool{constructor(e){super(e),this.tool_name=\"Wheel Pan\",this.tool_icon=h.tool_icon_wheel_pan,this.event_type=\"scroll\",this.default_order=12}get tooltip(){return this._get_dim_tooltip(this.dimension)}supports_auto(){const{alt:e,ctrl:t,shift:s}=this.modifiers;return null!=e||null!=t||null!=s}}s.WheelPanTool=c,n=c,c.__name__=\"WheelPanTool\",n.prototype.default_view=p,n.define((()=>({dimension:[r.Dimension,\"width\"],modifiers:[l.Modifiers,{}]}))),n.internal((({Float:e})=>({speed:[e,.001]}))),n.register_alias(\"xwheel_pan\",(()=>new n({dimension:\"width\"}))),n.register_alias(\"ywheel_pan\",(()=>new n({dimension:\"height\"})))},\n function _(e,t,o,s,n){var i;s();const l=e(299),r=e(522),a=e(218),c=e(105),_=e(420),d=e(500),h=e(20),m=e(19),u=e(123),f=e(21),p=(0,f.Enum)(\"none\",\"cross\",\"all\"),w=(0,f.Or)((0,f.List)((0,f.Ref)(a.DataRenderer)),f.Auto);class v extends l.GestureToolView{_scroll(e){const{modifiers:t}=this.model;if(!(0,r.satisfies_modifiers)(t,e.modifiers))return this.plot_view.notify_about(`use ${(0,r.print_modifiers)(t)} + scroll to zoom`),!1;const{sx:o,sy:s,delta:n}=e;return this.zoom(o,s,n),!0}_pinch(e){const{sx:t,sy:o,scale:s}=e,n=s>=1?20*(s-1):-20/s;this.zoom(t,o,n)}zoom(e,t,o){const s=this.plot_view.axis_views.find((o=>o.bbox.contains(e,t)));if(null!=s&&!this.model.zoom_on_axis)return;const{frame:n}=this.plot_view;if(null==s&&!n.bbox.contains(e,t))return;const[i,l]=(()=>{const e=[...n.x_scales.values()],t=[...n.y_scales.values()];if(null==s)return[e,t];{const{zoom_together:o}=this.model;if(\"all\"==o)return 0==s.dimension?[e,[]]:[[],t];{const{x_scale:e,y_scale:t}=s.coordinates;switch(o){case\"cross\":return[[e],[t]];case\"none\":return 0==s.dimension?[[e],[]]:[[],[t]]}}}})(),r=(()=>{const{renderers:o}=this.model,s=new Set(\"auto\"!=o?o:this.plot_view.model.data_renderers);if(this.model.hit_test){const o=new Set,n=new Set;for(const i of s){if(null==i.coordinates){o.add(i);continue}const s=(()=>{switch(this.model.hit_test_mode){case\"point\":return{type:\"point\",sx:e,sy:t};case\"hline\":return{type:\"span\",sx:e,sy:t,direction:\"v\"};case\"vline\":return{type:\"span\",sx:e,sy:t,direction:\"h\"}}})(),l=this.plot_view.views.get_one(i),r=l.hit_test(s);null==r||r.is_empty()||n.add(l.model)}if(0!=n.size){const{hit_test_behavior:e}=this.model;if(\"only_hit\"==e)for(const e of n)o.add(e);else for(const t of e.query_groups(n,s))for(const e of t)e instanceof a.DataRenderer&&s.has(e)&&o.add(e)}return[...o]}return s})(),_=new Set(i),h=new Set(l),u=new Set,f=new Set;for(const e of r){if(null==e.coordinates)continue;const t=this.plot_view.views.get_one(e),{x_scale:o,y_scale:s}=t.coordinates;o instanceof c.CompositeScale&&_.has(o.target_scale)&&u.add(o),s instanceof c.CompositeScale&&h.has(s.target_scale)&&f.add(s)}const[p,w]=(()=>\"auto\"==this.model.renderers?[new Set([..._,...u]),new Set([...h,...f])]:[u,f])(),v={x:!1,y:!1},y=(e,t)=>{const{level:o}=this.model;for(let s=0;s{const o=v.x?null:e,n=v.y?null:t;return null!=s?0==s.dimension?{x:o,y:null}:{x:null,y:n}:{x:o,y:n}})(),z=this.model.dimensions,b=\"width\"==z||\"both\"==z,T=\"height\"==z||\"both\"==z,{x_target:W,y_target:Z}=n,$=this.model.speed*o,C=(0,d.scale_range)(g,x,W,Z,$,b,T,S);this.plot_view.state.push(\"wheel_zoom\",{range:C});const{maintain_focus:R}=this.model;this.plot_view.update_range(C,{scrolling:!0,maintain_focus:R}),this.model.document?.interactive_start(this.plot_view.model,(()=>this.plot_view.trigger_ranges_update_event()))}}o.WheelZoomToolView=v,v.__name__=\"WheelZoomToolView\";class y extends l.GestureTool{constructor(e){super(e),this.tool_name=\"Wheel Zoom\",this.tool_icon=u.tool_icon_wheel_zoom,this.event_type=\"scroll\",this.default_order=10}get tooltip(){return this._get_dim_tooltip(this.dimensions)}supports_auto(){const{alt:e,ctrl:t,shift:o}=this.modifiers;return null!=e||null!=t||null!=o}}o.WheelZoomTool=y,i=y,y.__name__=\"WheelZoomTool\",i.prototype.default_view=v,i.define((({Bool:e,Float:t,NonNegative:o,Int:s,Ref:n,Or:i})=>({dimensions:[h.Dimensions,\"both\"],renderers:[w,\"auto\"],level:[o(s),0],hit_test:[e,!1],hit_test_mode:[(0,f.Enum)(\"point\",\"hline\",\"vline\"),\"point\"],hit_test_behavior:[i(n(_.GroupBy),(0,f.Enum)(\"only_hit\")),\"only_hit\"],maintain_focus:[e,!0],zoom_on_axis:[e,!0],zoom_together:[p,\"all\"],speed:[t,1/600],modifiers:[r.Modifiers,{}]}))),i.register_alias(\"wheel_zoom\",(()=>new i({dimensions:\"both\"}))),i.register_alias(\"xwheel_zoom\",(()=>new i({dimensions:\"width\"}))),i.register_alias(\"ywheel_zoom\",(()=>new i({dimensions:\"height\"})))},\n function _(o,r,s,e,l){e(),l(\"CrosshairTool\",o(526).CrosshairTool),l(\"CustomJSHover\",o(527).CustomJSHover),l(\"HoverTool\",o(528).HoverTool),l(\"InspectTool\",o(301).InspectTool)},\n function _(s,i,e,t,n){var o;t();const a=s(301),r=s(289),l=s(20),_=s(8),h=s(123);class c extends a.InspectToolView{get overlays(){return[...super.overlays,...this._spans]}initialize(){super.initialize(),this._update_overlays()}connect_signals(){super.connect_signals();const{overlay:s,dimensions:i,line_color:e,line_width:t,line_alpha:n}=this.model.properties;this.on_change([s,i,e,t,n],(()=>{this._update_overlays()}))}_update_overlays(){const{overlay:s}=this.model;if(\"auto\"==s){const{dimensions:i,line_color:e,line_alpha:t,line_width:n}=this.model;function o(s){return new r.Span({dimension:s,location_units:\"canvas\",level:\"overlay\",line_color:e,line_width:n,line_alpha:t})}switch(i){case\"width\":this._spans=[o(\"width\")];break;case\"height\":this._spans=[o(\"height\")];break;case\"both\":this._spans=[o(\"width\"),o(\"height\")]}}else(0,_.isArray)(s)?this._spans=[...s]:this._spans=[s]}_move(s){if(!this.model.active)return;const{sx:i,sy:e}=s;this.plot_view.frame.bbox.contains(i,e)?this._update_spans(i,e):this._update_spans(NaN,NaN)}_move_exit(s){this._update_spans(NaN,NaN)}_update_spans(s,i){const{frame:e}=this.plot_view;function t(s,i,t){const{dimension:n}=s;switch(s.location_units){case\"canvas\":return\"width\"==n?t:i;case\"screen\":{const{xview:s,yview:o}=e.bbox;return\"width\"==n?o.invert(t):s.invert(i)}case\"data\":{const{x_scale:s,y_scale:o}=e;return\"width\"==n?o.invert(t):s.invert(i)}}}for(const e of this._spans)e.location=t(e,s,i)}}e.CrosshairToolView=c,c.__name__=\"CrosshairToolView\";class p extends a.InspectTool{constructor(s){super(s),this.tool_name=\"Crosshair\",this.tool_icon=h.tool_icon_crosshair}get tooltip(){return this._get_dim_tooltip(this.dimensions)}}e.CrosshairTool=p,o=p,p.__name__=\"CrosshairTool\",o.prototype.default_view=c,o.define((({Alpha:s,Float:i,Color:e,Auto:t,Tuple:n,Ref:o,Or:a})=>({overlay:[a(t,o(r.Span),n(o(r.Span),o(r.Span))),\"auto\"],dimensions:[l.Dimensions,\"both\"],line_color:[e,\"black\"],line_width:[i,1],line_alpha:[s,1]}))),o.register_alias(\"crosshair\",(()=>new o)),o.register_alias(\"xcrosshair\",(()=>new o({dimensions:\"width\"}))),o.register_alias(\"ycrosshair\",(()=>new o({dimensions:\"height\"})))},\n function _(e,s,t,r,o){var a;r();const n=e(51),u=e(9),c=e(40);class i extends n.Model{constructor(e){super(e)}get values(){return(0,u.values)(this.args)}_make_code(e,s,t,r){return new Function(...(0,u.keys)(this.args),e,s,t,(0,c.use_strict)(r))}format(e,s,t){return this._make_code(\"value\",\"format\",\"special_vars\",this.code)(...this.values,e,s,t)}}t.CustomJSHover=i,a=i,i.__name__=\"CustomJSHover\",a.define((({Unknown:e,Str:s,Dict:t})=>({args:[t(e),{}],code:[s,\"\"]})))},\n function _(e,t,n,s,i){var o;s();const l=e(1),r=e(56),a=e(63),c=e(20),_=l.__importStar(e(228)),p=e(15),d=e(12),u=e(22),h=e(34),m=e(50),y=e(244),f=e(8),x=e(123),v=l.__importStar(e(415)),w=e(413),g=e(402),b=e(529),V=e(530),C=e(233),S=e(230),M=e(363),$=e(219),A=e(370),T=e(229),k=e(235),H=e(234),R=e(218),z=e(217),N=e(452),O=e(108),P=e(527),D=e(301);function G(e,t,n,s){const i={x:n[e],y:s[e]},o={x:n[e+1],y:s[e+1]},{sx:l,sy:r}=t,[a,c]=function(){if(\"span\"==t.type)return\"h\"==t.direction?[Math.abs(i.x-l),Math.abs(o.x-l)]:[Math.abs(i.y-r),Math.abs(o.y-r)];const e={x:l,y:r};return[_.dist_2_pts(i,e),_.dist_2_pts(o,e)]}();return adelete this._template_el)),this.on_change([e,t,n],(async()=>await this._update_ttmodels())),this.connect(this.plot_view.repainted,(()=>{if(this.model.active&&null!=this._current_sxy){const[e,t]=this._current_sxy;this._inspect(e,t)}}))}async _update_ttmodels(){const{ttmodels:e}=this;e.clear();const{tooltips:t}=this.model;if(null==t)return;const{computed_renderers:n}=this;for(const t of n){const n=new w.Tooltip({content:document.createElement(\"div\"),attachment:this.model.attachment,show_arrow:this.model.show_arrow,interactive:!1,visible:!0,position:null});t instanceof z.GlyphRenderer?e.set(t,n):t instanceof N.GraphRenderer&&(e.set(t.node_renderer,n),e.set(t.edge_renderer,n))}await(0,r.build_views)(this._ttviews,[...e.values()],{parent:this.plot_view});const s=[...function*(){for(const e of n)e instanceof z.GlyphRenderer?yield e:e instanceof N.GraphRenderer&&(yield e.node_renderer,yield e.edge_renderer)}()],i=this._slots.get(this.update);if(null!=i){const e=new Set(s.map((e=>e.data_source)));p.Signal.disconnect_receiver(this,i,e)}for(const e of s)this.connect(e.data_source.inspect,this.update)}get computed_renderers(){const{renderers:e}=this.model,t=this.plot_view.model.data_renderers;return(0,O.compute_renderers)(e,t)}_clear(){this._inspect(1/0,1/0);for(const[,e]of this.ttmodels)e.clear()}_move(e){if(!this.model.active)return;const{sx:t,sy:n}=e;this.plot_view.frame.bbox.contains(t,n)?(this._current_sxy=[t,n],this._inspect(t,n)):this._clear()}_move_exit(){this._current_sxy=null,this._clear()}_inspect(e,t){const n=(()=>{if(\"mouse\"==this.model.mode)return{type:\"point\",sx:e,sy:t};return{type:\"span\",direction:\"vline\"==this.model.mode?\"h\":\"v\",sx:e,sy:t}})();for(const e of this.computed_renderers){const t=e.get_selection_manager(),s=this.plot_view.views.find_one(e);null!=s&&t.inspect(s,n)}this._emit_callback(n)}_update(e,t,n){const s=e.get_selection_manager(),i=s.inspectors.get(e),o=e.view.convert_selection_to_subset(i);if(i.is_empty()&&null==i.view)return void n.clear();const l=s.source,r=this.plot_view.views.find_one(e);if(null==r)return;const{sx:c,sy:_}=t,p=r.coordinates.x_scale,u=r.coordinates.y_scale,h=p.invert(c),m=u.invert(_),{glyph:y}=r,f=[];if(y instanceof T.PatchView){const[t,n]=[c,_],[s,i]=[h,m],o={index:null,glyph_view:y,type:y.model.type,x:h,y:m,sx:c,sy:_,snap_x:s,snap_y:i,snap_sx:t,snap_sy:n,name:e.name},r=this._render_tooltips(l,o);f.push([t,n,r])}else if(y instanceof S.HAreaStepView||y instanceof C.HAreaView||y instanceof H.VAreaStepView||y instanceof k.VAreaView)for(const t of o.line_indices){const[n,s]=[h,m],[i,r]=[c,_],a={index:t,glyph_view:y,type:y.model.type,x:h,y:m,sx:c,sy:_,snap_x:n,snap_y:s,snap_sx:i,snap_sy:r,name:e.name,indices:o.line_indices},p=this._render_tooltips(l,a);f.push([i,r,p])}else if(y instanceof $.LineView){const{line_policy:n}=this.model;for(const s of o.line_indices){const[[i,a],[d,x],v]=(()=>{const{x:e,y:i}=y;switch(n){case\"interp\":{const[e,n]=y.get_interpolation_hit(s,t);return[[e,n],[p.compute(e),u.compute(n)],s]}case\"prev\":{const[t,n]=E(y.sx,y.sy,s);return[[e[s+1],i[s+1]],t,n]}case\"next\":{const[t,n]=E(y.sx,y.sy,s+1);return[[e[s+1],i[s+1]],t,n]}case\"nearest\":{const[n,o]=G(s,t,y.sx,y.sy);return[[e[o],i[o]],n,o]}case\"none\":{const e=r.coordinates.x_scale,t=r.coordinates.y_scale;return[[e.invert(c),t.invert(_)],[c,_],s]}}})(),w={index:v,glyph_view:y,type:y.model.type,x:h,y:m,sx:c,sy:_,snap_x:i,snap_y:a,snap_sx:d,snap_sy:x,name:e.name,indices:o.line_indices},g=this._render_tooltips(l,w);f.push([d,x,g])}}else if(y instanceof M.ImageBaseView)for(const t of i.image_indices){const[n,s]=[c,_],[i,o]=[h,m],r={index:t.index,glyph_view:y,type:y.model.type,x:h,y:m,sx:c,sy:_,snap_x:i,snap_y:o,snap_sx:n,snap_sy:s,name:e.name,image_index:t},a=this._render_tooltips(l,r);f.push([n,s,a])}else for(const n of o.indices)if(y instanceof A.MultiLineView&&0!=o.multiline_indices.size){const{line_policy:s}=this.model;for(const i of o.multiline_indices.get(n)??[]){const[[r,a],[x,v],w]=(()=>{if(\"interp\"==s){const[e,s]=y.get_interpolation_hit(n,i,t);return[[e,s],[p.compute(e),u.compute(s)],i]}const[e,o]=[y.xs.get(n),y.ys.get(n)];if(\"prev\"==s){const[t,s]=E(y.sxs.get(n),y.sys.get(n),i);return[[e[i],o[i]],t,s]}if(\"next\"==s){const[t,s]=E(y.sxs.get(n),y.sys.get(n),i+1);return[[e[i],o[i]],t,s]}if(\"nearest\"==s){const[s,l]=G(i,t,y.sxs.get(n),y.sys.get(n));return[[e[l],o[l]],s,l]}(0,d.unreachable)()})(),g={index:e.view.convert_indices_from_subset([n])[0],glyph_view:y,type:y.model.type,x:h,y:m,sx:c,sy:_,snap_x:r,snap_y:a,snap_sx:x,snap_sy:v,name:e.name,indices:o.multiline_indices,segment_index:w},b=this._render_tooltips(l,g);f.push([x,v,b])}}else{const t=y.x?.[n],s=y.y?.[n],{point_policy:i,anchor:r}=this.model,[a,p]=function(){if(\"snap_to_data\"==i){const e=y.get_anchor_point(r,n,[c,_]);if(null!=e)return[e.x,e.y];const t=y.get_anchor_point(\"center\",n,[c,_]);return null!=t?[t.x,t.y]:[c,_]}return[c,_]}(),d={index:e.view.convert_indices_from_subset([n])[0],glyph_view:y,type:y.model.type,x:h,y:m,sx:c,sy:_,snap_x:t,snap_y:s,snap_sx:a,snap_sy:p,name:e.name,indices:o.indices},u=this._render_tooltips(l,d);f.push([a,p,u])}const{bbox:x}=this.plot_view.frame,v=f.filter((([e,t])=>x.contains(e,t)));if(0==v.length)n.clear();else{const{content:e}=n;(0,d.assert)(e instanceof Node),(0,a.empty)(e);for(const[,,t]of v)null!=t&&e.appendChild(t);const[t,s]=v[v.length-1];n.show({x:t,y:s})}}update([e,{geometry:t}]){if(!this.model.active)return;if(\"point\"!=t.type&&\"span\"!=t.type)return;if(\"ignore\"==this.model.muted_policy&&e.muted)return;const n=this.ttmodels.get(e);(0,f.is_undefined)(n)||this._update(e,t,n)}_emit_callback(e){const{callback:t}=this.model;if(null!=t)for(const n of this.computed_renderers){if(!(n instanceof z.GlyphRenderer))continue;const s=this.plot_view.views.find_one(n);if(null==s)continue;const{x_scale:i,y_scale:o}=s.coordinates,l=i.invert(e.sx),r=o.invert(e.sy),a=n.data_source.inspected;(0,m.execute)(t,this.model,{geometry:{x:l,y:r,...e},renderer:n,index:a})}}_create_template(e){const t=(0,a.div)({style:{display:\"table\",borderSpacing:\"2px\"}});for(const[n]of e){const e=(0,a.div)({style:{display:\"table-row\"}});t.appendChild(e);const s=(0,a.div)({style:{display:\"table-cell\"},class:v.tooltip_row_label},0!=n.length?`${n}: `:\"\");e.appendChild(s);const i=(0,a.span)();i.dataset.value=\"\";const o=(0,a.span)({class:v.tooltip_color_block},\" \");o.dataset.swatch=\"\",(0,a.undisplay)(o);const l=(0,a.div)({style:{display:\"table-cell\"},class:v.tooltip_row_value},i,o);e.appendChild(l)}return t}_render_template(e,t,n,s){const i=e.cloneNode(!0),o=(0,f.is_undefined)(s.image_index)?s.index:s.image_index,l=i.querySelectorAll(\"[data-value]\"),r=i.querySelectorAll(\"[data-swatch]\"),c=/\\$color(\\[.*\\])?:(\\w*)/,_=/\\$swatch:(\\w*)/;for(const[[,e],i]of(0,h.enumerate)(t)){const t=e.match(_),p=e.match(c);if(null!=t||null!=p){if(null!=t){const[,e]=t,s=n.get_column(e);if(null==s)l[i].textContent=`${e} unknown`;else{const e=(0,f.isNumber)(o)?s[o]:null;null!=e&&(r[i].style.backgroundColor=(0,u.color2css)(e),(0,a.display)(r[i]))}}if(null!=p){const[,e=\"\",t]=p,s=n.get_column(t);if(null==s){l[i].textContent=`${t} unknown`;continue}const c=e.indexOf(\"hex\")>=0,_=e.indexOf(\"swatch\")>=0,d=(0,f.isNumber)(o)?s[o]:null;if(null==d){l[i].textContent=\"(null)\";continue}l[i].textContent=c?(0,u.color2hex)(d):(0,u.color2css)(d),_&&(r[i].style.backgroundColor=(0,u.color2css)(d),(0,a.display)(r[i]))}}else{const t=(0,y.replace_placeholders)(e.replace(\"$~\",\"$data_\"),n,o,this.model.formatters,s);if((0,f.isString)(t))l[i].textContent=t;else for(const e of t)l[i].appendChild(e)}}return i}_render_tooltips(e,t){const{tooltips:n}=this.model,s=t.index;if((0,f.isString)(n)){const i=(0,y.replace_placeholders)({html:n},e,s,this.model.formatters,t);return(0,a.div)(i)}if((0,f.isFunction)(n))return n(e,t);if(n instanceof g.DOMElement){const{_template_view:n}=this;return(0,d.assert)(null!=n),this._update_template(n,e,s,t),n.el.cloneNode(!0)}if(null!=n){const s=this._template_el??(this._template_el=this._create_template(n));return this._render_template(s,n,e,t)}return null}_update_template(e,t,n,s){const{formatters:i}=this.model;e instanceof V.TemplateView?e.update(t,n,s,i):(0,r.traverse_views)([e],(e=>{e instanceof b.PlaceholderView&&e.update(t,n,s,i)}))}}n.HoverToolView=L,L.__name__=\"HoverToolView\";class B extends D.InspectTool{constructor(e){super(e),this.tool_name=\"Hover\",this.tool_icon=x.tool_icon_hover}}n.HoverTool=B,o=B,B.__name__=\"HoverTool\",o.prototype.default_view=L,o.define((({Any:e,Bool:t,Str:n,List:s,Tuple:i,Dict:o,Or:l,Ref:r,Func:a,Auto:_,Nullable:p})=>({tooltips:[p(l(r(g.DOMElement),n,s(i(n,n)),a())),[[\"index\",\"$index\"],[\"data (x, y)\",\"($x, $y)\"],[\"screen (x, y)\",\"($sx, $sy)\"]]],formatters:[o(l(r(P.CustomJSHover),c.BuiltinFormatter)),{}],renderers:[l(s(r(R.DataRenderer)),_),\"auto\"],mode:[c.HoverMode,\"mouse\"],muted_policy:[c.MutedPolicy,\"show\"],point_policy:[c.PointPolicy,\"snap_to_data\"],line_policy:[c.LinePolicy,\"nearest\"],show_arrow:[t,!0],anchor:[c.Anchor,\"center\"],attachment:[c.TooltipAttachment,\"horizontal\"],callback:[p(e),null]}))),o.register_alias(\"hover\",(()=>new o))},\n function _(e,t,a,l,n){l();const o=e(402),r=e(314),s=e(527),c=e(20),_=e(21);a.Formatter=(0,_.Or)(c.BuiltinFormatter,(0,_.Ref)(r.CustomJS),(0,_.Ref)(s.CustomJSHover));class m extends o.DOMElementView{}a.PlaceholderView=m,m.__name__=\"PlaceholderView\",m.tag_name=\"span\";class d extends o.DOMElement{constructor(e){super(e)}}a.Placeholder=d,d.__name__=\"Placeholder\"},\n function _(e,i,t,s,a){var n;s();const l=e(402),o=e(531),c=e(529),_=e(56);class r extends l.DOMElementView{constructor(){super(...arguments),this.action_views=new Map}*children(){yield*super.children(),yield*this.action_views.values()}async lazy_initialize(){await super.lazy_initialize(),await(0,_.build_views)(this.action_views,this.model.actions,{parent:this})}remove(){(0,_.remove_views)(this.action_views),super.remove()}update(e,i,t,s){(0,_.traverse_views)([this],(a=>{a instanceof c.PlaceholderView&&a.update(e,i,t,s)}));for(const s of this.action_views.values())s.update(e,i,t)}}t.TemplateView=r,r.__name__=\"TemplateView\";class v extends l.DOMElement{}t.Template=v,n=v,v.__name__=\"Template\",n.prototype.default_view=r,n.define((({List:e,Ref:i})=>({actions:[e(i(o.Action)),[]]})))},\n function _(e,o,_,n,c){n();const s=e(51),t=e(58);class i extends t.View{}_.ActionView=i,i.__name__=\"ActionView\";class d extends s.Model{constructor(e){super(e)}}_.Action=d,d.__name__=\"Action\",d.__module__=\"bokeh.models.dom\"},\n function _(e,l,t,o,a){o(),a(\"Action\",e(531).Action),a(\"ColorRef\",e(533).ColorRef),a(\"DOMElement\",e(402).DOMElement),a(\"DOMNode\",e(125).DOMNode);var S=e(437);a(\"Span\",S.Span),a(\"Div\",S.Div),a(\"Table\",S.Table),a(\"TableRow\",S.TableRow),a(\"HTML\",e(401).HTML),a(\"Index\",e(535).Index),a(\"Placeholder\",e(529).Placeholder),a(\"Styles\",e(85).Styles);var n=e(86);a(\"InlineStyleSheet\",n.InlineStyleSheet),a(\"GlobalInlineStyleSheet\",n.GlobalInlineStyleSheet),a(\"ImportedStyleSheet\",n.ImportedStyleSheet),a(\"GlobalImportedStyleSheet\",n.GlobalImportedStyleSheet),a(\"Template\",e(530).Template),a(\"Text\",e(320).Text),a(\"ToggleGroup\",e(536).ToggleGroup),a(\"ValueOf\",e(537).ValueOf),a(\"ValueRef\",e(534).ValueRef)},\n function _(e,l,t,o,s){var a;o();const _=e(1),n=e(534),i=e(244),r=e(63),c=_.__importStar(e(415));class h extends n.ValueRefView{render(){super.render(),this.value_el=(0,r.span)(),this.swatch_el=(0,r.span)({class:c.tooltip_color_block},\" \"),this.el.appendChild(this.value_el),this.el.appendChild(this.swatch_el)}update(e,l,t,o){const s=(0,i._get_column_value)(this.model.field,e,l),a=null==s?\"???\":`${s}`;this.el.textContent=a}}t.ColorRefView=h,h.__name__=\"ColorRefView\";class d extends n.ValueRef{constructor(e){super(e)}}t.ColorRef=d,a=d,d.__name__=\"ColorRef\",a.prototype.default_view=h,a.define((({Bool:e})=>({hex:[e,!0],swatch:[e,!0]})))},\n function _(e,t,a,l,o){var n;l();const s=e(529),r=e(314),i=e(527),c=e(244),f=e(50),u=e(8);class d extends s.PlaceholderView{update(e,t,a,l){const{field:o,format:n,formatter:s}=this.model,d=(0,c._get_column_value)(o,e,t),m=e=>{null==e?this.el.textContent=c.MISSING:e instanceof Node?this.el.replaceChildren(e):(0,u.isArray)(e)?this.el.replaceChildren(...e.map((e=>e instanceof Node?e:`${e}`))):this.el.textContent=`${e}`};if(s instanceof r.CustomJS)(async()=>{const e=await(0,f.execute)(s,this.model,{value:d,format:n,vars:a});m(e)})();else{const e=null==n?c.DEFAULT_FORMATTERS.basic(d,\"\",a):s instanceof i.CustomJSHover?s.format(d,n,a):c.DEFAULT_FORMATTERS[s](d,n,a);m(e)}}}a.ValueRefView=d,d.__name__=\"ValueRefView\";class m extends s.Placeholder{constructor(e){super(e)}}a.ValueRef=m,n=m,m.__name__=\"ValueRef\",n.prototype.default_view=d,n.define((({Str:e,Nullable:t})=>({field:[e],format:[t(e),null],formatter:[s.Formatter,\"raw\"]})))},\n function _(e,n,t,l,d){var s;l();const _=e(529);class a extends _.PlaceholderView{update(e,n,t,l){this.el.textContent=null==n?\"(null)\":`${n}`}}t.IndexView=a,a.__name__=\"IndexView\";class o extends _.Placeholder{constructor(e){super(e)}}t.Index=o,s=o,o.__name__=\"Index\",s.prototype.default_view=a},\n function _(e,o,r,t,s){var n;t();const u=e(531),i=e(124),p=e(34);class g extends u.ActionView{update(e,o,r){for(const[e,r]of(0,p.enumerate)(this.model.groups))e.visible=o==r}}r.ToggleGroupView=g,g.__name__=\"ToggleGroupView\";class _ extends u.Action{constructor(e){super(e)}}r.ToggleGroup=_,n=_,_.__name__=\"ToggleGroup\",n.prototype.default_view=g,n.define((({List:e,Ref:o})=>({groups:[e(o(i.RendererGroup)),[]]})))},\n function _(e,t,n,s,r){var o;s();const i=e(402),a=e(14),l=e(42);class c extends i.DOMElementView{connect_signals(){super.connect_signals();const{obj:e,attr:t}=this.model;t in e.properties&&this.on_change(e.properties[t],(()=>this.render()))}render(){super.render(),this.el.style.display=\"contents\";const e=(()=>{const{obj:e,attr:t}=this.model;if(t in e.properties){const n=e.properties[t].get_value();return(0,l.to_string)(n)}return``})();this.el.textContent=e}}n.ValueOfView=c,c.__name__=\"ValueOfView\";class p extends i.DOMElement{constructor(e){super(e)}}n.ValueOf=p,o=p,p.__name__=\"ValueOf\",o.prototype.default_view=c,o.define((({Str:e,Ref:t})=>({obj:[t(a.HasProps)],attr:[e]})))},\n ], 0, {\"main\":0,\"tslib\":1,\"index\":2,\"version\":3,\"embed/index\":4,\"document/index\":5,\"document/document\":6,\"base\":7,\"core/util/types\":8,\"core/util/object\":9,\"core/util/array\":10,\"core/util/math\":11,\"core/util/assert\":12,\"core/util/arrayable\":13,\"core/has_props\":14,\"core/signaling\":15,\"core/util/defer\":16,\"core/util/refs\":17,\"core/properties\":18,\"core/logging\":19,\"core/enums\":20,\"core/kinds\":21,\"core/util/color\":22,\"core/util/svg_colors\":23,\"core/types\":24,\"core/util/bitset\":25,\"core/util/eq\":26,\"core/util/platform\":27,\"core/vectorization\":28,\"core/settings\":29,\"core/util/ndarray\":30,\"core/util/cloneable\":31,\"core/serialization/index\":32,\"core/serialization/serializer\":33,\"core/util/iterator\":34,\"core/serialization/buffer\":35,\"core/util/buffer\":36,\"core/serialization/reps\":37,\"core/diagnostics\":38,\"core/uniforms\":39,\"core/util/string\":40,\"document/events\":41,\"core/util/pretty\":42,\"core/patching\":43,\"core/util/set\":44,\"core/util/typed_array\":45,\"core/resolvers\":46,\"core/serialization/deserializer\":47,\"core/util/slice\":48,\"core/util/version\":49,\"core/util/callbacks\":50,\"model\":51,\"document/defs\":52,\"core/bokeh_events\":53,\"embed/standalone\":54,\"core/view_manager\":55,\"core/build_views\":56,\"core/dom_view\":57,\"core/view\":58,\"models/coordinates/node\":59,\"models/coordinates/coordinate\":60,\"models/coordinates/xy\":61,\"models/coordinates/indexed\":62,\"core/dom\":63,\"core/util/bbox\":64,\"core/css\":65,\"styles/base.css\":66,\"embed/server\":67,\"client/connection\":68,\"protocol/message\":69,\"protocol/receiver\":70,\"client/session\":71,\"embed/dom\":72,\"embed/notebook\":73,\"protocol/index\":74,\"safely\":75,\"models/main\":76,\"models/index\":77,\"models/annotations/index\":78,\"models/annotations/area_visuals\":79,\"core/property_mixins\":80,\"models/annotations/annotation\":81,\"models/renderers/composite_renderer\":82,\"models/renderers/renderer\":83,\"models/ui/styled_element\":84,\"models/dom/styles\":85,\"models/dom/stylesheets\":86,\"core/visuals/index\":87,\"core/visuals/line\":88,\"core/visuals/visual\":89,\"core/visuals/fill\":90,\"core/visuals/text\":91,\"core/visuals/hatch\":92,\"core/visuals/patterns\":93,\"core/visuals/image\":94,\"models/coordinates/coordinate_mapping\":95,\"models/scales/scale\":96,\"models/transforms/transform\":97,\"models/ranges/range\":98,\"models/ranges/range1d\":99,\"models/ranges/numerical_range\":100,\"models/scales/linear_scale\":101,\"models/scales/continuous_scale\":102,\"models/scales/log_scale\":103,\"models/scales/categorical_scale\":104,\"models/scales/composite_scale\":105,\"models/ranges/data_range1d\":106,\"models/ranges/data_range\":107,\"models/util\":108,\"models/ranges/factor_range\":109,\"models/ui/menus/menu\":110,\"models/ui/ui_element\":111,\"core/util/canvas\":112,\"core/util/svg\":113,\"core/util/affine\":114,\"core/util/random\":115,\"styles/ui.css\":116,\"models/ui/menus/menu_item\":117,\"models/ui/menus/action_item\":118,\"models/callbacks/callback\":119,\"models/ui/menus/checkable_item\":120,\"models/ui/menus/divider_item\":121,\"styles/menus_.css\":122,\"styles/icons.css\":123,\"models/renderers/renderer_group\":124,\"models/dom/dom_node\":125,\"models/annotations/arrow\":126,\"models/annotations/data_annotation\":127,\"models/sources/columnar_data_source\":128,\"core/selection_manager\":129,\"models/selections/selection\":130,\"models/selections/interaction_policy\":131,\"models/sources/data_source\":132,\"models/sources/column_data_source\":133,\"core/util/projections\":134,\"models/annotations/arrow_head\":168,\"models/graphics/marking\":169,\"models/annotations/base_color_bar\":170,\"models/annotations/title\":171,\"models/annotations/text_annotation\":172,\"core/layout/side_panel\":173,\"core/layout/types\":174,\"core/layout/layoutable\":175,\"models/text/base_text\":176,\"models/text/utils\":177,\"models/text/math_text\":178,\"core/util/image\":179,\"core/graphics\":180,\"core/util/text\":181,\"models/text/providers\":182,\"core/util/modules\":183,\"models/text/plain_text\":184,\"models/common/kinds\":185,\"models/common/resolve\":186,\"models/common/painting\":187,\"models/canvas/cartesian_frame\":188,\"models/axes/axis\":189,\"models/renderers/guide_renderer\":190,\"models/tickers/ticker\":191,\"models/formatters/tick_formatter\":192,\"models/policies/labeling\":193,\"models/axes/linear_axis\":194,\"models/axes/continuous_axis\":195,\"models/formatters/basic_tick_formatter\":196,\"models/tickers/basic_ticker\":197,\"models/tickers/adaptive_ticker\":198,\"models/tickers/continuous_ticker\":199,\"models/tickers/index\":200,\"models/tickers/categorical_ticker\":201,\"models/tickers/composite_ticker\":202,\"models/tickers/datetime_ticker\":203,\"models/tickers/days_ticker\":204,\"models/tickers/single_interval_ticker\":205,\"models/tickers/util\":206,\"models/tickers/months_ticker\":207,\"models/tickers/years_ticker\":208,\"models/tickers/fixed_ticker\":209,\"models/tickers/log_ticker\":210,\"models/tickers/mercator_ticker\":211,\"models/tickers/binned_ticker\":212,\"models/mappers/scanning_color_mapper\":213,\"models/mappers/continuous_color_mapper\":214,\"models/mappers/color_mapper\":215,\"models/mappers/mapper\":216,\"models/renderers/glyph_renderer\":217,\"models/renderers/data_renderer\":218,\"models/glyphs/line\":219,\"models/glyphs/xy_glyph\":220,\"models/glyphs/glyph\":221,\"core/util/ragged_array\":222,\"core/util/spatial\":223,\"models/graphics/decoration\":226,\"models/glyphs/utils\":227,\"core/hittest\":228,\"models/glyphs/patch\":229,\"models/glyphs/harea_step\":230,\"models/glyphs/area\":231,\"core/util/flip_step_mode\":232,\"models/glyphs/harea\":233,\"models/glyphs/varea_step\":234,\"models/glyphs/varea\":235,\"models/sources/cds_view\":236,\"models/filters/filter\":237,\"models/filters/all_indices\":238,\"models/filters/intersection_filter\":239,\"models/filters/composite_filter\":240,\"models/formatters/index\":241,\"models/formatters/categorical_tick_formatter\":242,\"models/formatters/datetime_tick_formatter\":243,\"core/util/templating\":244,\"models/formatters/customjs_tick_formatter\":248,\"models/formatters/log_tick_formatter\":249,\"models/formatters/mercator_tick_formatter\":250,\"models/formatters/numeral_tick_formatter\":251,\"models/formatters/printf_tick_formatter\":252,\"models/scales/index\":253,\"models/scales/linear_interpolation_scale\":254,\"models/ranges/index\":255,\"core/layout/index\":256,\"core/layout/alignments\":257,\"core/layout/grid\":258,\"core/layout/border\":259,\"models/annotations/band\":260,\"models/annotations/upper_lower\":261,\"models/annotations/box_annotation\":262,\"models/common/box_kinds\":263,\"models/annotations/color_bar\":264,\"models/axes/index\":265,\"models/axes/categorical_axis\":266,\"models/axes/datetime_axis\":267,\"models/axes/log_axis\":268,\"models/axes/mercator_axis\":269,\"models/mappers/index\":270,\"models/mappers/categorical_color_mapper\":271,\"models/mappers/categorical_mapper\":272,\"models/mappers/categorical_marker_mapper\":273,\"models/mappers/categorical_pattern_mapper\":274,\"models/mappers/linear_color_mapper\":275,\"models/mappers/log_color_mapper\":276,\"models/mappers/eqhist_color_mapper\":277,\"models/mappers/stack_color_mapper\":278,\"models/mappers/weighted_stack_color_mapper\":279,\"models/annotations/contour_color_bar\":280,\"models/annotations/label\":281,\"models/annotations/label_set\":282,\"models/annotations/legend\":283,\"models/annotations/legend_item\":284,\"models/annotations/poly_annotation\":285,\"models/annotations/scale_bar\":286,\"models/annotations/dimensional\":287,\"models/annotations/slope\":288,\"models/annotations/span\":289,\"models/annotations/toolbar_panel\":290,\"models/tools/toolbar\":291,\"models/tools/tool\":292,\"models/tools/tool_proxy\":293,\"models/tools/tool_button\":294,\"core/ui_gestures\":295,\"core/util/menus\":296,\"styles/menus.css\":297,\"styles/tool_button.css\":298,\"models/tools/gestures/gesture_tool\":299,\"models/tools/on_off_button\":300,\"models/tools/inspectors/inspect_tool\":301,\"models/tools/actions/action_tool\":302,\"models/tools/click_button\":303,\"models/tools/actions/help_tool\":304,\"styles/toolbar.css\":305,\"styles/logo.css\":306,\"models/annotations/whisker\":307,\"models/annotations/html/index\":308,\"models/annotations/html/label\":309,\"models/annotations/html/text_annotation\":310,\"models/annotations/html/label_set\":311,\"models/annotations/html/title\":312,\"models/callbacks/index\":313,\"models/callbacks/customjs\":314,\"models/callbacks/open_url\":315,\"models/callbacks/set_value\":316,\"models/callbacks/toggle_visibility\":317,\"models/callbacks/open_dialog\":318,\"models/ui/dialog\":319,\"models/dom/text\":320,\"styles/dialogs.css\":321,\"models/callbacks/close_dialog\":322,\"models/canvas/index\":323,\"models/canvas/canvas\":324,\"core/ui_events\":325,\"core/util/wheel\":326,\"styles/canvas.css\":327,\"models/coordinates/index\":328,\"models/expressions/index\":329,\"models/expressions/expression\":330,\"models/expressions/customjs_expr\":331,\"models/expressions/stack\":332,\"models/expressions/cumsum\":333,\"models/expressions/minimum\":334,\"models/expressions/maximum\":335,\"models/expressions/coordinate_transform\":336,\"models/expressions/polar\":337,\"models/filters/index\":338,\"models/filters/boolean_filter\":339,\"models/filters/customjs_filter\":340,\"models/filters/group_filter\":341,\"models/filters/index_filter\":342,\"models/filters/inversion_filter\":343,\"models/filters/union_filter\":344,\"models/filters/difference_filter\":345,\"models/filters/symmetric_difference_filter\":346,\"models/glyphs/index\":347,\"models/glyphs/annular_wedge\":348,\"models/glyphs/annulus\":349,\"models/glyphs/arc\":350,\"models/glyphs/bezier\":351,\"core/util/algorithms\":352,\"models/glyphs/block\":353,\"models/glyphs/lrtb\":354,\"models/glyphs/circle\":355,\"models/glyphs/ellipse\":356,\"models/glyphs/center_rotatable\":357,\"models/glyphs/hbar\":358,\"models/glyphs/hex_tile\":359,\"models/glyphs/hspan\":360,\"models/glyphs/hstrip\":361,\"models/glyphs/image\":362,\"models/glyphs/image_base\":363,\"models/glyphs/image_rgba\":364,\"models/glyphs/image_stack\":365,\"models/glyphs/image_url\":366,\"models/glyphs/mathml_glyph\":367,\"models/glyphs/math_text_glyph\":368,\"models/glyphs/text\":369,\"models/glyphs/multi_line\":370,\"models/glyphs/multi_polygons\":371,\"models/glyphs/patches\":372,\"models/glyphs/quad\":373,\"models/glyphs/quadratic\":374,\"models/glyphs/ray\":375,\"models/glyphs/rect\":376,\"models/glyphs/scatter\":377,\"models/glyphs/marker\":378,\"models/glyphs/defs\":379,\"models/glyphs/segment\":380,\"models/glyphs/spline\":381,\"core/util/interpolation\":382,\"models/glyphs/step\":383,\"models/glyphs/tex_glyph\":384,\"models/glyphs/vbar\":385,\"models/glyphs/vspan\":386,\"models/glyphs/vstrip\":387,\"models/glyphs/wedge\":388,\"models/graphics/index\":389,\"models/graphs/index\":390,\"models/graphs/graph_hit_test_policy\":391,\"models/graphs/layout_provider\":392,\"models/graphs/static_layout_provider\":393,\"models/grids/index\":394,\"models/grids/grid\":395,\"models/layouts/index\":396,\"models/layouts/column\":397,\"models/layouts/flex_box\":398,\"models/layouts/layout_dom\":399,\"models/ui/pane\":400,\"models/dom/html\":401,\"models/dom/dom_element\":402,\"models/layouts/alignments\":403,\"models/layouts/grid_box\":404,\"models/layouts/css_grid_box\":405,\"models/layouts/group_box\":406,\"styles/group_box.css\":407,\"models/layouts/hbox\":408,\"models/layouts/row\":409,\"models/layouts/scroll_box\":410,\"models/layouts/spacer\":411,\"models/layouts/tab_panel\":412,\"models/ui/tooltip\":413,\"models/selectors/selector\":414,\"styles/tooltips.css\":415,\"models/layouts/tabs\":416,\"styles/tabs.css\":417,\"models/layouts/vbox\":418,\"models/misc/index\":419,\"models/misc/group_by\":420,\"models/text/index\":421,\"models/transforms/index\":422,\"models/transforms/customjs_transform\":423,\"models/transforms/dodge\":424,\"models/transforms/range_transform\":425,\"models/transforms/interpolator\":426,\"models/transforms/jitter\":427,\"models/random/random_generator\":428,\"models/transforms/linear_interpolator\":429,\"models/transforms/step_interpolator\":430,\"models/plots/index\":431,\"models/plots/gmap_plot\":432,\"models/plots/plot\":433,\"models/plots/plot_canvas\":434,\"models/ui/panel\":435,\"styles/panels.css\":436,\"models/dom/elements\":437,\"core/util/throttle\":438,\"models/plots/range_manager\":439,\"models/plots/state_manager\":440,\"styles/plots.css\":441,\"styles/attribution.css\":442,\"models/plots/gmap_plot_canvas\":443,\"models/plots/gmap\":444,\"models/plots/grid_plot\":445,\"models/plots/figure\":446,\"models/policies/index\":447,\"models/random/index\":448,\"models/random/park_miller_lcg\":449,\"models/renderers/index\":450,\"models/renderers/contour_renderer\":451,\"models/renderers/graph_renderer\":452,\"models/selections/index\":453,\"models/selectors/index\":454,\"models/selectors/by_id\":455,\"models/selectors/by_class\":456,\"models/selectors/by_css\":457,\"models/selectors/by_xpath\":458,\"models/sources/index\":459,\"models/sources/server_sent_data_source\":460,\"models/sources/web_data_source\":461,\"models/sources/ajax_data_source\":462,\"models/sources/geojson_data_source\":463,\"models/tiles/index\":464,\"models/tiles/bbox_tile_source\":465,\"models/tiles/mercator_tile_source\":466,\"models/tiles/tile_source\":467,\"models/tiles/tile_utils\":468,\"models/tiles/quadkey_tile_source\":469,\"models/tiles/tile_renderer\":470,\"models/tiles/wmts_tile_source\":471,\"models/tiles/tms_tile_source\":472,\"models/textures/index\":473,\"models/textures/canvas_texture\":474,\"models/textures/texture\":475,\"models/textures/image_url_texture\":476,\"models/ui/index\":477,\"models/ui/icons/index\":478,\"models/ui/icons/builtin_icon\":479,\"models/ui/icons/icon\":480,\"models/ui/icons/svg_icon\":481,\"models/ui/icons/tabler_icon\":482,\"models/ui/menus/index\":483,\"models/ui/examiner\":484,\"styles/examiner.css\":485,\"styles/pretty.css\":486,\"models/tools/index\":487,\"models/tools/actions/index\":488,\"models/tools/actions/copy_tool\":489,\"models/tools/actions/custom_action\":490,\"models/tools/actions/fullscreen_tool\":491,\"models/tools/actions/examine_tool\":492,\"models/tools/actions/redo_tool\":493,\"models/tools/actions/plot_action_tool\":494,\"models/tools/actions/reset_tool\":495,\"models/tools/actions/save_tool\":496,\"models/tools/actions/undo_tool\":497,\"models/tools/actions/zoom_in_tool\":498,\"models/tools/actions/zoom_base_tool\":499,\"core/util/zoom\":500,\"models/tools/actions/zoom_out_tool\":501,\"models/tools/edit/index\":502,\"models/tools/edit/edit_tool\":503,\"models/tools/edit/box_edit_tool\":504,\"models/tools/edit/freehand_draw_tool\":505,\"models/tools/edit/line_edit_tool\":506,\"models/tools/edit/line_tool\":507,\"models/tools/edit/point_draw_tool\":508,\"models/tools/edit/poly_draw_tool\":509,\"models/tools/edit/poly_tool\":510,\"models/tools/edit/poly_edit_tool\":511,\"models/tools/gestures/index\":512,\"models/tools/gestures/box_select_tool\":513,\"models/tools/gestures/region_select_tool\":514,\"models/tools/gestures/select_tool\":515,\"models/tools/gestures/box_zoom_tool\":516,\"models/tools/gestures/lasso_select_tool\":517,\"models/tools/gestures/poly_select_tool\":518,\"models/tools/gestures/pan_tool\":519,\"models/tools/gestures/range_tool\":520,\"models/tools/gestures/tap_tool\":521,\"models/tools/gestures/common\":522,\"models/tools/gestures/wheel_pan_tool\":523,\"models/tools/gestures/wheel_zoom_tool\":524,\"models/tools/inspectors/index\":525,\"models/tools/inspectors/crosshair_tool\":526,\"models/tools/inspectors/customjs_hover\":527,\"models/tools/inspectors/hover_tool\":528,\"models/dom/placeholder\":529,\"models/dom/template\":530,\"models/dom/action\":531,\"models/dom/index\":532,\"models/dom/color_ref\":533,\"models/dom/value_ref\":534,\"models/dom/index_\":535,\"models/dom/toggle_group\":536,\"models/dom/value_of\":537}, {});});\n\n /* END bokeh.min.js */\n },\n function(Bokeh) {\n /* BEGIN bokeh-gl.min.js */\n 'use strict';\n /*!\n * Copyright (c) Anaconda, Inc., and Bokeh Contributors\n * All rights reserved.\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n * \n * Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n * \n * Neither the name of Anaconda nor the names of any contributors\n * may be used to endorse or promote products derived from this software\n * without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n (function(root, factory) {\n factory(root[\"Bokeh\"], \"3.5.2\");\n })(this, function(Bokeh, version) {\n let define;\n return (function(modules, entry, aliases, externals) {\n const bokeh = typeof Bokeh !== \"undefined\" ? (version != null ? Bokeh[version] : Bokeh) : null;\n if (bokeh != null) {\n return bokeh.register_plugin(modules, entry, aliases);\n } else {\n throw new Error(\"Cannot find Bokeh\" + (version != null ? \" \" + version : \"\") + \". You have to load it prior to loading plugins.\");\n }\n })\n ({\n 538: function _(n,c,f,i,o){i(),n(539)},\n 539: function _(t,_,r,e,o){e();const a=t(1);o(\"get_regl\",t(540).get_regl),a.__exportStar(t(552),r),a.__exportStar(t(559),r),a.__exportStar(t(560),r),a.__exportStar(t(555),r),a.__exportStar(t(561),r),a.__exportStar(t(562),r),a.__exportStar(t(563),r),a.__exportStar(t(564),r),a.__exportStar(t(566),r),a.__exportStar(t(567),r),a.__exportStar(t(568),r),a.__exportStar(t(569),r),a.__exportStar(t(565),r),a.__exportStar(t(554),r),a.__exportStar(t(570),r),a.__exportStar(t(571),r)},\n 540: function _(t,e,i,_,n){_();const r=t(1),a=r.__importDefault(t(541)),s=t(542),o=r.__importDefault(t(544)),f=r.__importDefault(t(545)),l=r.__importDefault(t(546)),u=r.__importDefault(t(547)),h=r.__importDefault(t(548)),c=r.__importDefault(t(549)),p=r.__importDefault(t(550)),g=r.__importDefault(t(551));let m=null;i.get_regl=function(t){return null==m&&(m=new b(t)),m};class b{constructor(t){this._marker_no_hatch_map=new Map,this._marker_hatch_map=new Map;try{this._regl=(0,a.default)({gl:t,extensions:[\"ANGLE_instanced_arrays\",\"EXT_blend_minmax\"]}),this._regl_available=!0,this._line_geometry=this._regl.buffer({usage:\"static\",type:\"float\",data:[[-2,0],[-1,-1],[1,-1],[1,1],[-1,1]]}),this._line_triangles=this._regl.elements({usage:\"static\",primitive:\"triangle fan\",data:[0,1,2,3,4]}),this._rect_geometry=this._regl.buffer({usage:\"static\",type:\"float\",data:[[-1,-1],[1,-1],[1,1],[-1,1]]}),this._rect_triangles=this._regl.elements({usage:\"static\",primitive:\"triangle fan\",data:[0,1,2,3]})}catch(t){this._regl_available=!1}}buffer(t){return this._regl.buffer(t)}clear(t,e){this._viewport={x:0,y:0,width:t,height:e},this._regl.clear({color:[0,0,0,0]})}clear_framebuffer(t){this._regl.clear({color:[0,0,0,0],framebuffer:t})}get framebuffer_and_texture(){const{_regl:t}=this,{_gl:e}=t,i={height:e.drawingBufferHeight,width:e.drawingBufferWidth};return null==this._framebuffer_texture?this._framebuffer_texture=t.texture(i):this._framebuffer_texture(i),null==this._framebuffer&&(this._framebuffer=t.framebuffer({color:this._framebuffer_texture,depth:!1,stencil:!1})),[this._framebuffer,this._framebuffer_texture]}get has_webgl(){return this._regl_available}get scissor(){return this._scissor}set_scissor(t,e,i,_){this._scissor={x:t,y:e,width:i,height:_}}texture(t){return this._regl.texture(t)}get viewport(){return this._viewport}accumulate(){return null==this._accumulate&&(this._accumulate=function(t,e,i){const _={vert:o.default,frag:f.default,attributes:{a_position:{buffer:e,divisor:0}},uniforms:{u_framebuffer_tex:t.prop(\"framebuffer_tex\")},elements:i,blend:{enable:!0,func:{srcRGB:\"one\",srcAlpha:\"one\",dstRGB:\"one minus src alpha\",dstAlpha:\"one minus src alpha\"}},depth:{enable:!1},scissor:{enable:!0,box:t.prop(\"scissor\")},viewport:t.prop(\"viewport\")};return t(_)}(this._regl,this._rect_geometry,this._rect_triangles)),this._accumulate}dashed_line(){return null==this._dashed_line&&(this._dashed_line=function(t,e,i){const _={vert:`#define DASHED\\n${h.default}\\n`,frag:`#define DASHED\\n${c.default}\\n`,attributes:{a_position:{buffer:e,divisor:0},a_point_prev:(t,e)=>e.points.to_attribute_config(e.point_offset),a_point_start:(t,e)=>e.points.to_attribute_config(e.point_offset+2),a_point_end:(t,e)=>e.points.to_attribute_config(e.point_offset+4),a_point_next:(t,e)=>e.points.to_attribute_config(e.point_offset+6),a_show_prev:(t,e)=>e.show.to_attribute_config(e.point_offset/2-e.line_offset),a_show_curr:(t,e)=>e.show.to_attribute_config(e.point_offset/2-e.line_offset+1),a_show_next:(t,e)=>e.show.to_attribute_config(e.point_offset/2-e.line_offset+2),a_linewidth:(t,e)=>e.linewidth.to_attribute_config_nested(e.line_offset,e.nsegments+3),a_line_color:(t,e)=>e.line_color.to_attribute_config_nested(e.line_offset,e.nsegments+3),a_line_cap:(t,e)=>e.line_cap.to_attribute_config_nested(e.line_offset,e.nsegments+3),a_line_join:(t,e)=>e.line_join.to_attribute_config_nested(e.line_offset,e.nsegments+3),a_length_so_far:(t,e)=>e.length_so_far.to_attribute_config(e.point_offset/2-3*e.line_offset),a_dash_tex_info:(t,e)=>e.dash_tex_info.to_attribute_config_nested(e.line_offset,e.nsegments+3),a_dash_scale:(t,e)=>e.dash_scale.to_attribute_config_nested(e.line_offset,e.nsegments+3),a_dash_offset:(t,e)=>e.dash_offset.to_attribute_config_nested(e.line_offset,e.nsegments+3)},uniforms:{u_canvas_size:t.prop(\"canvas_size\"),u_antialias:t.prop(\"antialias\"),u_miter_limit:t.prop(\"miter_limit\"),u_dash_tex:t.prop(\"dash_tex\")},elements:i,instances:t.prop(\"nsegments\"),blend:{enable:!0,equation:\"max\",func:{srcRGB:1,srcAlpha:1,dstRGB:1,dstAlpha:1}},depth:{enable:!1},framebuffer:t.prop(\"framebuffer\"),scissor:{enable:!0,box:t.prop(\"scissor\")},viewport:t.prop(\"viewport\")};return t(_)}(this._regl,this._line_geometry,this._line_triangles)),this._dashed_line}get_dash(t){return null==this._dash_cache&&(this._dash_cache=new s.DashCache(this._regl)),this._dash_cache.get(t)}image(){return null==this._image&&(this._image=function(t,e,i){const _={vert:l.default,frag:u.default,attributes:{a_position:{buffer:e,divisor:0},a_bounds:(t,e)=>e.bounds.to_attribute_config()},uniforms:{u_canvas_size:t.prop(\"canvas_size\"),u_tex:t.prop(\"tex\"),u_global_alpha:t.prop(\"global_alpha\")},elements:i,blend:{enable:!0,func:{srcRGB:\"one\",srcAlpha:\"one\",dstRGB:\"one minus src alpha\",dstAlpha:\"one minus src alpha\"}},depth:{enable:!1},scissor:{enable:!0,box:t.prop(\"scissor\")},viewport:t.prop(\"viewport\")};return t(_)}(this._regl,this._rect_geometry,this._rect_triangles)),this._image}marker_no_hatch(t){let e=this._marker_no_hatch_map.get(t);return null==e&&(e=d(this._regl,t),this._marker_no_hatch_map.set(t,e)),e}marker_hatch(t){let e=this._marker_hatch_map.get(t);return null==e&&(e=function(t,e){return d(t,e,[\"HATCH\"],[\"HATCH\"],{a_hatch_pattern:(t,e)=>e.hatch_pattern.to_attribute_config(0,e.nmarkers),a_hatch_scale:(t,e)=>e.hatch_scale.to_attribute_config(0,e.nmarkers),a_hatch_weight:(t,e)=>e.hatch_weight.to_attribute_config(0,e.nmarkers),a_hatch_color:(t,e)=>e.hatch_color.to_attribute_config(0,e.nmarkers)})}(this._regl,t),this._marker_hatch_map.set(t,e)),e}solid_line(){return null==this._solid_line&&(this._solid_line=function(t,e,i){const _={vert:h.default,frag:c.default,attributes:{a_position:{buffer:e,divisor:0},a_point_prev:(t,e)=>e.points.to_attribute_config(e.point_offset),a_point_start:(t,e)=>e.points.to_attribute_config(e.point_offset+2),a_point_end:(t,e)=>e.points.to_attribute_config(e.point_offset+4),a_point_next:(t,e)=>e.points.to_attribute_config(e.point_offset+6),a_show_prev:(t,e)=>e.show.to_attribute_config(e.point_offset/2-e.line_offset),a_show_curr:(t,e)=>e.show.to_attribute_config(e.point_offset/2-e.line_offset+1),a_show_next:(t,e)=>e.show.to_attribute_config(e.point_offset/2-e.line_offset+2),a_linewidth:(t,e)=>e.linewidth.to_attribute_config_nested(e.line_offset,e.nsegments+3),a_line_color:(t,e)=>e.line_color.to_attribute_config_nested(e.line_offset,e.nsegments+3),a_line_cap:(t,e)=>e.line_cap.to_attribute_config_nested(e.line_offset,e.nsegments+3),a_line_join:(t,e)=>e.line_join.to_attribute_config_nested(e.line_offset,e.nsegments+3)},uniforms:{u_canvas_size:t.prop(\"canvas_size\"),u_antialias:t.prop(\"antialias\"),u_miter_limit:t.prop(\"miter_limit\")},elements:i,instances:t.prop(\"nsegments\"),blend:{enable:!0,equation:\"max\",func:{srcRGB:1,srcAlpha:1,dstRGB:1,dstAlpha:1}},depth:{enable:!1},framebuffer:t.prop(\"framebuffer\"),scissor:{enable:!0,box:t.prop(\"scissor\")},viewport:t.prop(\"viewport\")};return t(_)}(this._regl,this._line_geometry,this._line_triangles)),this._solid_line}}function d(t,e,i=[],_=[],n){const r=i.map((t=>`#define ${t}`)).join(\"\\n\"),a=_.map((t=>`#define ${t}`)).join(\"\\n\"),s={vert:`${r}\\n#define MULTI_MARKER\\n#define USE_${e.toUpperCase()}\\n${p.default}\\n`,frag:`${a}\\n#define USE_${e.toUpperCase()}\\n${g.default}\\n`,attributes:{a_position:{buffer:t.buffer([[-.5,-.5],[-.5,.5],[.5,.5],[.5,-.5]]),divisor:0},a_center:(t,e)=>e.center.to_attribute_config(0,e.nmarkers),a_width:(t,e)=>e.width.to_attribute_config(0,e.nmarkers),a_height:(t,e)=>e.height.to_attribute_config(0,e.nmarkers),a_angle:(t,e)=>e.angle.to_attribute_config(0,e.nmarkers),a_aux:(t,e)=>e.aux.to_attribute_config(0,e.nmarkers),a_linewidth:(t,e)=>e.linewidth.to_attribute_config(0,e.nmarkers),a_line_color:(t,e)=>e.line_color.to_attribute_config(0,e.nmarkers),a_fill_color:(t,e)=>e.fill_color.to_attribute_config(0,e.nmarkers),a_line_cap:(t,e)=>e.line_cap.to_attribute_config(0,e.nmarkers),a_line_join:(t,e)=>e.line_join.to_attribute_config(0,e.nmarkers),a_show:(t,e)=>e.show.to_attribute_config(0,e.nmarkers),...n},uniforms:{u_canvas_size:t.prop(\"canvas_size\"),u_antialias:t.prop(\"antialias\"),u_size_hint:t.prop(\"size_hint\"),u_border_radius:t.prop(\"border_radius\")},count:4,primitive:\"triangle fan\",instances:t.prop(\"nmarkers\"),blend:{enable:!0,func:{srcRGB:\"one\",srcAlpha:\"one\",dstRGB:\"one minus src alpha\",dstAlpha:\"one minus src alpha\"}},depth:{enable:!1},scissor:{enable:!0,box:t.prop(\"scissor\")},viewport:t.prop(\"viewport\")};return t(s)}i.ReglWrapper=b,b.__name__=\"ReglWrapper\"},\n 541: function _(e,t,r,n,a){var i,o;i=this,o=function(){\"use strict\";var e=function(e){return e instanceof Uint8Array||e instanceof Uint16Array||e instanceof Uint32Array||e instanceof Int8Array||e instanceof Int16Array||e instanceof Int32Array||e instanceof Float32Array||e instanceof Float64Array||e instanceof Uint8ClampedArray},t=function(e,t){for(var r=Object.keys(t),n=0;n=0&&(0|e)===e||n(\"invalid parameter type, (\"+e+\")\"+i(t)+\". must be a nonnegative integer\")},oneOf:f,shaderError:function(e,t,n,i,o){if(!e.getShaderParameter(t,e.COMPILE_STATUS)){var f=e.getShaderInfoLog(t),u=i===e.FRAGMENT_SHADER?\"fragment\":\"vertex\";g(n,\"string\",u+\" shader source must be a string\",o);var c=h(n,o),l=function(e){var t=[];return e.split(\"\\n\").forEach((function(e){if(!(e.length<5)){var r=/^ERROR:\\s+(\\d+):(\\d+):\\s*(.*)$/.exec(e);r?t.push(new d(0|r[1],0|r[2],r[3].trim())):e.length>0&&t.push(new d(\"unknown\",0,e))}})),t}(f);!function(e,t){t.forEach((function(t){var r=e[t.file];if(r){var n=r.index[t.line];if(n)return n.errors.push(t),void(r.hasErrors=!0)}e.unknown.hasErrors=!0,e.unknown.lines[0].errors.push(t)}))}(c,l),Object.keys(c).forEach((function(e){var t=c[e];if(t.hasErrors){var n=[\"\"],a=[\"\"];i(\"file number \"+e+\": \"+t.name+\"\\n\",\"color:red;text-decoration:underline;font-weight:bold\"),t.lines.forEach((function(e){if(e.errors.length>0){i(s(e.number,4)+\"| \",\"background-color:yellow; font-weight:bold\"),i(e.line+r,\"color:red; background-color:yellow; font-weight:bold\");var t=0;e.errors.forEach((function(n){var a=n.message,o=/^\\s*'(.*)'\\s*:\\s*(.*)$/.exec(a);if(o){var f=o[1];a=o[2],\"assign\"===f&&(f=\"=\"),t=Math.max(e.line.indexOf(f,t),0)}else t=0;i(s(\"| \",6)),i(s(\"^^^\",t+3)+r,\"font-weight:bold\"),i(s(\"| \",6)),i(a+r,\"font-weight:bold\")})),i(s(\"| \",6)+r)}else i(s(e.number,4)+\"| \"),i(e.line+r,\"color:red\")})),\"undefined\"==typeof document||window.chrome?console.log(n.join(\"\")):(a[0]=n.join(\"%c\"),console.log.apply(console,a))}function i(e,t){n.push(e),a.push(t||\"\")}})),a.raise(\"Error compiling \"+u+\" shader, \"+c[0].name)}},linkError:function(e,t,n,i,o){if(!e.getProgramParameter(t,e.LINK_STATUS)){var f=e.getProgramInfoLog(t),u=h(n,o),s='Error linking program with vertex shader, \"'+h(i,o)[0].name+'\", and fragment shader \"'+u[0].name+'\"';\"undefined\"!=typeof document?console.log(\"%c\"+s+\"\\n%c\"+f,\"color:red;text-decoration:underline;font-weight:bold\",\"color:red\"):console.log(s+r+f),a.raise(s)}},callSite:p,saveCommandRef:b,saveDrawInfo:function(e,t,r,n){function a(e){return e?n.id(e):0}function i(e,t){Object.keys(t).forEach((function(t){e[n.id(t)]=!0}))}b(e),e._fragId=a(e.static.frag),e._vertId=a(e.static.vert);var o=e._uniformSet={};i(o,t.static),i(o,t.dynamic);var f=e._attributeSet={};i(f,r.static),i(f,r.dynamic),e._hasCount=\"count\"in e.static||\"count\"in e.dynamic||\"elements\"in e.static||\"elements\"in e.dynamic},framebufferFormat:function(e,t,r){e.texture?f(e.texture._texture.internalformat,t,\"unsupported texture format for attachment\"):f(e.renderbuffer._renderbuffer.format,r,\"unsupported renderbuffer format for attachment\")},guessCommand:m,texture2D:function(e,t,r){var n,i=t.width,o=t.height,f=t.channels;a(i>0&&i<=r.maxTextureSize&&o>0&&o<=r.maxTextureSize,\"invalid texture shape\"),e.wrapS===y&&e.wrapT===y||a(O(i)&&O(o),\"incompatible wrap mode for texture, both width and height must be power of 2\"),1===t.mipmask?1!==i&&1!==o&&a(9984!==e.minFilter&&9986!==e.minFilter&&9985!==e.minFilter&&9987!==e.minFilter,\"min filter requires mipmap\"):(a(O(i)&&O(o),\"texture must be a square power of 2 to support mipmapping\"),a(t.mipmask===(i<<1)-1,\"missing or incomplete mipmap data\")),5126===t.type&&(r.extensions.indexOf(\"oes_texture_float_linear\")<0&&a(9728===e.minFilter&&9728===e.magFilter,\"filter not supported, must enable oes_texture_float_linear\"),a(!e.genMipmaps,\"mipmap generation not supported with float textures\"));var u=t.images;for(n=0;n<16;++n)if(u[n]){var s=i>>n,c=o>>n;a(t.mipmask&1<0&&i<=n.maxTextureSize&&o>0&&o<=n.maxTextureSize,\"invalid texture shape\"),a(i===o,\"cube map must be square\"),a(t.wrapS===y&&t.wrapT===y,\"wrap mode not supported by cube map\");for(var u=0;u>l,p=o>>l;a(s.mipmask&1<1&&t===r&&('\"'===t||\"'\"===t))return['\"'+j(e.substr(1,e.length-2))+'\"'];var n=/\\[(false|true|null|\\d+|'[^']*'|\"[^\"]*\")\\]/.exec(e);if(n)return C(e.substr(0,n.index)).concat(C(n[1])).concat(C(e.substr(n.index+n[0].length)));var a=e.split(\".\");if(1===a.length)return['\"'+j(e)+'\"'];for(var i=[],o=0;o0,\"invalid pixel ratio\"))):E.raise(\"invalid arguments to regl\"),r&&(\"canvas\"===r.nodeName.toLowerCase()?a=r:n=r),!i){if(!a){E(\"undefined\"!=typeof document,\"must manually specify webgl context outside of DOM environments\");var h=function(e,r,n){var a,i=document.createElement(\"canvas\");function o(){var t=window.innerWidth,r=window.innerHeight;if(e!==document.body){var a=i.getBoundingClientRect();t=a.right-a.left,r=a.bottom-a.top}i.width=n*t,i.height=n*r}return t(i.style,{border:0,margin:0,padding:0,top:0,left:0,width:\"100%\",height:\"100%\"}),e.appendChild(i),e===document.body&&(i.style.position=\"absolute\",t(e.style,{margin:0,padding:0})),e!==document.body&&\"function\"==typeof ResizeObserver?(a=new ResizeObserver((function(){setTimeout(o)}))).observe(e):window.addEventListener(\"resize\",o,!1),o(),{canvas:i,onDestroy:function(){a?a.disconnect():window.removeEventListener(\"resize\",o),e.removeChild(i)}}}(n||document.body,0,l);if(!h)return null;a=h.canvas,p=h.onDestroy}void 0===u.premultipliedAlpha&&(u.premultipliedAlpha=!0),i=function(e,t){function r(r){try{return e.getContext(r,t)}catch(e){return null}}return r(\"webgl\")||r(\"experimental-webgl\")||r(\"webgl-experimental\")}(a,u)}return i?{gl:i,canvas:a,container:n,extensions:s,optionalExtensions:c,pixelRatio:l,profile:d,onDone:m,onDestroy:p}:(p(),m(\"webgl not supported, try upgrading your browser or graphics drivers http://get.webgl.org\"),null)}function R(e,t){for(var r=Array(e),n=0;n65535)<<4,t|=r=((e>>>=t)>255)<<3,t|=r=((e>>>=r)>15)<<2,(t|=r=((e>>>=r)>3)<<1)|(e>>>=r)>>1}function U(){var e=R(8,(function(){return[]}));function t(t){var r=function(e){for(var t=16;t<=1<<28;t*=16)if(e<=t)return t;return 0}(t),n=e[M(r)>>2];return n.length>0?n.pop():new ArrayBuffer(r)}function r(t){e[M(t.byteLength)>>2].push(t)}return{alloc:t,free:r,allocType:function(e,r){var n=null;switch(e){case 5120:n=new Int8Array(t(r),0,r);break;case 5121:n=new Uint8Array(t(r),0,r);break;case 5122:n=new Int16Array(t(2*r),0,r);break;case 5123:n=new Uint16Array(t(2*r),0,r);break;case 5124:n=new Int32Array(t(4*r),0,r);break;case 5125:n=new Uint32Array(t(4*r),0,r);break;case 5126:n=new Float32Array(t(4*r),0,r);break;default:return null}return n.length!==r?n.subarray(0,r):n},freeType:function(e){r(e.buffer)}}}var W=U();W.zero=U();var G=3553,H=6408,N=5126,q=36160;function Q(t){return!!t&&\"object\"==typeof t&&Array.isArray(t.shape)&&Array.isArray(t.stride)&&\"number\"==typeof t.offset&&t.shape.length===t.stride.length&&(Array.isArray(t.data)||e(t.data))}var Y=function(e){return Object.keys(e).map((function(t){return e[t]}))},X={shape:function(e){for(var t=[],r=e;r.length;r=r[0])t.push(r.length);return t},flatten:function(e,t,r,n){var a=1;if(t.length)for(var i=0;i>>31<<15,i=(n<<1>>>24)-127,o=n>>13&1023;if(i<-24)t[r]=a;else if(i<-14){var f=-14-i;t[r]=a+(o+1024>>f)}else t[r]=i>15?a+31744:a+(i+15<<10)+o}return t}function Te(t){return Array.isArray(t)||e(t)}var De=function(e){return!(e&e-1||!e)},je=34467,Ce=3553,ze=34067,Fe=34069,Ve=6408,Be=6406,Ie=6407,Pe=6409,Le=6410,Re=32854,Me=32855,Ue=36194,We=32819,Ge=32820,He=33635,Ne=34042,qe=6402,Qe=34041,Ye=35904,Xe=35906,$e=36193,Ke=33776,Je=33777,Ze=33778,et=33779,tt=35986,rt=35987,nt=34798,at=35840,it=35841,ot=35842,ft=35843,ut=36196,st=5121,ct=5123,lt=5125,dt=5126,mt=10242,pt=10243,ht=10497,bt=33071,vt=33648,gt=10240,yt=10241,xt=9728,wt=9729,At=9984,_t=9985,kt=9986,St=9987,Ot=33170,Et=4352,Tt=4353,Dt=4354,jt=34046,Ct=3317,zt=37440,Ft=37441,Vt=37443,Bt=37444,It=33984,Pt=[At,kt,_t,St],Lt=[0,Pe,Le,Ie,Ve],Rt={};function Mt(e){return\"[object \"+e+\"]\"}Rt[Pe]=Rt[Be]=Rt[qe]=1,Rt[Qe]=Rt[Le]=2,Rt[Ie]=Rt[Ye]=3,Rt[Ve]=Rt[Xe]=4;var Ut=Mt(\"HTMLCanvasElement\"),Wt=Mt(\"OffscreenCanvas\"),Gt=Mt(\"CanvasRenderingContext2D\"),Ht=Mt(\"ImageBitmap\"),Nt=Mt(\"HTMLImageElement\"),qt=Mt(\"HTMLVideoElement\"),Qt=Object.keys(J).concat([Ut,Wt,Gt,Ht,Nt,qt]),Yt=[];Yt[st]=1,Yt[dt]=4,Yt[$e]=2,Yt[ct]=2,Yt[lt]=4;var Xt=[];function $t(e){return Array.isArray(e)&&(0===e.length||\"number\"==typeof e[0])}function Kt(e){return!!Array.isArray(e)&&!(0===e.length||!Te(e[0]))}function Jt(e){return Object.prototype.toString.call(e)}function Zt(e){return Jt(e)===Ut}function er(e){return Jt(e)===Wt}function tr(e){if(!e)return!1;var t=Jt(e);return Qt.indexOf(t)>=0||$t(e)||Kt(e)||Q(e)}function rr(e){return 0|J[Object.prototype.toString.call(e)]}function nr(e,t){return W.allocType(e.type===$e?dt:e.type,t)}function ar(e,t){e.type===$e?(e.data=Ee(t),W.freeType(t)):e.data=t}function ir(e,t,r,n,a,i){var o;if(o=void 0!==Xt[e]?Xt[e]:Rt[e]*Yt[t],i&&(o*=6),a){for(var f=0,u=r;u>=1;)f+=o*u*u,u/=2;return f}return o*r*n}function or(r,n,a,i,o,f,u){var s={\"don't care\":Et,\"dont care\":Et,nice:Dt,fast:Tt},c={repeat:ht,clamp:bt,mirror:vt},l={nearest:xt,linear:wt},d=t({mipmap:St,\"nearest mipmap nearest\":At,\"linear mipmap nearest\":_t,\"nearest mipmap linear\":kt,\"linear mipmap linear\":St},l),m={none:0,browser:Bt},p={uint8:st,rgba4:We,rgb565:He,\"rgb5 a1\":Ge},h={alpha:Be,luminance:Pe,\"luminance alpha\":Le,rgb:Ie,rgba:Ve,rgba4:Re,\"rgb5 a1\":Me,rgb565:Ue},b={};n.ext_srgb&&(h.srgb=Ye,h.srgba=Xe),n.oes_texture_float&&(p.float32=p.float=dt),n.oes_texture_half_float&&(p.float16=p[\"half float\"]=$e),n.webgl_depth_texture&&(t(h,{depth:qe,\"depth stencil\":Qe}),t(p,{uint16:ct,uint32:lt,\"depth stencil\":Ne})),n.webgl_compressed_texture_s3tc&&t(b,{\"rgb s3tc dxt1\":Ke,\"rgba s3tc dxt1\":Je,\"rgba s3tc dxt3\":Ze,\"rgba s3tc dxt5\":et}),n.webgl_compressed_texture_atc&&t(b,{\"rgb atc\":tt,\"rgba atc explicit alpha\":rt,\"rgba atc interpolated alpha\":nt}),n.webgl_compressed_texture_pvrtc&&t(b,{\"rgb pvrtc 4bppv1\":at,\"rgb pvrtc 2bppv1\":it,\"rgba pvrtc 4bppv1\":ot,\"rgba pvrtc 2bppv1\":ft}),n.webgl_compressed_texture_etc1&&(b[\"rgb etc1\"]=ut);var v=Array.prototype.slice.call(r.getParameter(je));Object.keys(b).forEach((function(e){var t=b[e];v.indexOf(t)>=0&&(h[e]=t)}));var g=Object.keys(h);a.textureFormats=g;var y=[];Object.keys(h).forEach((function(e){var t=h[e];y[t]=e}));var x=[];Object.keys(p).forEach((function(e){var t=p[e];x[t]=e}));var w=[];Object.keys(l).forEach((function(e){var t=l[e];w[t]=e}));var A=[];Object.keys(d).forEach((function(e){var t=d[e];A[t]=e}));var _=[];Object.keys(c).forEach((function(e){var t=c[e];_[t]=e}));var k=g.reduce((function(e,t){var r=h[t];return r===Pe||r===Be||r===Pe||r===Le||r===qe||r===Qe||n.ext_srgb&&(r===Ye||r===Xe)?e[r]=r:r===Me||t.indexOf(\"rgba\")>=0?e[r]=Ve:e[r]=Ie,e}),{});function S(){this.internalformat=Ve,this.format=Ve,this.type=st,this.compressed=!1,this.premultiplyAlpha=!1,this.flipY=!1,this.unpackAlignment=1,this.colorSpace=Bt,this.width=0,this.height=0,this.channels=0}function O(e,t){e.internalformat=t.internalformat,e.format=t.format,e.type=t.type,e.compressed=t.compressed,e.premultiplyAlpha=t.premultiplyAlpha,e.flipY=t.flipY,e.unpackAlignment=t.unpackAlignment,e.colorSpace=t.colorSpace,e.width=t.width,e.height=t.height,e.channels=t.channels}function T(e,t){if(\"object\"==typeof t&&t){if(\"premultiplyAlpha\"in t&&(E.type(t.premultiplyAlpha,\"boolean\",\"invalid premultiplyAlpha\"),e.premultiplyAlpha=t.premultiplyAlpha),\"flipY\"in t&&(E.type(t.flipY,\"boolean\",\"invalid texture flip\"),e.flipY=t.flipY),\"alignment\"in t&&(E.oneOf(t.alignment,[1,2,4,8],\"invalid texture unpack alignment\"),e.unpackAlignment=t.alignment),\"colorSpace\"in t&&(E.parameter(t.colorSpace,m,\"invalid colorSpace\"),e.colorSpace=m[t.colorSpace]),\"type\"in t){var r=t.type;E(n.oes_texture_float||!(\"float\"===r||\"float32\"===r),\"you must enable the OES_texture_float extension in order to use floating point textures.\"),E(n.oes_texture_half_float||!(\"half float\"===r||\"float16\"===r),\"you must enable the OES_texture_half_float extension in order to use 16-bit floating point textures.\"),E(n.webgl_depth_texture||!(\"uint16\"===r||\"uint32\"===r||\"depth stencil\"===r),\"you must enable the WEBGL_depth_texture extension in order to use depth/stencil textures.\"),E.parameter(r,p,\"invalid texture type\"),e.type=p[r]}var i=e.width,o=e.height,f=e.channels,u=!1;\"shape\"in t?(E(Array.isArray(t.shape)&&t.shape.length>=2,\"shape must be an array\"),i=t.shape[0],o=t.shape[1],3===t.shape.length&&(f=t.shape[2],E(f>0&&f<=4,\"invalid number of channels\"),u=!0),E(i>=0&&i<=a.maxTextureSize,\"invalid width\"),E(o>=0&&o<=a.maxTextureSize,\"invalid height\")):(\"radius\"in t&&(i=o=t.radius,E(i>=0&&i<=a.maxTextureSize,\"invalid radius\")),\"width\"in t&&(i=t.width,E(i>=0&&i<=a.maxTextureSize,\"invalid width\")),\"height\"in t&&(o=t.height,E(o>=0&&o<=a.maxTextureSize,\"invalid height\")),\"channels\"in t&&(f=t.channels,E(f>0&&f<=4,\"invalid number of channels\"),u=!0)),e.width=0|i,e.height=0|o,e.channels=0|f;var s=!1;if(\"format\"in t){var c=t.format;E(n.webgl_depth_texture||!(\"depth\"===c||\"depth stencil\"===c),\"you must enable the WEBGL_depth_texture extension in order to use depth/stencil textures.\"),E.parameter(c,h,\"invalid texture format\");var l=e.internalformat=h[c];e.format=k[l],c in p&&(\"type\"in t||(e.type=p[c])),c in b&&(e.compressed=!0),s=!0}!u&&s?e.channels=Rt[e.format]:u&&!s?e.channels!==Lt[e.format]&&(e.format=e.internalformat=Lt[e.channels]):s&&u&&E(e.channels===Rt[e.format],\"number of channels inconsistent with specified format\")}}function D(e){r.pixelStorei(zt,e.flipY),r.pixelStorei(Ft,e.premultiplyAlpha),r.pixelStorei(Vt,e.colorSpace),r.pixelStorei(Ct,e.unpackAlignment)}function j(){S.call(this),this.xOffset=0,this.yOffset=0,this.data=null,this.needsFree=!1,this.element=null,this.needsCopy=!1}function C(t,r){var n=null;if(tr(r)?n=r:r&&(E.type(r,\"object\",\"invalid pixel data type\"),T(t,r),\"x\"in r&&(t.xOffset=0|r.x),\"y\"in r&&(t.yOffset=0|r.y),tr(r.data)&&(n=r.data)),E(!t.compressed||n instanceof Uint8Array,\"compressed texture data must be stored in a uint8array\"),r.copy){E(!n,\"can not specify copy and data field for the same texture\");var i=o.viewportWidth,f=o.viewportHeight;t.width=t.width||i-t.xOffset,t.height=t.height||f-t.yOffset,t.needsCopy=!0,E(t.xOffset>=0&&t.xOffset=0&&t.yOffset0&&t.width<=i&&t.height>0&&t.height<=f,\"copy texture read out of bounds\")}else if(n){if(e(n))t.channels=t.channels||4,t.data=n,\"type\"in r||t.type!==st||(t.type=rr(n));else if($t(n))t.channels=t.channels||4,function(e,t){var r=t.length;switch(e.type){case st:case ct:case lt:case dt:var n=W.allocType(e.type,r);n.set(t),e.data=n;break;case $e:e.data=Ee(t);break;default:E.raise(\"unsupported texture type, must specify a typed array\")}}(t,n),t.alignment=1,t.needsFree=!0;else if(Q(n)){var u=n.data;Array.isArray(u)||t.type!==st||(t.type=rr(u));var s,c,l,d,m,p,h=n.shape,b=n.stride;3===h.length?(l=h[2],p=b[2]):(E(2===h.length,\"invalid ndarray pixel data, must be 2 or 3D\"),l=1,p=1),s=h[0],c=h[1],d=b[0],m=b[1],t.alignment=1,t.width=s,t.height=c,t.channels=l,t.format=t.internalformat=Lt[l],t.needsFree=!0,function(e,t,r,n,a,i){for(var o=e.width,f=e.height,u=e.channels,s=nr(e,o*f*u),c=0,l=0;l=0,\"oes_texture_float extension not enabled\"):t.type===$e&&E(a.extensions.indexOf(\"oes_texture_half_float\")>=0,\"oes_texture_half_float extension not enabled\")}function z(e,t,n){var a=e.element,o=e.data,f=e.internalformat,u=e.format,s=e.type,c=e.width,l=e.height;D(e),a?r.texImage2D(t,n,u,u,s,a):e.compressed?r.compressedTexImage2D(t,n,f,c,l,0,o):e.needsCopy?(i(),r.copyTexImage2D(t,n,u,e.xOffset,e.yOffset,c,l,0)):r.texImage2D(t,n,u,c,l,0,u,s,o||null)}function F(e,t,n,a,o){var f=e.element,u=e.data,s=e.internalformat,c=e.format,l=e.type,d=e.width,m=e.height;D(e),f?r.texSubImage2D(t,o,n,a,c,l,f):e.compressed?r.compressedTexSubImage2D(t,o,n,a,s,d,m,u):e.needsCopy?(i(),r.copyTexSubImage2D(t,o,n,a,e.xOffset,e.yOffset,d,m)):r.texSubImage2D(t,o,n,a,d,m,c,l,u)}var V=[];function B(){return V.pop()||new j}function I(e){e.needsFree&&W.freeType(e.data),j.call(e),V.push(e)}function P(){S.call(this),this.genMipmaps=!1,this.mipmapHint=Et,this.mipmask=0,this.images=Array(16)}function L(e,t,r){var n=e.images[0]=B();e.mipmask=1,n.width=e.width=t,n.height=e.height=r,n.channels=e.channels=4}function R(e,t){var r=null;if(tr(t))O(r=e.images[0]=B(),e),C(r,t),e.mipmask=1;else if(T(e,t),Array.isArray(t.mipmap))for(var n=t.mipmap,a=0;a>=a,r.height>>=a,C(r,n[a]),e.mipmask|=1<=0&&!(\"faces\"in t)&&(e.genMipmaps=!0)}if(\"mag\"in t){var n=t.mag;E.parameter(n,l),e.magFilter=l[n]}var i=e.wrapS,o=e.wrapT;if(\"wrap\"in t){var f=t.wrap;\"string\"==typeof f?(E.parameter(f,c),i=o=c[f]):Array.isArray(f)&&(E.parameter(f[0],c),E.parameter(f[1],c),i=c[f[0]],o=c[f[1]])}else{if(\"wrapS\"in t){var u=t.wrapS;E.parameter(u,c),i=c[u]}if(\"wrapT\"in t){var m=t.wrapT;E.parameter(m,c),o=c[m]}}if(e.wrapS=i,e.wrapT=o,\"anisotropic\"in t){var p=t.anisotropic;E(\"number\"==typeof p&&p>=1&&p<=a.maxAnisotropic,\"aniso samples must be between 1 and \"),e.anisotropic=t.anisotropic}if(\"mipmap\"in t){var h=!1;switch(typeof t.mipmap){case\"string\":E.parameter(t.mipmap,s,\"invalid mipmap hint\"),e.mipmapHint=s[t.mipmap],e.genMipmaps=!0,h=!0;break;case\"boolean\":h=e.genMipmaps=t.mipmap;break;case\"object\":E(Array.isArray(t.mipmap),\"invalid mipmap type\"),e.genMipmaps=!1,h=!0;break;default:E.raise(\"invalid mipmap type\")}h&&!(\"min\"in t)&&(e.minFilter=At)}}function $(e,t){r.texParameteri(t,yt,e.minFilter),r.texParameteri(t,gt,e.magFilter),r.texParameteri(t,mt,e.wrapS),r.texParameteri(t,pt,e.wrapT),n.ext_texture_filter_anisotropic&&r.texParameteri(t,jt,e.anisotropic),e.genMipmaps&&(r.hint(Ot,e.mipmapHint),r.generateMipmap(t))}var K=0,J={},Z=a.maxTextureUnits,ee=Array(Z).map((function(){return null}));function te(e){S.call(this),this.mipmask=0,this.internalformat=Ve,this.id=K++,this.refCount=1,this.target=e,this.texture=r.createTexture(),this.unit=-1,this.bindCount=0,this.texInfo=new N,u.profile&&(this.stats={size:0})}function re(e){r.activeTexture(It),r.bindTexture(e.target,e.texture)}function ne(){var e=ee[0];e?r.bindTexture(e.target,e.texture):r.bindTexture(Ce,null)}function ae(e){var t=e.texture;E(t,\"must not double destroy texture\");var n=e.unit,a=e.target;n>=0&&(r.activeTexture(It+n),r.bindTexture(a,null),ee[n]=null),r.deleteTexture(t),e.texture=null,e.params=null,e.pixels=null,e.refCount=0,delete J[e.id],f.textureCount--}return t(te.prototype,{bind:function(){var e=this;e.bindCount+=1;var t=e.unit;if(t<0){for(var n=0;n0)continue;a.unit=-1}ee[n]=e,t=n;break}t>=Z&&E.raise(\"insufficient number of texture units\"),u.profile&&f.maxTextureUnits>u)-o,s.height=s.height||(n.height>>u)-f,E(n.type===s.type&&n.format===s.format&&n.internalformat===s.internalformat,\"incompatible format for texture.subimage\"),E(o>=0&&f>=0&&o+s.width<=n.width&&f+s.height<=n.height,\"texture.subimage write out of bounds\"),E(n.mipmask&1<>f;++f){var s=a>>f,c=o>>f;if(!s||!c)break;r.texImage2D(Ce,f,n.format,s,c,0,n.format,n.type,null)}return ne(),u.profile&&(n.stats.size=ir(n.internalformat,n.type,a,o,!1,!1)),i},i._reglType=\"texture2d\",i._texture=n,u.profile&&(i.stats=n.stats),i.destroy=function(){n.decRef()},i},createCube:function(e,t,n,i,o,s){var c=new te(ze);J[c.id]=c,f.cubeCount++;var l=new Array(6);function d(e,t,r,n,i,o){var f,s=c.texInfo;for(N.call(s),f=0;f<6;++f)l[f]=G();if(\"number\"!=typeof e&&e)if(\"object\"==typeof e)if(t)R(l[0],e),R(l[1],t),R(l[2],r),R(l[3],n),R(l[4],i),R(l[5],o);else if(q(s,e),T(c,e),\"faces\"in e){var m=e.faces;for(E(Array.isArray(m)&&6===m.length,\"cube faces must be a length 6 array\"),f=0;f<6;++f)E(\"object\"==typeof m[f]&&!!m[f],\"invalid input for cube map face\"),O(l[f],c),R(l[f],m[f])}else for(f=0;f<6;++f)R(l[f],e);else E.raise(\"invalid arguments to cube map\");else{var p=0|e||1;for(f=0;f<6;++f)L(l[f],p,p)}for(O(c,l[0]),E.optional((function(){a.npotTextureCube||E(De(c.width)&&De(c.height),\"your browser does not support non power or two texture dimensions\")})),s.genMipmaps?c.mipmask=(l[0].width<<1)-1:c.mipmask=l[0].mipmask,E.textureCube(c,s,l,a),c.internalformat=l[0].internalformat,d.width=l[0].width,d.height=l[0].height,re(c),f=0;f<6;++f)M(l[f],Fe+f);for($(s,ze),ne(),u.profile&&(c.stats.size=ir(c.internalformat,c.type,d.width,d.height,s.genMipmaps,!0)),d.format=y[c.internalformat],d.type=x[c.type],d.mag=w[s.magFilter],d.min=A[s.minFilter],d.wrapS=_[s.wrapS],d.wrapT=_[s.wrapT],f=0;f<6;++f)H(l[f]);return d}return d(e,t,n,i,o,s),d.subimage=function(e,t,r,n,a){E(!!t,\"must specify image data\"),E(\"number\"==typeof e&&e===(0|e)&&e>=0&&e<6,\"invalid face\");var i=0|r,o=0|n,f=0|a,u=B();return O(u,c),u.width=0,u.height=0,C(u,t),u.width=u.width||(c.width>>f)-i,u.height=u.height||(c.height>>f)-o,E(c.type===u.type&&c.format===u.format&&c.internalformat===u.internalformat,\"incompatible format for texture.subimage\"),E(i>=0&&o>=0&&i+u.width<=c.width&&o+u.height<=c.height,\"texture.subimage write out of bounds\"),E(c.mipmask&1<>a;++a)r.texImage2D(Fe+n,a,c.format,t>>a,t>>a,0,c.format,c.type,null);return ne(),u.profile&&(c.stats.size=ir(c.internalformat,c.type,d.width,d.height,!1,!0)),d}},d._reglType=\"textureCube\",d._texture=c,u.profile&&(d.stats=c.stats),d.destroy=function(){c.decRef()},d},clear:function(){for(var e=0;e>t,e.height>>t,0,e.internalformat,e.type,null);else for(var n=0;n<6;++n)r.texImage2D(Fe+n,t,e.internalformat,e.width>>t,e.height>>t,0,e.internalformat,e.type,null);$(e.texInfo,e.target)}))},refresh:function(){for(var e=0;e=0&&c=0&&l0&&d+c<=a.framebufferWidth,\"invalid width for read pixels\"),E(m>0&&m+l<=a.framebufferHeight,\"invalid height for read pixels\"),n();var h=d*m*4;return p||(s===Rr?p=new Uint8Array(h):s===Ur&&(p=p||new Float32Array(h))),E.isTypedArray(p,\"data buffer for regl.read() must be a typedarray\"),E(p.byteLength>=h,\"data buffer for regl.read() too small\"),t.pixelStorei(Mr,4),t.readPixels(c,l,d,m,Lr,s,p),p}return function(e){return e&&\"framebuffer\"in e?function(e){var t;return r.setFBO({framebuffer:e.framebuffer},(function(){t=u(e)})),t}(e):u(e)}}function Gr(e){return Array.prototype.slice.call(e)}function Hr(e){return Gr(e).join(\"\")}var Nr=\"xyzw\".split(\"\"),qr=5121,Qr=1,Yr=2,Xr=0,$r=1,Kr=2,Jr=3,Zr=4,en=5,tn=6,rn=\"dither\",nn=\"blend.enable\",an=\"blend.color\",on=\"blend.equation\",fn=\"blend.func\",un=\"depth.enable\",sn=\"depth.func\",cn=\"depth.range\",ln=\"depth.mask\",dn=\"colorMask\",mn=\"cull.enable\",pn=\"cull.face\",hn=\"frontFace\",bn=\"lineWidth\",vn=\"polygonOffset.enable\",gn=\"polygonOffset.offset\",yn=\"sample.alpha\",xn=\"sample.enable\",wn=\"sample.coverage\",An=\"stencil.enable\",_n=\"stencil.mask\",kn=\"stencil.func\",Sn=\"stencil.opFront\",On=\"stencil.opBack\",En=\"scissor.enable\",Tn=\"scissor.box\",Dn=\"viewport\",jn=\"profile\",Cn=\"framebuffer\",zn=\"vert\",Fn=\"frag\",Vn=\"elements\",Bn=\"primitive\",In=\"count\",Pn=\"offset\",Ln=\"instances\",Rn=\"vao\",Mn=\"Width\",Un=\"Height\",Wn=Cn+Mn,Gn=Cn+Un,Hn=Dn+Mn,Nn=Dn+Un,qn=\"drawingBuffer\",Qn=qn+Mn,Yn=qn+Un,Xn=[fn,on,kn,Sn,On,wn,Dn,Tn,gn],$n=34962,Kn=34963,Jn=3553,Zn=34067,ea=2884,ta=3042,ra=3024,na=2960,aa=2929,ia=3089,oa=32823,fa=32926,ua=32928,sa=5126,ca=35664,la=35665,da=35666,ma=5124,pa=35667,ha=35668,ba=35669,va=35670,ga=35671,ya=35672,xa=35673,wa=35674,Aa=35675,_a=35676,ka=35678,Sa=35680,Oa=4,Ea=1028,Ta=1029,Da=2304,ja=2305,Ca=32775,za=32776,Fa=519,Va=7680,Ba=0,Ia=1,Pa=32774,La=513,Ra=36160,Ma=36064,Ua={0:0,1:1,zero:0,one:1,\"src color\":768,\"one minus src color\":769,\"src alpha\":770,\"one minus src alpha\":771,\"dst color\":774,\"one minus dst color\":775,\"dst alpha\":772,\"one minus dst alpha\":773,\"constant color\":32769,\"one minus constant color\":32770,\"constant alpha\":32771,\"one minus constant alpha\":32772,\"src alpha saturate\":776},Wa=[\"constant color, constant alpha\",\"one minus constant color, constant alpha\",\"constant color, one minus constant alpha\",\"one minus constant color, one minus constant alpha\",\"constant alpha, constant color\",\"constant alpha, one minus constant color\",\"one minus constant alpha, constant color\",\"one minus constant alpha, one minus constant color\"],Ga={never:512,less:513,\"<\":513,equal:514,\"=\":514,\"==\":514,\"===\":514,lequal:515,\"<=\":515,greater:516,\">\":516,notequal:517,\"!=\":517,\"!==\":517,gequal:518,\">=\":518,always:519},Ha={0:0,zero:0,keep:7680,replace:7681,increment:7682,decrement:7683,\"increment wrap\":34055,\"decrement wrap\":34056,invert:5386},Na={frag:35632,vert:35633},qa={cw:Da,ccw:ja};function Qa(t){return Array.isArray(t)||e(t)||Q(t)}function Ya(e){return e.sort((function(e,t){return e===Dn?-1:t===Dn?1:e=1,n>=2,t)}if(r===Zr){var a=e.data;return new Xa(a.thisDep,a.contextDep,a.propDep,t)}if(r===en)return new Xa(!1,!1,!1,t);if(r===tn){for(var i=!1,o=!1,f=!1,u=0;u=1&&(o=!0),c>=2&&(f=!0)}else s.type===Zr&&(i=i||s.data.thisDep,o=o||s.data.contextDep,f=f||s.data.propDep)}return new Xa(i,o,f,t)}return new Xa(r===Jr,r===Kr,r===$r,t)}var Za=new Xa(!1,!1,!1,(function(){}));function ei(e,r,n,a,i,o,f,u,s,c,l,d,m,p,h){var b=c.Record,v={add:32774,subtract:32778,\"reverse subtract\":32779};n.ext_blend_minmax&&(v.min=Ca,v.max=za);var g=n.angle_instanced_arrays,y=n.webgl_draw_buffers,x=n.oes_vertex_array_object,w={dirty:!0,profile:h.profile},A={},_=[],k={},S={};function O(e){return e.replace(\".\",\"_\")}function T(e,t,r){var n=O(e);_.push(e),A[n]=w[n]=!!r,k[n]=t}function D(e,t,r){var n=O(e);_.push(e),Array.isArray(r)?(w[n]=r.slice(),A[n]=r.slice()):w[n]=A[n]=r,S[n]=t}T(rn,ra),T(nn,ta),D(an,\"blendColor\",[0,0,0,0]),D(on,\"blendEquationSeparate\",[Pa,Pa]),D(fn,\"blendFuncSeparate\",[Ia,Ba,Ia,Ba]),T(un,aa,!0),D(sn,\"depthFunc\",La),D(cn,\"depthRange\",[0,1]),D(ln,\"depthMask\",!0),D(dn,dn,[!0,!0,!0,!0]),T(mn,ea),D(pn,\"cullFace\",Ta),D(hn,hn,ja),D(bn,bn,1),T(vn,oa),D(gn,\"polygonOffset\",[0,0]),T(yn,fa),T(xn,ua),D(wn,\"sampleCoverage\",[1,!1]),T(An,na),D(_n,\"stencilMask\",-1),D(kn,\"stencilFunc\",[Fa,0,-1]),D(Sn,\"stencilOpSeparate\",[Ea,Va,Va,Va]),D(On,\"stencilOpSeparate\",[Ta,Va,Va,Va]),T(En,ia),D(Tn,\"scissor\",[0,0,e.drawingBufferWidth,e.drawingBufferHeight]),D(Dn,Dn,[0,0,e.drawingBufferWidth,e.drawingBufferHeight]);var j={gl:e,context:m,strings:r,next:A,current:w,draw:d,elements:o,buffer:i,shader:l,attributes:c.state,vao:c,uniforms:s,framebuffer:u,extensions:n,timer:p,isBufferArgs:Qa},C={primTypes:le,compareFuncs:Ga,blendFuncs:Ua,blendEquations:v,stencilOps:Ha,glTypes:Z,orientationType:qa};E.optional((function(){j.isArrayLike=Te})),y&&(C.backBuffer=[Ta],C.drawBuffer=R(a.maxDrawbuffers,(function(e){return 0===e?[0]:R(e,(function(e){return Ma+e}))})));var z=0;function V(){var e=function(){var e=0,r=[],n=[];function a(){var r=[],n=[];return t((function(){r.push.apply(r,Gr(arguments))}),{def:function(){var t=\"v\"+e++;return n.push(t),arguments.length>0&&(r.push(t,\"=\"),r.push.apply(r,Gr(arguments)),r.push(\";\")),t},toString:function(){return Hr([n.length>0?\"var \"+n.join(\",\")+\";\":\"\",Hr(r)])}})}function i(){var e=a(),r=a(),n=e.toString,i=r.toString;function o(t,n){r(t,n,\"=\",e.def(t,n),\";\")}return t((function(){e.apply(e,Gr(arguments))}),{def:e.def,entry:e,exit:r,save:o,set:function(t,r,n){o(t,r),e(t,r,\"=\",n,\";\")},toString:function(){return n()+i()}})}var o=a(),f={};return{global:o,link:function(t){for(var a=0;a=0,'unknown parameter \"'+t+'\"',d.commandStr)}))}t(m),t(p)}));var h=function(e,t){var r=e.static;if(\"string\"==typeof r[Fn]&&\"string\"==typeof r[zn]){if(Object.keys(t.dynamic).length>0)return null;var n=t.static,a=Object.keys(n);if(a.length>0&&\"number\"==typeof n[a[0]]){for(var i=[],o=0;o=0,\"invalid \"+e,r.commandStr)):u=!1,\"height\"in i?(f=0|i.height,E.command(f>=0,\"invalid \"+e,r.commandStr)):u=!1,new Xa(!u&&t&&t.thisDep,!u&&t&&t.contextDep,!u&&t&&t.propDep,(function(e,t){var r=e.shared.context,n=o;\"width\"in i||(n=t.def(r,\".\",Wn,\"-\",s));var a=f;return\"height\"in i||(a=t.def(r,\".\",Gn,\"-\",c)),[s,c,n,a]}))}if(e in a){var l=a[e],d=Ja(l,(function(t,r){var n=t.invoke(r,l);E.optional((function(){t.assert(r,n+\"&&typeof \"+n+'===\"object\"',\"invalid \"+e)}));var a=t.shared.context,i=r.def(n,\".x|0\"),o=r.def(n,\".y|0\"),f=r.def('\"width\" in ',n,\"?\",n,\".width|0:\",\"(\",a,\".\",Wn,\"-\",i,\")\"),u=r.def('\"height\" in ',n,\"?\",n,\".height|0:\",\"(\",a,\".\",Gn,\"-\",o,\")\");return E.optional((function(){t.assert(r,f+\">=0&&\"+u+\">=0\",\"invalid \"+e)})),[i,o,f,u]}));return t&&(d.thisDep=d.thisDep||t.thisDep,d.contextDep=d.contextDep||t.contextDep,d.propDep=d.propDep||t.propDep),d}return t?new Xa(t.thisDep,t.contextDep,t.propDep,(function(e,t){var r=e.shared.context;return[0,0,t.def(r,\".\",Wn),t.def(r,\".\",Gn)]})):null}var o=i(Dn);if(o){var f=o;o=new Xa(o.thisDep,o.contextDep,o.propDep,(function(e,t){var r=f.append(e,t),n=e.shared.context;return t.set(n,\".\"+Hn,r[2]),t.set(n,\".\"+Nn,r[3]),r}))}return{viewport:o,scissor_box:i(Tn)}}(e,y,d),w=function(e,t){var r=e.static,n=e.dynamic,a={},i=!1,f=function(){if(Rn in r){var e=r[Rn];return null!==e&&null===c.getVAO(e)&&(e=c.createVAO(e)),i=!0,a.vao=e,Ka((function(t){var r=c.getVAO(e);return r?t.link(r):\"null\"}))}if(Rn in n){i=!0;var t=n[Rn];return Ja(t,(function(e,r){var n=e.invoke(r,t);return r.def(e.shared.vao+\".getVAO(\"+n+\")\")}))}return null}(),u=!1,s=function(){if(Vn in r){var e=r[Vn];if(a.elements=e,Qa(e)){var s=a.elements=o.create(e,!0);e=o.getElements(s),u=!0}else e&&(e=o.getElements(e),u=!0,E.command(e,\"invalid elements\",t.commandStr));var c=Ka((function(t,r){if(e){var n=t.link(e);return t.ELEMENTS=n,n}return t.ELEMENTS=null,null}));return c.value=e,c}if(Vn in n){u=!0;var l=n[Vn];return Ja(l,(function(e,t){var r=e.shared,n=r.isBufferArgs,a=r.elements,i=e.invoke(t,l),o=t.def(\"null\"),f=t.def(n,\"(\",i,\")\"),u=e.cond(f).then(o,\"=\",a,\".createStream(\",i,\");\").else(o,\"=\",a,\".getElements(\",i,\");\");return E.optional((function(){e.assert(u.else,\"!\"+i+\"||\"+o,\"invalid elements\")})),t.entry(u),t.exit(e.cond(f).then(a,\".destroyStream(\",o,\");\")),e.ELEMENTS=o,o}))}return i?new Xa(f.thisDep,f.contextDep,f.propDep,(function(e,t){return t.def(e.shared.vao+\".currentVAO?\"+e.shared.elements+\".getElements(\"+e.shared.vao+\".currentVAO.elements):null\")})):null}();function l(e,o){if(e in r){var s=0|r[e];return o?a.offset=s:a.instances=s,E.command(!o||s>=0,\"invalid \"+e,t.commandStr),Ka((function(e,t){return o&&(e.OFFSET=s),s}))}if(e in n){var c=n[e];return Ja(c,(function(t,r){var n=t.invoke(r,c);return o&&(t.OFFSET=n,E.optional((function(){t.assert(r,n+\">=0\",\"invalid \"+e)}))),n}))}if(o){if(u)return Ka((function(e,t){return e.OFFSET=0,0}));if(i)return new Xa(f.thisDep,f.contextDep,f.propDep,(function(e,t){return t.def(e.shared.vao+\".currentVAO?\"+e.shared.vao+\".currentVAO.offset:0\")}))}else if(i)return new Xa(f.thisDep,f.contextDep,f.propDep,(function(e,t){return t.def(e.shared.vao+\".currentVAO?\"+e.shared.vao+\".currentVAO.instances:-1\")}));return null}var d=l(Pn,!0),m=function(){if(Bn in r){var e=r[Bn];return a.primitive=e,E.commandParameter(e,le,\"invalid primitve\",t.commandStr),Ka((function(t,r){return le[e]}))}if(Bn in n){var o=n[Bn];return Ja(o,(function(e,t){var r=e.constants.primTypes,n=e.invoke(t,o);return E.optional((function(){e.assert(t,n+\" in \"+r,\"invalid primitive, must be one of \"+Object.keys(le))})),t.def(r,\"[\",n,\"]\")}))}return u?$a(s)?s.value?Ka((function(e,t){return t.def(e.ELEMENTS,\".primType\")})):Ka((function(){return Oa})):new Xa(s.thisDep,s.contextDep,s.propDep,(function(e,t){var r=e.ELEMENTS;return t.def(r,\"?\",r,\".primType:\",Oa)})):i?new Xa(f.thisDep,f.contextDep,f.propDep,(function(e,t){return t.def(e.shared.vao+\".currentVAO?\"+e.shared.vao+\".currentVAO.primitive:\"+Oa)})):null}(),p=function(){if(In in r){var e=0|r[In];return a.count=e,E.command(\"number\"==typeof e&&e>=0,\"invalid vertex count\",t.commandStr),Ka((function(){return e}))}if(In in n){var o=n[In];return Ja(o,(function(e,t){var r=e.invoke(t,o);return E.optional((function(){e.assert(t,\"typeof \"+r+'===\"number\"&&'+r+\">=0&&\"+r+\"===(\"+r+\"|0)\",\"invalid vertex count\")})),r}))}if(u){if($a(s)){if(s)return d?new Xa(d.thisDep,d.contextDep,d.propDep,(function(e,t){var r=t.def(e.ELEMENTS,\".vertCount-\",e.OFFSET);return E.optional((function(){e.assert(t,r+\">=0\",\"invalid vertex offset/element buffer too small\")})),r})):Ka((function(e,t){return t.def(e.ELEMENTS,\".vertCount\")}));var c=Ka((function(){return-1}));return E.optional((function(){c.MISSING=!0})),c}var l=new Xa(s.thisDep||d.thisDep,s.contextDep||d.contextDep,s.propDep||d.propDep,(function(e,t){var r=e.ELEMENTS;return e.OFFSET?t.def(r,\"?\",r,\".vertCount-\",e.OFFSET,\":-1\"):t.def(r,\"?\",r,\".vertCount:-1\")}));return E.optional((function(){l.DYNAMIC=!0})),l}if(i){var m=new Xa(f.thisDep,f.contextDep,f.propDep,(function(e,t){return t.def(e.shared.vao,\".currentVAO?\",e.shared.vao,\".currentVAO.count:-1\")}));return m}return null}(),h=l(Ln,!1);return{elements:s,primitive:m,count:p,instances:h,offset:d,vao:f,vaoActive:i,elementsActive:u,static:a}}(e,d),A=function(e,t){var r=e.static,n=e.dynamic,i={};return _.forEach((function(e){var o=O(e);function f(t,a){if(e in r){var f=t(r[e]);i[o]=Ka((function(){return f}))}else if(e in n){var u=n[e];i[o]=Ja(u,(function(e,t){return a(e,t,e.invoke(t,u))}))}}switch(e){case mn:case nn:case rn:case An:case un:case En:case vn:case yn:case xn:case ln:return f((function(r){return E.commandType(r,\"boolean\",e,t.commandStr),r}),(function(t,r,n){return E.optional((function(){t.assert(r,\"typeof \"+n+'===\"boolean\"',\"invalid flag \"+e,t.commandStr)})),n}));case sn:return f((function(r){return E.commandParameter(r,Ga,\"invalid \"+e,t.commandStr),Ga[r]}),(function(t,r,n){var a=t.constants.compareFuncs;return E.optional((function(){t.assert(r,n+\" in \"+a,\"invalid \"+e+\", must be one of \"+Object.keys(Ga))})),r.def(a,\"[\",n,\"]\")}));case cn:return f((function(e){return E.command(Te(e)&&2===e.length&&\"number\"==typeof e[0]&&\"number\"==typeof e[1]&&e[0]<=e[1],\"depth range is 2d array\",t.commandStr),e}),(function(e,t,r){return E.optional((function(){e.assert(t,e.shared.isArrayLike+\"(\"+r+\")&&\"+r+\".length===2&&typeof \"+r+'[0]===\"number\"&&typeof '+r+'[1]===\"number\"&&'+r+\"[0]<=\"+r+\"[1]\",\"depth range must be a 2d array\")})),[t.def(\"+\",r,\"[0]\"),t.def(\"+\",r,\"[1]\")]}));case fn:return f((function(e){E.commandType(e,\"object\",\"blend.func\",t.commandStr);var r=\"srcRGB\"in e?e.srcRGB:e.src,n=\"srcAlpha\"in e?e.srcAlpha:e.src,a=\"dstRGB\"in e?e.dstRGB:e.dst,i=\"dstAlpha\"in e?e.dstAlpha:e.dst;return E.commandParameter(r,Ua,o+\".srcRGB\",t.commandStr),E.commandParameter(n,Ua,o+\".srcAlpha\",t.commandStr),E.commandParameter(a,Ua,o+\".dstRGB\",t.commandStr),E.commandParameter(i,Ua,o+\".dstAlpha\",t.commandStr),E.command(-1===Wa.indexOf(r+\", \"+a),\"unallowed blending combination (srcRGB, dstRGB) = (\"+r+\", \"+a+\")\",t.commandStr),[Ua[r],Ua[a],Ua[n],Ua[i]]}),(function(t,r,n){var a=t.constants.blendFuncs;function i(i,o){var f=r.def('\"',i,o,'\" in ',n,\"?\",n,\".\",i,o,\":\",n,\".\",i);return E.optional((function(){t.assert(r,f+\" in \"+a,\"invalid \"+e+\".\"+i+o+\", must be one of \"+Object.keys(Ua))})),f}E.optional((function(){t.assert(r,n+\"&&typeof \"+n+'===\"object\"',\"invalid blend func, must be an object\")}));var o=i(\"src\",\"RGB\"),f=i(\"dst\",\"RGB\");E.optional((function(){var e=t.constants.invalidBlendCombinations;t.assert(r,e+\".indexOf(\"+o+'+\", \"+'+f+\") === -1 \",\"unallowed blending combination for (srcRGB, dstRGB)\")}));var u=r.def(a,\"[\",o,\"]\"),s=r.def(a,\"[\",i(\"src\",\"Alpha\"),\"]\");return[u,r.def(a,\"[\",f,\"]\"),s,r.def(a,\"[\",i(\"dst\",\"Alpha\"),\"]\")]}));case on:return f((function(r){return\"string\"==typeof r?(E.commandParameter(r,v,\"invalid \"+e,t.commandStr),[v[r],v[r]]):\"object\"==typeof r?(E.commandParameter(r.rgb,v,e+\".rgb\",t.commandStr),E.commandParameter(r.alpha,v,e+\".alpha\",t.commandStr),[v[r.rgb],v[r.alpha]]):void E.commandRaise(\"invalid blend.equation\",t.commandStr)}),(function(t,r,n){var a=t.constants.blendEquations,i=r.def(),o=r.def(),f=t.cond(\"typeof \",n,'===\"string\"');return E.optional((function(){function r(e,r,n){t.assert(e,n+\" in \"+a,\"invalid \"+r+\", must be one of \"+Object.keys(v))}r(f.then,e,n),t.assert(f.else,n+\"&&typeof \"+n+'===\"object\"',\"invalid \"+e),r(f.else,e+\".rgb\",n+\".rgb\"),r(f.else,e+\".alpha\",n+\".alpha\")})),f.then(i,\"=\",o,\"=\",a,\"[\",n,\"];\"),f.else(i,\"=\",a,\"[\",n,\".rgb];\",o,\"=\",a,\"[\",n,\".alpha];\"),r(f),[i,o]}));case an:return f((function(e){return E.command(Te(e)&&4===e.length,\"blend.color must be a 4d array\",t.commandStr),R(4,(function(t){return+e[t]}))}),(function(e,t,r){return E.optional((function(){e.assert(t,e.shared.isArrayLike+\"(\"+r+\")&&\"+r+\".length===4\",\"blend.color must be a 4d array\")})),R(4,(function(e){return t.def(\"+\",r,\"[\",e,\"]\")}))}));case _n:return f((function(e){return E.commandType(e,\"number\",o,t.commandStr),0|e}),(function(e,t,r){return E.optional((function(){e.assert(t,\"typeof \"+r+'===\"number\"',\"invalid stencil.mask\")})),t.def(r,\"|0\")}));case kn:return f((function(r){E.commandType(r,\"object\",o,t.commandStr);var n=r.cmp||\"keep\",a=r.ref||0,i=\"mask\"in r?r.mask:-1;return E.commandParameter(n,Ga,e+\".cmp\",t.commandStr),E.commandType(a,\"number\",e+\".ref\",t.commandStr),E.commandType(i,\"number\",e+\".mask\",t.commandStr),[Ga[n],a,i]}),(function(e,t,r){var n=e.constants.compareFuncs;return E.optional((function(){function a(){e.assert(t,Array.prototype.join.call(arguments,\"\"),\"invalid stencil.func\")}a(r+\"&&typeof \",r,'===\"object\"'),a('!(\"cmp\" in ',r,\")||(\",r,\".cmp in \",n,\")\")})),[t.def('\"cmp\" in ',r,\"?\",n,\"[\",r,\".cmp]\",\":\",Va),t.def(r,\".ref|0\"),t.def('\"mask\" in ',r,\"?\",r,\".mask|0:-1\")]}));case Sn:case On:return f((function(r){E.commandType(r,\"object\",o,t.commandStr);var n=r.fail||\"keep\",a=r.zfail||\"keep\",i=r.zpass||\"keep\";return E.commandParameter(n,Ha,e+\".fail\",t.commandStr),E.commandParameter(a,Ha,e+\".zfail\",t.commandStr),E.commandParameter(i,Ha,e+\".zpass\",t.commandStr),[e===On?Ta:Ea,Ha[n],Ha[a],Ha[i]]}),(function(t,r,n){var a=t.constants.stencilOps;function i(i){return E.optional((function(){t.assert(r,'!(\"'+i+'\" in '+n+\")||(\"+n+\".\"+i+\" in \"+a+\")\",\"invalid \"+e+\".\"+i+\", must be one of \"+Object.keys(Ha))})),r.def('\"',i,'\" in ',n,\"?\",a,\"[\",n,\".\",i,\"]:\",Va)}return E.optional((function(){t.assert(r,n+\"&&typeof \"+n+'===\"object\"',\"invalid \"+e)})),[e===On?Ta:Ea,i(\"fail\"),i(\"zfail\"),i(\"zpass\")]}));case gn:return f((function(e){E.commandType(e,\"object\",o,t.commandStr);var r=0|e.factor,n=0|e.units;return E.commandType(r,\"number\",o+\".factor\",t.commandStr),E.commandType(n,\"number\",o+\".units\",t.commandStr),[r,n]}),(function(t,r,n){return E.optional((function(){t.assert(r,n+\"&&typeof \"+n+'===\"object\"',\"invalid \"+e)})),[r.def(n,\".factor|0\"),r.def(n,\".units|0\")]}));case pn:return f((function(e){var r=0;return\"front\"===e?r=Ea:\"back\"===e&&(r=Ta),E.command(!!r,o,t.commandStr),r}),(function(e,t,r){return E.optional((function(){e.assert(t,r+'===\"front\"||'+r+'===\"back\"',\"invalid cull.face\")})),t.def(r,'===\"front\"?',Ea,\":\",Ta)}));case bn:return f((function(e){return E.command(\"number\"==typeof e&&e>=a.lineWidthDims[0]&&e<=a.lineWidthDims[1],\"invalid line width, must be a positive number between \"+a.lineWidthDims[0]+\" and \"+a.lineWidthDims[1],t.commandStr),e}),(function(e,t,r){return E.optional((function(){e.assert(t,\"typeof \"+r+'===\"number\"&&'+r+\">=\"+a.lineWidthDims[0]+\"&&\"+r+\"<=\"+a.lineWidthDims[1],\"invalid line width\")})),r}));case hn:return f((function(e){return E.commandParameter(e,qa,o,t.commandStr),qa[e]}),(function(e,t,r){return E.optional((function(){e.assert(t,r+'===\"cw\"||'+r+'===\"ccw\"',\"invalid frontFace, must be one of cw,ccw\")})),t.def(r+'===\"cw\"?'+Da+\":\"+ja)}));case dn:return f((function(e){return E.command(Te(e)&&4===e.length,\"color.mask must be length 4 array\",t.commandStr),e.map((function(e){return!!e}))}),(function(e,t,r){return E.optional((function(){e.assert(t,e.shared.isArrayLike+\"(\"+r+\")&&\"+r+\".length===4\",\"invalid color.mask\")})),R(4,(function(e){return\"!!\"+r+\"[\"+e+\"]\"}))}));case wn:return f((function(e){E.command(\"object\"==typeof e&&e,o,t.commandStr);var r=\"value\"in e?e.value:1,n=!!e.invert;return E.command(\"number\"==typeof r&&r>=0&&r<=1,\"sample.coverage.value must be a number between 0 and 1\",t.commandStr),[r,n]}),(function(e,t,r){return E.optional((function(){e.assert(t,r+\"&&typeof \"+r+'===\"object\"',\"invalid sample.coverage\")})),[t.def('\"value\" in ',r,\"?+\",r,\".value:1\"),t.def(\"!!\",r,\".invert\")]}))}})),i}(e,d),k=function(e,t,n){var a=e.static,i=e.dynamic;function o(e){if(e in a){var t=r.id(a[e]);E.optional((function(){l.shader(Na[e],t,E.guessCommand())}));var n=Ka((function(){return t}));return n.id=t,n}if(e in i){var o=i[e];return Ja(o,(function(t,r){var n=t.invoke(r,o),a=r.def(t.shared.strings,\".id(\",n,\")\");return E.optional((function(){r(t.shared.shader,\".shader(\",Na[e],\",\",a,\",\",t.command,\");\")})),a}))}return null}var f,u=o(Fn),s=o(zn),c=null;return $a(u)&&$a(s)?(c=l.program(s.id,u.id,null,n),f=Ka((function(e,t){return e.link(c)}))):f=new Xa(u&&u.thisDep||s&&s.thisDep,u&&u.contextDep||s&&s.contextDep,u&&u.propDep||s&&s.propDep,(function(e,t){var r,n=e.shared.shader;r=u?u.append(e,t):t.def(n,\".\",Fn);var a=n+\".program(\"+(s?s.append(e,t):t.def(n,\".\",zn))+\",\"+r;return E.optional((function(){a+=\",\"+e.command})),t.def(a+\")\")})),{frag:u,vert:s,progVar:f,program:c}}(e,0,h);function S(e){var t=x[e];t&&(A[e]=t)}S(Dn),S(O(Tn));var T=Object.keys(A).length>0,D={framebuffer:y,draw:w,shader:k,state:A,dirty:T,scopeVAO:null,drawVAO:null,useVAO:!1,attributes:{}};if(D.profile=function(e){var t,r=e.static,n=e.dynamic;if(jn in r){var a=!!r[jn];(t=Ka((function(e,t){return a}))).enable=a}else if(jn in n){var i=n[jn];t=Ja(i,(function(e,t){return e.invoke(t,i)}))}return t}(e),D.uniforms=function(e,t){var r=e.static,n=e.dynamic,a={};return Object.keys(r).forEach((function(e){var n,i=r[e];if(\"number\"==typeof i||\"boolean\"==typeof i)n=Ka((function(){return i}));else if(\"function\"==typeof i){var o=i._reglType;\"texture2d\"===o||\"textureCube\"===o?n=Ka((function(e){return e.link(i)})):\"framebuffer\"===o||\"framebufferCube\"===o?(E.command(i.color.length>0,'missing color attachment for framebuffer sent to uniform \"'+e+'\"',t.commandStr),n=Ka((function(e){return e.link(i.color[0])}))):E.commandRaise('invalid data for uniform \"'+e+'\"',t.commandStr)}else Te(i)?n=Ka((function(t){return t.global.def(\"[\",R(i.length,(function(r){return E.command(\"number\"==typeof i[r]||\"boolean\"==typeof i[r],\"invalid uniform \"+e,t.commandStr),i[r]})),\"]\")})):E.commandRaise('invalid or missing data for uniform \"'+e+'\"',t.commandStr);n.value=i,a[e]=n})),Object.keys(n).forEach((function(e){var t=n[e];a[e]=Ja(t,(function(e,r){return e.invoke(r,t)}))})),a}(f,d),D.drawVAO=D.scopeVAO=w.vao,!D.drawVAO&&k.program&&!h&&n.angle_instanced_arrays&&w.static.elements){var j=!0,C=k.program.attributes.map((function(e){var r=t.static[e];return j=j&&!!r,r}));if(j&&C.length>0){var z=c.getVAO(c.createVAO({attributes:C,elements:w.static.elements}));D.drawVAO=new Xa(null,null,null,(function(e,t){return e.link(z)})),D.useVAO=!0}}return h?D.useVAO=!0:D.attributes=function(e,t){var n=e.static,a=e.dynamic,o={};return Object.keys(n).forEach((function(e){var a=n[e],f=r.id(e),u=new b;if(Qa(a))u.state=Qr,u.buffer=i.getBuffer(i.create(a,$n,!1,!0)),u.type=0;else{var s=i.getBuffer(a);if(s)u.state=Qr,u.buffer=s,u.type=0;else if(E.command(\"object\"==typeof a&&a,\"invalid data for attribute \"+e,t.commandStr),\"constant\"in a){var c=a.constant;u.buffer=\"null\",u.state=Yr,\"number\"==typeof c?u.x=c:(E.command(Te(c)&&c.length>0&&c.length<=4,\"invalid constant for attribute \"+e,t.commandStr),Nr.forEach((function(e,t){t=0,'invalid offset for attribute \"'+e+'\"',t.commandStr);var d=0|a.stride;E.command(d>=0&&d<256,'invalid stride for attribute \"'+e+'\", must be integer betweeen [0, 255]',t.commandStr);var m=0|a.size;E.command(!(\"size\"in a)||m>0&&m<=4,'invalid size for attribute \"'+e+'\", must be 1,2,3,4',t.commandStr);var p=!!a.normalized,h=0;\"type\"in a&&(E.commandParameter(a.type,Z,\"invalid type for attribute \"+e,t.commandStr),h=Z[a.type]);var v=0|a.divisor;E.optional((function(){\"divisor\"in a&&(E.command(0===v||g,'cannot specify divisor for attribute \"'+e+'\", instancing not supported',t.commandStr),E.command(v>=0,'invalid divisor for attribute \"'+e+'\"',t.commandStr));var r=t.commandStr,n=[\"buffer\",\"offset\",\"divisor\",\"normalized\",\"type\",\"size\",\"stride\"];Object.keys(a).forEach((function(t){E.command(n.indexOf(t)>=0,'unknown parameter \"'+t+'\" for attribute pointer \"'+e+'\" (valid parameters are '+n+\")\",r)}))})),u.buffer=s,u.state=Qr,u.size=m,u.normalized=p,u.type=h||s.dtype,u.offset=l,u.stride=d,u.divisor=v}}o[e]=Ka((function(e,t){var r=e.attribCache;if(f in r)return r[f];var n={isStream:!1};return Object.keys(u).forEach((function(e){n[e]=u[e]})),u.buffer&&(n.buffer=e.link(u.buffer),n.type=n.type||n.buffer+\".dtype\"),r[f]=n,n}))})),Object.keys(a).forEach((function(e){var t=a[e];o[e]=Ja(t,(function(r,n){var a=r.invoke(n,t),i=r.shared,o=r.constants,f=i.isBufferArgs,u=i.buffer;E.optional((function(){r.assert(n,a+\"&&(typeof \"+a+'===\"object\"||typeof '+a+'===\"function\")&&('+f+\"(\"+a+\")||\"+u+\".getBuffer(\"+a+\")||\"+u+\".getBuffer(\"+a+\".buffer)||\"+f+\"(\"+a+'.buffer)||(\"constant\" in '+a+\"&&(typeof \"+a+'.constant===\"number\"||'+i.isArrayLike+\"(\"+a+\".constant))))\",'invalid dynamic attribute \"'+e+'\"')}));var s={isStream:n.def(!1)},c=new b;c.state=Qr,Object.keys(c).forEach((function(e){s[e]=n.def(\"\"+c[e])}));var l=s.buffer,d=s.type;function m(e){n(s[e],\"=\",a,\".\",e,\"|0;\")}return n(\"if(\",f,\"(\",a,\")){\",s.isStream,\"=true;\",l,\"=\",u,\".createStream(\",$n,\",\",a,\");\",d,\"=\",l,\".dtype;\",\"}else{\",l,\"=\",u,\".getBuffer(\",a,\");\",\"if(\",l,\"){\",d,\"=\",l,\".dtype;\",'}else if(\"constant\" in ',a,\"){\",s.state,\"=\",Yr,\";\",\"if(typeof \"+a+'.constant === \"number\"){',s[Nr[0]],\"=\",a,\".constant;\",Nr.slice(1).map((function(e){return s[e]})).join(\"=\"),\"=0;\",\"}else{\",Nr.map((function(e,t){return s[e]+\"=\"+a+\".constant.length>\"+t+\"?\"+a+\".constant[\"+t+\"]:0;\"})).join(\"\"),\"}}else{\",\"if(\",f,\"(\",a,\".buffer)){\",l,\"=\",u,\".createStream(\",$n,\",\",a,\".buffer);\",\"}else{\",l,\"=\",u,\".getBuffer(\",a,\".buffer);\",\"}\",d,'=\"type\" in ',a,\"?\",o.glTypes,\"[\",a,\".type]:\",l,\".dtype;\",s.normalized,\"=!!\",a,\".normalized;\"),m(\"size\"),m(\"offset\"),m(\"stride\"),m(\"divisor\"),n(\"}}\"),n.exit(\"if(\",s.isStream,\"){\",u,\".destroyStream(\",l,\");\",\"}\"),s}))})),o}(t,d),D.context=function(e){var t=e.static,r=e.dynamic,n={};return Object.keys(t).forEach((function(e){var r=t[e];n[e]=Ka((function(e,t){return\"number\"==typeof r||\"boolean\"==typeof r?\"\"+r:e.link(r)}))})),Object.keys(r).forEach((function(e){var t=r[e];n[e]=Ja(t,(function(e,r){return e.invoke(r,t)}))})),n}(s),D}function I(e,t,r){var n=e.shared.context,a=e.scope();Object.keys(r).forEach((function(i){t.save(n,\".\"+i);var o=r[i].append(e,t);Array.isArray(o)?a(n,\".\",i,\"=[\",o.join(),\"];\"):a(n,\".\",i,\"=\",o,\";\")})),t(a)}function P(e,t,r,n){var a,i=e.shared,o=i.gl,f=i.framebuffer;y&&(a=t.def(i.extensions,\".webgl_draw_buffers\"));var u,s=e.constants,c=s.drawBuffer,l=s.backBuffer;u=r?r.append(e,t):t.def(f,\".next\"),n||t(\"if(\",u,\"!==\",f,\".cur){\"),t(\"if(\",u,\"){\",o,\".bindFramebuffer(\",Ra,\",\",u,\".framebuffer);\"),y&&t(a,\".drawBuffersWEBGL(\",c,\"[\",u,\".colorAttachments.length]);\"),t(\"}else{\",o,\".bindFramebuffer(\",Ra,\",null);\"),y&&t(a,\".drawBuffersWEBGL(\",l,\");\"),t(\"}\",f,\".cur=\",u,\";\"),n||t(\"}\")}function L(e,t,r){var n=e.shared,a=n.gl,i=e.current,o=e.next,f=n.current,u=n.next,s=e.cond(f,\".dirty\");_.forEach((function(t){var n,c,l=O(t);if(!(l in r.state))if(l in o){n=o[l],c=i[l];var d=R(w[l].length,(function(e){return s.def(n,\"[\",e,\"]\")}));s(e.cond(d.map((function(e,t){return e+\"!==\"+c+\"[\"+t+\"]\"})).join(\"||\")).then(a,\".\",S[l],\"(\",d,\");\",d.map((function(e,t){return c+\"[\"+t+\"]=\"+e})).join(\";\"),\";\"))}else{n=s.def(u,\".\",l);var m=e.cond(n,\"!==\",f,\".\",l);s(m),l in k?m(e.cond(n).then(a,\".enable(\",k[l],\");\").else(a,\".disable(\",k[l],\");\"),f,\".\",l,\"=\",n,\";\"):m(a,\".\",S[l],\"(\",n,\");\",f,\".\",l,\"=\",n,\";\")}})),0===Object.keys(r.state).length&&s(f,\".dirty=false;\"),t(s)}function M(e,t,r,n){var a=e.shared,i=e.current,o=a.current,f=a.gl;Ya(Object.keys(r)).forEach((function(a){var u=r[a];if(!n||n(u)){var s=u.append(e,t);if(k[a]){var c=k[a];$a(u)?t(f,s?\".enable(\":\".disable(\",c,\");\"):t(e.cond(s).then(f,\".enable(\",c,\");\").else(f,\".disable(\",c,\");\")),t(o,\".\",a,\"=\",s,\";\")}else if(Te(s)){var l=i[a];t(f,\".\",S[a],\"(\",s,\");\",s.map((function(e,t){return l+\"[\"+t+\"]=\"+e})).join(\";\"),\";\")}else t(f,\".\",S[a],\"(\",s,\");\",o,\".\",a,\"=\",s,\";\")}}))}function U(e,t){g&&(e.instancing=t.def(e.shared.extensions,\".angle_instanced_arrays\"))}function W(e,t,r,n,a){var i,o,f,u=e.shared,s=e.stats,c=u.current,l=u.timer,d=r.profile;function m(){return\"undefined\"==typeof performance?\"Date.now()\":\"performance.now()\"}function h(e){e(i=t.def(),\"=\",m(),\";\"),\"string\"==typeof a?e(s,\".count+=\",a,\";\"):e(s,\".count++;\"),p&&(n?e(o=t.def(),\"=\",l,\".getNumPendingQueries();\"):e(l,\".beginQuery(\",s,\");\"))}function b(e){e(s,\".cpuTime+=\",m(),\"-\",i,\";\"),p&&(n?e(l,\".pushScopeStats(\",o,\",\",l,\".getNumPendingQueries(),\",s,\");\"):e(l,\".endQuery();\"))}function v(e){var r=t.def(c,\".profile\");t(c,\".profile=\",e,\";\"),t.exit(c,\".profile=\",r,\";\")}if(d){if($a(d))return void(d.enable?(h(t),b(t.exit),v(\"true\")):v(\"false\"));v(f=d.append(e,t))}else f=t.def(c,\".profile\");var g=e.block();h(g),t(\"if(\",f,\"){\",g,\"}\");var y=e.block();b(y),t.exit(\"if(\",f,\"){\",y,\"}\")}function G(e,t,r,n,a){var i=e.shared;n.forEach((function(n){var o,f=n.name,u=r.attributes[f];if(u){if(!a(u))return;o=u.append(e,t)}else{if(!a(Za))return;var s=e.scopeAttrib(f);E.optional((function(){e.assert(t,s+\".state\",\"missing attribute \"+f)})),o={},Object.keys(new b).forEach((function(e){o[e]=t.def(s,\".\",e)}))}!function(r,n,a){var o=i.gl,f=t.def(r,\".location\"),u=t.def(i.attributes,\"[\",f,\"]\"),s=a.state,c=a.buffer,l=[a.x,a.y,a.z,a.w],d=[\"buffer\",\"normalized\",\"offset\",\"stride\"];function m(){t(\"if(!\",u,\".buffer){\",o,\".enableVertexAttribArray(\",f,\");}\");var r,i=a.type;if(r=a.size?t.def(a.size,\"||\",n):n,t(\"if(\",u,\".type!==\",i,\"||\",u,\".size!==\",r,\"||\",d.map((function(e){return u+\".\"+e+\"!==\"+a[e]})).join(\"||\"),\"){\",o,\".bindBuffer(\",$n,\",\",c,\".buffer);\",o,\".vertexAttribPointer(\",[f,r,i,a.normalized,a.stride,a.offset],\");\",u,\".type=\",i,\";\",u,\".size=\",r,\";\",d.map((function(e){return u+\".\"+e+\"=\"+a[e]+\";\"})).join(\"\"),\"}\"),g){var s=a.divisor;t(\"if(\",u,\".divisor!==\",s,\"){\",e.instancing,\".vertexAttribDivisorANGLE(\",[f,s],\");\",u,\".divisor=\",s,\";}\")}}function p(){t(\"if(\",u,\".buffer){\",o,\".disableVertexAttribArray(\",f,\");\",u,\".buffer=null;\",\"}if(\",Nr.map((function(e,t){return u+\".\"+e+\"!==\"+l[t]})).join(\"||\"),\"){\",o,\".vertexAttrib4f(\",f,\",\",l,\");\",Nr.map((function(e,t){return u+\".\"+e+\"=\"+l[t]+\";\"})).join(\"\"),\"}\")}s===Qr?m():s===Yr?p():(t(\"if(\",s,\"===\",Qr,\"){\"),m(),t(\"}else{\"),p(),t(\"}\"))}(e.link(n),function(e){switch(e){case ca:case pa:case ga:return 2;case la:case ha:case ya:return 3;case da:case ba:case xa:return 4;default:return 1}}(n.info.type),o)}))}function H(e,t,n,a,i,o){for(var f,u=e.shared,s=u.gl,c={},l=0;l1){if(!b)continue;var v=m.replace(\"[0]\",\"\");if(c[v])continue;c[v]=1}var g,y=e.link(d)+\".location\";if(b){if(!i(b))continue;if($a(b)){var x=b.value;if(E.command(null!=x,'missing uniform \"'+m+'\"',e.commandStr),p===ka||p===Sa){E.command(\"function\"==typeof x&&(p===ka&&(\"texture2d\"===x._reglType||\"framebuffer\"===x._reglType)||p===Sa&&(\"textureCube\"===x._reglType||\"framebufferCube\"===x._reglType)),\"invalid texture for uniform \"+m,e.commandStr);var w=e.link(x._texture||x.color[0]._texture);t(s,\".uniform1i(\",y,\",\",w+\".bind());\"),t.exit(w,\".unbind();\")}else if(p===wa||p===Aa||p===_a){E.optional((function(){E.command(Te(x),\"invalid matrix for uniform \"+m,e.commandStr),E.command(p===wa&&4===x.length||p===Aa&&9===x.length||p===_a&&16===x.length,\"invalid length for matrix uniform \"+m,e.commandStr)}));var A=e.global.def(\"new Float32Array([\"+Array.prototype.slice.call(x)+\"])\"),_=2;p===Aa?_=3:p===_a&&(_=4),t(s,\".uniformMatrix\",_,\"fv(\",y,\",false,\",A,\");\")}else{switch(p){case sa:1===h?E.commandType(x,\"number\",\"uniform \"+m,e.commandStr):E.command(Te(x)&&x.length===h,\"uniform \"+m,e.commandStr),f=\"1f\";break;case ca:E.command(Te(x)&&x.length&&x.length%2==0&&x.length<=2*h,\"uniform \"+m,e.commandStr),f=\"2f\";break;case la:E.command(Te(x)&&x.length&&x.length%3==0&&x.length<=3*h,\"uniform \"+m,e.commandStr),f=\"3f\";break;case da:E.command(Te(x)&&x.length&&x.length%4==0&&x.length<=4*h,\"uniform \"+m,e.commandStr),f=\"4f\";break;case va:1===h?E.commandType(x,\"boolean\",\"uniform \"+m,e.commandStr):E.command(Te(x)&&x.length===h,\"uniform \"+m,e.commandStr),f=\"1i\";break;case ma:1===h?E.commandType(x,\"number\",\"uniform \"+m,e.commandStr):E.command(Te(x)&&x.length===h,\"uniform \"+m,e.commandStr),f=\"1i\";break;case ga:case pa:E.command(Te(x)&&x.length&&x.length%2==0&&x.length<=2*h,\"uniform \"+m,e.commandStr),f=\"2i\";break;case ya:case ha:E.command(Te(x)&&x.length&&x.length%3==0&&x.length<=3*h,\"uniform \"+m,e.commandStr),f=\"3i\";break;case xa:case ba:E.command(Te(x)&&x.length&&x.length%4==0&&x.length<=4*h,\"uniform \"+m,e.commandStr),f=\"4i\"}h>1?(f+=\"v\",x=e.global.def(\"[\"+Array.prototype.slice.call(x)+\"]\")):x=Te(x)?Array.prototype.slice.call(x):x,t(s,\".uniform\",f,\"(\",y,\",\",x,\");\")}continue}g=b.append(e,t)}else{if(!i(Za))continue;g=t.def(u.uniforms,\"[\",r.id(m),\"]\")}p===ka?(E(!Array.isArray(g),\"must specify a scalar prop for textures\"),t(\"if(\",g,\"&&\",g,'._reglType===\"framebuffer\"){',g,\"=\",g,\".color[0];\",\"}\")):p===Sa&&(E(!Array.isArray(g),\"must specify a scalar prop for cube maps\"),t(\"if(\",g,\"&&\",g,'._reglType===\"framebufferCube\"){',g,\"=\",g,\".color[0];\",\"}\")),E.optional((function(){function r(r,n){e.assert(t,r,'bad data or missing for uniform \"'+m+'\". '+n)}function n(e,t){1===t&&E(!Array.isArray(g),\"must not specify an array type for uniform\"),r(\"Array.isArray(\"+g+\") && typeof \"+g+'[0]===\" '+e+'\" || typeof '+g+'===\"'+e+'\"',\"invalid type, expected \"+e)}function a(t,n,a){Array.isArray(g)?E(g.length&&g.length%t==0&&g.length<=t*a,\"must have length of \"+(1===a?\"\":\"n * \")+t):r(u.isArrayLike+\"(\"+g+\")&&\"+g+\".length && \"+g+\".length % \"+t+\" === 0 && \"+g+\".length<=\"+t*a,\"invalid vector, should have length of \"+(1===a?\"\":\"n * \")+t,e.commandStr)}function i(t){E(!Array.isArray(g),\"must not specify a value type\"),r(\"typeof \"+g+'===\"function\"&&'+g+'._reglType===\"texture'+(t===Jn?\"2d\":\"Cube\")+'\"',\"invalid texture type\",e.commandStr)}switch(p){case ma:n(\"number\",h);break;case pa:a(2,0,h);break;case ha:a(3,0,h);break;case ba:a(4,0,h);break;case sa:n(\"number\",h);break;case ca:a(2,0,h);break;case la:a(3,0,h);break;case da:a(4,0,h);break;case va:n(\"boolean\",h);break;case ga:a(2,0,h);break;case ya:a(3,0,h);break;case xa:case wa:a(4,0,h);break;case Aa:a(9,0,h);break;case _a:a(16,0,h);break;case ka:i(Jn);break;case Sa:i(Zn)}}));var k=1;switch(p){case ka:case Sa:var S=t.def(g,\"._texture\");t(s,\".uniform1i(\",y,\",\",S,\".bind());\"),t.exit(S,\".unbind();\");continue;case ma:case va:f=\"1i\";break;case pa:case ga:f=\"2i\",k=2;break;case ha:case ya:f=\"3i\",k=3;break;case ba:case xa:f=\"4i\",k=4;break;case sa:f=\"1f\";break;case ca:f=\"2f\",k=2;break;case la:f=\"3f\",k=3;break;case da:f=\"4f\",k=4;break;case wa:f=\"Matrix2fv\";break;case Aa:f=\"Matrix3fv\";break;case _a:f=\"Matrix4fv\"}if(-1===f.indexOf(\"Matrix\")&&h>1&&(f+=\"v\",k=1),\"M\"===f.charAt(0)){t(s,\".uniform\",f,\"(\",y,\",\");var O=Math.pow(p-wa+2,2),T=e.global.def(\"new Float32Array(\",O,\")\");Array.isArray(g)?t(\"false,(\",R(O,(function(e){return T+\"[\"+e+\"]=\"+g[e]})),\",\",T,\")\"):t(\"false,(Array.isArray(\",g,\")||\",g,\" instanceof Float32Array)?\",g,\":(\",R(O,(function(e){return T+\"[\"+e+\"]=\"+g+\"[\"+e+\"]\"})),\",\",T,\")\"),t(\");\")}else if(k>1){for(var D=[],j=[],C=0;C=0\",\"missing vertex count\")}))):(a=u.def(o,\".\",In),E.optional((function(){e.assert(u,a+\">=0\",\"missing vertex count\")}))),a}();if(\"number\"==typeof p){if(0===p)return}else r(\"if(\",p,\"){\"),r.exit(\"}\");g&&(c=s(Ln),l=e.instancing);var h=u+\".type\",b=f.elements&&$a(f.elements)&&!f.vaoActive;function v(){function e(){r(l,\".drawElementsInstancedANGLE(\",[d,p,h,m+\"<<((\"+h+\"-\"+qr+\")>>1)\",c],\");\")}function t(){r(l,\".drawArraysInstancedANGLE(\",[d,m,p,c],\");\")}u&&\"null\"!==u?b?e():(r(\"if(\",u,\"){\"),e(),r(\"}else{\"),t(),r(\"}\")):t()}function y(){function e(){r(i+\".drawElements(\"+[d,p,h,m+\"<<((\"+h+\"-\"+qr+\")>>1)\"]+\");\")}function t(){r(i+\".drawArrays(\"+[d,m,p]+\");\")}u&&\"null\"!==u?b?e():(r(\"if(\",u,\"){\"),e(),r(\"}else{\"),t(),r(\"}\")):t()}g&&(\"number\"!=typeof c||c>=0)?\"string\"==typeof c?(r(\"if(\",c,\">0){\"),v(),r(\"}else if(\",c,\"<0){\"),y(),r(\"}\")):v():y()}function q(e,t,r,n,a){var i=V(),o=i.proc(\"body\",a);return E.optional((function(){i.commandStr=t.commandStr,i.command=i.link(t.commandStr)})),g&&(i.instancing=o.def(i.shared.extensions,\".angle_instanced_arrays\")),e(i,o,r,n),i.compile().body}function Q(e,t,r,n){U(e,t),r.useVAO?r.drawVAO?t(e.shared.vao,\".setVAO(\",r.drawVAO.append(e,t),\");\"):t(e.shared.vao,\".setVAO(\",e.shared.vao,\".targetVAO);\"):(t(e.shared.vao,\".setVAO(null);\"),G(e,t,r,n.attributes,(function(){return!0}))),H(e,t,r,n.uniforms,(function(){return!0}),!1),N(e,t,t,r)}function Y(e,t,r,n){function a(){return!0}e.batchId=\"a1\",U(e,t),G(e,t,r,n.attributes,a),H(e,t,r,n.uniforms,a,!1),N(e,t,t,r)}function X(e,t,r,n){U(e,t);var a=r.contextDep,i=t.def(),o=t.def();e.shared.props=o,e.batchId=i;var f=e.scope(),u=e.scope();function s(e){return e.contextDep&&a||e.propDep}function c(e){return!s(e)}if(t(f.entry,\"for(\",i,\"=0;\",i,\"<\",\"a1\",\";++\",i,\"){\",o,\"=\",\"a0\",\"[\",i,\"];\",u,\"}\",f.exit),r.needsContext&&I(e,u,r.context),r.needsFramebuffer&&P(e,u,r.framebuffer),M(e,u,r.state,s),r.profile&&s(r.profile)&&W(e,u,r,!1,!0),n)r.useVAO?r.drawVAO?s(r.drawVAO)?u(e.shared.vao,\".setVAO(\",r.drawVAO.append(e,u),\");\"):f(e.shared.vao,\".setVAO(\",r.drawVAO.append(e,f),\");\"):f(e.shared.vao,\".setVAO(\",e.shared.vao,\".targetVAO);\"):(f(e.shared.vao,\".setVAO(null);\"),G(e,f,r,n.attributes,c),G(e,u,r,n.attributes,s)),H(e,f,r,n.uniforms,c,!1),H(e,u,r,n.uniforms,s,!0),N(e,f,u,r);else{var l=e.global.def(\"{}\"),d=r.shader.progVar.append(e,u),m=u.def(d,\".id\"),p=u.def(l,\"[\",m,\"]\");u(e.shared.gl,\".useProgram(\",d,\".program);\",\"if(!\",p,\"){\",p,\"=\",l,\"[\",m,\"]=\",e.link((function(t){return q(Y,e,r,t,2)})),\"(\",d,\");}\",p,\".call(this,a0[\",i,\"],\",i,\");\")}}function $(e,t,r){var n=t.static[r];if(n&&function(e){if(\"object\"==typeof e&&!Te(e)){for(var t=Object.keys(e),r=0;r0&&r(e.shared.current,\".dirty=true;\"),e.shared.vao&&r(e.shared.vao,\".setVAO(null);\")}(f,u),function(e,t){var n=e.proc(\"scope\",3);e.batchId=\"a2\";var a=e.shared,i=a.current;function o(r){var i=t.shader[r];i&&n.set(a.shader,\".\"+r,i.append(e,n))}I(e,n,t.context),t.framebuffer&&t.framebuffer.append(e,n),Ya(Object.keys(t.state)).forEach((function(r){var i=t.state[r].append(e,n);Te(i)?i.forEach((function(t,a){n.set(e.next[r],\"[\"+a+\"]\",t)})):n.set(a.next,\".\"+r,i)})),W(e,n,t,!0,!0),[Vn,Pn,In,Ln,Bn].forEach((function(r){var i=t.draw[r];i&&n.set(a.draw,\".\"+r,\"\"+i.append(e,n))})),Object.keys(t.uniforms).forEach((function(i){var o=t.uniforms[i].append(e,n);Array.isArray(o)&&(o=\"[\"+o.join()+\"]\"),n.set(a.uniforms,\"[\"+r.id(i)+\"]\",o)})),Object.keys(t.attributes).forEach((function(r){var a=t.attributes[r].append(e,n),i=e.scopeAttrib(r);Object.keys(new b).forEach((function(e){n.set(i,\".\"+e,a[e])}))})),t.scopeVAO&&n.set(a.vao,\".targetVAO\",t.scopeVAO.append(e,n)),o(zn),o(Fn),Object.keys(t.state).length>0&&(n(i,\".dirty=true;\"),n.exit(i,\".dirty=true;\")),n(\"a1(\",e.shared.context,\",a0,\",e.batchId,\");\")}(f,u),function(e,t){var r=e.proc(\"batch\",2);e.batchId=\"0\",U(e,r);var n=!1,a=!0;Object.keys(t.context).forEach((function(e){n=n||t.context[e].propDep})),n||(I(e,r,t.context),a=!1);var i=t.framebuffer,o=!1;function f(e){return e.contextDep&&n||e.propDep}i?(i.propDep?n=o=!0:i.contextDep&&n&&(o=!0),o||P(e,r,i)):P(e,r,null),t.state.viewport&&t.state.viewport.propDep&&(n=!0),L(e,r,t),M(e,r,t.state,(function(e){return!f(e)})),t.profile&&f(t.profile)||W(e,r,t,!1,\"a1\"),t.contextDep=n,t.needsContext=a,t.needsFramebuffer=o;var u=t.shader.progVar;if(u.contextDep&&n||u.propDep)X(e,r,t,null);else{var s=u.append(e,r);if(r(e.shared.gl,\".useProgram(\",s,\".program);\"),t.shader.program)X(e,r,t,t.shader.program);else{r(e.shared.vao,\".setVAO(null);\");var c=e.global.def(\"{}\"),l=r.def(s,\".id\"),d=r.def(c,\"[\",l,\"]\");r(e.cond(d).then(d,\".call(this,a0,a1);\").else(d,\"=\",c,\"[\",l,\"]=\",e.link((function(r){return q(X,e,t,r,2)})),\"(\",s,\");\",d,\".call(this,a0,a1);\"))}}Object.keys(t.state).length>0&&r(e.shared.current,\".dirty=true;\"),e.shared.vao&&r(e.shared.vao,\".setVAO(null);\")}(f,u),t(f.compile(),{destroy:function(){u.shader.program.destroy()}})}}}var ti=function(e,t){if(!t.ext_disjoint_timer_query)return null;var r=[];function n(e){r.push(e)}var a=[];function i(){this.startQueryIndex=-1,this.endQueryIndex=-1,this.sum=0,this.stats=null}var o=[];function f(e){o.push(e)}var u=[];function s(e,t,r){var n=o.pop()||new i;n.startQueryIndex=e,n.endQueryIndex=t,n.sum=0,n.stats=r,u.push(n)}var c=[],l=[];return{beginQuery:function(e){var n=r.pop()||t.ext_disjoint_timer_query.createQueryEXT();t.ext_disjoint_timer_query.beginQueryEXT(35007,n),a.push(n),s(a.length-1,a.length,e)},endQuery:function(){t.ext_disjoint_timer_query.endQueryEXT(35007)},pushScopeStats:s,update:function(){var e,r,i=a.length;if(0!==i){l.length=Math.max(l.length,i+1),c.length=Math.max(c.length,i+1),c[0]=0,l[0]=0;var o=0;for(e=0,r=0;r0)if(Array.isArray(r[0])){f=re(r);for(var c=1,l=1;l0)if(\"number\"==typeof t[0]){var i=W.allocType(d.dtype,t.length);se(i,t),p(i,a),W.freeType(i)}else if(Array.isArray(t[0])||e(t[0])){n=re(t);var o=te(t,n,d.dtype);p(o,a),W.freeType(o)}else E.raise(\"invalid buffer data\")}else if(Q(t)){n=t.shape;var f=t.stride,u=0,s=0,c=0,l=0;1===n.length?(u=n[0],s=1,c=f[0],l=0):2===n.length?(u=n[0],s=n[1],c=f[0],l=f[1]):E.raise(\"invalid shape\");var h=Array.isArray(t.data)?d.dtype:ue(t.data),b=W.allocType(h,u*s);ce(b,t.data,u,s,c,l,t.offset),p(b,a),W.freeType(b)}else E.raise(\"invalid data for buffer subdata\");return m},n.profile&&(m.stats=d.stats),m.destroy=function(){l(d)},m},createStream:function(e,t){var r=u.pop();return r||(r=new f(e)),r.bind(),c(r,t,ae,0,1,!1),r},destroyStream:function(e){u.push(e)},clear:function(){Y(o).forEach(l),u.forEach(l)},getBuffer:function(e){return e&&e._buffer instanceof f?e._buffer:null},restore:function(){Y(o).forEach((function(e){e.buffer=t.createBuffer(),t.bindBuffer(e.type,e.buffer),t.bufferData(e.type,e.persistentData||e.byteLength,e.usage)}))},_initBuffer:c}}(a,l,n,(function(e){return A.destroyBuffer(e)})),w=function(t,r,n,a){var i={},o=0,f={uint8:be,uint16:ge};function u(e){this.id=o++,i[this.id]=this,this.buffer=e,this.primType=pe,this.vertCount=0,this.type=0}r.oes_element_index_uint&&(f.uint32=xe),u.prototype.bind=function(){this.buffer.bind()};var s=[];function c(a,i,o,f,u,s,c){var l;if(a.buffer.bind(),i){var d=c;c||e(i)&&(!Q(i)||e(i.data))||(d=r.oes_element_index_uint?xe:ge),n._initBuffer(a.buffer,i,o,d,3)}else t.bufferData(we,s,o),a.buffer.dtype=l||be,a.buffer.usage=o,a.buffer.dimension=3,a.buffer.byteLength=s;if(l=c,!c){switch(a.buffer.dtype){case be:case he:l=be;break;case ge:case ve:l=ge;break;case xe:case ye:l=xe;break;default:E.raise(\"unsupported type for element array\")}a.buffer.dtype=l}a.type=l,E(l!==xe||!!r.oes_element_index_uint,\"32 bit element buffers not supported, enable oes_element_index_uint first\");var m=u;m<0&&(m=a.buffer.byteLength,l===ge?m>>=1:l===xe&&(m>>=2)),a.vertCount=m;var p=f;if(f<0){p=pe;var h=a.buffer.dimension;1===h&&(p=de),2===h&&(p=me),3===h&&(p=pe)}a.primType=p}function l(e){a.elementsCount--,E(null!==e.buffer,\"must not double destroy elements\"),delete i[e.id],e.buffer.destroy(),e.buffer=null}return{create:function(t,r){var i=n.create(null,we,!0),o=new u(i._buffer);function s(t){if(t)if(\"number\"==typeof t)i(t),o.primType=pe,o.vertCount=0|t,o.type=be;else{var r=null,n=_e,a=-1,u=-1,l=0,d=0;Array.isArray(t)||e(t)||Q(t)?r=t:(E.type(t,\"object\",\"invalid arguments for elements\"),\"data\"in t&&(r=t.data,E(Array.isArray(r)||e(r)||Q(r),\"invalid data for element buffer\")),\"usage\"in t&&(E.parameter(t.usage,ee,\"invalid element buffer usage\"),n=ee[t.usage]),\"primitive\"in t&&(E.parameter(t.primitive,le,\"invalid element buffer primitive\"),a=le[t.primitive]),\"count\"in t&&(E(\"number\"==typeof t.count&&t.count>=0,\"invalid vertex count for elements\"),u=0|t.count),\"type\"in t&&(E.parameter(t.type,f,\"invalid buffer type\"),d=f[t.type]),\"length\"in t?l=0|t.length:(l=u,d===ge||d===ve?l*=2:d!==xe&&d!==ye||(l*=4))),c(o,r,n,a,u,l,d)}else i(),o.primType=pe,o.vertCount=0,o.type=be;return s}return a.elementsCount++,s(t),s._reglType=\"elements\",s._elements=o,s.subdata=function(e,t){return i.subdata(e,t),s},s.destroy=function(){l(o)},s},createStream:function(e){var t=s.pop();return t||(t=new u(n.create(null,we,!0,!1)._buffer)),c(t,e,Ae,-1,-1,0,0),t},destroyStream:function(e){s.push(e)},getElements:function(e){return\"function\"==typeof e&&e._elements instanceof u?e._elements:null},clear:function(){Y(i).forEach(l)}}}(a,d,x,l),A=function(t,r,n,a,i,o,f){for(var u=n.maxAttributes,s=new Array(u),c=0;c{for(var e=Object.keys(t),r=0;r=0,'invalid option for vao: \"'+e[r]+'\" valid options are '+zr)})),E(Array.isArray(a),\"attributes must be an array\")}E(a.length0,\"must specify at least one attribute\");var c={},l=n.attributes;l.length=a.length;for(var d=0;d=b.byteLength?m.subdata(b):(m.destroy(),n.buffers[d]=null)),n.buffers[d]||(m=n.buffers[d]=i.create(p,jr,!1,!0)),h.buffer=i.getBuffer(m),h.size=0|h.buffer.dimension,h.normalized=!1,h.type=h.buffer.dtype,h.offset=0,h.stride=0,h.divisor=0,h.state=1,c[d]=1):i.getBuffer(p)?(h.buffer=i.getBuffer(p),h.size=0|h.buffer.dimension,h.normalized=!1,h.type=h.buffer.dtype,h.offset=0,h.stride=0,h.divisor=0,h.state=1):i.getBuffer(p.buffer)?(h.buffer=i.getBuffer(p.buffer),h.size=0|(+p.size||h.buffer.dimension),h.normalized=!!p.normalized||!1,\"type\"in p?(E.parameter(p.type,Z,\"invalid buffer type\"),h.type=Z[p.type]):h.type=h.buffer.dtype,h.offset=0|(p.offset||0),h.stride=0|(p.stride||0),h.divisor=0|(p.divisor||0),h.state=1,E(h.size>=1&&h.size<=4,\"size must be between 1 and 4\"),E(h.offset>=0,\"invalid offset\"),E(h.stride>=0&&h.stride<=255,\"stride must be between 0 and 255\"),E(h.divisor>=0,\"divisor must be positive\"),E(!h.divisor||!!r.angle_instanced_arrays,\"ANGLE_instanced_arrays must be enabled to use divisor\")):\"x\"in p?(E(d>0,\"first attribute must not be a constant\"),h.x=+p.x||0,h.y=+p.y||0,h.z=+p.z||0,h.w=+p.w||0,h.state=2):E(!1,\"invalid attribute spec for location \"+d)}for(var v=0;v1)for(var v=0;v1&&(y=y.replace(\"[0]\",\"\")),u(b,new f(y,r.id(y),e.getUniformLocation(m,y),c))}var x=e.getProgramParameter(m,Pr);a.profile&&(t.stats.attributesCount=x);var w=t.attributes;for(o=0;oe&&(e=t.stats.uniformsCount)})),e},n.getMaxAttributesCount=function(){var e=0;return l.forEach((function(t){t.stats.attributesCount>e&&(e=t.stats.attributesCount)})),e}),{clear:function(){var t=e.deleteShader.bind(e);Y(i).forEach(t),i={},Y(o).forEach(t),o={},l.forEach((function(t){e.deleteProgram(t.program)})),l.length=0,c={},n.shaderCount=0},program:function(r,a,f,u){E.command(r>=0,\"missing vertex shader\",f),E.command(a>=0,\"missing fragment shader\",f);var s=c[a];s||(s=c[a]={});var d=s[r];if(d&&(d.refCount++,!u))return d;var h=new m(a,r);return n.shaderCount++,p(h,f,u),d||(s[r]=h),l.push(h),t(h,{destroy:function(){if(h.refCount--,h.refCount<=0){e.deleteProgram(h.program);var t=l.indexOf(h);l.splice(t,1),n.shaderCount--}s[h.vertId].refCount<=0&&(e.deleteShader(o[h.vertId]),delete o[h.vertId],delete c[h.fragId][h.vertId]),Object.keys(c[h.fragId]).length||(e.deleteShader(i[h.fragId]),delete i[h.fragId],delete c[h.fragId])}})},restore:function(){i={},o={};for(var e=0;e=2,\"invalid renderbuffer shape\"),f=0|m[0],u=0|m[1]}else\"radius\"in d&&(f=u=0|d.radius),\"width\"in d&&(f=0|d.width),\"height\"in d&&(u=0|d.height);\"format\"in d&&(E.parameter(d.format,i,\"invalid renderbuffer format\"),s=i[d.format])}else\"number\"==typeof t?(f=0|t,u=\"number\"==typeof n?0|n:f):t?E.raise(\"invalid arguments to renderbuffer constructor\"):f=u=1;if(E(f>0&&u>0&&f<=r.maxRenderbufferSize&&u<=r.maxRenderbufferSize,\"invalid renderbuffer size\"),f!==c.width||u!==c.height||s!==c.format)return l.width=c.width=f,l.height=c.height=u,c.format=s,e.bindRenderbuffer(fr,c.renderbuffer),e.renderbufferStorage(fr,s,f,u),E(0===e.getError(),\"invalid render buffer format\"),a.profile&&(c.stats.size=cr(c.format,c.width,c.height)),l.format=o[c.format],l}return u[c.id]=c,n.renderbufferCount++,l(t,f),l.resize=function(t,n){var i=0|t,o=0|n||i;return i===c.width&&o===c.height||(E(i>0&&o>0&&i<=r.maxRenderbufferSize&&o<=r.maxRenderbufferSize,\"invalid renderbuffer size\"),l.width=c.width=i,l.height=c.height=o,e.bindRenderbuffer(fr,c.renderbuffer),e.renderbufferStorage(fr,c.format,i,o),E(0===e.getError(),\"invalid render buffer format\"),a.profile&&(c.stats.size=cr(c.format,c.width,c.height))),l},l._reglType=\"renderbuffer\",l._renderbuffer=c,a.profile&&(l.stats=c.stats),l.destroy=function(){c.decRef()},l},clear:function(){Y(u).forEach(c)},restore:function(){Y(u).forEach((function(t){t.renderbuffer=e.createRenderbuffer(),e.bindRenderbuffer(fr,t.renderbuffer),e.renderbufferStorage(fr,t.format,t.width,t.height)})),e.bindRenderbuffer(fr,null)}}}(a,d,y,l,n),O=function(e,r,n,a,i,o){var f={cur:null,next:null,dirty:!1,setFBO:null},u=[\"rgba\"],s=[\"rgba4\",\"rgb565\",\"rgb5 a1\"];r.ext_srgb&&s.push(\"srgba\"),r.ext_color_buffer_half_float&&s.push(\"rgba16f\",\"rgb16f\"),r.webgl_color_buffer_float&&s.push(\"rgba32f\");var c=[\"uint8\"];function l(e,t,r){this.target=e,this.texture=t,this.renderbuffer=r;var n=0,a=0;t?(n=t.width,a=t.height):r&&(n=r.width,a=r.height),this.width=n,this.height=a}function d(e){e&&(e.texture&&e.texture._texture.decRef(),e.renderbuffer&&e.renderbuffer._renderbuffer.decRef())}function m(e,t,r){if(e)if(e.texture){var n=e.texture._texture,a=Math.max(1,n.width),i=Math.max(1,n.height);E(a===t&&i===r,\"inconsistent width/height for supplied texture\"),n.refCount+=1}else{var o=e.renderbuffer._renderbuffer;E(o.width===t&&o.height===r,\"inconsistent width/height for renderbuffer\"),o.refCount+=1}}function p(t,r){r&&(r.texture?e.framebufferTexture2D(lr,t,r.target,r.texture._texture.texture,0):e.framebufferRenderbuffer(lr,t,dr,r.renderbuffer._renderbuffer.renderbuffer))}function h(e){var t=mr,r=null,n=null,a=e;\"object\"==typeof e&&(a=e.data,\"target\"in e&&(t=0|e.target)),E.type(a,\"function\",\"invalid attachment data\");var i=a._reglType;return\"texture2d\"===i?(r=a,E(t===mr)):\"textureCube\"===i?(r=a,E(t>=pr&&t=2,\"invalid shape for framebuffer\"),o=z[0],d=z[1]}else\"radius\"in C&&(o=d=C.radius),\"width\"in C&&(o=C.width),\"height\"in C&&(d=C.height);(\"color\"in C||\"colors\"in C)&&(y=C.color||C.colors,Array.isArray(y)&&E(1===y.length||r.webgl_draw_buffers,\"multiple render targets not supported\")),y||(\"colorCount\"in C&&(S=0|C.colorCount,E(S>0,\"invalid color buffer count\")),\"colorTexture\"in C&&(x=!!C.colorTexture,w=\"rgba4\"),\"colorType\"in C&&(_=C.colorType,x?(E(r.oes_texture_float||!(\"float\"===_||\"float32\"===_),\"you must enable OES_texture_float in order to use floating point framebuffer objects\"),E(r.oes_texture_half_float||!(\"half float\"===_||\"float16\"===_),\"you must enable OES_texture_half_float in order to use 16-bit floating point framebuffer objects\")):\"half float\"===_||\"float16\"===_?(E(r.ext_color_buffer_half_float,\"you must enable EXT_color_buffer_half_float to use 16-bit render buffers\"),w=\"rgba16f\"):\"float\"!==_&&\"float32\"!==_||(E(r.webgl_color_buffer_float,\"you must enable WEBGL_color_buffer_float in order to use 32-bit floating point renderbuffers\"),w=\"rgba32f\"),E.oneOf(_,c,\"invalid color type\")),\"colorFormat\"in C&&(w=C.colorFormat,u.indexOf(w)>=0?x=!0:s.indexOf(w)>=0?x=!1:E.optional((function(){x?E.oneOf(C.colorFormat,u,\"invalid color format for texture\"):E.oneOf(C.colorFormat,s,\"invalid color format for renderbuffer\")})))),(\"depthTexture\"in C||\"depthStencilTexture\"in C)&&(j=!(!C.depthTexture&&!C.depthStencilTexture),E(!j||r.webgl_depth_texture,\"webgl_depth_texture extension not supported\")),\"depth\"in C&&(\"boolean\"==typeof C.depth?p=C.depth:(O=C.depth,g=!1)),\"stencil\"in C&&(\"boolean\"==typeof C.stencil?g=C.stencil:(T=C.stencil,p=!1)),\"depthStencil\"in C&&(\"boolean\"==typeof C.depthStencil?p=g=C.depthStencil:(D=C.depthStencil,p=!1,g=!1))}else o=d=1;var F=null,V=null,B=null,I=null;if(Array.isArray(y))F=y.map(h);else if(y)F=[h(y)];else for(F=new Array(S),a=0;a=0||F[a].renderbuffer&&Er.indexOf(F[a].renderbuffer._renderbuffer.format)>=0,\"framebuffer color attachment \"+a+\" is invalid\"),F[a]&&F[a].texture){var L=Ar[F[a].texture._texture.format]*_r[F[a].texture._texture.type];null===P?P=L:E(P===L,\"all color attachments much have the same number of bits per pixel.\")}return m(V,o,d),E(!V||V.texture&&V.texture._texture.format===xr||V.renderbuffer&&V.renderbuffer._renderbuffer.format===kr,\"invalid depth attachment for framebuffer object\"),m(B,o,d),E(!B||B.renderbuffer&&B.renderbuffer._renderbuffer.format===Sr,\"invalid stencil attachment for framebuffer object\"),m(I,o,d),E(!I||I.texture&&I.texture._texture.format===Or||I.renderbuffer&&I.renderbuffer._renderbuffer.format===Or,\"invalid depth-stencil attachment for framebuffer object\"),A(i),i.width=o,i.height=d,i.colorAttachments=F,i.depthAttachment=V,i.stencilAttachment=B,i.depthStencilAttachment=I,l.color=F.map(v),l.depth=v(V),l.stencil=v(B),l.depthStencil=v(I),l.width=i.width,l.height=i.height,k(i),l}return o.framebufferCount++,l(e,a),t(l,{resize:function(e,t){E(f.next!==i,\"can not resize a framebuffer which is currently in use\");var r=Math.max(0|e,1),n=Math.max(0|t||r,1);if(r===i.width&&n===i.height)return l;for(var a=i.colorAttachments,o=0;o=2,\"invalid shape for framebuffer\"),E(g[0]===g[1],\"cube framebuffer must be square\"),d=g[0]}else\"radius\"in v&&(d=0|v.radius),\"width\"in v?(d=0|v.width,\"height\"in v&&E(v.height===d,\"must be square\")):\"height\"in v&&(d=0|v.height);(\"color\"in v||\"colors\"in v)&&(m=v.color||v.colors,Array.isArray(m)&&E(1===m.length||r.webgl_draw_buffers,\"multiple render targets not supported\")),m||(\"colorCount\"in v&&(b=0|v.colorCount,E(b>0,\"invalid color buffer count\")),\"colorType\"in v&&(E.oneOf(v.colorType,c,\"invalid color type\"),h=v.colorType),\"colorFormat\"in v&&(p=v.colorFormat,E.oneOf(v.colorFormat,u,\"invalid color format for texture\"))),\"depth\"in v&&(l.depth=v.depth),\"stencil\"in v&&(l.stencil=v.stencil),\"depthStencil\"in v&&(l.depthStencil=v.depthStencil)}else d=1;if(m)if(Array.isArray(m))for(s=[],n=0;n0&&(l.depth=i[0].depth,l.stencil=i[0].stencil,l.depthStencil=i[0].depthStencil),i[n]?i[n](l):i[n]=S(l)}return t(o,{width:d,height:d,color:s})}return o(e),t(o,{faces:i,resize:function(e){var t,r=0|e;if(E(r>0&&r<=n.maxCubeMapSize,\"invalid radius for cube fbo\"),r===o.width)return o;var a=o.color;for(t=0;t=0;--e){var t=z[e];t&&t(v,null,0)}a.flush(),m&&m.update()}function X(){!M&&z.length>0&&(M=V.next(U))}function $(){M&&(V.cancel(U),M=null)}function K(e){e.preventDefault(),o=!0,$(),I.forEach((function(e){e()}))}function J(e){a.getError(),o=!1,f.restore(),_.restore(),x.restore(),k.restore(),S.restore(),O.restore(),A.restore(),m&&m.restore(),T.procs.refresh(),X(),P.forEach((function(e){e()}))}function ke(e){function r(e,t){var r={},n={};return Object.keys(e).forEach((function(a){var i=e[a];if(F.isDynamic(i))n[a]=F.unbox(i,a);else{if(t&&Array.isArray(i))for(var o=0;o0)return l.call(this,function(e){for(;m.length=0,\"cannot cancel a frame twice\"),z[t]=function e(){var t=ai(z,e);z[t]=z[z.length-1],z.length-=1,z.length<=0&&$()}}}}function Te(){var e=j.viewport,t=j.scissor_box;e[0]=e[1]=t[0]=t[1]=0,v.viewportWidth=v.framebufferWidth=v.drawingBufferWidth=e[2]=t[2]=a.drawingBufferWidth,v.viewportHeight=v.framebufferHeight=v.drawingBufferHeight=e[3]=t[3]=a.drawingBufferHeight}function De(){v.tick+=1,v.time=Ce(),Te(),T.procs.poll()}function je(){k.refresh(),Te(),T.procs.refresh(),m&&m.update()}function Ce(){return(B()-p)/1e3}je();var ze=t(ke,{clear:function(e){if(E(\"object\"==typeof e&&e,\"regl.clear() takes an object as input\"),\"framebuffer\"in e)if(e.framebuffer&&\"framebufferCube\"===e.framebuffer_reglType)for(var r=0;r<6;++r)Se(t({framebuffer:e.framebuffer.faces[r]},e),Oe);else Se(e,Oe);else Oe(0,e)},prop:F.define.bind(null,1),context:F.define.bind(null,2),this:F.define.bind(null,3),draw:ke({}),buffer:function(e){return x.create(e,34962,!1,!1)},elements:function(e){return w.create(e,!1)},texture:k.create2D,cube:k.createCube,renderbuffer:S.create,framebuffer:O.create,framebufferCube:O.createCube,vao:A.createVAO,attributes:i,frame:Ee,on:function(e,t){var r;switch(E.type(t,\"function\",\"listener callback must be a function\"),e){case\"frame\":return Ee(t);case\"lost\":r=I;break;case\"restore\":r=P;break;case\"destroy\":r=R;break;default:E.raise(\"invalid event, must be one of frame,lost,restore,destroy\")}return r.push(t),{cancel:function(){for(var e=0;e=0},read:D,destroy:function(){z.length=0,$(),C&&(C.removeEventListener(ri,K),C.removeEventListener(ni,J)),_.clear(),O.clear(),S.clear(),A.clear(),k.clear(),w.clear(),x.clear(),m&&m.clear(),R.forEach((function(e){e()}))},_gl:a,_refresh:je,poll:function(){De(),m&&m.update()},now:Ce,stats:l});return n.onDone(null,ze),ze}},\"object\"==typeof r&&void 0!==t?t.exports=o():\"function\"==typeof define&&define.amd?define(o):i.createREGL=o()},\n 542: function _(t,e,a,s,r){s();const n=t(543),_=t(10),o=t(13);class c{constructor(t){this._regl=t,this._map=new Map}_create_texture(t){const e=t.length;let a=0;const s=[];let r=0,_=0;for(let n=0;nc[f+1]&&f++;const s=t[f],n=c[f]+.5*s;let o=.5*s-Math.abs(a-n);f%2==1&&(o=-o),m[e]=Math.round(255*(o-r)/(_-r))}return[[a,u,r,_],this._regl.texture({shape:[l,1,1],data:m,wrapS:\"repeat\",format:\"alpha\",type:\"uint8\",mag:\"linear\",min:\"linear\"})]}_get_key(t){return t.join(\",\")}_get_or_create(t){const e=this._get_key(t);let a=this._map.get(e);if(null==a){const s=(0,n.gcd)(t);if(s>1){t=(0,o.map)(t,(t=>t/s)),a=this._get_or_create(t);const[r,n,_]=a;a=[r,n,s],this._map.set(e,a)}else{const[r,n]=this._create_texture(t);a=[r,n,s],this._map.set(e,a)}}return a}get(t){return t.length%2==1&&(t=(0,_.concat)([t,t])),this._get_or_create(t)}}a.DashCache=c,c.__name__=\"DashCache\"},\n 543: function _(n,t,e,r,o){function u(n,t){let e,r;n>t?(e=n,r=t):(e=t,r=n);let o=e%r;for(;0!=o;)e=r,r=o,o=e%r;return r}r(),e.gcd=function(n){let t=n[0];for(let e=1;e= 0.0 ? 1.0 : -1.0;\\n\\n bool miter_too_large_start = !has_start_cap && miter_too_large(join_type, v_cos_turn_angle_start);\\n bool miter_too_large_end = !has_end_cap && miter_too_large(join_type, v_cos_turn_angle_end);\\n\\n float sign_at_start = -sign(a_position.x); // +ve at segment start, -ve end.\\n vec2 point = sign_at_start > 0.0 ? a_point_start : a_point_end;\\n\\n if ( (has_start_cap && sign_at_start > 0.0) ||\\n (has_end_cap && sign_at_start < 0.0) ) {\\n // Cap.\\n xy = point - segment_right*(halfwidth*a_position.y);\\n if (cap_type == butt_cap)\\n xy -= sign_at_start*0.5*u_antialias*segment_along;\\n else\\n xy -= sign_at_start*halfwidth*segment_along;\\n }\\n else if (sign_at_start > 0.0) {\\n vec2 inside_point = a_point_start + segment_right*(sign_turn_right_start*halfwidth);\\n vec2 prev_outside_point = a_point_start - prev_right*(sign_turn_right_start*halfwidth);\\n\\n // join at start.\\n if (join_type == round_join || join_type == bevel_join || miter_too_large_start) {\\n if (v_cos_turn_angle_start <= 0.0) { // |turn_angle| > 90 degrees\\n xy = a_point_start - segment_right*(halfwidth*a_position.y) - halfwidth*segment_along;\\n }\\n else {\\n if (a_position.x < -1.5) {\\n xy = prev_outside_point;\\n v_coords.y = -dot(xy - a_point_start, segment_right);\\n }\\n else if (a_position.y*sign_turn_right_start > 0.0) { // outside corner of turn\\n float d = halfwidth*abs(sin_turn_angle_start);\\n xy = a_point_start - segment_right*(halfwidth*a_position.y) - d*segment_along;\\n }\\n else { // inside corner of turn\\n xy = inside_point;\\n }\\n }\\n }\\n else { // miter join\\n if (a_position.x < -1.5) {\\n xy = prev_outside_point;\\n v_coords.y = -dot(xy - a_point_start, segment_right);\\n }\\n else if (a_position.y*sign_turn_right_start > 0.0) { // outside corner of turn\\n float tan_half_turn_angle = (1.0-v_cos_turn_angle_start) / sin_turn_angle_start; // Trig identity\\n float d = sign_turn_right_start*halfwidth*tan_half_turn_angle;\\n xy = a_point_start - segment_right*(halfwidth*a_position.y) - d*segment_along;\\n }\\n else { // inside corner if turn\\n xy = inside_point;\\n }\\n }\\n }\\n else {\\n xy = point - segment_right*(halfwidth*a_position.y);\\n }\\n\\n vec2 pos = xy + 0.5; // Bokeh's offset.\\n pos /= u_canvas_size; // in 0..1\\n gl_Position = vec4(2.0*pos.x - 1.0, 1.0 - 2.0*pos.y, 0.0, 1.0);\\n\\n bool turn_right_start = sin_turn_angle_start >= 0.0;\\n bool turn_right_end = sin_turn_angle_end >= 0.0;\\n\\n v_coords.x = dot(xy - a_point_start, segment_along);\\n v_flags = float(int(has_start_cap) +\\n 2*int(has_end_cap) +\\n 4*int(miter_too_large_start) +\\n 8*int(miter_too_large_end) +\\n 16*int(turn_right_start) +\\n 32*int(turn_right_end));\\n\\n v_line_cap = a_line_cap;\\n v_line_join = a_line_join;\\n\\n#ifdef DASHED\\n v_length_so_far = a_length_so_far;\\n v_dash_tex_info = a_dash_tex_info;\\n v_dash_scale = a_dash_scale;\\n v_dash_offset = a_dash_offset;\\n#endif\\n}\\n\"},\n 549: function _(n,t,a,i,e){i();a.default=\"\\nprecision mediump float;\\n\\nconst int butt_cap = 0;\\nconst int round_cap = 1;\\nconst int square_cap = 2;\\n\\nconst int miter_join = 0;\\nconst int round_join = 1;\\nconst int bevel_join = 2;\\n\\nuniform float u_antialias;\\n#ifdef DASHED\\nuniform sampler2D u_dash_tex;\\n#endif\\n\\nvarying float v_linewidth;\\nvarying vec4 v_line_color;\\nvarying float v_line_cap;\\nvarying float v_line_join;\\nvarying float v_segment_length;\\nvarying vec2 v_coords;\\nvarying float v_flags;\\nvarying float v_cos_turn_angle_start;\\nvarying float v_cos_turn_angle_end;\\n#ifdef DASHED\\nvarying float v_length_so_far;\\nvarying vec4 v_dash_tex_info;\\nvarying float v_dash_scale;\\nvarying float v_dash_offset;\\n#endif\\n\\n#define ONE_MINUS_SMALL (1.0 - 1e-6)\\n\\nfloat cross_z(in vec2 v0, in vec2 v1)\\n{\\n return v0.x*v1.y - v0.y*v1.x;\\n}\\n\\nvec2 right_vector(in vec2 v)\\n{\\n return vec2(v.y, -v.x);\\n}\\n\\nfloat bevel_join_distance(in vec2 coords, in vec2 other_right, in float sign_turn_right)\\n{\\n // other_right is unit vector facing right of the other (previous or next) segment, in coord reference frame\\n float hw = 0.5*v_linewidth; // Not including antialiasing\\n if (other_right.y >= ONE_MINUS_SMALL) { // other_right.y is -cos(turn_angle)\\n // 180 degree turn.\\n return abs(hw - v_coords.x);\\n }\\n else {\\n const vec2 segment_right = vec2(0.0, -1.0);\\n // corner_right is unit vector bisecting corner facing right, in coord reference frame\\n vec2 corner_right = normalize(other_right + segment_right);\\n vec2 outside_point = (-hw*sign_turn_right)*segment_right;\\n return hw + sign_turn_right*dot(outside_point - coords, corner_right);\\n }\\n}\\n\\nfloat cap(in int cap_type, in float x, in float y)\\n{\\n // x is distance along segment in direction away from end of segment,\\n // y is distance across segment.\\n if (cap_type == butt_cap)\\n return max(0.5*v_linewidth - x, abs(y));\\n else if (cap_type == square_cap)\\n return max(-x, abs(y));\\n else // cap_type == round_cap\\n return distance(vec2(min(x, 0.0), y), vec2(0.0, 0.0));\\n}\\n\\nfloat distance_to_alpha(in float dist)\\n{\\n return 1.0 - smoothstep(0.5*(v_linewidth - u_antialias),\\n 0.5*(v_linewidth + u_antialias), dist);\\n}\\n\\nvec2 turn_angle_to_right_vector(in float cos_turn_angle, in float sign_turn_right)\\n{\\n float sin_turn_angle = sign_turn_right*sqrt(1.0 - cos_turn_angle*cos_turn_angle);\\n return vec2(sin_turn_angle, -cos_turn_angle);\\n}\\n\\n#ifdef DASHED\\nfloat dash_distance(in float x)\\n{\\n // x is in direction of v_coords.x, i.e. along segment.\\n float tex_length = v_dash_tex_info.x;\\n float tex_offset = v_dash_tex_info.y;\\n float tex_dist_min = v_dash_tex_info.z;\\n float tex_dist_max = v_dash_tex_info.w;\\n\\n // Apply offset.\\n x += v_length_so_far - v_dash_scale*tex_offset + v_dash_offset;\\n\\n // Interpolate within texture to obtain distance to dash.\\n float dist = texture2D(u_dash_tex,\\n vec2(x / (tex_length*v_dash_scale), 0.0)).a;\\n\\n // Scale distance within min and max limits.\\n dist = tex_dist_min + dist*(tex_dist_max - tex_dist_min);\\n\\n return v_dash_scale*dist;\\n}\\n\\nmat2 rotation_matrix(in vec2 other_right)\\n{\\n float sin_angle = other_right.x;\\n float cos_angle = -other_right.y;\\n return mat2(cos_angle, -sin_angle, sin_angle, cos_angle);\\n}\\n#endif\\n\\nvoid main()\\n{\\n int join_type = int(v_line_join + 0.5);\\n int cap_type = int(v_line_cap + 0.5);\\n float halfwidth = 0.5*(v_linewidth + u_antialias);\\n float half_antialias = 0.5*u_antialias;\\n\\n // Extract flags.\\n int flags = int(v_flags + 0.5);\\n bool turn_right_end = (flags / 32 > 0);\\n float sign_turn_right_end = turn_right_end ? 1.0 : -1.0;\\n flags -= 32*int(turn_right_end);\\n bool turn_right_start = (flags / 16 > 0);\\n float sign_turn_right_start = turn_right_start ? 1.0 : -1.0;\\n flags -= 16*int(turn_right_start);\\n bool miter_too_large_end = (flags / 8 > 0);\\n flags -= 8*int(miter_too_large_end);\\n bool miter_too_large_start = (flags / 4 > 0);\\n flags -= 4*int(miter_too_large_start);\\n bool has_end_cap = (flags / 2 > 0);\\n flags -= 2*int(has_end_cap);\\n bool has_start_cap = flags > 0;\\n\\n // Unit vectors to right of previous and next segments in coord reference frame\\n vec2 prev_right = turn_angle_to_right_vector(v_cos_turn_angle_start, sign_turn_right_start);\\n vec2 next_right = turn_angle_to_right_vector(v_cos_turn_angle_end, sign_turn_right_end);\\n\\n float dist = v_coords.y; // For straight segment, and miter join.\\n\\n // Along-segment coords with respect to end of segment, facing inwards\\n vec2 end_coords = vec2(v_segment_length, 0.0) - v_coords;\\n\\n if (v_coords.x <= half_antialias) {\\n // At start of segment, either cap or join.\\n if (has_start_cap)\\n dist = cap(cap_type, v_coords.x, v_coords.y);\\n else if (join_type == round_join) {\\n if (v_coords.x <= 0.0)\\n dist = distance(v_coords, vec2(0.0, 0.0));\\n }\\n else { // bevel or miter join\\n if (join_type == bevel_join || miter_too_large_start)\\n dist = max(abs(dist), bevel_join_distance(v_coords, prev_right, sign_turn_right_start));\\n float prev_sideways_dist = -sign_turn_right_start*dot(v_coords, prev_right);\\n dist = max(abs(dist), prev_sideways_dist);\\n }\\n }\\n\\n if (end_coords.x <= half_antialias) {\\n if (has_end_cap) {\\n dist = max(abs(dist), cap(cap_type, end_coords.x, v_coords.y));\\n }\\n else if (join_type == bevel_join || miter_too_large_end) {\\n // Bevel join at end impacts half antialias distance\\n dist = max(abs(dist), bevel_join_distance(end_coords, next_right, sign_turn_right_end));\\n }\\n }\\n\\n float alpha = distance_to_alpha(abs(dist));\\n\\n#ifdef DASHED\\n if (v_dash_tex_info.x >= 0.0) {\\n // Dashes in straight segments (outside of joins) are easily calculated.\\n dist = dash_distance(v_coords.x);\\n\\n vec2 prev_coords = rotation_matrix(prev_right)*v_coords;\\n float start_dash_distance = dash_distance(0.0);\\n\\n if (!has_start_cap && cap_type == butt_cap) {\\n // Outer of start join rendered solid color or not at all depending on whether corner\\n // point is in dash or gap, with antialiased ends.\\n bool outer_solid = start_dash_distance >= 0.0 && v_coords.x < half_antialias && prev_coords.x > -half_antialias;\\n if (outer_solid) {\\n // Within solid outer region, antialiased at ends\\n float half_aa_dist = dash_distance(half_antialias);\\n if (half_aa_dist > 0.0) // Next dash near, do not want antialiased gap\\n dist = half_aa_dist - v_coords.x + half_antialias;\\n else\\n dist = start_dash_distance - v_coords.x;\\n\\n half_aa_dist = dash_distance(-half_antialias);\\n if (half_aa_dist > 0.0) // Prev dash nearm do not want antialiased gap\\n dist = min(dist, half_aa_dist + prev_coords.x + half_antialias);\\n else\\n dist = min(dist, start_dash_distance + prev_coords.x);\\n }\\n else {\\n // Outer not rendered, antialias ends.\\n if (v_coords.x < half_antialias)\\n dist = min(0.0, dash_distance(half_antialias) - half_antialias) + v_coords.x;\\n\\n if (prev_coords.x > -half_antialias && prev_coords.x <= half_antialias) {\\n // Antialias from end of previous segment into join\\n float prev_dist = min(0.0, dash_distance(-half_antialias) - half_antialias) - prev_coords.x;\\n // Consider width of previous segment\\n prev_dist = min(prev_dist, 0.5*v_linewidth - abs(prev_coords.y));\\n dist = max(dist, prev_dist);\\n }\\n }\\n }\\n\\n if (!has_end_cap && cap_type == butt_cap && end_coords.x < half_antialias) {\\n float end_dash_distance = dash_distance(v_segment_length);\\n bool increasing = end_dash_distance >= 0.0 && sign_turn_right_end*v_coords.y < 0.0;\\n if (!increasing) {\\n float half_aa_dist = dash_distance(v_segment_length - half_antialias);\\n dist = min(0.0, half_aa_dist - half_antialias) + end_coords.x;\\n }\\n }\\n\\n dist = cap(cap_type, dist, v_coords.y);\\n\\n float dash_alpha = distance_to_alpha(dist);\\n alpha = min(alpha, dash_alpha);\\n }\\n#endif\\n\\n alpha = v_line_color.a*alpha;\\n gl_FragColor = vec4(v_line_color.rgb*alpha, alpha); // Premultiplied alpha.\\n}\\n\"},\n 550: function _(n,e,i,a,_){a();i.default=\"\\nprecision mediump float;\\n\\nattribute vec2 a_position;\\nattribute vec2 a_center;\\nattribute float a_width; // or radius or outer_radius\\nattribute float a_height; // or inner_radius\\nattribute float a_angle; // or start_angle\\nattribute float a_aux; // or end_angle\\nattribute float a_linewidth;\\nattribute vec4 a_line_color;\\nattribute vec4 a_fill_color;\\nattribute float a_line_cap;\\nattribute float a_line_join;\\nattribute float a_show;\\n\\n#ifdef HATCH\\nattribute float a_hatch_pattern;\\nattribute float a_hatch_scale;\\nattribute float a_hatch_weight;\\nattribute vec4 a_hatch_color;\\n#endif\\n\\nuniform vec2 u_canvas_size;\\nuniform float u_antialias;\\n\\n#ifdef MULTI_MARKER\\nuniform float u_size_hint;\\n#endif\\n\\n#ifdef USE_ROUND_RECT\\nuniform vec4 u_border_radius;\\nvarying vec4 v_border_radius;\\n#endif\\n\\n#ifdef USE_ANNULAR_WEDGE\\nvarying float v_outer_radius;\\nvarying float v_inner_radius;\\nvarying float v_start_angle;\\nvarying float v_end_angle;\\n#endif\\n\\n#ifdef USE_ANNULUS\\nvarying float v_outer_radius;\\nvarying float v_inner_radius;\\n#endif\\n\\n#ifdef USE_WEDGE\\nvarying float v_radius;\\nvarying float v_start_angle;\\nvarying float v_end_angle;\\n#endif\\n\\n#ifdef USE_CIRCLE\\nvarying float v_radius;\\n#endif\\n\\nvarying float v_linewidth;\\nvarying vec2 v_size; // 2D size for rects compared to 1D for markers.\\nvarying vec4 v_line_color;\\nvarying vec4 v_fill_color;\\nvarying float v_line_cap;\\nvarying float v_line_join;\\nvarying vec2 v_coords;\\n\\n#ifdef HATCH\\nvarying float v_hatch_pattern;\\nvarying float v_hatch_scale;\\nvarying float v_hatch_weight;\\nvarying vec4 v_hatch_color;\\nvarying vec2 v_hatch_coords;\\n#endif\\n\\n#ifdef MULTI_MARKER\\n\\n#define M_DASH 1\\n#define M_DOT 2\\n#define M_DIAMOND 3\\n#define M_HEX 4\\n#define M_SQUARE_PIN 5\\n#define M_TRIANGLE 6\\n#define M_TRIANGLE_PIN 7\\n#define M_STAR 8\\n\\nvec2 enclosing_size() {\\n // Need extra size of (v_linewidth+u_antialias) if edge of marker parallel to\\n // edge of bounding box. If symmetric spike towards edge then multiply by\\n // 1/cos(theta) where theta is angle between spike and bbox edges.\\n int size_hint = int(u_size_hint + 0.5);\\n if (size_hint == M_DASH)\\n return vec2(v_size.x + v_linewidth + u_antialias,\\n v_linewidth + u_antialias);\\n else if (size_hint == M_DOT)\\n return 0.25*v_size + u_antialias;\\n else if (size_hint == M_DIAMOND)\\n return vec2(v_size.x*(2.0/3.0) + (v_linewidth + u_antialias)*1.20185,\\n v_size.y + (v_linewidth + u_antialias)*1.80278);\\n else if (size_hint == M_HEX)\\n return v_size + (v_linewidth + u_antialias)*vec2(2.0/sqrt(3.0), 1.0);\\n else if (size_hint == M_SQUARE_PIN) // Square pin\\n return v_size + (v_linewidth + u_antialias)*3.1;\\n else if (size_hint == M_TRIANGLE)\\n return vec2(v_size.x + (v_linewidth + u_antialias)*sqrt(3.0),\\n v_size.y*(2.0/sqrt(3.0)) + (v_linewidth + u_antialias)*2.0);\\n else if (size_hint == M_TRIANGLE_PIN)\\n return v_size + (v_linewidth + u_antialias)*vec2(4.8, 6.0);\\n else if (size_hint == M_STAR)\\n return vec2(v_size.x*0.95106 + (v_linewidth + u_antialias)*3.0,\\n v_size.y + (v_linewidth + u_antialias)*3.2);\\n else\\n return v_size + v_linewidth + u_antialias;\\n}\\n#else\\nvec2 enclosing_size() {\\n return v_size + v_linewidth + u_antialias;\\n}\\n#endif\\n\\nvoid main()\\n{\\n#if defined(USE_RECT) || defined(USE_ROUND_RECT) || defined(USE_HEX_TILE)\\n v_size = vec2(a_width, a_height);\\n#elif defined(USE_ANNULUS) || defined(USE_ANNULAR_WEDGE) || defined(USE_WEDGE)\\n v_size = vec2(2.0*a_width, 2.0*a_width);\\n#else\\n v_size = vec2(a_width, a_width);\\n#endif\\n\\n if (a_show < 0.5 || v_size.x < 0.0 || v_size.y < 0.0 || (v_size.x == 0.0 && v_size.y == 0.0)) {\\n // Do not show this marker.\\n gl_Position = vec4(-2.0, -2.0, 0.0, 1.0);\\n return;\\n }\\n\\n#ifdef USE_ANNULAR_WEDGE\\n v_outer_radius = a_width;\\n v_inner_radius = a_height;\\n v_start_angle = a_angle;\\n v_end_angle = a_aux;\\n#endif\\n\\n#ifdef USE_ANNULUS\\n v_outer_radius = a_width;\\n v_inner_radius = a_height;\\n#endif\\n\\n#ifdef USE_WEDGE\\n v_radius = a_width;\\n v_start_angle = a_angle;\\n v_end_angle = a_aux;\\n#endif\\n\\n#ifdef USE_CIRCLE\\n v_radius = 0.5*a_width;\\n#endif\\n\\n#ifdef USE_ROUND_RECT\\n // Scale corner radii if they are too large, the same as canvas\\n // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-roundrect\\n // Order of border_radius is top_left, top_right, bottom_right, bottom_left\\n const vec2 unit2 = vec2(1.0, 1.0);\\n float scale = min(v_size.x / max(dot(u_border_radius.xy, unit2), dot(u_border_radius.zw, unit2)),\\n v_size.y / max(dot(u_border_radius.yz, unit2), dot(u_border_radius.wx, unit2)));\\n v_border_radius = u_border_radius*min(scale, 1.0);\\n#endif\\n\\n v_linewidth = a_linewidth;\\n v_line_color = a_line_color;\\n v_fill_color = a_fill_color;\\n v_line_cap = a_line_cap;\\n v_line_join = a_line_join;\\n\\n if (v_linewidth < 1.0) {\\n // Linewidth less than 1 is implemented as 1 but with reduced alpha.\\n v_line_color.a *= v_linewidth;\\n v_linewidth = 1.0;\\n }\\n\\n#ifdef HATCH\\n v_hatch_pattern = a_hatch_pattern;\\n v_hatch_scale = a_hatch_scale;\\n v_hatch_weight = a_hatch_weight;\\n v_hatch_color = a_hatch_color;\\n#endif\\n\\n // Coordinates in rotated frame with respect to center of marker, used for\\n // distance functions in fragment shader.\\n v_coords = a_position*enclosing_size();\\n\\n#if defined(USE_CIRCLE) || defined(USE_ANNULUS) || defined(USE_ANNULAR_WEDGE) || defined(USE_WEDGE)\\n vec2 pos = a_center + v_coords;\\n#else\\n float c = cos(-a_angle);\\n float s = sin(-a_angle);\\n mat2 rotation = mat2(c, -s, s, c);\\n\\n vec2 pos = a_center + rotation*v_coords;\\n#endif\\n\\n#ifdef HATCH\\n // Coordinates for hatching in unrotated frame of reference.\\n v_hatch_coords = pos - 0.5;\\n#endif\\n\\n pos += 0.5; // Make up for Bokeh's offset.\\n pos /= u_canvas_size; // 0 to 1.\\n gl_Position = vec4(2.0*pos.x - 1.0, 1.0 - 2.0*pos.y, 0.0, 1.0);\\n}\\n\"},\n 551: function _(n,i,e,t,a){t();e.default=\"\\nprecision mediump float;\\n\\nconst float SQRT2 = sqrt(2.0);\\nconst float SQRT3 = sqrt(3.0);\\nconst float PI = 3.14159265358979323846;\\n\\nconst int butt_cap = 0;\\nconst int round_cap = 1;\\nconst int square_cap = 2;\\n\\nconst int miter_join = 0;\\nconst int round_join = 1;\\nconst int bevel_join = 2;\\n\\n#ifdef HATCH\\nconst int hatch_dot = 1;\\nconst int hatch_ring = 2;\\nconst int hatch_horizontal_line = 3;\\nconst int hatch_vertical_line = 4;\\nconst int hatch_cross = 5;\\nconst int hatch_horizontal_dash = 6;\\nconst int hatch_vertical_dash = 7;\\nconst int hatch_spiral = 8;\\nconst int hatch_right_diagonal_line = 9;\\nconst int hatch_left_diagonal_line = 10;\\nconst int hatch_diagonal_cross = 11;\\nconst int hatch_right_diagonal_dash = 12;\\nconst int hatch_left_diagonal_dash = 13;\\nconst int hatch_horizontal_wave = 14;\\nconst int hatch_vertical_wave = 15;\\nconst int hatch_criss_cross = 16;\\n#endif\\n\\nuniform float u_antialias;\\n\\nvarying vec2 v_coords;\\nvarying vec2 v_size;\\n\\n#ifdef USE_ANNULAR_WEDGE\\nvarying float v_outer_radius;\\nvarying float v_inner_radius;\\nvarying float v_start_angle;\\nvarying float v_end_angle;\\n#endif\\n\\n#ifdef USE_ANNULUS\\nvarying float v_outer_radius;\\nvarying float v_inner_radius;\\n#endif\\n\\n#ifdef USE_WEDGE\\nvarying float v_radius;\\nvarying float v_start_angle;\\nvarying float v_end_angle;\\n#endif\\n\\n#ifdef USE_CIRCLE\\nvarying float v_radius;\\n#endif\\n\\n#ifdef USE_ROUND_RECT\\nvarying vec4 v_border_radius;\\n#endif\\n\\nvarying float v_linewidth;\\nvarying vec4 v_line_color;\\nvarying vec4 v_fill_color;\\nvarying float v_line_cap;\\nvarying float v_line_join;\\n\\n#ifdef HATCH\\nvarying float v_hatch_pattern;\\nvarying float v_hatch_scale;\\nvarying float v_hatch_weight;\\nvarying vec4 v_hatch_color;\\nvarying vec2 v_hatch_coords;\\n#endif\\n\\n// Lines within the marker (dot, cross, x and y) are added at the end as they are\\n// on top of the fill rather than astride it.\\n#if defined(USE_CIRCLE_DOT) || defined(USE_DIAMOND_DOT) || defined(USE_DOT) || defined(USE_HEX_DOT) || defined(USE_SQUARE_DOT) || defined(USE_STAR_DOT) || defined(USE_TRIANGLE_DOT)\\n #define APPEND_DOT\\n#endif\\n\\n#if defined(USE_CIRCLE_CROSS) || defined(USE_SQUARE_CROSS)\\n #define APPEND_CROSS\\n#endif\\n\\n#ifdef USE_DIAMOND_CROSS\\n #define APPEND_CROSS_2\\n#endif\\n\\n#ifdef USE_CIRCLE_X\\n #define APPEND_X\\n #define APPEND_X_LEN (0.5*v_size.x)\\n#endif\\n\\n#ifdef USE_SQUARE_X\\n #define APPEND_X\\n #define APPEND_X_LEN (v_size.x/SQRT2)\\n#endif\\n\\n#ifdef USE_CIRCLE_Y\\n #define APPEND_Y\\n#endif\\n\\n#if defined(USE_ASTERISK) || defined(USE_CROSS) || defined(USE_DASH) || defined(USE_DOT) || defined(USE_X) || defined(USE_Y)\\n // No fill.\\n #define LINE_ONLY\\n#endif\\n\\n#if defined(LINE_ONLY) || defined(APPEND_CROSS) || defined(APPEND_CROSS_2) || defined(APPEND_X) || defined(APPEND_Y)\\nfloat end_cap_distance(in vec2 p, in vec2 end_point, in vec2 unit_direction, in int line_cap)\\n{\\n vec2 offset = p - end_point;\\n if (line_cap == butt_cap)\\n return dot(offset, unit_direction) + 0.5*v_linewidth;\\n else if (line_cap == square_cap)\\n return dot(offset, unit_direction);\\n else if (line_cap == round_cap && dot(offset, unit_direction) > 0.0)\\n return length(offset);\\n else\\n // Default is outside of line and should be -0.5*(v_linewidth+u_antialias) or less,\\n // so here avoid the multiplication.\\n return -v_linewidth-u_antialias;\\n}\\n#endif\\n\\n#if !(defined(LINE_ONLY) || defined(USE_SQUARE_PIN) || defined(USE_TRIANGLE_PIN))\\n// For line join at a vec2 corner where 2 line segments meet, consider bevel points which are the 2\\n// points obtained by moving half a linewidth away from the corner point in the directions normal to\\n// the line segments. The line through these points is the bevel line, characterised by a vec2\\n// unit_normal and offset distance from the corner point. Edge of bevel join straddles this line,\\n// round join occurs outside of this line centred on the corner point. In general\\n// offset = (linewidth/2)*sin(alpha/2)\\n// where alpha is the angle between the 2 line segments at the corner.\\nfloat line_join_distance_no_miter(\\n in vec2 p, in vec2 corner, in vec2 unit_normal, in float offset, in int line_join)\\n{\\n // Simplified version of line_join_distance ignoring miter which most markers do implicitly\\n // as they are composed of straight line segments.\\n float dist_outside = dot((p - corner), unit_normal) - offset;\\n\\n if (line_join == bevel_join && dist_outside > -0.5*u_antialias)\\n return dist_outside + 0.5*v_linewidth;\\n else if (dist_outside > 0.0) // round_join\\n return distance(p, corner);\\n else\\n // Default is outside of line and should be -0.5*(v_linewidth+u_antialias) or less,\\n // so here avoid the multiplication.\\n return -v_linewidth-u_antialias;\\n}\\n#endif\\n\\n#if defined(USE_SQUARE_PIN) || defined(USE_TRIANGLE_PIN)\\n// Line join distance including miter but only one-sided check as assuming use of symmetry in\\n// calling function.\\nfloat line_join_distance_incl_miter(\\n in vec2 p, in vec2 corner, in vec2 unit_normal, in float offset, in int line_join,\\n vec2 miter_unit_normal)\\n{\\n float dist_outside = dot((p - corner), unit_normal) - offset;\\n\\n if (line_join == miter_join && dist_outside > 0.0)\\n return dot((p - corner), miter_unit_normal);\\n else if (line_join == bevel_join && dist_outside > -0.5*u_antialias)\\n return dist_outside + 0.5*v_linewidth;\\n else if (dist_outside > 0.0) // round_join\\n return distance(p, corner);\\n else\\n return -v_linewidth-u_antialias;\\n}\\n#endif\\n\\n#if defined(APPEND_CROSS) || defined(APPEND_X) || defined(USE_ASTERISK) || defined(USE_CROSS) || defined(USE_X)\\nfloat one_cross(in vec2 p, in int line_cap, in float len)\\n{\\n p = abs(p);\\n p = (p.y > p.x) ? p.yx : p.xy;\\n float dist = p.y;\\n float end_dist = end_cap_distance(p, vec2(len, 0.0), vec2(1.0, 0.0), line_cap);\\n return max(dist, end_dist);\\n}\\n#endif\\n\\n#ifdef APPEND_CROSS_2\\nfloat one_cross_2(in vec2 p, in int line_cap, in vec2 lengths)\\n{\\n // Cross with different length in x and y directions.\\n p = abs(p);\\n bool switch_xy = (p.y > p.x);\\n p = switch_xy ? p.yx : p.xy;\\n float len = switch_xy ? lengths.y : lengths.x;\\n float dist = p.y;\\n float end_dist = end_cap_distance(p, vec2(len, 0.0), vec2(1.0, 0.0), line_cap);\\n return max(dist, end_dist);\\n}\\n#endif\\n\\n#if defined(APPEND_Y) || defined(USE_Y)\\nfloat one_y(in vec2 p, in int line_cap, in float len)\\n{\\n p = vec2(abs(p.x), -p.y);\\n\\n // End point of line to right is (1/2, 1/3)*len*SQRT3.\\n // Unit vector along line is (1/2, 1/3)*k where k = 6/SQRT13.\\n const float k = 6.0/sqrt(13.0);\\n vec2 unit_along = vec2(0.5*k, k/3.0);\\n vec2 end_point = vec2(0.5*len*SQRT3, len*SQRT3/3.0);\\n float dist = max(abs(dot(p, vec2(-unit_along.y, unit_along.x))),\\n end_cap_distance(p, end_point, unit_along, line_cap));\\n\\n if (p.y < 0.0) {\\n // Vertical line.\\n float vert_dist = max(p.x,\\n end_cap_distance(p, vec2(0.0, -len), vec2(0.0, -1.0), line_cap));\\n dist = min(dist, vert_dist);\\n }\\n return dist;\\n}\\n#endif\\n\\n// One marker_distance function per marker type.\\n// Distance is zero on edge of marker, +ve outside and -ve inside.\\n\\n#ifdef USE_ASTERISK\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n vec2 p_diag = vec2((p.x + p.y)/SQRT2, (p.x - p.y)/SQRT2);\\n float len = 0.5*v_size.x;\\n return min(one_cross(p, line_cap, len), // cross\\n one_cross(p_diag, line_cap, len)); // x\\n}\\n#endif\\n\\n#if defined(USE_ANNULUS) || defined(USE_WEDGE) || defined(USE_ANNULAR_WEDGE)\\nfloat merge(in float d1, in float d2)\\n{\\n return min(d1, d2);\\n}\\n\\nfloat intersect(in float d1, in float d2)\\n{\\n return max(d1, d2);\\n}\\n\\nfloat subtract(in float d1, in float d2)\\n{\\n return max(d1, -d2);\\n}\\n\\nfloat circle(in vec2 p, in float radius)\\n{\\n return length(p) - radius;\\n}\\n\\nfloat segment_square(in vec2 p, in vec2 q) {\\n vec2 v = p - q*clamp(dot(p, q)/dot(q, q), 0.0, 1.0);\\n return dot(v, v);\\n}\\n\\nvec2 xy(in float angle)\\n{\\n return vec2(cos(angle), sin(angle));\\n}\\n\\nfloat cross_z(in vec2 v0, in vec2 v1)\\n{\\n return v0.x*v1.y - v0.y*v1.x;\\n}\\n\\n// From https://www.shadertoy.com/view/wldXWB (MIT licensed)\\nfloat wedge(in vec2 p, in float r, in float start_angle, in float end_angle)\\n{\\n vec2 a = r*xy(start_angle);\\n vec2 b = r*xy(end_angle);\\n\\n // distance\\n float d = sqrt(merge(segment_square(p, a), segment_square(p, b)));\\n\\n // sign\\n float s;\\n if (cross_z(a, b) < 0.0) {\\n s = sign(max(cross_z(a, p), cross_z(p, b)));\\n } else {\\n s = -sign(max(cross_z(p, a), cross_z(b, p)));\\n }\\n\\n return s*d;\\n}\\n\\nfloat annulus(in vec2 p, in float outer_radius, in float inner_radius)\\n{\\n float outer = circle(p, outer_radius);\\n float inner = circle(p, inner_radius);\\n\\n return subtract(outer, inner);\\n}\\n#endif\\n\\n#if defined(USE_ANNULUS)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n return annulus(p, v_outer_radius, v_inner_radius);\\n}\\n#endif\\n\\n#if defined(USE_WEDGE)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n return intersect(\\n circle(p, v_radius),\\n wedge(p, v_radius, v_start_angle, v_end_angle));\\n}\\n#endif\\n\\n#if defined(USE_ANNULAR_WEDGE)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n return intersect(\\n annulus(p, v_outer_radius, v_inner_radius),\\n wedge(p, v_outer_radius, v_start_angle, v_end_angle));\\n}\\n#endif\\n\\n#if defined(USE_CIRCLE) || defined(USE_CIRCLE_CROSS) || defined(USE_CIRCLE_DOT) || defined(USE_CIRCLE_X) || defined(USE_CIRCLE_Y)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n return length(p) - 0.5*v_size.x;\\n}\\n#endif\\n\\n#ifdef USE_CROSS\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n return one_cross(p, line_cap, 0.5*v_size.x);\\n}\\n#endif\\n\\n#ifdef USE_DASH\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n p = abs(p);\\n float dist = p.y;\\n float end_dist = end_cap_distance(p, vec2(0.5*v_size.x, 0.0), vec2(1.0, 0.0), line_cap);\\n return max(dist, end_dist);\\n}\\n#endif\\n\\n#if defined(USE_DIAMOND) || defined(USE_DIAMOND_CROSS) || defined(USE_DIAMOND_DOT)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n // Only need to consider +ve quadrant, the 2 end points are (2r/3, 0) and (0, r)\\n // where r = radius = v_size.x/2.\\n // Line has outward-facing unit normal vec2(1, 2/3)/k where k = SQRT13/3\\n // hence vec2(3, 2)/SQRT13, and distance from origin of 2r/(3k) = 2r/SQRT13.\\n p = abs(p);\\n float r = 0.5*v_size.x;\\n const float SQRT13 = sqrt(13.0);\\n float dist = dot(p, vec2(3.0, 2.0))/SQRT13 - 2.0*r/SQRT13;\\n\\n if (line_join != miter_join) {\\n dist = max(dist, line_join_distance_no_miter(\\n p, vec2(0.0, r), vec2(0.0, 1.0), v_linewidth/SQRT13, line_join));\\n\\n dist = max(dist, line_join_distance_no_miter(\\n p, vec2(r*2.0/3.0, 0.0), vec2(1.0, 0.0), v_linewidth*(1.5/SQRT13), line_join));\\n }\\n\\n return dist;\\n}\\n#endif\\n\\n#ifdef USE_DOT\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Dot is always appended.\\n return v_linewidth+u_antialias;\\n}\\n#endif\\n\\n#if defined(USE_HEX_TILE) || defined(USE_HEX) || defined(USE_HEX_DOT)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // A regular hexagon has v_size.x == v.size_y = r where r is the length of\\n // each of the 3 sides of the 6 equilateral triangles that comprise the hex.\\n // Only consider +ve quadrant, the 3 corners are at (0, h), (rx/2, h), (rx, 0)\\n // where rx = 0.5*v_size.x, ry = 0.5*v_size.y and h = ry*SQRT3/2.\\n // Sloping line has outward normal vec2(h, rx/2). Length of this is\\n // len = sqrt(h**2 + rx**2/4) to give unit normal (h, rx/2)/len and distance\\n // from origin of this line is rx*h/len.\\n p = abs(p);\\n float rx = v_size.x/2.0;\\n float h = v_size.y*(SQRT3/4.0);\\n float len_normal = sqrt(h*h + 0.25*rx*rx);\\n vec2 unit_normal = vec2(h, 0.5*rx) / len_normal;\\n float dist = max(dot(p, unit_normal) - rx*h/len_normal, // Distance from sloping line.\\n p.y - h); // Distance from horizontal line.\\n\\n if (line_join != miter_join) {\\n dist = max(dist, line_join_distance_no_miter(\\n p, vec2(rx, 0.0), vec2(1.0, 0.0), 0.5*v_linewidth*unit_normal.x, line_join));\\n\\n unit_normal = normalize(unit_normal + vec2(0.0, 1.0)); // At (rx/2, h) corner.\\n dist = max(dist, line_join_distance_no_miter(\\n p, vec2(0.5*rx, h), unit_normal, 0.5*v_linewidth*unit_normal.y, line_join));\\n }\\n return dist;\\n}\\n#endif\\n\\n#ifdef USE_PLUS\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n // Only need to consider one octant, the +ve quadrant with x >= y.\\n p = abs(p);\\n p = (p.y > p.x) ? p.yx : p.xy;\\n\\n // 3 corners are (r, 0), (r, 3r/8) and (3r/8, 3r/8).\\n float r = 0.5*v_size.x;\\n p = p - vec2(r, 0.375*r); // Distance with respect to outside corner\\n float dist = max(p.x, p.y);\\n\\n if (line_join != miter_join) {\\n // Outside corner\\n dist = max(dist, line_join_distance_no_miter(\\n p, vec2(0.0, 0.0), vec2(1.0/SQRT2, 1.0/SQRT2), v_linewidth/(2.0*SQRT2), line_join));\\n\\n // Inside corner\\n dist = min(dist, -line_join_distance_no_miter(\\n p, vec2(-5.0*r/8.0, 0.0), vec2(-1.0/SQRT2, -1.0/SQRT2), v_linewidth/(2.0*SQRT2), line_join));\\n }\\n\\n return dist;\\n}\\n#endif\\n\\n#if defined(USE_ROUND_RECT)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n vec2 halfsize = v_size/2.0;\\n vec2 p2 = abs(p) - halfsize; // Offset from corner\\n float dist = max(p2.x, p2.y);\\n\\n if (line_join != miter_join) {\\n dist = max(dist, line_join_distance_no_miter(\\n p2, vec2(0.0, 0.0), vec2(1.0/SQRT2, 1.0/SQRT2), v_linewidth/(2.0*SQRT2), line_join));\\n }\\n\\n // Need to consider distance to all 4 corners\\n // Order of border_radius is top_left, top_right, bottom_right, bottom_left\\n vec4 border_radius = v_border_radius;\\n vec4 xsign = vec4(-1.0, 1.0, 1.0, -1.0);\\n vec4 ysign = vec4(-1.0, -1.0, 1.0, 1.0);\\n for (int i = 0; i < 4; i++) {\\n float radius = border_radius.x;\\n p2 = p*vec2(xsign.x, ysign.x); // In +ve quadrant\\n vec2 offset = p2 - halfsize + radius;\\n if (min(radius, min(offset.x, offset.y)) > 0.0) {\\n dist = max(dist, length(offset) - radius);\\n }\\n // Swizzle\\n border_radius.xyzw = border_radius.yzwx;\\n xsign.xyzw = xsign.yzwx;\\n ysign.xyzw = ysign.yzwx;\\n }\\n\\n return dist;\\n}\\n#endif\\n\\n#if defined(USE_RECT) || defined(USE_SQUARE) || defined(USE_SQUARE_CROSS) || defined(USE_SQUARE_DOT) || defined(USE_SQUARE_X)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n vec2 p2 = abs(p) - v_size/2.0; // Offset from corner\\n float dist = max(p2.x, p2.y);\\n\\n if (line_join != miter_join) {\\n dist = max(dist, line_join_distance_no_miter(\\n p2, vec2(0.0, 0.0), vec2(1.0/SQRT2, 1.0/SQRT2), v_linewidth/(2.0*SQRT2), line_join));\\n }\\n\\n return dist;\\n}\\n#endif\\n\\n#ifdef USE_SQUARE_PIN\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n p = abs(p);\\n p = (p.y > p.x) ? p.yx : p.xy;\\n // p is in octant between y=0 and y=x.\\n // Quadratic bezier curve passes through (r, r), (11r/16, 0) and (r, -r).\\n // Circular arc that passes through the same points has center at\\n // x = r + 231r/160 = 2.44275r and y = 0 and hence radius is\\n // x - 11r/16 = 1.75626 precisely.\\n float r = 0.5*v_size.x;\\n float center_x = r*2.44375;\\n float radius = r*1.75626;\\n float dist = radius - distance(p, vec2(center_x, 0.0));\\n\\n // Magic number is 0.5*sin(atan(8/5) - pi/4)\\n dist = max(dist, line_join_distance_incl_miter(\\n p, vec2(r, r), vec2(1.0/SQRT2, 1.0/SQRT2), v_linewidth*0.1124297533493792, line_join,\\n vec2(8.0/sqrt(89.0), -5.0/sqrt(89.0))));\\n\\n return dist;\\n}\\n#endif\\n\\n#if defined(USE_STAR) || defined(USE_STAR_DOT)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n const float SQRT5 = sqrt(5.0);\\n const float COS72 = 0.25*(SQRT5 - 1.0);\\n const float SIN72 = sqrt((5.0+SQRT5) / 8.0);\\n\\n float angle = atan(p.x, p.y); // In range -pi to +pi clockwise from +y direction.\\n angle = mod(angle, 0.4*PI) - 0.2*PI; // In range -pi/5 to +pi/5 clockwise from +y direction.\\n p = length(p)*vec2(cos(angle), abs(sin(angle))); // (x,y) in pi/10 (36 degree) sector.\\n\\n // 2 corners are at (r, 0) and (r-a*SIN72, a*COS72) where a = r sqrt(5-2*sqrt(5)).\\n // Line has outward-facing unit normal vec2(COS72, SIN72) and distance from\\n // origin of dot(vec2(r, 0), vec2(COS72, SIN72)) = r*COS72\\n float r = 0.5*v_size.x;\\n float a = r*sqrt(5.0 - 2.0*SQRT5);\\n float dist = dot(p, vec2(COS72, SIN72)) - r*COS72;\\n\\n if (line_join != miter_join) {\\n // Outside corner\\n dist = max(dist, line_join_distance_no_miter(\\n p, vec2(r, 0.0), vec2(1.0, 0.0), v_linewidth*(0.5*COS72), line_join));\\n\\n // Inside corner\\n const float COS36 = sqrt(0.5 + COS72/2.0);\\n const float SIN36 = sqrt(0.5 - COS72/2.0);\\n dist = min(dist, -line_join_distance_no_miter(\\n p, vec2(r-a*SIN72, a*COS72), vec2(-COS36, -SIN36), v_linewidth*(0.5*COS36), line_join));\\n }\\n\\n return dist;\\n}\\n#endif\\n\\n#if defined(USE_TRIANGLE) || defined(USE_TRIANGLE_DOT) || defined(USE_INVERTED_TRIANGLE)\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n // For normal triangle 3 corners are at (-r, a), (r, a), (0, a-h)=(0, -2h/3)\\n // given r = radius = v_size.x/2, h = SQRT3*r, a = h/3.\\n // Sloping line has outward-facing unit normal vec2(h, -r)/2r = vec2(SQRT3, -1)/2\\n // and distance from origin of a. Horizontal line has outward-facing unit normal\\n // vec2(0, 1) and distance from origin of a.\\n float r = 0.5*v_size.x;\\n float a = r*SQRT3/3.0;\\n\\n // Only need to consider +ve x.\\n#ifdef USE_INVERTED_TRIANGLE\\n p = vec2(abs(p.x), -p.y);\\n#else\\n p = vec2(abs(p.x), p.y);\\n#endif\\n\\n float dist = max(0.5*dot(p, vec2(SQRT3, -1.0)) - a, // Distance from sloping line.\\n p.y - a); // Distance from horizontal line.\\n\\n if (line_join != miter_join) {\\n dist = max(dist, line_join_distance_no_miter(\\n p, vec2(0.0, -(2.0/SQRT3)*r), vec2(0.0, -1.0), v_linewidth*0.25, line_join));\\n\\n dist = max(dist, line_join_distance_no_miter(\\n p, vec2(r, a), vec2(SQRT3/2.0, 0.5), v_linewidth*0.25, line_join));\\n }\\n\\n return dist;\\n}\\n#endif\\n\\n#ifdef USE_TRIANGLE_PIN\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n float angle = atan(p.x, -p.y); // In range -pi to +pi.\\n angle = mod(angle, PI*2.0/3.0) - PI/3.0; // In range -pi/3 to pi/3.\\n p = length(p)*vec2(cos(angle), abs(sin(angle))); // (x,y) in range 0 to pi/3.\\n // Quadratic bezier curve passes through (a, r), ((a+b)/2, 0) and (a, -r) where\\n // a = r/SQRT3, b = 3a/8 = r SQRT3/8. Circular arc that passes through the same points has\\n // center at (a+x, 0) and radius x+c where c = (a-b)/2 and x = (r**2 - c**2) / (2c).\\n // Ignore r factor until the end so can use const.\\n const float a = 1.0/SQRT3;\\n const float b = SQRT3/8.0;\\n const float c = (a-b)/2.0;\\n const float x = (1.0 - c*c) / (2.0*c);\\n const float center_x = x + a;\\n const float radius = x + c;\\n float r = 0.5*v_size.x;\\n float dist = r*radius - distance(p, vec2(r*center_x, 0.0));\\n\\n // Magic number is 0.5*sin(atan(8*sqrt(3)/5) - pi/3)\\n dist = max(dist, line_join_distance_incl_miter(\\n p, vec2(a*r, r), vec2(0.5, 0.5*SQRT3), v_linewidth*0.0881844526878324, line_join,\\n vec2(8.0*SQRT3, -5.0)/sqrt(217.0)));\\n\\n return dist;\\n}\\n#endif\\n\\n#ifdef USE_X\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n p = vec2((p.x + p.y)/SQRT2, (p.x - p.y)/SQRT2);\\n return one_cross(p, line_cap, 0.5*v_size.x);\\n}\\n#endif\\n\\n#ifdef USE_Y\\nfloat marker_distance(in vec2 p, in int line_cap, in int line_join)\\n{\\n // Assuming v_size.x == v.size_y\\n return one_y(p, line_cap, 0.5*v_size.x);\\n}\\n#endif\\n\\n// Convert distance from edge of marker to fraction in range 0 to 1, depending\\n// on antialiasing width.\\nfloat distance_to_fraction(in float dist)\\n{\\n return 1.0 - smoothstep(-0.5*u_antialias, 0.5*u_antialias, dist);\\n}\\n\\n// Return fraction from 0 (no fill color) to 1 (full fill color).\\nfloat fill_fraction(in float dist)\\n{\\n return distance_to_fraction(dist);\\n}\\n\\n// Return fraction in range 0 (no line color) to 1 (full line color).\\nfloat line_fraction(in float dist)\\n{\\n return distance_to_fraction(abs(dist) - 0.5*v_linewidth);\\n}\\n\\n// Return fraction (in range 0 to 1) of a color, with premultiplied alpha.\\nvec4 fractional_color(in vec4 color, in float fraction)\\n{\\n color.a *= fraction;\\n color.rgb *= color.a;\\n return color;\\n}\\n\\n// Blend colors that have premultiplied alpha.\\nvec4 blend_colors(in vec4 src, in vec4 dest)\\n{\\n return (1.0 - src.a)*dest + src;\\n}\\n\\n#ifdef APPEND_DOT\\nfloat dot_fraction(in vec2 p)\\n{\\n // Assuming v_size.x == v_size.y\\n float radius = 0.125*v_size.x;\\n float dot_distance = max(length(p) - radius, -0.5*u_antialias);\\n return fill_fraction(dot_distance);\\n}\\n#endif\\n\\n#ifdef HATCH\\n// Wrap coordinate(s) by removing integer part to give distance from center of\\n// repeat, in the range -0.5 to +0.5.\\nfloat wrap(in float x)\\n{\\n return fract(x) - 0.5;\\n}\\n\\nvec2 wrap(in vec2 xy)\\n{\\n return fract(xy) - 0.5;\\n}\\n\\n// Return fraction from 0 (no hatch color) to 1 (full hatch color).\\nfloat hatch_fraction(in vec2 coords, in int hatch_pattern)\\n{\\n float scale = v_hatch_scale; // Hatch repeat distance.\\n\\n // Coordinates and linewidth/halfwidth are scaled to hatch repeat distance.\\n coords = coords / scale;\\n float halfwidth = 0.5*v_hatch_weight / scale; // Half the hatch linewidth.\\n\\n // Default is to return fraction of zero, i.e. no pattern.\\n float dist = u_antialias;\\n\\n if (hatch_pattern == hatch_dot) {\\n const float dot_radius = 0.25;\\n dist = length(wrap(coords)) - dot_radius;\\n }\\n else if (hatch_pattern == hatch_ring) {\\n const float ring_radius = 0.25;\\n dist = abs(length(wrap(coords)) - ring_radius) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_horizontal_line) {\\n dist = abs(wrap(coords.y)) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_vertical_line) {\\n dist = abs(wrap(coords.x)) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_cross) {\\n dist = min(abs(wrap(coords.x)), abs(wrap(coords.y))) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_horizontal_dash) {\\n // Dashes have square caps.\\n const float halflength = 0.25;\\n dist = max(abs(wrap(coords.y)),\\n abs(wrap(coords.x) + 0.25) - halflength) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_vertical_dash) {\\n const float halflength = 0.25;\\n dist = max(abs(wrap(coords.x)),\\n abs(wrap(coords.y) + 0.25) - halflength) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_spiral) {\\n vec2 wrap2 = wrap(coords);\\n float angle = wrap(atan(wrap2.y, wrap2.x) / (2.0*PI));\\n // Canvas spiral radius increases by scale*pi/15 each rotation.\\n const float dr = PI/15.0;\\n float radius = length(wrap2);\\n // At any angle, spiral lines are equally spaced dr apart.\\n // Find distance to nearest of these lines.\\n float frac = fract((radius - dr*angle) / dr); // 0 to 1.\\n dist = dr*(abs(frac - 0.5));\\n dist = min(dist, radius) - halfwidth; // Consider center point also.\\n }\\n else if (hatch_pattern == hatch_right_diagonal_line) {\\n dist = abs(wrap(2.0*coords.x + coords.y))/sqrt(5.0) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_left_diagonal_line) {\\n dist = abs(wrap(2.0*coords.x - coords.y))/sqrt(5.0) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_diagonal_cross) {\\n coords = vec2(coords.x + coords.y + 0.5, coords.x - coords.y + 0.5);\\n dist = min(abs(wrap(coords.x)), abs(wrap(coords.y))) / SQRT2 - halfwidth;\\n }\\n else if (hatch_pattern == hatch_right_diagonal_dash) {\\n float across = coords.x + coords.y + 0.5;\\n dist = abs(wrap(across)) / SQRT2; // Distance to nearest solid line.\\n\\n across = floor(across); // Offset for dash.\\n float along = wrap(0.5*(coords.x - coords.y + across));\\n const float halflength = 0.25;\\n along = abs(along) - halflength; // Distance along line.\\n\\n dist = max(dist, along) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_left_diagonal_dash) {\\n float across = coords.x - coords.y + 0.5;\\n dist = abs(wrap(across)) / SQRT2; // Distance to nearest solid line.\\n\\n across = floor(across); // Offset for dash.\\n float along = wrap(0.5*(coords.x + coords.y + across));\\n const float halflength = 0.25;\\n along = abs(along) - halflength; // Distance along line.\\n\\n dist = max(dist, along) - halfwidth;\\n }\\n else if (hatch_pattern == hatch_horizontal_wave) {\\n float wrapx = wrap(coords.x);\\n float wrapy = wrap(coords.y - 0.25 + abs(wrapx));\\n dist = abs(wrapy) / SQRT2 - halfwidth;\\n }\\n else if (hatch_pattern == hatch_vertical_wave) {\\n float wrapy = wrap(coords.y);\\n float wrapx = wrap(coords.x - 0.25 + abs(wrapy));\\n dist = abs(wrapx) / SQRT2 - halfwidth;\\n }\\n else if (hatch_pattern == hatch_criss_cross) {\\n float plus = min(abs(wrap(coords.x)), abs(wrap(coords.y)));\\n\\n coords = vec2(coords.x + coords.y + 0.5, coords.x - coords.y + 0.5);\\n float X = min(abs(wrap(coords.x)), abs(wrap(coords.y))) / SQRT2;\\n\\n dist = min(plus, X) - halfwidth;\\n }\\n\\n return distance_to_fraction(dist*scale);\\n}\\n#endif\\n\\nvoid main()\\n{\\n int line_cap = int(v_line_cap + 0.5);\\n int line_join = int(v_line_join + 0.5);\\n#ifdef HATCH\\n int hatch_pattern = int(v_hatch_pattern + 0.5);\\n#endif\\n\\n float dist = marker_distance(v_coords, line_cap, line_join);\\n\\n#ifdef LINE_ONLY\\n vec4 color = vec4(0.0, 0.0, 0.0, 0.0);\\n#else\\n float fill_frac = fill_fraction(dist);\\n vec4 color = fractional_color(v_fill_color, fill_frac);\\n#endif\\n\\n#if defined(HATCH) && !defined(LINE_ONLY)\\n if (hatch_pattern > 0 && fill_frac > 0.0) {\\n float hatch_frac = hatch_fraction(v_hatch_coords, hatch_pattern);\\n vec4 hatch_color = fractional_color(v_hatch_color, hatch_frac*fill_frac);\\n color = blend_colors(hatch_color, color);\\n }\\n#endif\\n\\n float line_frac = line_fraction(dist);\\n\\n#ifdef APPEND_DOT\\n line_frac = max(line_frac, dot_fraction(v_coords));\\n#endif\\n#ifdef APPEND_CROSS\\n line_frac = max(line_frac, line_fraction(one_cross(v_coords, line_cap, 0.5*v_size.x)));\\n#endif\\n#ifdef APPEND_CROSS_2\\n vec2 lengths = vec2(v_size.x/3.0, v_size.x/2.0);\\n line_frac = max(line_frac, line_fraction(one_cross_2(v_coords, line_cap, lengths)));\\n#endif\\n#ifdef APPEND_X\\n vec2 p = vec2((v_coords.x + v_coords.y)/SQRT2, (v_coords.x - v_coords.y)/SQRT2);\\n line_frac = max(line_frac, line_fraction(one_cross(p, line_cap, APPEND_X_LEN)));\\n#endif\\n#ifdef APPEND_Y\\n line_frac = max(line_frac, line_fraction(one_y(v_coords, line_cap, 0.5*v_size.x)));\\n#endif\\n\\n if (line_frac > 0.0) {\\n vec4 line_color = fractional_color(v_line_color, line_frac);\\n color = blend_colors(line_color, color);\\n }\\n\\n gl_FragColor = color;\\n}\\n\"},\n 552: function _(t,e,r,s,_){s();const n=t(553);class a extends n.SXSYGlyphGL{constructor(t,e){super(t,e),this.glyph=e}get marker_type(){return\"annular_wedge\"}get outer_radius(){return this._widths}get inner_radius(){return this._heights}get start_angle(){return this._angles}get end_angle(){return this._auxs}_set_data(){super._set_data(),this.outer_radius.set_from_array(this.glyph.souter_radius),this.inner_radius.set_from_array(this.glyph.sinner_radius),\"anticlock\"==this.glyph.model.direction?(this.start_angle.set_from_prop(this.glyph.start_angle),this.end_angle.set_from_prop(this.glyph.end_angle)):(this.start_angle.set_from_prop(this.glyph.end_angle),this.end_angle.set_from_prop(this.glyph.start_angle))}}r.AnnularWedgeGL=a,a.__name__=\"AnnularWedgeGL\"},\n 553: function _(s,e,t,i,n){i();const r=s(554),_=s(558);class h extends r.SingleMarkerGL{constructor(s,e){super(s,e),this.glyph=e}_set_data(){const s=this.nvertices,e=this._centers.get_sized_array(2*s);(0,_.interleave)(this.glyph.sx,this.glyph.sy,s,r.SingleMarkerGL.missing_point,e),this._centers.update()}}t.SXSYGlyphGL=h,h.__name__=\"SXSYGlyphGL\"},\n 554: function _(s,a,t,e,_){e();const h=s(555);class l extends h.BaseMarkerGL{constructor(s,a){super(s,a),this.glyph=a}_get_visuals(){return this.glyph.visuals}draw(s,a,t){this._draw_impl(s,t,a.glglyph)}_draw_impl(s,a,t){(t.data_changed||t.data_mapped)&&(t.set_data(),t.data_changed=!1,t.data_mapped=!1),this.visuals_changed&&(this._set_visuals(),this.visuals_changed=!1);const e=t.nvertices,_=this._show.length,h=this._show.get_sized_array(e);if(s.length2&&e[0]==e[t-1]&&i[0]==i[t-1]&&isFinite(e[0]+i[0]);for(let _=1;_2&&e[0]==e[2*i-2]&&e[1]==e[2*i-1]?(s[0]=s[i-1],s[i]=s[1]):(s[0]=0,s[i]=0)}_set_visuals(){const s=this._get_visuals();this._line_color.set_from_color(s.line_color,s.line_alpha),this._linewidth.set_from_prop(s.line_width),this._line_cap.set_from_line_cap(s.line_cap),this._line_join.set_from_line_join(s.line_join);const{line_dash:e}=s;if(this._is_dashed=!(e.is_Scalar()&&0==e.get(0).length),this._is_dashed){null==this._dash_offset&&(this._dash_offset=new l.Float32Buffer(this.regl_wrapper)),this._dash_offset.set_from_prop(s.line_dash_offset);const i=e.length;null==this._dash_tex_info&&(this._dash_tex_info=new l.Float32Buffer(this.regl_wrapper,4));const t=this._dash_tex_info.get_sized_array(4*i);null==this._dash_scale&&(this._dash_scale=new l.Float32Buffer(this.regl_wrapper));const _=this._dash_scale.get_sized_array(i);for(let s=0;s0){const[e,h,l]=this.regl_wrapper.get_dash(i);this._dash_tex.push(h);for(let i=0;i<4;i++)t[4*s+i]=e[i];_[s]=l}else this._dash_tex.push(null),t.fill(0,4*s,4*(s+1)),_[s]=0}this._dash_tex_info.update(),this._dash_scale.update()}}}i.BaseLineGL=a,a.__name__=\"BaseLineGL\"},\n 561: function _(s,e,t,_,r){_();const a=s(553),i=s(13);class c extends a.SXSYGlyphGL{constructor(s,e){super(s,e),this.glyph=e}get marker_type(){return\"circle\"}get size(){return this._widths}_set_data(){super._set_data(),this.size.set_from_array((0,i.mul)(this.glyph.sradius,2))}_set_once(){super._set_once(),this._heights.set_from_scalar(0),this._angles.set_from_scalar(0)}}t.CircleGL=c,c.__name__=\"CircleGL\"},\n 562: function _(s,t,_,e,h){e();const a=s(553);class r extends a.SXSYGlyphGL{constructor(s,t){super(s,t),this.glyph=t}get marker_type(){return\"hex_tile\"}_set_data(){super._set_data(),\"pointytop\"==this.glyph.model.orientation?(this._angles.set_from_scalar(.5*Math.PI),this._widths.set_from_scalar(2*this.glyph.svy[0]),this._heights.set_from_scalar(4*this.glyph.svx[4]/Math.sqrt(3))):(this._angles.set_from_scalar(0),this._widths.set_from_scalar(2*this.glyph.svx[0]),this._heights.set_from_scalar(4*this.glyph.svy[4]/Math.sqrt(3)))}_set_once(){super._set_once(),this._auxs.set_from_scalar(0)}}_.HexTileGL=r,r.__name__=\"HexTileGL\"},\n 563: function _(t,e,s,a,i){a();const n=t(556),_=t(557),h=t(12);class l extends n.BaseGLGlyph{constructor(t,e){super(t,e),this._tex=[],this._bounds=[],this._image_changed=!1,this.glyph=e}draw(t,e,s){const a=e.glglyph;(a.data_changed||a.data_mapped)&&a._set_data(),(a._image_changed||a.data_changed)&&a._set_image(),a.data_changed=!1,a.data_mapped=!1,a._image_changed=!1;const{global_alpha:i}=this.glyph.visuals.image;for(const e of t){if(null==a._tex[e]||null==a._bounds[e])continue;const t={scissor:this.regl_wrapper.scissor,viewport:this.regl_wrapper.viewport,canvas_size:[s.width,s.height],bounds:a._bounds[e],tex:a._tex[e],global_alpha:i.get(e)};this.regl_wrapper.image()(t)}}set_image_changed(){this._image_changed=!0}_set_data(){const{image:t}=this.glyph,e=t.length;this._bounds.length!=e&&(this._bounds=Array(e).fill(null));for(let t=0;t0&&(l[r]=_[r]);for(let e=1;e0}else this._border_radius=[0,0,0,0],this._border_radius_nonzero=!1}_set_once(){super._set_once(),this._auxs.set_from_scalar(0)}}r.LRTBGL=a,a.__name__=\"LRTBGL\"},\n 567: function _(s,t,e,a,_){a();const h=s(560),i=s(557);class r extends h.BaseLineGL{constructor(s,t){super(s,t),this.glyph=t}draw(s,t,e){this.visuals_changed&&(this._set_visuals(),this.visuals_changed=!1);const a=t.glglyph,_=a.data_changed||a.data_mapped;_&&a._set_data(a.data_changed),(_&&a._is_dashed||this._is_dashed)&&a._set_length(),_&&(a.data_changed=!1,a.data_mapped=!1);const{data_size:h}=this.glyph;let i=null,r=null;h>1&&([i,r]=this.regl_wrapper.framebuffer_and_texture);let l=0,n=-1;for(const _ of s){for(let s=n+1;s<_;s++){l+=2*(t.sxs.get(s).length+2)}const s=t.sxs.get(_).length,h=s-1;if(null!=i&&this.regl_wrapper.clear_framebuffer(i),this._draw_single(a,e,_,l,h,i),null!=i){const s={scissor:this.regl_wrapper.scissor,viewport:this.regl_wrapper.viewport,framebuffer_tex:r};this.regl_wrapper.accumulate()(s)}l+=2*(s+2),n=_}}_get_visuals(){return this.glyph.visuals.line}_set_data(s){const t=this.glyph.data_size,e=this.glyph.sxs.data.length;null==this._points&&(this._points=new i.Float32Buffer(this.regl_wrapper));const a=this._points.get_sized_array(2*(e+2*t));let _=0;for(let s=0;s1||s.length0}_set_once(){super._set_once(),this._auxs.set_from_scalar(0)}}_.RectGL=o,o.__name__=\"RectGL\"},\n 570: function _(e,t,s,i,a){i();const n=e(557),r=e(565),l=e(12);class _ extends r.SingleLineGL{constructor(e,t){super(e,t),this.glyph=t}draw(e,t,s){this._draw_impl(e,s,t.glglyph)}_get_show_buffer(e,t){return t._show}_get_visuals(){return this.glyph.visuals.line}_set_data_points(){const e=this.glyph.sx,t=this.glyph.sy,s=this.glyph.model.mode;let i=e.length;const a=i>2&&e[0]==e[i-1]&&t[0]==t[i-1]&&isFinite(e[0])&&isFinite(t[0]),r=\"center\"==s?2*i:2*i-1;null==this._points&&(this._points=new n.Float32Buffer(this.regl_wrapper));const _=this._points.get_sized_array(2*(r+2));let h=isFinite(e[0]+t[0]),o=2;\"center\"==s&&(_[o++]=h?e[0]:NaN,_[o++]=t[0]);for(let a=0;a{const{label:e}=this.model;return(0,c.isString)(e)?new d.Text({content:e}):e})();this.label_view=await this.owner.build_view(e,this)}async _rebuild_icon(){this.icon_view?.remove();const{icon:e}=this.model;null!=e&&(this.icon_view=await(0,_.build_view)(e,{parent:this}))}connect_signals(){super.connect_signals();const{label:e,icon:t,button_type:i,disabled:n}=this.model.properties;this.on_transitive_change(e,(async()=>{await this._rebuild_label(),this.render()})),this.on_transitive_change(t,(async()=>{await this._rebuild_icon(),this.render()})),this.on_change([i,n],(()=>{this.render()}))}remove(){this.label_view?.remove(),this.icon_view?.remove(),super.remove()}stylesheets(){return[...super.stylesheets(),w.default]}_render_button(...e){return(0,a.button)({type:\"button\",disabled:this.model.disabled,class:[v.btn,v[`btn_${this.model.button_type}`]]},...e)}render(){if(super.render(),this.label_view?.render(),this.button_el=this._render_button(this.label_view?.el),this.button_el.addEventListener(\"click\",(()=>this.click())),null!=this.icon_view){const e=\"\"!=this.model.label?(0,a.nbsp)():(0,a.text)(\"\");(0,a.prepend)(this.button_el,this.icon_view.el,e),this.icon_view.render()}this.group_el=(0,a.div)({class:v.btn_group},this.button_el),this.shadow_el.append(this.group_el)}click(){}}i.AbstractButtonView=p,p.__name__=\"AbstractButtonView\";class y extends h.Control{constructor(e){super(e)}}i.AbstractButton=y,l=y,y.__name__=\"AbstractButton\",l.define((({Str:e,Ref:t,Or:i,Nullable:n})=>({label:[i(t(u.DOMNode),e),\"Button\"],icon:[n(t(b.Icon)),null],button_type:[r.ButtonType,\"default\"]})))},\n 591: function _(t,n,o,s,e){s();const c=t(697),i=t(63);class l extends c.WidgetView{connect_signals(){super.connect_signals(),this.connect(this.disabled,(t=>{for(const n of this.controls())(0,i.toggle_attribute)(n,\"disabled\",t)}))}}o.ControlView=l,l.__name__=\"ControlView\";class _ extends c.Widget{constructor(t){super(t)}}o.Control=_,_.__name__=\"Control\"},\n 697: function _(t,e,i,r,s){var a;r();const n=t(399),o=t(182);class d extends n.LayoutDOMView{get child_models(){return[]}get provider(){return o.default_provider}async lazy_initialize(){await super.lazy_initialize(),\"not_started\"==this.provider.status&&await this.provider.fetch()}_after_layout(){super._after_layout(),\"loading\"==this.provider.status&&(this._has_finished=!1)}process_tex(t){if(null==this.provider.MathJax)return t;const e=this.provider.MathJax.find_tex(t),i=[];let r=0;for(const s of e)i.push(t.slice(r,s.start.n)),i.push(this.provider.MathJax.tex2svg(s.math,{display:s.display}).outerHTML),r=s.end.n;return r0}}i.WidgetView=d,d.__name__=\"WidgetView\";class _ extends n.LayoutDOM{constructor(t){super(t)}}i.Widget=_,a=_,_.__name__=\"Widget\",a.override({margin:5})},\n 593: function _(b,o,r,e,t){e(),r.btn=\"bk-btn\",r.active=\"bk-active\",r.btn_default=\"bk-btn-default\",r.btn_primary=\"bk-btn-primary\",r.btn_success=\"bk-btn-success\",r.btn_warning=\"bk-btn-warning\",r.btn_danger=\"bk-btn-danger\",r.btn_light=\"bk-btn-light\",r.btn_group=\"bk-btn-group\",r.vertical=\"bk-vertical\",r.horizontal=\"bk-horizontal\",r.dropdown_toggle=\"bk-dropdown-toggle\",r.default=\".bk-btn,::file-selector-button{height:100%;display:inline-block;text-align:center;vertical-align:middle;white-space:nowrap;cursor:pointer;padding:var(--padding-vertical) var(--padding-horizontal);font-size:var(--font-size);border:1px solid transparent;border-radius:var(--border-radius);outline:0;outline-offset:-5px;user-select:none;-webkit-user-select:none;}.bk-btn:hover,::file-selector-button:hover,.bk-btn:focus,::file-selector-button:focus{text-decoration:none;}.bk-btn:active,::file-selector-button:active,.bk-active.bk-btn,.bk-active::file-selector-button{background-image:none;box-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);}.bk-btn[disabled]{cursor:not-allowed;pointer-events:none;opacity:0.65;box-shadow:none;}::file-selector-button{color:#333;background-color:#fff;border-color:#ccc;}::file-selector-button:hover{background-color:#f5f5f5;border-color:#b8b8b8;}.bk-active::file-selector-button{background-color:#ebebeb;border-color:#adadad;}::file-selector-button[disabled],::file-selector-button[disabled]:hover,::file-selector-button[disabled]:focus,::file-selector-button[disabled]:active,.bk-active::file-selector-button[disabled]{background-color:#e6e6e6;border-color:#ccc;}::file-selector-button:focus,::file-selector-button:active{outline:1px dotted #ccc;}.bk-btn-default{color:#333;background-color:#fff;border-color:#ccc;}.bk-btn-default:hover{background-color:#f5f5f5;border-color:#b8b8b8;}.bk-active.bk-btn-default{background-color:#ebebeb;border-color:#adadad;}.bk-btn-default[disabled],.bk-btn-default[disabled]:hover,.bk-btn-default[disabled]:focus,.bk-btn-default[disabled]:active,.bk-active.bk-btn-default[disabled]{background-color:#e6e6e6;border-color:#ccc;}.bk-btn-default:focus,.bk-btn-default:active{outline:1px dotted #ccc;}.bk-btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd;}.bk-btn-primary:hover{background-color:#3681c1;border-color:#2c699e;}.bk-active.bk-btn-primary{background-color:#3276b1;border-color:#285e8e;}.bk-btn-primary[disabled],.bk-btn-primary[disabled]:hover,.bk-btn-primary[disabled]:focus,.bk-btn-primary[disabled]:active,.bk-active.bk-btn-primary[disabled]{background-color:#506f89;border-color:#357ebd;}.bk-btn-primary:focus,.bk-btn-primary:active{outline:1px dotted #ccc;}.bk-btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c;}.bk-btn-success:hover{background-color:#4eb24e;border-color:#409240;}.bk-active.bk-btn-success{background-color:#47a447;border-color:#398439;}.bk-btn-success[disabled],.bk-btn-success[disabled]:hover,.bk-btn-success[disabled]:focus,.bk-btn-success[disabled]:active,.bk-active.bk-btn-success[disabled]{background-color:#667b66;border-color:#4cae4c;}.bk-btn-success:focus,.bk-btn-success:active{outline:1px dotted #ccc;}.bk-btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236;}.bk-btn-warning:hover{background-color:#eea43b;border-color:#e89014;}.bk-active.bk-btn-warning{background-color:#ed9c28;border-color:#d58512;}.bk-btn-warning[disabled],.bk-btn-warning[disabled]:hover,.bk-btn-warning[disabled]:focus,.bk-btn-warning[disabled]:active,.bk-active.bk-btn-warning[disabled]{background-color:#c89143;border-color:#eea236;}.bk-btn-warning:focus,.bk-btn-warning:active{outline:1px dotted #ccc;}.bk-btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a;}.bk-btn-danger:hover{background-color:#d5433e;border-color:#bd2d29;}.bk-active.bk-btn-danger{background-color:#d2322d;border-color:#ac2925;}.bk-btn-danger[disabled],.bk-btn-danger[disabled]:hover,.bk-btn-danger[disabled]:focus,.bk-btn-danger[disabled]:active,.bk-active.bk-btn-danger[disabled]{background-color:#a55350;border-color:#d43f3a;}.bk-btn-danger:focus,.bk-btn-danger:active{outline:1px dotted #ccc;}.bk-btn-light{color:#333;background-color:#fff;border-color:#ccc;border-color:transparent;}.bk-btn-light:hover{background-color:#f5f5f5;border-color:#b8b8b8;}.bk-active.bk-btn-light{background-color:#ebebeb;border-color:#adadad;}.bk-btn-light[disabled],.bk-btn-light[disabled]:hover,.bk-btn-light[disabled]:focus,.bk-btn-light[disabled]:active,.bk-active.bk-btn-light[disabled]{background-color:#e6e6e6;border-color:#ccc;}.bk-btn-light:focus,.bk-btn-light:active{outline:1px dotted #ccc;}.bk-btn-group{height:100%;display:flex;flex-wrap:nowrap;align-items:center;}.bk-btn-group:not(.bk-vertical),.bk-btn-group.bk-horizontal{flex-direction:row;}.bk-btn-group.bk-vertical{flex-direction:column;}.bk-btn-group > .bk-btn{flex-grow:1;}.bk-btn-group:not(.bk-vertical) > .bk-btn + .bk-btn{margin-left:-1px;}.bk-btn-group.bk-vertical > .bk-btn + .bk-btn{margin-top:-1px;}.bk-btn-group:not(.bk-vertical) > .bk-btn:first-child:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;}.bk-btn-group.bk-vertical > .bk-btn:first-child:not(:last-child){border-bottom-left-radius:0;border-bottom-right-radius:0;}.bk-btn-group:not(.bk-vertical) > .bk-btn:not(:first-child):last-child{border-bottom-left-radius:0;border-top-left-radius:0;}.bk-btn-group.bk-vertical > .bk-btn:not(:first-child):last-child{border-top-left-radius:0;border-top-right-radius:0;}.bk-btn-group > .bk-btn:not(:first-child):not(:last-child){border-radius:0;}.bk-btn-group.bk-vertical > .bk-btn{width:100%;}.bk-btn-group .bk-dropdown-toggle{flex:0 0 0;padding:var(--padding-vertical) calc(var(--padding-horizontal)/2);}\"},\n 594: function _(e,t,s,i,n){var h;i();const o=e(1),_=e(595),u=e(63),r=e(34),c=e(11),l=e(21),a=o.__importStar(e(599)),m=a,d=(0,l.Enum)(\"starts_with\",\"includes\");class p extends _.TextInputView{constructor(){super(...arguments),this._open=!1,this._last_value=\"\",this._hover_index=0}stylesheets(){return[...super.stylesheets(),a.default]}render(){super.render(),this.input_el.addEventListener(\"focusin\",(()=>this._toggle_menu())),this.menu=(0,u.div)({class:[m.menu,m.below]}),this.menu.addEventListener(\"click\",(e=>this._menu_click(e))),this.menu.addEventListener(\"mouseover\",(e=>this._menu_hover(e))),this.shadow_el.appendChild(this.menu),(0,u.undisplay)(this.menu)}change_input(){this._open&&this.menu.children.length>0?(this.model.value=this.menu.children[this._hover_index].textContent,this.input_el.focus(),this._hide_menu()):this.model.restrict||super.change_input()}_update_completions(e){(0,u.empty)(this.menu);const{max_completions:t}=this.model,s=null!=t?(0,r.take)(e,t):e;for(const e of s){const t=(0,u.div)(e);this.menu.append(t)}this.menu.firstElementChild?.classList.add(m.active)}compute_completions(e){const t=(()=>{const{case_sensitive:e}=this.model;return e?e=>e:e=>e.toLowerCase()})(),s=(()=>{switch(this.model.search_strategy){case\"starts_with\":return(e,t)=>e.startsWith(t);case\"includes\":return(e,t)=>e.includes(t)}})(),i=t(e),n=[];for(const e of this.model.completions){s(t(e),i)&&n.push(e)}return n}_toggle_menu(){const{value:e}=this.input_el;if(e.length{t.composedPath().includes(this.el)||(document.removeEventListener(\"click\",e),this._hide_menu())};document.addEventListener(\"click\",e)}}_hide_menu(){this._open&&(this._open=!1,(0,u.undisplay)(this.menu))}_menu_click(e){e.target!=e.currentTarget&&e.target instanceof Element&&(this.model.value=e.target.textContent,this.input_el.focus(),this._hide_menu())}_menu_hover(e){if(e.target!=e.currentTarget&&e.target instanceof Element)for(let t=0;t0&&(this.menu.children[this._hover_index].classList.remove(m.active),this._hover_index=(0,c.clamp)(e,0,t-1),this.menu.children[this._hover_index].classList.add(m.active))}_keyup(e){switch(super._keyup(e),e.key){case\"Enter\":this.change_input();break;case\"Escape\":this._hide_menu();break;case\"ArrowUp\":this._bump_hover(this._hover_index-1);break;case\"ArrowDown\":this._bump_hover(this._hover_index+1);break;default:this._toggle_menu()}}}s.AutocompleteInputView=p,p.__name__=\"AutocompleteInputView\";class v extends _.TextInput{constructor(e){super(e)}}s.AutocompleteInput=v,h=v,v.__name__=\"AutocompleteInput\",h.prototype.default_view=p,h.define((({Bool:e,Int:t,Str:s,List:i,NonNegative:n,Positive:h,Nullable:o})=>({completions:[i(s),[]],min_characters:[n(t),2],max_completions:[o(h(t)),null],case_sensitive:[e,!0],restrict:[e,!0],search_strategy:[d,\"starts_with\"]})))},\n 595: function _(e,t,n,i,s){var u;i();const l=e(1),p=e(596),r=e(63),_=e(53),a=l.__importStar(e(598));class c extends p.TextLikeInputView{connect_signals(){super.connect_signals();const{prefix:e,suffix:t}=this.model.properties;this.on_change([e,t],(()=>this.render()))}_render_input(){this.input_el=(0,r.input)({type:\"text\",class:a.input});const{prefix:e,suffix:t}=this.model,n=null!=e?(0,r.div)({class:\"bk-input-prefix\"},e):null,i=null!=t?(0,r.div)({class:\"bk-input-suffix\"},t):null;return(0,r.div)({class:\"bk-input-container\"},n,this.input_el,i)}render(){super.render(),this.input_el.addEventListener(\"keyup\",(e=>this._keyup(e)))}_keyup(e){\"Enter\"!=e.key||e.shiftKey||e.ctrlKey||e.altKey||this.model.trigger_event(new _.ValueSubmit(this.input_el.value))}}n.TextInputView=c,c.__name__=\"TextInputView\";class o extends p.TextLikeInput{constructor(e){super(e)}}n.TextInput=o,u=o,o.__name__=\"TextInput\",u.prototype.default_view=c,u.define((({Str:e,Nullable:t})=>({prefix:[t(e),null],suffix:[t(e),null]})))},\n 596: function _(e,t,n,i,l){var s;i();const h=e(597);class a extends h.InputWidgetView{connect_signals(){super.connect_signals(),this.connect(this.model.properties.value.change,(()=>this.input_el.value=this.model.value)),this.connect(this.model.properties.value_input.change,(()=>this.input_el.value=this.model.value_input)),this.connect(this.model.properties.disabled.change,(()=>this.input_el.disabled=this.model.disabled)),this.connect(this.model.properties.placeholder.change,(()=>this.input_el.placeholder=this.model.placeholder)),this.connect(this.model.properties.max_length.change,(()=>{const{max_length:e}=this.model;null!=e?this.input_el.maxLength=e:this.input_el.removeAttribute(\"maxLength\")}))}render(){super.render();const{input_el:e}=this;e.value=this.model.value,e.disabled=this.model.disabled,e.placeholder=this.model.placeholder,null!=this.model.max_length&&(e.maxLength=this.model.max_length),e.addEventListener(\"change\",(()=>this.change_input())),e.addEventListener(\"input\",(()=>this.change_input_value()))}change_input(){this.model.value=this.input_el.value,super.change_input()}change_input_value(){this.model.value_input=this.input_el.value,super.change_input()}}n.TextLikeInputView=a,a.__name__=\"TextLikeInputView\";class u extends h.InputWidget{constructor(e){super(e)}}n.TextLikeInput=u,s=u,u.__name__=\"TextLikeInput\",s.define((({Int:e,Str:t,Nullable:n})=>({value:[t,\"\"],value_input:[t,\"\"],placeholder:[t,\"\"],max_length:[n(e),null]})))},\n 597: function _(e,t,i,s,n){var l,o;s();const r=e(1);var c,d=this&&this.__decorate||function(e,t,i,s){var n,l=arguments.length,o=l<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if(\"object\"==typeof Reflect&&\"function\"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var r=e.length-1;r>=0;r--)(n=e[r])&&(o=(l<3?n(o):l>3?n(t,i,o):n(t,i))||o);return l>3&&o&&Object.defineProperty(t,i,o),o};const a=e(591),u=e(413),_=e(401),p=e(8),h=e(56),f=e(63),v=e(58),m=e(53),w=r.__importStar(e(598)),b=w,g=r.__importDefault(e(123));let y=(l=class extends m.ModelEvent{constructor(e){super(),this.model=e,this.origin=e}static from_values(e){const{model:t}=e;return new c(t)}},c=l,l.__name__=\"ClearInput\",l);i.ClearInput=y,i.ClearInput=y=c=d([(0,m.server_event)(\"clear_input\")],y);class L extends a.ControlView{constructor(){super(...arguments),this.description=null,this.desc_el=null}*controls(){yield this.input_el}*children(){yield*super.children();const{title:e,description:t}=this;e instanceof v.View&&(yield e),t instanceof v.View&&(yield t)}async lazy_initialize(){await super.lazy_initialize(),await this._build_title(),await this._build_description()}remove(){const{title:e,description:t}=this;e instanceof v.View&&e.remove(),t instanceof v.View&&t.remove(),super.remove()}connect_signals(){super.connect_signals();const{title:e,description:t}=this.model.properties;this.on_change(e,(async()=>{await this._build_title(),this.render()})),this.on_change(t,(async()=>{await this._build_description(),this.render()}))}stylesheets(){return[...super.stylesheets(),w.default,g.default]}render(){super.render(),this.desc_el=this._build_description_el(),this.title_el=this._build_title_el();const e=this._render_input();this.input_el.id=\"input\",this.group_el=(0,f.div)({class:b.input_group},this.title_el,e),this.shadow_el.append(this.group_el)}_build_description_el(){const{description:e}=this;if(null==e)return null;{const t=(0,f.div)({class:b.icon}),i=(0,f.div)({class:b.description},t);if((0,p.isString)(e))i.title=e;else{\"auto\"==e.model.target&&(e.target=i);let s=!1;const n=i=>{e.model.setv({visible:i,closable:s}),t.classList.toggle(b.opaque,i&&s)};this.on_change(e.model.properties.visible,(()=>{const{visible:t}=e.model;t||(s=!1),n(t)})),i.addEventListener(\"mouseenter\",(()=>{n(!0)})),i.addEventListener(\"mouseleave\",(()=>{s||n(!1)})),document.addEventListener(\"mousedown\",(t=>{const l=t.composedPath();l.includes(e.el)||(l.includes(i)?(s=!s,n(s)):(s=!1,n(!1)))})),window.addEventListener(\"blur\",(()=>{s=!1,n(!1)}))}return i}}async _build_title(){const{title:e}=this.model;e instanceof _.HTML?this.title=await(0,h.build_view)(e,{parent:this}):this.title=e}async _build_description(){const{description:e}=this.model;e instanceof u.Tooltip?this.description=await(0,h.build_view)(e,{parent:this}):this.description=e}_build_title_el(){const{title:e}=this,t=e instanceof _.HTMLView?(e.render(),e.el):e,i=\"\"==e?\"none\":\"\";return(0,f.label)({for:\"input\",style:{display:i}},t,this.desc_el)}change_input(){}}i.InputWidgetView=L,L.__name__=\"InputWidgetView\";class V extends a.Control{constructor(e){super(e)}}i.InputWidget=V,o=V,V.__name__=\"InputWidget\",o.define((({Str:e,Nullable:t,Or:i,Ref:s})=>({title:[i(e,s(_.HTML)),\"\"],description:[t(i(e,s(u.Tooltip))),null]})))},\n 598: function _(i,n,t,e,p){e(),t.input=\"bk-input\",t.disabled=\"bk-disabled\",t.input_container=\"bk-input-container\",t.input_prefix=\"bk-input-prefix\",t.input_suffix=\"bk-input-suffix\",t.input_group=\"bk-input-group\",t.inline=\"bk-inline\",t.spin_wrapper=\"bk-spin-wrapper\",t.spin_btn=\"bk-spin-btn\",t.spin_btn_up=\"bk-spin-btn-up\",t.spin_btn_down=\"bk-spin-btn-down\",t.description=\"bk-description\",t.icon=\"bk-icon\",t.opaque=\"bk-opaque\",t.default=':host{--input-min-height:calc(var(--line-height-computed) + 2*var(--padding-vertical) + 2px);}.bk-input{position:relative;display:inline-block;width:100%;flex-grow:1;min-height:var(--input-min-height);padding:0 var(--padding-horizontal);background-color:#fff;border:1px solid #ccc;border-radius:var(--border-radius);resize:none;}.bk-input:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);}.bk-input::placeholder,.bk-input:-ms-input-placeholder,.bk-input::-moz-placeholder,.bk-input::-webkit-input-placeholder{color:#999;opacity:1;}.bk-input[disabled],.bk-input.bk-disabled{cursor:not-allowed;background-color:#eee;opacity:1;}.bk-input-container{width:100%;height:100%;display:flex;flex-direction:row;flex-wrap:nowrap;}.bk-input-container .bk-input-prefix,.bk-input-container .bk-input-suffix{display:flex;align-items:center;flex:0 1 0;border:1px solid #ccc;border-radius:var(--border-radius);padding:0 var(--padding-horizontal);background-color:#e6e6e6;}.bk-input-container .bk-input-prefix{border-right:none;border-top-right-radius:0;border-bottom-right-radius:0;}.bk-input-container .bk-input-suffix{border-left:none;border-top-left-radius:0;border-bottom-left-radius:0;}.bk-input-container .bk-input{flex:1 0 0;}.bk-input-container .bk-input:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0;}.bk-input-container .bk-input:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0;}input[type=file].bk-input{padding-left:0;}input[type=file]::file-selector-button{box-sizing:inherit;font-family:inherit;font-size:inherit;line-height:inherit;}select:not([multiple]).bk-input,select:not([size]).bk-input{height:auto;appearance:none;-webkit-appearance:none;background-image:url(\\'data:image/svg+xml;utf8,\\');background-position:right 0.5em center;background-size:8px 6px;background-repeat:no-repeat;padding-right:calc(var(--padding-horizontal) + 8px);}option{padding:0;}select[multiple].bk-input,select[size].bk-input,textarea.bk-input{height:auto;}.bk-input-group{position:relative;width:100%;height:100%;display:inline-flex;flex-wrap:nowrap;align-items:start;flex-direction:column;white-space:nowrap;}.bk-input-group.bk-inline{flex-direction:row;}.bk-input-group.bk-inline > *:not(:first-child){margin-left:5px;}.bk-input-group > .bk-spin-wrapper{display:inherit;width:inherit;height:inherit;position:relative;overflow:hidden;padding:0;vertical-align:middle;}.bk-input-group > .bk-spin-wrapper input{padding-right:20px;}.bk-input-group > .bk-spin-wrapper > .bk-spin-btn{position:absolute;display:block;height:50%;min-height:0;min-width:0;width:30px;padding:0;margin:0;right:0;border:none;background:none;cursor:pointer;}.bk-input-group > .bk-spin-wrapper > .bk-spin-btn:before{content:\"\";display:inline-block;transform:translateY(-50%);border-left:5px solid transparent;border-right:5px solid transparent;}.bk-input-group > .bk-spin-wrapper > .bk-spin-btn.bk-spin-btn-up{top:0;}.bk-input-group > .bk-spin-wrapper > .bk-spin-btn.bk-spin-btn-up:before{border-bottom:5px solid black;}.bk-input-group > .bk-spin-wrapper > .bk-spin-btn.bk-spin-btn-up:disabled:before{border-bottom-color:grey;}.bk-input-group > .bk-spin-wrapper > .bk-spin-btn.bk-spin-btn-down{bottom:0;}.bk-input-group > .bk-spin-wrapper > .bk-spin-btn.bk-spin-btn-down:before{border-top:5px solid black;}.bk-input-group > .bk-spin-wrapper > .bk-spin-btn.bk-spin-btn-down:disabled:before{border-top-color:grey;}.bk-description{position:relative;display:inline-block;margin-left:0.25em;vertical-align:middle;margin-top:-2px;cursor:pointer;}.bk-description > .bk-icon{opacity:0.5;width:18px;height:18px;background-color:gray;mask-image:var(--bokeh-icon-help);mask-size:contain;mask-repeat:no-repeat;-webkit-mask-image:var(--bokeh-icon-help);-webkit-mask-size:contain;-webkit-mask-repeat:no-repeat;}label:hover > .bk-description > .bk-icon,.bk-icon.bk-opaque{opacity:1;}'},\n 599: function _(e,o,i,b,r){b(),i.menu=\"bk-menu\",i.above=\"bk-above\",i.below=\"bk-below\",i.divider=\"bk-divider\",i.active=\"bk-active\",i.default=\":host{position:relative;}.bk-menu{position:absolute;left:0;width:100%;z-index:var(--bokeh-top-level);cursor:pointer;font-size:var(--font-size);background-color:#fff;border:1px solid #ccc;border-radius:var(--border-radius);box-shadow:0 6px 12px rgba(0, 0, 0, 0.175);}.bk-menu.bk-above{bottom:100%;}.bk-menu.bk-below{top:100%;}.bk-menu > .bk-divider{height:1px;margin:calc(var(--line-height-computed)/2 - 1px) 0;overflow:hidden;background-color:#e5e5e5;}.bk-menu > :not(.bk-divider){padding:var(--padding-vertical) var(--padding-horizontal);}.bk-menu > :not(.bk-divider):hover,.bk-menu > :not(.bk-divider).bk-active{background-color:#e6e6e6;}\"},\n 600: function _(t,e,n,o,c){var i;o();const s=t(590),u=t(53);class _ extends s.AbstractButtonView{click(){this.model.trigger_event(new u.ButtonClick),super.click()}}n.ButtonView=_,_.__name__=\"ButtonView\";class r extends s.AbstractButton{constructor(t){super(t)}on_click(t){this.on_event(u.ButtonClick,t)}}n.Button=r,i=r,r.__name__=\"Button\",i.prototype.default_view=_,i.override({label:\"Button\"})},\n 601: function _(t,e,o,c,s){var a;c();const i=t(1),n=t(602),u=i.__importStar(t(593));class r extends n.ToggleButtonGroupView{get active(){return new Set(this.model.active)}change_active(t){const{active:e}=this;e.has(t)?e.delete(t):e.add(t),this.model.active=[...e].sort()}_update_active(){const{active:t}=this;this._buttons.forEach(((e,o)=>{e.classList.toggle(u.active,t.has(o))}))}}o.CheckboxButtonGroupView=r,r.__name__=\"CheckboxButtonGroupView\";class _ extends n.ToggleButtonGroup{constructor(t){super(t)}}o.CheckboxButtonGroup=_,a=_,_.__name__=\"CheckboxButtonGroup\",a.prototype.default_view=r,a.define((({Int:t,List:e})=>({active:[e(t),[]]})))},\n 602: function _(t,e,n,s,o){var i;s();const r=t(1),l=t(603),a=t(53),_=t(20),d=t(63),u=r.__importStar(t(593)),c=u;class h extends l.OrientedControlView{*controls(){yield*this._buttons}connect_signals(){super.connect_signals();const t=this.model.properties;this.on_change(t.button_type,(()=>this.render())),this.on_change(t.labels,(()=>this.render())),this.on_change(t.active,(()=>this._update_active()))}stylesheets(){return[...super.stylesheets(),u.default]}render(){super.render(),this._buttons=this.model.labels.map(((t,e)=>{const n=(0,d.button)({class:[c.btn,c[`btn_${this.model.button_type}`]],disabled:this.model.disabled},t);return n.addEventListener(\"click\",(()=>{this.change_active(e),this.model.trigger_event(new a.ButtonClick)})),n})),this._update_active();const t=\"horizontal\"==this.model.orientation?c.horizontal:c.vertical,e=(0,d.div)({class:[c.btn_group,t]},this._buttons);this.shadow_el.appendChild(e)}}n.ToggleButtonGroupView=h,h.__name__=\"ToggleButtonGroupView\";class p extends l.OrientedControl{constructor(t){super(t)}}n.ToggleButtonGroup=p,i=p,p.__name__=\"ToggleButtonGroup\",i.define((({Str:t,List:e})=>({labels:[e(t),[]],button_type:[_.ButtonType,\"default\"]})))},\n 603: function _(n,e,o,t,r){var i;t();const l=n(591),s=n(20);class _ extends l.ControlView{}o.OrientedControlView=_,_.__name__=\"OrientedControlView\";class a extends l.Control{constructor(n){super(n)}}o.OrientedControl=a,i=a,a.__name__=\"OrientedControl\",i.define((()=>({orientation:[s.Orientation,\"horizontal\"]})))},\n 604: function _(e,t,s,n,i){var c;n();const o=e(1),a=e(605),h=e(63),l=e(10),d=e(34),p=o.__importStar(e(598));class r extends a.ToggleInputGroupView{get active(){return new Set(this.model.active)}connect_signals(){super.connect_signals();const{active:e}=this.model.properties;this.on_change(e,(()=>{const{active:e}=this;for(const[t,s]of(0,d.enumerate)(this._inputs))t.checked=e.has(s)}))}render(){super.render();const e=(0,h.div)({class:[p.input_group,this.model.inline?p.inline:null]});this.shadow_el.appendChild(e);const{active:t,labels:s}=this.model;this._inputs=[];for(let n=0;nthis.change_active(n))),this._inputs.push(i),this.model.disabled&&(i.disabled=!0),(0,l.includes)(t,n)&&(i.checked=!0);const c=(0,h.label)(i,(0,h.span)(s[n]));e.appendChild(c)}}change_active(e){const{active:t}=this;t.has(e)?t.delete(e):t.add(e),this.model.active=[...t].sort()}}s.CheckboxGroupView=r,r.__name__=\"CheckboxGroupView\";class u extends a.ToggleInputGroup{constructor(e){super(e)}}s.CheckboxGroup=u,c=u,u.__name__=\"CheckboxGroup\",c.prototype.default_view=r,c.define((({Int:e,List:t})=>({active:[t(e),[]]})))},\n 605: function _(e,t,n,s,o){var l;s();const r=e(1),i=e(591),u=r.__importDefault(e(598)),_=r.__importDefault(e(606));class p extends i.ControlView{*controls(){yield*this._inputs}connect_signals(){super.connect_signals();const{labels:e,inline:t}=this.model.properties;this.on_change([e,t],(()=>this.render()))}stylesheets(){return[...super.stylesheets(),u.default,_.default]}}n.ToggleInputGroupView=p,p.__name__=\"ToggleInputGroupView\";class a extends i.Control{constructor(e){super(e)}}n.ToggleInputGroup=a,l=a,a.__name__=\"ToggleInputGroup\",l.define((({Bool:e,Str:t,List:n})=>({labels:[n(t),[]],inline:[e,!1]})))},\n 606: function _(t,i,p,e,n){e(),p.default='input[type=\"checkbox\"],input[type=\"radio\"]{margin:0;}input[type=\"checkbox\"] + *,input[type=\"radio\"] + *{position:relative;top:-2px;margin-left:3px;}'},\n 607: function _(e,t,s,l,i){var a;l();const _=e(1),c=e(608),h=e(63),o=_.__importDefault(e(606));class d extends c.ToggleInputView{stylesheets(){return[...super.stylesheets(),o.default]}connect_signals(){super.connect_signals();const{label:e}=this.model.properties;this.on_change(e,(()=>this._update_label()))}render(){super.render(),this.checkbox_el=(0,h.input)({type:\"checkbox\"}),this.label_el=(0,h.span)(this.model.label),this.checkbox_el.addEventListener(\"change\",(()=>this._toggle_active())),this._update_active(),this._update_disabled(),this.shadow_el.append(this.checkbox_el,this.label_el)}_update_active(){this.checkbox_el.checked=this.model.active}_update_disabled(){this.checkbox_el.toggleAttribute(\"disabled\",this.model.disabled)}_update_label(){this.label_el.textContent=this.model.label}}s.CheckboxView=d,d.__name__=\"CheckboxView\";class n extends c.ToggleInput{constructor(e){super(e)}}s.Checkbox=n,a=n,n.__name__=\"Checkbox\",a.prototype.default_view=d,a.define((({Str:e})=>({label:[e,\"\"]})))},\n 608: function _(e,t,i,s,n){var o;s();const a=e(697);class c extends a.WidgetView{connect_signals(){super.connect_signals();const{active:e,disabled:t}=this.model.properties;this.on_change(e,(()=>this._update_active())),this.on_change(t,(()=>this._update_disabled()))}_toggle_active(){this.model.disabled||(this.model.active=!this.model.active)}}i.ToggleInputView=c,c.__name__=\"ToggleInputView\";class _ extends a.Widget{constructor(e){super(e)}}i.ToggleInput=_,o=_,_.__name__=\"ToggleInput\",o.define((({Bool:e})=>({active:[e,!1]})))},\n 609: function _(e,t,i,n,o){var s;n();const r=e(1),l=e(597),c=e(63),a=e(22),d=r.__importStar(e(598));class h extends l.InputWidgetView{connect_signals(){super.connect_signals(),this.connect(this.model.properties.name.change,(()=>this.input_el.name=this.model.name??\"\")),this.connect(this.model.properties.color.change,(()=>this.input_el.value=(0,a.color2hexrgb)(this.model.color))),this.connect(this.model.properties.disabled.change,(()=>this.input_el.disabled=this.model.disabled))}_render_input(){return this.input_el=(0,c.input)({type:\"color\",class:d.input,name:this.model.name,value:(0,a.color2hexrgb)(this.model.color),disabled:this.model.disabled})}render(){super.render(),this.input_el.addEventListener(\"change\",(()=>this.change_input()))}change_input(){this.model.color=this.input_el.value,super.change_input()}}i.ColorPickerView=h,h.__name__=\"ColorPickerView\";class p extends l.InputWidget{constructor(e){super(e)}}i.ColorPicker=p,s=p,p.__name__=\"ColorPicker\",s.prototype.default_view=h,s.define((({Color:e})=>({color:[e,\"#000000\"]})))},\n 610: function _(e,t,a,n,r){var i;n();const s=e(611),l=e(12);class c extends s.BaseDatePickerView{get flatpickr_options(){return{...super.flatpickr_options,mode:\"single\"}}_on_change(e){(0,l.assert)(e.length<=1),this.model.value=(()=>{if(0==e.length)return null;{const[t]=e;return this._format_date(t)}})()}}a.DatePickerView=c,c.__name__=\"DatePickerView\";class _ extends s.BaseDatePicker{constructor(e){super(e)}}a.DatePicker=_,i=_,_.__name__=\"DatePicker\",i.prototype.default_view=c,i.define((({Nullable:e})=>({value:[e(s.DateLike),null]})))},\n 611: function _(e,t,a,i,s){var n;i();const l=e(612),c=e(8),r=e(21);a.DateLike=(0,r.Or)((0,r.Ref)(Date),r.Str,r.Float),a.DateLikeList=(0,r.List)((0,r.Or)(a.DateLike,(0,r.Tuple)(a.DateLike,a.DateLike),(0,r.Struct)({from:a.DateLike,to:a.DateLike})));class d extends l.PickerBaseView{_format_date(e){const{picker:t}=this;return t.formatDate(e,t.config.dateFormat)}connect_signals(){super.connect_signals();const{value:e,min_date:t,max_date:a,disabled_dates:i,enabled_dates:s,date_format:n}=this.model.properties;this.connect(e.change,(()=>{const{value:e}=this.model;null!=e?this.picker.setDate(e):this.picker.clear()})),this.connect(t.change,(()=>this.picker.set(\"minDate\",this.model.min_date))),this.connect(a.change,(()=>this.picker.set(\"maxDate\",this.model.max_date))),this.connect(i.change,(()=>{const{disabled_dates:e}=this.model;this.picker.set(\"disable\",null!=e?this._convert_date_list(e):[])})),this.connect(s.change,(()=>{const{enabled_dates:e}=this.model;null!=e?this.picker.set(\"enable\",this._convert_date_list(e)):(this.picker.config._enable=void 0,this.picker.redraw(),this.picker.updateValue(!0))})),this.connect(n.change,(()=>this.picker.set(\"altFormat\",this.model.date_format)))}get flatpickr_options(){const{value:e,min_date:t,max_date:a,disabled_dates:i,enabled_dates:s,date_format:n}=this.model,l=super.flatpickr_options;return l.altInput=!0,l.altFormat=n,l.dateFormat=\"Y-m-d\",null!=e&&(l.defaultDate=e),null!=t&&(l.minDate=t),null!=a&&(l.maxDate=a),null!=i&&(l.disable=this._convert_date_list(i)),null!=s&&(l.enable=this._convert_date_list(s)),l}_convert_date_list(e){const t=[];for(const a of e)if((0,c.isArray)(a)){const[e,i]=a;t.push({from:e,to:i})}else t.push(a);return t}}a.BaseDatePickerView=d,d.__name__=\"BaseDatePickerView\";class o extends l.PickerBase{constructor(e){super(e)}}a.BaseDatePicker=o,n=o,o.__name__=\"BaseDatePicker\",n.define((({Nullable:e})=>({min_date:[e(a.DateLike),null],max_date:[e(a.DateLike),null],disabled_dates:[e(a.DateLikeList),null],enabled_dates:[e(a.DateLikeList),null],date_format:[r.Str,\"Y-m-d\"]})))},\n 612: function _(e,t,i,n,o){var s;n();const r=e(1),a=r.__importDefault(e(613)),l=e(597),c=e(63),d=e(20),h=e(63),p=e(12),f=r.__importDefault(e(621)),g=r.__importStar(e(598));class u extends l.InputWidgetView{get picker(){return(0,p.assert)(null!=this._picker),this._picker}*controls(){yield this.picker.altInput??this.input_el}remove(){this._picker?.destroy(),super.remove()}stylesheets(){return[...super.stylesheets(),f.default]}connect_signals(){super.connect_signals();const{inline:e}=this.model.properties;this.connect(e.change,(()=>this.picker.set(\"inline\",this.model.inline)))}get flatpickr_options(){return{appendTo:this.group_el,inline:this.model.inline,position:this._position.bind(this),onChange:e=>{this._on_change(e),this.change_input()}}}_render_input(){return this.input_el=(0,c.input)({type:\"text\",class:g.input,disabled:this.model.disabled})}render(){super.render(),this._picker?.destroy();const e=this.flatpickr_options;this._picker=(0,a.default)(this.input_el,e)}_position(e,t){const i=t??e._positionElement,n=[...e.calendarContainer.children].reduce(((e,t)=>e+(0,h.bounding_box)(t).height),0),o=e.calendarContainer.offsetWidth,s=this.model.position.split(\" \"),r=s[0],a=s.length>1?s[1]:null,l=i.offsetTop,c=i.offsetTop+i.offsetHeight,d=i.offsetLeft,p=i.offsetLeft+i.offsetWidth,f=i.offsetWidth,g=window.innerHeight-c,u=\"above\"===r||\"below\"!==r&&gn,_=null!=e.config.appendTo?l+(u?-n-2:i.offsetHeight+2):window.scrollY+l+(u?-n-2:i.offsetHeight+2);if(e.calendarContainer.classList.toggle(\"arrowTop\",!u),e.calendarContainer.classList.toggle(\"arrowBottom\",u),e.config.inline)return;let w=window.scrollX+d,C=!1,m=!1;\"center\"===a?(w-=(o-f)/2,C=!0):\"right\"===a&&(w-=o-f,m=!0),e.calendarContainer.classList.toggle(\"arrowLeft\",!C&&!m),e.calendarContainer.classList.toggle(\"arrowCenter\",C),e.calendarContainer.classList.toggle(\"arrowRight\",m);const y=window.document.body.offsetWidth-(window.scrollX+p),k=w+o>window.document.body.offsetWidth,b=y+o>window.document.body.offsetWidth;if(e.calendarContainer.classList.toggle(\"rightMost\",k),!e.config.static)if(e.calendarContainer.style.top=`${_}px`,k)if(b){const t=this.shadow_el.styleSheets[0],i=window.document.body.offsetWidth,n=Math.max(0,i/2-o/2),s=\".flatpickr-calendar.centerMost:before\",r=\".flatpickr-calendar.centerMost:after\",a=t.cssRules.length,l=`{left:${d}px;right:auto;}`;e.calendarContainer.classList.toggle(\"rightMost\",!1),e.calendarContainer.classList.toggle(\"centerMost\",!0),t.insertRule(`${s},${r}${l}`,a),e.calendarContainer.style.left=`${n}px`,e.calendarContainer.style.right=\"auto\"}else e.calendarContainer.style.left=\"auto\",e.calendarContainer.style.right=`${y}px`;else e.calendarContainer.style.left=`${w}px`,e.calendarContainer.style.right=\"auto\"}}i.PickerBaseView=u,u.__name__=\"PickerBaseView\";class _ extends l.InputWidget{constructor(e){super(e)}}i.PickerBase=_,s=_,_.__name__=\"PickerBase\",s.define((({Bool:e})=>({position:[d.CalendarPosition,\"auto\"],inline:[e,!1]})))},\n 613: function _(e,t,n,a,i){a();const o=e(1);var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,a=arguments.length;n=0?new Date:new Date(n.config.minDate.getTime()),a=(0,f.getDefaultHours)(n.config);t.setHours(a.hours,a.minutes,a.seconds,t.getMilliseconds()),n.selectedDates=[t],n.latestSelectedDateObj=t}void 0!==e&&\"blur\"!==e.type&&function(e){e.preventDefault();var t=\"keydown\"===e.type,a=(0,u.getEventTarget)(e),i=a;void 0!==n.amPM&&a===n.amPM&&(n.amPM.textContent=n.l10n.amPM[(0,d.int)(n.amPM.textContent===n.l10n.amPM[0])]);var o=parseFloat(i.getAttribute(\"min\")),r=parseFloat(i.getAttribute(\"max\")),l=parseFloat(i.getAttribute(\"step\")),c=parseInt(i.value,10),s=e.delta||(t?38===e.which?1:-1:0),f=c+l*s;if(void 0!==i.value&&2===i.value.length){var m=i===n.hourElement,g=i===n.minuteElement;fr&&(f=i===n.hourElement?f-r-(0,d.int)(!n.amPM):o,g&&x(void 0,1,n.hourElement)),n.amPM&&m&&(1===l?f+c===23:Math.abs(f-c)>l)&&(n.amPM.textContent=n.l10n.amPM[(0,d.int)(n.amPM.textContent===n.l10n.amPM[0])]),i.value=(0,d.pad)(f)}}(e);var i=n._input.value;h(),se(),n._input.value!==i&&n._debouncedChange()}function h(){if(void 0!==n.hourElement&&void 0!==n.minuteElement){var e,t,a=(parseInt(n.hourElement.value.slice(-2),10)||0)%24,i=(parseInt(n.minuteElement.value,10)||0)%60,o=void 0!==n.secondElement?(parseInt(n.secondElement.value,10)||0)%60:0;void 0!==n.amPM&&(e=a,t=n.amPM.textContent,a=e%12+12*(0,d.int)(t===n.l10n.amPM[1]));var r=void 0!==n.config.minTime||n.config.minDate&&n.minDateHasTime&&n.latestSelectedDateObj&&0===(0,f.compareDates)(n.latestSelectedDateObj,n.config.minDate,!0),l=void 0!==n.config.maxTime||n.config.maxDate&&n.maxDateHasTime&&n.latestSelectedDateObj&&0===(0,f.compareDates)(n.latestSelectedDateObj,n.config.maxDate,!0);if(void 0!==n.config.maxTime&&void 0!==n.config.minTime&&n.config.minTime>n.config.maxTime){var c=(0,f.calculateSecondsSinceMidnight)(n.config.minTime.getHours(),n.config.minTime.getMinutes(),n.config.minTime.getSeconds()),s=(0,f.calculateSecondsSinceMidnight)(n.config.maxTime.getHours(),n.config.maxTime.getMinutes(),n.config.maxTime.getSeconds()),u=(0,f.calculateSecondsSinceMidnight)(a,i,o);if(u>s&&u=12)]),void 0!==n.secondElement&&(n.secondElement.value=(0,d.pad)(a)))}function b(e){var t=(0,u.getEventTarget)(e),n=parseInt(t.value)+(e.delta||0);(n/1e3>1||\"Enter\"===e.key&&!/[^\\d]/.test(n.toString()))&&R(n)}function M(e,t,a,i){return t instanceof Array?t.forEach((function(t){return M(e,t,a,i)})):e instanceof Array?e.forEach((function(e){return M(e,t,a,i)})):(e.addEventListener(t,a,i),void n._handlers.push({remove:function(){return e.removeEventListener(t,a,i)}}))}function y(){ie(\"onChange\")}function w(e,t){var a=void 0!==e?n.parseDate(e):n.latestSelectedDateObj||(n.config.minDate&&n.config.minDate>n.now?n.config.minDate:n.config.maxDate&&n.config.maxDate=0&&(0,f.compareDates)(e,n.selectedDates[1])<=0)}(t)&&!re(t)&&r.classList.add(\"inRange\"),n.weekNumbers&&1===n.config.showMonths&&\"prevMonthDay\"!==e&&i%7==6&&n.weekNumbers.insertAdjacentHTML(\"beforeend\",\"\"+n.config.getWeek(t)+\"\"),ie(\"onDayCreate\",r),r}function T(e){e.focus(),\"range\"===n.config.mode&&J(e)}function _(e){for(var t=e>0?0:n.config.showMonths-1,a=e>0?n.config.showMonths:-1,i=t;i!=a;i+=e)for(var o=n.daysContainer.children[i],r=e>0?0:o.children.length-1,l=e>0?o.children.length:-1,c=r;c!=l;c+=e){var s=o.children[c];if(-1===s.className.indexOf(\"hidden\")&&W(s.dateObj))return s}}function I(e,t){var i=a(),o=B(i||document.body),r=void 0!==e?e:o?i:void 0!==n.selectedDateElem&&B(n.selectedDateElem)?n.selectedDateElem:void 0!==n.todayDateElem&&B(n.todayDateElem)?n.todayDateElem:_(t>0?1:-1);void 0===r?n._input.focus():o?function(e,t){for(var a=-1===e.className.indexOf(\"Month\")?e.dateObj.getMonth():n.currentMonth,i=t>0?n.config.showMonths:-1,o=t>0?1:-1,r=a-n.currentMonth;r!=i;r+=o)for(var l=n.daysContainer.children[r],c=a-n.currentMonth===r?e.$i+t:t<0?l.children.length-1:0,s=l.children.length,d=c;d>=0&&d0?s:-1);d+=o){var u=l.children[d];if(-1===u.className.indexOf(\"hidden\")&&W(u.dateObj)&&Math.abs(e.$i-d)>=Math.abs(t))return T(u)}n.changeMonth(o),I(_(o),0)}(r,t):T(r)}function S(e,t){for(var a=(new Date(e,t,1).getDay()-n.l10n.firstDayOfWeek+7)%7,i=n.utils.getDaysInMonth((t-1+12)%12,e),o=n.utils.getDaysInMonth(t,e),r=window.document.createDocumentFragment(),l=n.config.showMonths>1,c=l?\"prevMonthDay hidden\":\"prevMonthDay\",s=l?\"nextMonthDay hidden\":\"nextMonthDay\",d=i+1-a,f=0;d<=i;d++,f++)r.appendChild(k(\"flatpickr-day \"+c,new Date(e,t-1,d),0,f));for(d=1;d<=o;d++,f++)r.appendChild(k(\"flatpickr-day\",new Date(e,t,d),0,f));for(var m=o+1;m<=42-a&&(1===n.config.showMonths||f%7!=0);m++,f++)r.appendChild(k(\"flatpickr-day \"+s,new Date(e,t+1,m%o),0,f));var g=(0,u.createElement)(\"div\",\"dayContainer\");return g.appendChild(r),g}function O(){if(void 0!==n.daysContainer){(0,u.clearNode)(n.daysContainer),n.weekNumbers&&(0,u.clearNode)(n.weekNumbers);for(var e=document.createDocumentFragment(),t=0;t1||\"dropdown\"!==n.config.monthSelectorType)){var e=function(e){return!(void 0!==n.config.minDate&&n.currentYear===n.config.minDate.getFullYear()&&en.config.maxDate.getMonth())};n.monthsDropdownContainer.tabIndex=-1,n.monthsDropdownContainer.innerHTML=\"\";for(var t=0;t<12;t++)if(e(t)){var a=(0,u.createElement)(\"option\",\"flatpickr-monthDropdown-month\");a.value=new Date(n.currentYear,t).getMonth().toString(),a.textContent=(0,m.monthToStr)(t,n.config.shorthandCurrentMonth,n.l10n),a.tabIndex=-1,n.currentMonth===t&&(a.selected=!0),n.monthsDropdownContainer.appendChild(a)}}}function A(){var e,t=(0,u.createElement)(\"div\",\"flatpickr-month\"),a=window.document.createDocumentFragment();n.config.showMonths>1||\"static\"===n.config.monthSelectorType?e=(0,u.createElement)(\"span\",\"cur-month\"):(n.monthsDropdownContainer=(0,u.createElement)(\"select\",\"flatpickr-monthDropdown-months\"),n.monthsDropdownContainer.setAttribute(\"aria-label\",n.l10n.monthAriaLabel),M(n.monthsDropdownContainer,\"change\",(function(e){var t=(0,u.getEventTarget)(e),a=parseInt(t.value,10);n.changeMonth(a-n.currentMonth),ie(\"onMonthChange\")})),N(),e=n.monthsDropdownContainer);var i=(0,u.createNumberInput)(\"cur-year\",{tabindex:\"-1\"}),o=i.getElementsByTagName(\"input\")[0];o.setAttribute(\"aria-label\",n.l10n.yearAriaLabel),n.config.minDate&&o.setAttribute(\"min\",n.config.minDate.getFullYear().toString()),n.config.maxDate&&(o.setAttribute(\"max\",n.config.maxDate.getFullYear().toString()),o.disabled=!!n.config.minDate&&n.config.minDate.getFullYear()===n.config.maxDate.getFullYear());var r=(0,u.createElement)(\"div\",\"flatpickr-current-month\");return r.appendChild(e),r.appendChild(i),a.appendChild(r),t.appendChild(a),{container:t,yearElement:o,monthElement:e}}function P(){(0,u.clearNode)(n.monthNav),n.monthNav.appendChild(n.prevMonthNav),n.config.showMonths&&(n.yearElements=[],n.monthElements=[]);for(var e=n.config.showMonths;e--;){var t=A();n.yearElements.push(t.yearElement),n.monthElements.push(t.monthElement),n.monthNav.appendChild(t.container)}n.monthNav.appendChild(n.nextMonthNav)}function Y(){n.weekdayContainer?(0,u.clearNode)(n.weekdayContainer):n.weekdayContainer=(0,u.createElement)(\"div\",\"flatpickr-weekdays\");for(var e=n.config.showMonths;e--;){var t=(0,u.createElement)(\"div\",\"flatpickr-weekdaycontainer\");n.weekdayContainer.appendChild(t)}return F(),n.weekdayContainer}function F(){if(n.weekdayContainer){var e=n.l10n.firstDayOfWeek,t=l(n.l10n.weekdays.shorthand);e>0&&e\\n \"+t.join(\"\")+\"\\n \\n \"}}function j(e,t){void 0===t&&(t=!0);var a=t?e:e-n.currentMonth;a<0&&!0===n._hidePrevMonthArrow||a>0&&!0===n._hideNextMonthArrow||(n.currentMonth+=a,(n.currentMonth<0||n.currentMonth>11)&&(n.currentYear+=n.currentMonth>11?1:-1,n.currentMonth=(n.currentMonth+12)%12,ie(\"onYearChange\"),N()),O(),ie(\"onMonthChange\"),le())}function L(e){return n.calendarContainer.contains(e)}function H(e){if(n.isOpen&&!n.config.inline){var t=(0,u.getEventTarget)(e),a=L(t),i=!(t===n.input||t===n.altInput||n.element.contains(t)||e.path&&e.path.indexOf&&(~e.path.indexOf(n.input)||~e.path.indexOf(n.altInput)))&&!a&&!L(e.relatedTarget),o=!n.config.ignoredFocusElements.some((function(e){return e.contains(t)}));i&&o&&(n.config.allowInput&&n.setDate(n._input.value,!1,n.config.altInput?n.config.altFormat:n.config.dateFormat),void 0!==n.timeContainer&&void 0!==n.minuteElement&&void 0!==n.hourElement&&\"\"!==n.input.value&&void 0!==n.input.value&&p(),n.close(),n.config&&\"range\"===n.config.mode&&1===n.selectedDates.length&&n.clear(!1))}}function R(e){if(!(!e||n.config.minDate&&en.config.maxDate.getFullYear())){var t=e,a=n.currentYear!==t;n.currentYear=t||n.currentYear,n.config.maxDate&&n.currentYear===n.config.maxDate.getFullYear()?n.currentMonth=Math.min(n.config.maxDate.getMonth(),n.currentMonth):n.config.minDate&&n.currentYear===n.config.minDate.getFullYear()&&(n.currentMonth=Math.max(n.config.minDate.getMonth(),n.currentMonth)),a&&(n.redraw(),ie(\"onYearChange\"),N())}}function W(e,t){var a;void 0===t&&(t=!0);var i=n.parseDate(e,void 0,t);if(n.config.minDate&&i&&(0,f.compareDates)(i,n.config.minDate,void 0!==t?t:!n.minDateHasTime)<0||n.config.maxDate&&i&&(0,f.compareDates)(i,n.config.maxDate,void 0!==t?t:!n.maxDateHasTime)>0)return!1;if(!n.config.enable&&0===n.config.disable.length)return!0;if(void 0===i)return!1;for(var o=!!n.config.enable,r=null!==(a=n.config.enable)&&void 0!==a?a:n.config.disable,l=0,c=void 0;l=c.from.getTime()&&i.getTime()<=c.to.getTime())return o}return!o}function B(e){return void 0!==n.daysContainer&&(-1===e.className.indexOf(\"hidden\")&&-1===e.className.indexOf(\"flatpickr-disabled\")&&n.daysContainer.contains(e))}function K(e){var t=e.target===n._input,a=n._input.value.trimEnd()!==ce();!t||!a||e.relatedTarget&&L(e.relatedTarget)||n.setDate(n._input.value,!0,e.target===n.altInput?n.config.altFormat:n.config.dateFormat)}function q(t){var i=(0,u.getEventTarget)(t),o=n.config.wrap?e.contains(i):i===n._input,r=n.config.allowInput,l=n.isOpen&&(!r||!o),c=n.config.inline&&o&&!r;if(13===t.keyCode&&o){if(r)return n.setDate(n._input.value,!0,i===n.altInput?n.config.altFormat:n.config.dateFormat),n.close(),i.blur();n.open()}else if(L(i)||l||c){var s=!!n.timeContainer&&n.timeContainer.contains(i);switch(t.keyCode){case 13:s?(t.preventDefault(),p(),G()):Z(t);break;case 27:t.preventDefault(),G();break;case 8:case 46:o&&!n.config.allowInput&&(t.preventDefault(),n.clear());break;case 37:case 39:if(s||o)n.hourElement&&n.hourElement.focus();else{t.preventDefault();var d=a();if(void 0!==n.daysContainer&&(!1===r||d&&B(d))){var f=39===t.keyCode?1:-1;t.ctrlKey?(t.stopPropagation(),j(f),I(_(1),0)):I(void 0,f)}}break;case 38:case 40:t.preventDefault();var m=40===t.keyCode?1:-1;n.daysContainer&&void 0!==i.$i||i===n.input||i===n.altInput?t.ctrlKey?(t.stopPropagation(),R(n.currentYear-m),I(_(1),0)):s||I(void 0,7*m):i===n.currentYearElement?R(n.currentYear-m):n.config.enableTime&&(!s&&n.hourElement&&n.hourElement.focus(),p(t),n._debouncedChange());break;case 9:if(s){var g=[n.hourElement,n.minuteElement,n.secondElement,n.amPM].concat(n.pluginElements).filter((function(e){return e})),v=g.indexOf(i);if(-1!==v){var D=g[v+(t.shiftKey?-1:1)];t.preventDefault(),(D||n._input).focus()}}else!n.config.noCalendar&&n.daysContainer&&n.daysContainer.contains(i)&&t.shiftKey&&(t.preventDefault(),n._input.focus())}}if(void 0!==n.amPM&&i===n.amPM)switch(t.key){case n.l10n.amPM[0].charAt(0):case n.l10n.amPM[0].charAt(0).toLowerCase():n.amPM.textContent=n.l10n.amPM[0],h(),se();break;case n.l10n.amPM[1].charAt(0):case n.l10n.amPM[1].charAt(0).toLowerCase():n.amPM.textContent=n.l10n.amPM[1],h(),se()}(o||L(i))&&ie(\"onKeyDown\",t)}function J(e,t){if(void 0===t&&(t=\"flatpickr-day\"),1===n.selectedDates.length&&(!e||e.classList.contains(t)&&!e.classList.contains(\"flatpickr-disabled\"))){for(var a=e?e.dateObj.getTime():n.days.firstElementChild.dateObj.getTime(),i=n.parseDate(n.selectedDates[0],void 0,!0).getTime(),o=Math.min(a,n.selectedDates[0].getTime()),r=Math.max(a,n.selectedDates[0].getTime()),l=!1,c=0,s=0,d=o;do&&dc)?c=d:d>i&&(!s||d .\"+t)).forEach((function(t){var o=t.dateObj.getTime(),r=c>0&&o0&&o>s;if(r)return t.classList.add(\"notAllowed\"),void[\"inRange\",\"startRange\",\"endRange\"].forEach((function(e){t.classList.remove(e)}));l&&!r||([\"startRange\",\"inRange\",\"endRange\",\"notAllowed\"].forEach((function(e){t.classList.remove(e)})),void 0!==e&&(e.classList.add(a<=n.selectedDates[0].getTime()?\"startRange\":\"endRange\"),ia&&o===i&&t.classList.add(\"endRange\"),o>=c&&(0===s||o<=s)&&(0,f.isBetween)(o,i,a)&&t.classList.add(\"inRange\")))}))}}function U(){!n.isOpen||n.config.static||n.config.inline||X()}function $(e){return function(t){var a=n.config[\"_\"+e+\"Date\"]=n.parseDate(t,n.config.dateFormat),i=n.config[\"_\"+(\"min\"===e?\"max\":\"min\")+\"Date\"];void 0!==a&&(n[\"min\"===e?\"minDateHasTime\":\"maxDateHasTime\"]=a.getHours()>0||a.getMinutes()>0||a.getSeconds()>0),n.selectedDates&&(n.selectedDates=n.selectedDates.filter((function(e){return W(e)})),n.selectedDates.length||\"min\"!==e||D(a),se()),n.daysContainer&&(z(),void 0!==a?n.currentYearElement[e]=a.getFullYear().toString():n.currentYearElement.removeAttribute(e),n.currentYearElement.disabled=!!i&&void 0!==a&&i.getFullYear()===a.getFullYear())}}function Q(){return n.config.wrap?e.querySelector(\"[data-input]\"):e}function V(){\"object\"!=typeof n.config.locale&&void 0===v.l10ns[n.config.locale]&&n.config.errorHandler(new Error(\"flatpickr: invalid locale \"+n.config.locale)),n.l10n=r(r({},v.l10ns.default),\"object\"==typeof n.config.locale?n.config.locale:\"default\"!==n.config.locale?v.l10ns[n.config.locale]:void 0),m.tokenRegex.D=\"(\"+n.l10n.weekdays.shorthand.join(\"|\")+\")\",m.tokenRegex.l=\"(\"+n.l10n.weekdays.longhand.join(\"|\")+\")\",m.tokenRegex.M=\"(\"+n.l10n.months.shorthand.join(\"|\")+\")\",m.tokenRegex.F=\"(\"+n.l10n.months.longhand.join(\"|\")+\")\",m.tokenRegex.K=\"(\"+n.l10n.amPM[0]+\"|\"+n.l10n.amPM[1]+\"|\"+n.l10n.amPM[0].toLowerCase()+\"|\"+n.l10n.amPM[1].toLowerCase()+\")\",void 0===r(r({},t),JSON.parse(JSON.stringify(e.dataset||{}))).time_24hr&&void 0===v.defaultConfig.time_24hr&&(n.config.time_24hr=n.l10n.time_24hr),n.formatDate=(0,f.createDateFormatter)(n),n.parseDate=(0,f.createDateParser)({config:n.config,l10n:n.l10n})}function X(e){if(\"function\"!=typeof n.config.position){if(void 0!==n.calendarContainer){ie(\"onPreCalendarPosition\");var t=e||n._positionElement,a=Array.prototype.reduce.call(n.calendarContainer.children,(function(e,t){return e+t.offsetHeight}),0),i=n.calendarContainer.offsetWidth,o=n.config.position.split(\" \"),r=o[0],l=o.length>1?o[1]:null,c=t.getBoundingClientRect(),s=window.innerHeight-c.bottom,d=\"above\"===r||\"below\"!==r&&sa,f=window.pageYOffset+c.top+(d?-a-2:t.offsetHeight+2);if((0,u.toggleClass)(n.calendarContainer,\"arrowTop\",!d),(0,u.toggleClass)(n.calendarContainer,\"arrowBottom\",d),!n.config.inline){var m=window.pageXOffset+c.left,g=!1,p=!1;\"center\"===l?(m-=(i-c.width)/2,g=!0):\"right\"===l&&(m-=i-c.width,p=!0),(0,u.toggleClass)(n.calendarContainer,\"arrowLeft\",!g&&!p),(0,u.toggleClass)(n.calendarContainer,\"arrowCenter\",g),(0,u.toggleClass)(n.calendarContainer,\"arrowRight\",p);var h=window.document.body.offsetWidth-(window.pageXOffset+c.right),v=m+i>window.document.body.offsetWidth,D=h+i>window.document.body.offsetWidth;if((0,u.toggleClass)(n.calendarContainer,\"rightMost\",v),!n.config.static)if(n.calendarContainer.style.top=f+\"px\",v)if(D){var C=function(){for(var e=null,t=0;tn.currentMonth+n.config.showMonths-1)&&\"range\"!==n.config.mode;if(n.selectedDateElem=a,\"single\"===n.config.mode)n.selectedDates=[i];else if(\"multiple\"===n.config.mode){var r=re(i);r?n.selectedDates.splice(parseInt(r),1):n.selectedDates.push(i)}else\"range\"===n.config.mode&&(2===n.selectedDates.length&&n.clear(!1,!1),n.latestSelectedDateObj=i,n.selectedDates.push(i),0!==(0,f.compareDates)(i,n.selectedDates[0],!0)&&n.selectedDates.sort((function(e,t){return e.getTime()-t.getTime()})));if(h(),o){var l=n.currentYear!==i.getFullYear();n.currentYear=i.getFullYear(),n.currentMonth=i.getMonth(),l&&(ie(\"onYearChange\"),N()),ie(\"onMonthChange\")}if(le(),O(),se(),o||\"range\"===n.config.mode||1!==n.config.showMonths?void 0!==n.selectedDateElem&&void 0===n.hourElement&&n.selectedDateElem&&n.selectedDateElem.focus():T(a),void 0!==n.hourElement&&void 0!==n.hourElement&&n.hourElement.focus(),n.config.closeOnSelect){var c=\"single\"===n.config.mode&&!n.config.enableTime,s=\"range\"===n.config.mode&&2===n.selectedDates.length&&!n.config.enableTime;(c||s)&&G()}y()}}n.parseDate=(0,f.createDateParser)({config:n.config,l10n:n.l10n}),n._handlers=[],n.pluginElements=[],n.loadedPlugins=[],n._bind=M,n._setHoursFromDate=D,n._positionCalendar=X,n.changeMonth=j,n.changeYear=R,n.clear=function(e,t){void 0===e&&(e=!0);void 0===t&&(t=!0);n.input.value=\"\",void 0!==n.altInput&&(n.altInput.value=\"\");void 0!==n.mobileInput&&(n.mobileInput.value=\"\");n.selectedDates=[],n.latestSelectedDateObj=void 0,!0===t&&(n.currentYear=n._initialDate.getFullYear(),n.currentMonth=n._initialDate.getMonth());if(!0===n.config.enableTime){var a=(0,f.getDefaultHours)(n.config);C(a.hours,a.minutes,a.seconds)}n.redraw(),e&&ie(\"onChange\")},n.close=function(){n.isOpen=!1,n.isMobile||(void 0!==n.calendarContainer&&n.calendarContainer.classList.remove(\"open\"),void 0!==n._input&&n._input.classList.remove(\"active\"));ie(\"onClose\")},n.onMouseOver=J,n._createElement=u.createElement,n.createDay=k,n.destroy=function(){void 0!==n.config&&ie(\"onDestroy\");for(var e=n._handlers.length;e--;)n._handlers[e].remove();if(n._handlers=[],n.mobileInput)n.mobileInput.parentNode&&n.mobileInput.parentNode.removeChild(n.mobileInput),n.mobileInput=void 0;else if(n.calendarContainer&&n.calendarContainer.parentNode)if(n.config.static&&n.calendarContainer.parentNode){var t=n.calendarContainer.parentNode;if(t.lastChild&&t.removeChild(t.lastChild),t.parentNode){for(;t.firstChild;)t.parentNode.insertBefore(t.firstChild,t);t.parentNode.removeChild(t)}}else n.calendarContainer.parentNode.removeChild(n.calendarContainer);n.altInput&&(n.input.type=\"text\",n.altInput.parentNode&&n.altInput.parentNode.removeChild(n.altInput),delete n.altInput);n.input&&(n.input.type=n.input._type,n.input.classList.remove(\"flatpickr-input\"),n.input.removeAttribute(\"readonly\"));[\"_showTimeInput\",\"latestSelectedDateObj\",\"_hideNextMonthArrow\",\"_hidePrevMonthArrow\",\"__hideNextMonthArrow\",\"__hidePrevMonthArrow\",\"isMobile\",\"isOpen\",\"selectedDateElem\",\"minDateHasTime\",\"maxDateHasTime\",\"days\",\"daysContainer\",\"_input\",\"_positionElement\",\"innerContainer\",\"rContainer\",\"monthNav\",\"todayDateElem\",\"calendarContainer\",\"weekdayContainer\",\"prevMonthNav\",\"nextMonthNav\",\"monthsDropdownContainer\",\"currentMonthElement\",\"currentYearElement\",\"navigationCurrentMonth\",\"selectedDateElem\",\"config\"].forEach((function(e){try{delete n[e]}catch(e){}}))},n.isEnabled=W,n.jumpToDate=w,n.updateValue=se,n.open=function(e,t){void 0===t&&(t=n._positionElement);if(!0===n.isMobile){if(e){e.preventDefault();var a=(0,u.getEventTarget)(e);a&&a.blur()}return void 0!==n.mobileInput&&(n.mobileInput.focus(),n.mobileInput.click()),void ie(\"onOpen\")}if(n._input.disabled||n.config.inline)return;var i=n.isOpen;n.isOpen=!0,i||(n.calendarContainer.classList.add(\"open\"),n._input.classList.add(\"active\"),ie(\"onOpen\"),X(t));!0===n.config.enableTime&&!0===n.config.noCalendar&&(!1!==n.config.allowInput||void 0!==e&&n.timeContainer.contains(e.relatedTarget)||setTimeout((function(){return n.hourElement.select()}),50))},n.redraw=z,n.set=function(e,t){if(null!==e&&\"object\"==typeof e)for(var a in Object.assign(n.config,e),e)void 0!==ee[a]&&ee[a].forEach((function(e){return e()}));else n.config[e]=t,void 0!==ee[e]?ee[e].forEach((function(e){return e()})):c.HOOKS.indexOf(e)>-1&&(n.config[e]=(0,d.arrayify)(t));n.redraw(),se(!0)},n.setDate=function(e,t,a){void 0===t&&(t=!1);void 0===a&&(a=n.config.dateFormat);if(0!==e&&!e||e instanceof Array&&0===e.length)return n.clear(t);te(e,a),n.latestSelectedDateObj=n.selectedDates[n.selectedDates.length-1],n.redraw(),w(void 0,t),D(),0===n.selectedDates.length&&n.clear(!1);se(t),t&&ie(\"onChange\")},n.toggle=function(e){if(!0===n.isOpen)return n.close();n.open(e)};var ee={locale:[V,F],showMonths:[P,o,Y],minDate:[w],maxDate:[w],positionElement:[ae],clickOpens:[function(){!0===n.config.clickOpens?(M(n._input,\"focus\",n.open),M(n._input,\"click\",n.open)):(n._input.removeEventListener(\"focus\",n.open),n._input.removeEventListener(\"click\",n.open))}]};function te(e,t){var a=[];if(e instanceof Array)a=e.map((function(e){return n.parseDate(e,t)}));else if(e instanceof Date||\"number\"==typeof e)a=[n.parseDate(e,t)];else if(\"string\"==typeof e)switch(n.config.mode){case\"single\":case\"time\":a=[n.parseDate(e,t)];break;case\"multiple\":a=e.split(n.config.conjunction).map((function(e){return n.parseDate(e,t)}));break;case\"range\":a=e.split(n.l10n.rangeSeparator).map((function(e){return n.parseDate(e,t)}))}else n.config.errorHandler(new Error(\"Invalid date supplied: \"+JSON.stringify(e)));n.selectedDates=n.config.allowInvalidPreload?a:a.filter((function(e){return e instanceof Date&&W(e,!1)})),\"range\"===n.config.mode&&n.selectedDates.sort((function(e,t){return e.getTime()-t.getTime()}))}function ne(e){return e.slice().map((function(e){return\"string\"==typeof e||\"number\"==typeof e||e instanceof Date?n.parseDate(e,void 0,!0):e&&\"object\"==typeof e&&e.from&&e.to?{from:n.parseDate(e.from,void 0),to:n.parseDate(e.to,void 0)}:e})).filter((function(e){return e}))}function ae(){n._positionElement=n.config.positionElement||n._input}function ie(e,t){if(void 0!==n.config){var a=n.config[e];if(void 0!==a&&a.length>0)for(var i=0;a[i]&&i1||\"static\"===n.config.monthSelectorType?n.monthElements[t].textContent=(0,m.monthToStr)(a.getMonth(),n.config.shorthandCurrentMonth,n.l10n)+\" \":n.monthsDropdownContainer.value=a.getMonth().toString(),e.value=a.getFullYear().toString()})),n._hidePrevMonthArrow=void 0!==n.config.minDate&&(n.currentYear===n.config.minDate.getFullYear()?n.currentMonth<=n.config.minDate.getMonth():n.currentYearn.config.maxDate.getMonth():n.currentYear>n.config.maxDate.getFullYear()))}function ce(e){var t=e||(n.config.altInput?n.config.altFormat:n.config.dateFormat);return n.selectedDates.map((function(e){return n.formatDate(e,t)})).filter((function(e,t,a){return\"range\"!==n.config.mode||n.config.enableTime||a.indexOf(e)===t})).join(\"range\"!==n.config.mode?n.config.conjunction:n.l10n.rangeSeparator)}function se(e){void 0===e&&(e=!0),void 0!==n.mobileInput&&n.mobileFormatStr&&(n.mobileInput.value=void 0!==n.latestSelectedDateObj?n.formatDate(n.latestSelectedDateObj,n.mobileFormatStr):\"\"),n.input.value=ce(n.config.dateFormat),void 0!==n.altInput&&(n.altInput.value=ce(n.config.altFormat)),!1!==e&&ie(\"onValueUpdate\")}function de(e){var t=(0,u.getEventTarget)(e),a=n.prevMonthNav.contains(t),i=n.nextMonthNav.contains(t);a||i?j(a?-1:1):n.yearElements.indexOf(t)>=0?t.select():t.classList.contains(\"arrowUp\")?n.changeYear(n.currentYear+1):t.classList.contains(\"arrowDown\")&&n.changeYear(n.currentYear-1)}return function(){n.element=n.input=e,n.isOpen=!1,function(){var a=[\"wrap\",\"weekNumbers\",\"allowInput\",\"allowInvalidPreload\",\"clickOpens\",\"time_24hr\",\"enableTime\",\"noCalendar\",\"altInput\",\"shorthandCurrentMonth\",\"inline\",\"static\",\"enableSeconds\",\"disableMobile\"],o=r(r({},JSON.parse(JSON.stringify(e.dataset||{}))),t),l={};n.config.parseDate=o.parseDate,n.config.formatDate=o.formatDate,Object.defineProperty(n.config,\"enable\",{get:function(){return n.config._enable},set:function(e){n.config._enable=ne(e)}}),Object.defineProperty(n.config,\"disable\",{get:function(){return n.config._disable},set:function(e){n.config._disable=ne(e)}});var s=\"time\"===o.mode;if(!o.dateFormat&&(o.enableTime||s)){var u=v.defaultConfig.dateFormat||c.defaults.dateFormat;l.dateFormat=o.noCalendar||s?\"H:i\"+(o.enableSeconds?\":S\":\"\"):u+\" H:i\"+(o.enableSeconds?\":S\":\"\")}if(o.altInput&&(o.enableTime||s)&&!o.altFormat){var f=v.defaultConfig.altFormat||c.defaults.altFormat;l.altFormat=o.noCalendar||s?\"h:i\"+(o.enableSeconds?\":S K\":\" K\"):f+\" h:i\"+(o.enableSeconds?\":S\":\"\")+\" K\"}Object.defineProperty(n.config,\"minDate\",{get:function(){return n.config._minDate},set:$(\"min\")}),Object.defineProperty(n.config,\"maxDate\",{get:function(){return n.config._maxDate},set:$(\"max\")});var m=function(e){return function(t){n.config[\"min\"===e?\"_minTime\":\"_maxTime\"]=n.parseDate(t,\"H:i:S\")}};Object.defineProperty(n.config,\"minTime\",{get:function(){return n.config._minTime},set:m(\"min\")}),Object.defineProperty(n.config,\"maxTime\",{get:function(){return n.config._maxTime},set:m(\"max\")}),\"time\"===o.mode&&(n.config.noCalendar=!0,n.config.enableTime=!0);Object.assign(n.config,l,o);for(var g=0;g-1?n.config[h]=(0,d.arrayify)(p[h]).map(i).concat(n.config[h]):void 0===o[h]&&(n.config[h]=p[h])}o.altInputClass||(n.config.altInputClass=Q().className+\" \"+n.config.altInputClass);ie(\"onParseConfig\")}(),V(),function(){if(n.input=Q(),!n.input)return void n.config.errorHandler(new Error(\"Invalid input element specified\"));n.input._type=n.input.type,n.input.type=\"text\",n.input.classList.add(\"flatpickr-input\"),n._input=n.input,n.config.altInput&&(n.altInput=(0,u.createElement)(n.input.nodeName,n.config.altInputClass),n._input=n.altInput,n.altInput.placeholder=n.input.placeholder,n.altInput.disabled=n.input.disabled,n.altInput.required=n.input.required,n.altInput.tabIndex=n.input.tabIndex,n.altInput.type=\"text\",n.input.setAttribute(\"type\",\"hidden\"),!n.config.static&&n.input.parentNode&&n.input.parentNode.insertBefore(n.altInput,n.input.nextSibling));n.config.allowInput||n._input.setAttribute(\"readonly\",\"readonly\");ae()}(),function(){n.selectedDates=[],n.now=n.parseDate(n.config.now)||new Date;var e=n.config.defaultDate||(\"INPUT\"!==n.input.nodeName&&\"TEXTAREA\"!==n.input.nodeName||!n.input.placeholder||n.input.value!==n.input.placeholder?n.input.value:null);e&&te(e,n.config.dateFormat);n._initialDate=n.selectedDates.length>0?n.selectedDates[0]:n.config.minDate&&n.config.minDate.getTime()>n.now.getTime()?n.config.minDate:n.config.maxDate&&n.config.maxDate.getTime()0&&(n.latestSelectedDateObj=n.selectedDates[0]);void 0!==n.config.minTime&&(n.config.minTime=n.parseDate(n.config.minTime,\"H:i\"));void 0!==n.config.maxTime&&(n.config.maxTime=n.parseDate(n.config.maxTime,\"H:i\"));n.minDateHasTime=!!n.config.minDate&&(n.config.minDate.getHours()>0||n.config.minDate.getMinutes()>0||n.config.minDate.getSeconds()>0),n.maxDateHasTime=!!n.config.maxDate&&(n.config.maxDate.getHours()>0||n.config.maxDate.getMinutes()>0||n.config.maxDate.getSeconds()>0)}(),n.utils={getDaysInMonth:function(e,t){return void 0===e&&(e=n.currentMonth),void 0===t&&(t=n.currentYear),1===e&&(t%4==0&&t%100!=0||t%400==0)?29:n.l10n.daysInMonth[e]}},n.isMobile||function(){var e=window.document.createDocumentFragment();if(n.calendarContainer=(0,u.createElement)(\"div\",\"flatpickr-calendar\"),n.calendarContainer.tabIndex=-1,!n.config.noCalendar){if(e.appendChild((n.monthNav=(0,u.createElement)(\"div\",\"flatpickr-months\"),n.yearElements=[],n.monthElements=[],n.prevMonthNav=(0,u.createElement)(\"span\",\"flatpickr-prev-month\"),n.prevMonthNav.innerHTML=n.config.prevArrow,n.nextMonthNav=(0,u.createElement)(\"span\",\"flatpickr-next-month\"),n.nextMonthNav.innerHTML=n.config.nextArrow,P(),Object.defineProperty(n,\"_hidePrevMonthArrow\",{get:function(){return n.__hidePrevMonthArrow},set:function(e){n.__hidePrevMonthArrow!==e&&((0,u.toggleClass)(n.prevMonthNav,\"flatpickr-disabled\",e),n.__hidePrevMonthArrow=e)}}),Object.defineProperty(n,\"_hideNextMonthArrow\",{get:function(){return n.__hideNextMonthArrow},set:function(e){n.__hideNextMonthArrow!==e&&((0,u.toggleClass)(n.nextMonthNav,\"flatpickr-disabled\",e),n.__hideNextMonthArrow=e)}}),n.currentYearElement=n.yearElements[0],le(),n.monthNav)),n.innerContainer=(0,u.createElement)(\"div\",\"flatpickr-innerContainer\"),n.config.weekNumbers){var t=function(){n.calendarContainer.classList.add(\"hasWeeks\");var e=(0,u.createElement)(\"div\",\"flatpickr-weekwrapper\");e.appendChild((0,u.createElement)(\"span\",\"flatpickr-weekday\",n.l10n.weekAbbreviation));var t=(0,u.createElement)(\"div\",\"flatpickr-weeks\");return e.appendChild(t),{weekWrapper:e,weekNumbers:t}}(),a=t.weekWrapper,i=t.weekNumbers;n.innerContainer.appendChild(a),n.weekNumbers=i,n.weekWrapper=a}n.rContainer=(0,u.createElement)(\"div\",\"flatpickr-rContainer\"),n.rContainer.appendChild(Y()),n.daysContainer||(n.daysContainer=(0,u.createElement)(\"div\",\"flatpickr-days\"),n.daysContainer.tabIndex=-1),O(),n.rContainer.appendChild(n.daysContainer),n.innerContainer.appendChild(n.rContainer),e.appendChild(n.innerContainer)}n.config.enableTime&&e.appendChild(function(){n.calendarContainer.classList.add(\"hasTime\"),n.config.noCalendar&&n.calendarContainer.classList.add(\"noCalendar\");var e=(0,f.getDefaultHours)(n.config);n.timeContainer=(0,u.createElement)(\"div\",\"flatpickr-time\"),n.timeContainer.tabIndex=-1;var t=(0,u.createElement)(\"span\",\"flatpickr-time-separator\",\":\"),a=(0,u.createNumberInput)(\"flatpickr-hour\",{\"aria-label\":n.l10n.hourAriaLabel});n.hourElement=a.getElementsByTagName(\"input\")[0];var i=(0,u.createNumberInput)(\"flatpickr-minute\",{\"aria-label\":n.l10n.minuteAriaLabel});n.minuteElement=i.getElementsByTagName(\"input\")[0],n.hourElement.tabIndex=n.minuteElement.tabIndex=-1,n.hourElement.value=(0,d.pad)(n.latestSelectedDateObj?n.latestSelectedDateObj.getHours():n.config.time_24hr?e.hours:function(e){switch(e%24){case 0:case 12:return 12;default:return e%12}}(e.hours)),n.minuteElement.value=(0,d.pad)(n.latestSelectedDateObj?n.latestSelectedDateObj.getMinutes():e.minutes),n.hourElement.setAttribute(\"step\",n.config.hourIncrement.toString()),n.minuteElement.setAttribute(\"step\",n.config.minuteIncrement.toString()),n.hourElement.setAttribute(\"min\",n.config.time_24hr?\"0\":\"1\"),n.hourElement.setAttribute(\"max\",n.config.time_24hr?\"23\":\"12\"),n.hourElement.setAttribute(\"maxlength\",\"2\"),n.minuteElement.setAttribute(\"min\",\"0\"),n.minuteElement.setAttribute(\"max\",\"59\"),n.minuteElement.setAttribute(\"maxlength\",\"2\"),n.timeContainer.appendChild(a),n.timeContainer.appendChild(t),n.timeContainer.appendChild(i),n.config.time_24hr&&n.timeContainer.classList.add(\"time24hr\");if(n.config.enableSeconds){n.timeContainer.classList.add(\"hasSeconds\");var o=(0,u.createNumberInput)(\"flatpickr-second\");n.secondElement=o.getElementsByTagName(\"input\")[0],n.secondElement.value=(0,d.pad)(n.latestSelectedDateObj?n.latestSelectedDateObj.getSeconds():e.seconds),n.secondElement.setAttribute(\"step\",n.minuteElement.getAttribute(\"step\")),n.secondElement.setAttribute(\"min\",\"0\"),n.secondElement.setAttribute(\"max\",\"59\"),n.secondElement.setAttribute(\"maxlength\",\"2\"),n.timeContainer.appendChild((0,u.createElement)(\"span\",\"flatpickr-time-separator\",\":\")),n.timeContainer.appendChild(o)}n.config.time_24hr||(n.amPM=(0,u.createElement)(\"span\",\"flatpickr-am-pm\",n.l10n.amPM[(0,d.int)((n.latestSelectedDateObj?n.hourElement.value:n.config.defaultHour)>11)]),n.amPM.title=n.l10n.toggleTitle,n.amPM.tabIndex=-1,n.timeContainer.appendChild(n.amPM));return n.timeContainer}());(0,u.toggleClass)(n.calendarContainer,\"rangeMode\",\"range\"===n.config.mode),(0,u.toggleClass)(n.calendarContainer,\"animate\",!0===n.config.animate),(0,u.toggleClass)(n.calendarContainer,\"multiMonth\",n.config.showMonths>1),n.calendarContainer.appendChild(e);var o=void 0!==n.config.appendTo&&void 0!==n.config.appendTo.nodeType;if((n.config.inline||n.config.static)&&(n.calendarContainer.classList.add(n.config.inline?\"inline\":\"static\"),n.config.inline&&(!o&&n.element.parentNode?n.element.parentNode.insertBefore(n.calendarContainer,n._input.nextSibling):void 0!==n.config.appendTo&&n.config.appendTo.appendChild(n.calendarContainer)),n.config.static)){var r=(0,u.createElement)(\"div\",\"flatpickr-wrapper\");n.element.parentNode&&n.element.parentNode.insertBefore(r,n.element),r.appendChild(n.element),n.altInput&&r.appendChild(n.altInput),r.appendChild(n.calendarContainer)}n.config.static||n.config.inline||(void 0!==n.config.appendTo?n.config.appendTo:window.document.body).appendChild(n.calendarContainer)}(),function(){n.config.wrap&&[\"open\",\"close\",\"toggle\",\"clear\"].forEach((function(e){Array.prototype.forEach.call(n.element.querySelectorAll(\"[data-\"+e+\"]\"),(function(t){return M(t,\"click\",n[e])}))}));if(n.isMobile)return void function(){var e=n.config.enableTime?n.config.noCalendar?\"time\":\"datetime-local\":\"date\";n.mobileInput=(0,u.createElement)(\"input\",n.input.className+\" flatpickr-mobile\"),n.mobileInput.tabIndex=1,n.mobileInput.type=e,n.mobileInput.disabled=n.input.disabled,n.mobileInput.required=n.input.required,n.mobileInput.placeholder=n.input.placeholder,n.mobileFormatStr=\"datetime-local\"===e?\"Y-m-d\\\\TH:i:S\":\"date\"===e?\"Y-m-d\":\"H:i:S\",n.selectedDates.length>0&&(n.mobileInput.defaultValue=n.mobileInput.value=n.formatDate(n.selectedDates[0],n.mobileFormatStr));n.config.minDate&&(n.mobileInput.min=n.formatDate(n.config.minDate,\"Y-m-d\"));n.config.maxDate&&(n.mobileInput.max=n.formatDate(n.config.maxDate,\"Y-m-d\"));n.input.getAttribute(\"step\")&&(n.mobileInput.step=String(n.input.getAttribute(\"step\")));n.input.type=\"hidden\",void 0!==n.altInput&&(n.altInput.type=\"hidden\");try{n.input.parentNode&&n.input.parentNode.insertBefore(n.mobileInput,n.input.nextSibling)}catch(e){}M(n.mobileInput,\"change\",(function(e){n.setDate((0,u.getEventTarget)(e).value,!1,n.mobileFormatStr),ie(\"onChange\"),ie(\"onClose\")}))}();var e=(0,d.debounce)(U,50);n._debouncedChange=(0,d.debounce)(y,g),n.daysContainer&&!/iPhone|iPad|iPod/i.test(navigator.userAgent)&&M(n.daysContainer,\"mouseover\",(function(e){\"range\"===n.config.mode&&J((0,u.getEventTarget)(e))}));M(n._input,\"keydown\",q),void 0!==n.calendarContainer&&M(n.calendarContainer,\"keydown\",q);n.config.inline||n.config.static||M(window,\"resize\",e);void 0!==window.ontouchstart?M(window.document,\"touchstart\",H):M(window.document,\"mousedown\",H);M(window.document,\"focus\",H,{capture:!0}),!0===n.config.clickOpens&&(M(n._input,\"focus\",n.open),M(n._input,\"click\",n.open));void 0!==n.daysContainer&&(M(n.monthNav,\"click\",de),M(n.monthNav,[\"keyup\",\"increment\"],b),M(n.daysContainer,\"click\",Z));if(void 0!==n.timeContainer&&void 0!==n.minuteElement&&void 0!==n.hourElement){var t=function(e){return(0,u.getEventTarget)(e).select()};M(n.timeContainer,[\"increment\"],p),M(n.timeContainer,\"blur\",p,{capture:!0}),M(n.timeContainer,\"click\",E),M([n.hourElement,n.minuteElement],[\"focus\",\"click\"],t),void 0!==n.secondElement&&M(n.secondElement,\"focus\",(function(){return n.secondElement&&n.secondElement.select()})),void 0!==n.amPM&&M(n.amPM,\"click\",(function(e){p(e)}))}n.config.allowInput&&M(n._input,\"blur\",K)}(),(n.selectedDates.length||n.config.noCalendar)&&(n.config.enableTime&&D(n.config.noCalendar?n.latestSelectedDateObj:void 0),se(!1)),o();var a=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);!n.isMobile&&a&&X(),ie(\"onReady\")}(),n}function h(e,t){for(var n=Array.prototype.slice.call(e).filter((function(e){return e instanceof HTMLElement})),a=[],i=0;i\",noCalendar:!1,now:new Date,onChange:[],onClose:[],onDayCreate:[],onDestroy:[],onKeyDown:[],onMonthChange:[],onOpen:[],onParseConfig:[],onReady:[],onValueUpdate:[],onYearChange:[],onPreCalendarPosition:[],plugins:[],position:\"auto\",positionElement:void 0,prevArrow:\"\",shorthandCurrentMonth:!1,showMonths:1,static:!1,time_24hr:!1,weekNumbers:!1,wrap:!1}},\n 615: function _(e,r,a,n,t){n(),a.english={weekdays:{shorthand:[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],longhand:[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"]},months:{shorthand:[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],longhand:[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"]},daysInMonth:[31,28,31,30,31,30,31,31,30,31,30,31],firstDayOfWeek:0,ordinal:function(e){var r=e%100;if(r>3&&r<21)return\"th\";switch(r%10){case 1:return\"st\";case 2:return\"nd\";case 3:return\"rd\";default:return\"th\"}},rangeSeparator:\" to \",weekAbbreviation:\"Wk\",scrollTitle:\"Scroll to increment\",toggleTitle:\"Click to toggle\",amPM:[\"AM\",\"PM\"],yearAriaLabel:\"Year\",monthAriaLabel:\"Month\",hourAriaLabel:\"Hour\",minuteAriaLabel:\"Minute\",time_24hr:!1},a.default=a.english},\n 616: function _(n,t,r,i,u){i();r.pad=function(n,t){return void 0===t&&(t=2),(\"000\"+n).slice(-1*t)};r.int=function(n){return!0===n?1:0},r.debounce=function(n,t){var r;return function(){var i=this,u=arguments;clearTimeout(r),r=setTimeout((function(){return n.apply(i,u)}),t)}};r.arrayify=function(n){return n instanceof Array?n:[n]}},\n 617: function _(t,e,n,r,a){function i(t,e,n){var r=window.document.createElement(t);return e=e||\"\",n=n||\"\",r.className=e,void 0!==n&&(r.textContent=n),r}r(),n.toggleClass=function(t,e,n){if(!0===n)return t.classList.add(e);t.classList.remove(e)},n.createElement=i,n.clearNode=function(t){for(;t.firstChild;)t.removeChild(t.firstChild)},n.findParent=function t(e,n){return n(e)?e:e.parentNode?t(e.parentNode,n):void 0},n.createNumberInput=function(t,e){var n=i(\"div\",\"numInputWrapper\"),r=i(\"input\",\"numInput \"+t),a=i(\"span\",\"arrowUp\"),o=i(\"span\",\"arrowDown\");if(-1===navigator.userAgent.indexOf(\"MSIE 9.0\")?r.type=\"number\":(r.type=\"text\",r.pattern=\"\\\\d*\"),void 0!==e)for(var d in e)r.setAttribute(d,e[d]);return n.appendChild(r),n.appendChild(a),n.appendChild(o),n},n.getEventTarget=function(t){try{return\"function\"==typeof t.composedPath?t.composedPath()[0]:t.target}catch(e){return t.target}}},\n 618: function _(e,t,n,a,r){a();const i=e(619),o=e(614),s=e(615);n.createDateFormatter=function(e){var t=e.config,n=void 0===t?o.defaults:t,a=e.l10n,r=void 0===a?s.english:a,u=e.isMobile,f=void 0!==u&&u;return function(e,t,a){var o=a||r;return void 0===n.formatDate||f?t.split(\"\").map((function(t,a,r){return i.formats[t]&&\"\\\\\"!==r[a-1]?i.formats[t](e,o,n):\"\\\\\"!==t?t:\"\"})).join(\"\"):n.formatDate(e,t,o)}};n.createDateParser=function(e){var t=e.config,n=void 0===t?o.defaults:t,a=e.l10n,r=void 0===a?s.english:a;return function(e,t,a,s){if(0===e||e){var u,f=s||r,d=e;if(e instanceof Date)u=new Date(e.getTime());else if(\"string\"!=typeof e&&void 0!==e.toFixed)u=new Date(e);else if(\"string\"==typeof e){var c=t||(n||o.defaults).dateFormat,g=String(e).trim();if(\"today\"===g)u=new Date,a=!0;else if(n&&n.parseDate)u=n.parseDate(e,c);else if(/Z$/.test(g)||/GMT$/.test(g))u=new Date(e);else{for(var m=void 0,l=[],v=0,D=0,h=\"\";vMath.min(t,n)&&e=12?12:0)+parseFloat(n))},H:function(t,n){t.setHours(parseFloat(n))},J:function(t,n){t.setDate(parseFloat(n))},K:function(t,n,e){t.setHours(t.getHours()%12+12*(0,u.int)(new RegExp(e.amPM[1],\"i\").test(n)))},M:function(t,n,e){t.setMonth(e.months.shorthand.indexOf(n))},S:function(t,n){t.setSeconds(parseFloat(n))},U:function(t,n){return new Date(1e3*parseFloat(n))},W:function(t,n,e){var o=parseInt(n),r=new Date(t.getFullYear(),0,2+7*(o-1),0,0,0,0);return r.setDate(r.getDate()-r.getDay()+e.firstDayOfWeek),r},Y:function(t,n){t.setFullYear(parseFloat(n))},Z:function(t,n){return new Date(n)},d:function(t,n){t.setDate(parseFloat(n))},h:function(t,n){t.setHours((t.getHours()>=12?12:0)+parseFloat(n))},i:function(t,n){t.setMinutes(parseFloat(n))},j:function(t,n){t.setDate(parseFloat(n))},l:a,m:function(t,n){t.setMonth(parseFloat(n)-1)},n:function(t,n){t.setMonth(parseFloat(n)-1)},s:function(t,n){t.setSeconds(parseFloat(n))},u:function(t,n){return new Date(parseFloat(n))},w:a,y:function(t,n){t.setFullYear(2e3+parseFloat(n))}},e.tokenRegex={D:\"\",F:\"\",G:\"(\\\\d\\\\d|\\\\d)\",H:\"(\\\\d\\\\d|\\\\d)\",J:\"(\\\\d\\\\d|\\\\d)\\\\w+\",K:\"\",M:\"\",S:\"(\\\\d\\\\d|\\\\d)\",U:\"(.+)\",W:\"(\\\\d\\\\d|\\\\d)\",Y:\"(\\\\d{4})\",Z:\"(.+)\",d:\"(\\\\d\\\\d|\\\\d)\",h:\"(\\\\d\\\\d|\\\\d)\",i:\"(\\\\d\\\\d|\\\\d)\",j:\"(\\\\d\\\\d|\\\\d)\",l:\"\",m:\"(\\\\d\\\\d|\\\\d)\",n:\"(\\\\d\\\\d|\\\\d)\",s:\"(\\\\d\\\\d|\\\\d)\",u:\"(.+)\",w:\"(\\\\d\\\\d|\\\\d)\",y:\"(\\\\d{2})\"},e.formats={Z:function(t){return t.toISOString()},D:function(t,n,o){return n.weekdays.shorthand[e.formats.w(t,n,o)]},F:function(t,n,o){return(0,e.monthToStr)(e.formats.n(t,n,o)-1,!1,n)},G:function(t,n,o){return(0,u.pad)(e.formats.h(t,n,o))},H:function(t){return(0,u.pad)(t.getHours())},J:function(t,n){return void 0!==n.ordinal?t.getDate()+n.ordinal(t.getDate()):t.getDate()},K:function(t,n){return n.amPM[(0,u.int)(t.getHours()>11)]},M:function(t,n){return(0,e.monthToStr)(t.getMonth(),!0,n)},S:function(t){return(0,u.pad)(t.getSeconds())},U:function(t){return t.getTime()/1e3},W:function(t,n,e){return e.getWeek(t)},Y:function(t){return(0,u.pad)(t.getFullYear(),4)},d:function(t){return(0,u.pad)(t.getDate())},h:function(t){return t.getHours()%12?t.getHours()%12:12},i:function(t){return(0,u.pad)(t.getMinutes())},j:function(t){return t.getDate()},l:function(t,n){return n.weekdays.longhand[t.getDay()]},m:function(t){return(0,u.pad)(t.getMonth()+1)},n:function(t){return t.getMonth()+1},s:function(t){return t.getSeconds()},u:function(t){return t.getTime()},w:function(t){return t.getDay()},y:function(t){return String(t.getFullYear()).substring(2)}}},\n 620: function _(n,t,o,r,e){\"function\"!=typeof Object.assign&&(Object.assign=function(n){for(var t=[],o=1;o({value:[a(e(r.DateLike,r.DateLike)),null]})))},\n 623: function _(e,t,i,a,n){var r;a();const s=e(624),l=e(611),c=e(12);class _ extends s.BaseDatetimePickerView{get flatpickr_options(){return{...super.flatpickr_options,mode:\"single\"}}_on_change(e){(0,c.assert)(e.length<=1),this.model.value=(()=>{if(0==e.length)return null;{const[t]=e;return this._format_date(t)}})()}}i.DatetimePickerView=_,_.__name__=\"DatetimePickerView\";class o extends s.BaseDatetimePicker{constructor(e){super(e)}}i.DatetimePicker=o,r=o,o.__name__=\"DatetimePicker\",r.prototype.default_view=_,r.define((({Nullable:e})=>({value:[e(l.DateLike),null]})))},\n 624: function _(e,t,n,c,i){var s;c();const r=e(611),o=e(20);class m extends r.BaseDatePickerView{connect_signals(){super.connect_signals();const{value:e,hour_increment:t,minute_increment:n,second_increment:c,seconds:i,clock:s}=this.model.properties;this.connect(e.change,(()=>{const{value:e}=this.model;null!=e?this.picker.setDate(e):this.picker.clear()})),this.connect(t.change,(()=>this.picker.set(\"hourIncrement\",this.model.hour_increment))),this.connect(n.change,(()=>this.picker.set(\"minuteIncrement\",this.model.minute_increment))),this.connect(c.change,(()=>this._update_second_increment())),this.connect(i.change,(()=>this.picker.set(\"enableSeconds\",this.model.seconds))),this.connect(s.change,(()=>this.picker.set(\"time_24hr\",\"24h\"==this.model.clock)))}get flatpickr_options(){const{hour_increment:e,minute_increment:t,seconds:n,clock:c}=this.model,i=super.flatpickr_options;return i.enableTime=!0,i.dateFormat=\"Y-m-dTH:i:S\",i.hourIncrement=e,i.minuteIncrement=t,i.enableSeconds=n,i.time_24hr=\"24h\"==c,i}render(){super.render(),this._update_second_increment()}_update_second_increment(){const{second_increment:e}=this.model;this.picker.secondElement?.setAttribute(\"step\",e.toString())}}n.BaseDatetimePickerView=m,m.__name__=\"BaseDatetimePickerView\";class a extends r.BaseDatePicker{constructor(e){super(e)}}n.BaseDatetimePicker=a,s=a,a.__name__=\"BaseDatetimePicker\",s.define((({Bool:e,Positive:t,Int:n})=>({hour_increment:[t(n),1],minute_increment:[t(n),1],second_increment:[t(n),1],seconds:[e,!1],clock:[o.Clock,\"24h\"]}))),s.override({date_format:\"Y-m-d H:i\"})},\n 625: function _(e,t,a,i,n){var s;i();const r=e(624),l=e(611),c=e(12);class _ extends r.BaseDatetimePickerView{get flatpickr_options(){return{...super.flatpickr_options,mode:\"range\"}}_on_change(e){switch(e.length){case 0:this.model.value=null;break;case 1:break;case 2:{const[t,a]=e,i=this._format_date(t),n=this._format_date(a);this.model.value=[i,n];break}default:(0,c.assert)(!1,\"invalid length\")}}}a.DatetimeRangePickerView=_,_.__name__=\"DatetimeRangePickerView\";class o extends r.BaseDatetimePicker{constructor(e){super(e)}}a.DatetimeRangePicker=o,s=o,o.__name__=\"DatetimeRangePicker\",s.prototype.default_view=_,s.define((({Nullable:e,Tuple:t})=>({value:[e(t(l.DateLike,l.DateLike)),null]})))},\n 626: function _(e,t,s,i,r){var _;i();const n=e(627);class a extends n.MarkupView{render(){super.render(),this.model.render_as_text?this.markup_el.textContent=this.model.text:this.markup_el.innerHTML=this.has_math_disabled()?this.model.text:this.process_tex(this.model.text)}}s.DivView=a,a.__name__=\"DivView\";class d extends n.Markup{constructor(e){super(e)}}s.Div=d,_=d,d.__name__=\"Div\",_.prototype.default_view=a,_.define((({Bool:e})=>({render_as_text:[e,!1]})))},\n 627: function _(t,e,s,i,r){var a;i();const n=t(1),d=t(63),h=t(697),o=n.__importStar(t(628));class _ extends h.WidgetView{constructor(){super(...arguments),this._auto_width=\"fit-content\",this._auto_height=\"auto\"}async lazy_initialize(){await super.lazy_initialize(),\"not_started\"!=this.provider.status&&\"loading\"!=this.provider.status||this.provider.ready.connect((()=>{this.contains_tex_string(this.model.text)&&this.rerender()}))}has_math_disabled(){return this.model.disable_math||!this.contains_tex_string(this.model.text)}rerender(){this.render()}connect_signals(){super.connect_signals(),this.connect(this.model.change,(()=>{this.rerender()}))}stylesheets(){return[...super.stylesheets(),o.default,\"p { margin: 0; }\"]}render(){super.render(),this.markup_el=(0,d.div)({class:o.clearfix,style:{display:\"inline-block\"}}),this.shadow_el.appendChild(this.markup_el),\"failed\"!=this.provider.status&&\"loaded\"!=this.provider.status||(this._has_finished=!0)}}s.MarkupView=_,_.__name__=\"MarkupView\";class l extends h.Widget{constructor(t){super(t)}}s.Markup=l,a=l,l.__name__=\"Markup\",a.define((({Bool:t,Str:e})=>({text:[e,\"\"],disable_math:[t,!1]})))},\n 628: function _(e,a,f,l,r){l(),f.clearfix=\"bk-clearfix\",f.default='.bk-clearfix:before,.bk-clearfix:after{content:\"\";display:table;}.bk-clearfix:after{clear:both;}'},\n 629: function _(e,t,i,s,n){var o;s();const l=e(1),_=e(590),r=e(53),d=e(63),u=e(8),c=e(50),h=l.__importStar(e(593)),m=l.__importStar(e(599)),p=m,a=l.__importStar(e(630)),g=a;class w extends _.AbstractButtonView{constructor(){super(...arguments),this._open=!1}stylesheets(){return[...super.stylesheets(),m.default,a.default]}connect_signals(){super.connect_signals();const{menu:e}=this.model.properties;this.on_change(e,(()=>this.rebuild_menu()))}render(){super.render();const e=(0,d.div)({class:[g.caret,g.down]});if(this.model.is_split){const t=this._render_button(e);t.classList.add(h.dropdown_toggle),t.addEventListener(\"click\",(()=>this._toggle_menu())),this.group_el.append(t)}else this.button_el.append(e);this.menu_el=(0,d.div)({class:[p.menu,p.below]}),this.shadow_el.append(this.menu_el),this.rebuild_menu(),(0,d.undisplay)(this.menu_el)}_show_menu(){if(!this._open){this._open=!0,(0,d.display)(this.menu_el);const e=t=>{t.composedPath().includes(this.el)||(document.removeEventListener(\"click\",e),this._hide_menu())};document.addEventListener(\"click\",e)}}_hide_menu(){this._open&&(this._open=!1,(0,d.undisplay)(this.menu_el))}_toggle_menu(){this._open?this._hide_menu():this._show_menu()}click(){this.model.is_split?(this._hide_menu(),this.model.trigger_event(new r.ButtonClick),super.click()):this._toggle_menu()}_item_click(e){this._hide_menu();const t=this.model.menu[e];if(null!=t){const i=(0,u.isString)(t)?t:t[1];(0,u.isString)(i)?this.model.trigger_event(new r.MenuItemClick(i)):(0,c.execute)(i,this.model,{index:e})}}rebuild_menu(){(0,d.empty)(this.menu_el);const e=this.model.menu.map(((e,t)=>{if(null==e)return(0,d.div)({class:p.divider});{const i=(0,u.isString)(e)?e:e[0],s=(0,d.div)(i);return s.addEventListener(\"click\",(()=>this._item_click(t))),s}}));this.menu_el.append(...e)}}i.DropdownView=w,w.__name__=\"DropdownView\";class v extends _.AbstractButton{constructor(e){super(e)}get is_split(){return this.split}}i.Dropdown=v,o=v,v.__name__=\"Dropdown\",o.prototype.default_view=w,o.define((({Null:e,Bool:t,Str:i,List:s,Tuple:n,Or:o})=>({split:[t,!1],menu:[s(o(i,n(i,o(i)),e)),[]]}))),o.override({label:\"Dropdown\"})},\n 630: function _(t,r,e,a,d){a(),e.caret=\"bk-caret\",e.down=\"bk-down\",e.up=\"bk-up\",e.left=\"bk-left\",e.right=\"bk-right\",e.default=\":host{--caret-width:4px;}.bk-caret{display:inline-block;vertical-align:middle;width:0;height:0;margin:0 5px;}.bk-caret.bk-down{border-top:var(--caret-width) solid;}.bk-caret.bk-up{border-bottom:var(--caret-width) solid;}.bk-caret.bk-down,.bk-caret.bk-up{border-right:var(--caret-width) solid transparent;border-left:var(--caret-width) solid transparent;}.bk-caret.bk-left{border-right:var(--caret-width) solid;}.bk-caret.bk-right{border-left:var(--caret-width) solid;}.bk-caret.bk-left,.bk-caret.bk-right{border-top:var(--caret-width) solid transparent;border-bottom:var(--caret-width) solid transparent;}\"},\n 631: function _(e,t,n,i,s){var l;i();const r=e(1),a=e(597),o=e(63),u=e(8),p=r.__importStar(e(18)),d=r.__importStar(e(598)),c=r.__importDefault(e(593));class _ extends a.InputWidgetView{connect_signals(){super.connect_signals(),this.model.on_event(a.ClearInput,(()=>{this.model.setv({value:\"\",mime_type:\"\",filename:\"\"}),this.input_el.value=\"\"}))}stylesheets(){return[...super.stylesheets(),c.default]}_render_input(){const{multiple:e,disabled:t,directory:n}=this.model,i=(()=>{const{accept:e}=this.model;return(0,u.isString)(e)?e:e.join(\",\")})();return this.input_el=(0,o.input)({type:\"file\",class:d.input,multiple:e,accept:i,disabled:t,webkitdirectory:n})}render(){super.render(),this.input_el.addEventListener(\"change\",(async()=>{const{files:e}=this.input_el;null!=e&&await this.load_files(e)}))}async load_files(e){const t=[],n=[],i=[],{directory:s,multiple:l}=this.model,r=(()=>{const{accept:e}=this.model;return(0,u.isString)(e)?e:e.join(\",\")})();for(const l of e){const e=await this._read_file(l),[,a=\"\",,o=\"\"]=e.split(/[:;,]/,4);if(s){const e=l.name.split(\".\").pop();r.length>0&&(0,u.isString)(e)&&!r.includes(`.${e}`)||(n.push(l.webkitRelativePath),t.push(o),i.push(a))}else n.push(l.name),t.push(o),i.push(a)}const[a,o,p]=s||l?[t,n,i]:0!=e.length?[t[0],n[0],i[0]]:[\"\",\"\",\"\"];this.model.setv({value:a,filename:o,mime_type:p})}_read_file(e){return new Promise(((t,n)=>{const i=new FileReader;i.onload=()=>{const{result:s}=i;null!=s?t(s):n(i.error??new Error(`unable to read '${e.name}'`))},i.readAsDataURL(e)}))}}n.FileInputView=_,_.__name__=\"FileInputView\";class m extends a.InputWidget{constructor(e){super(e)}}n.FileInput=m,l=m,m.__name__=\"FileInput\",l.prototype.default_view=_,l.define((({Bool:e,Str:t,List:n,Or:i})=>({value:[i(t,n(t)),p.unset,{readonly:!0}],mime_type:[i(t,n(t)),p.unset,{readonly:!0}],filename:[i(t,n(t)),p.unset,{readonly:!0}],accept:[i(t,n(t)),\"\"],multiple:[e,!1],directory:[e,!1]})))},\n 632: function _(e,t,i,o,n){var s;o();const l=e(590),d=e(413),r=e(479),a=e(56);class p extends l.AbstractButtonView{*children(){yield*super.children(),yield this.tooltip}async lazy_initialize(){await super.lazy_initialize();const{tooltip:e}=this.model;this.tooltip=await(0,a.build_view)(e,{parent:this})}remove(){this.tooltip.remove(),super.remove()}render(){super.render();let e=!1;const t=t=>{this.tooltip.model.setv({visible:t,closable:e})};this.on_change(this.tooltip.model.properties.visible,(()=>{const{visible:i}=this.tooltip.model;i||(e=!1),t(i)})),this.el.addEventListener(\"mouseenter\",(()=>{t(!0)})),this.el.addEventListener(\"mouseleave\",(()=>{e||t(!1)})),document.addEventListener(\"mousedown\",(i=>{const o=i.composedPath();o.includes(this.tooltip.el)||(o.includes(this.el)?(e=!e,t(e)):(e=!1,t(!1)))})),window.addEventListener(\"blur\",(()=>{e=!1,t(!1)}))}}i.HelpButtonView=p,p.__name__=\"HelpButtonView\";class u extends l.AbstractButton{constructor(e){super(e)}}i.HelpButton=u,s=u,u.__name__=\"HelpButton\",s.prototype.default_view=p,s.define((({Ref:e})=>({tooltip:[e(d.Tooltip)]}))),s.override({label:\"\",icon:()=>new r.BuiltinIcon({icon_name:\"help\",size:18}),button_type:\"default\"})},\n 633: function _(e,t,i,s,o){var l;s();const n=e(1),u=n.__importDefault(e(634)),_=e(63),r=e(8),a=e(26),c=n.__importStar(e(598)),h=n.__importDefault(e(635)),d=e(597);function p(e){return Object.defineProperty(e,\"target\",{get:()=>e.composedPath()[0]??null,configurable:!0}),e}class m extends u.default{_onFocus(e){super._onFocus(p(e))}_onBlur(e){super._onBlur(p(e))}_onKeyUp(e){super._onKeyUp(p(e))}_onKeyDown(e){super._onKeyDown(p(e))}_onClick(e){super._onClick(p(e))}_onTouchEnd(e){super._onTouchEnd(p(e))}_onMouseDown(e){super._onMouseDown(p(e))}_onMouseOver(e){super._onMouseOver(p(e))}}m.__name__=\"OurChoices\";class g extends d.InputWidgetView{connect_signals(){super.connect_signals(),this.connect(this.model.properties.disabled.change,(()=>this.set_disabled()));const{value:e,max_items:t,option_limit:i,search_option_limit:s,delete_button:o,placeholder:l,options:n,name:u,title:_}=this.model.properties;this.on_change([t,i,s,o,l,n,u,_],(()=>this.render())),this.on_change(e,(()=>{(0,a.is_equal)(this.model.value,this._current_values)||this.render()}))}stylesheets(){return[...super.stylesheets(),h.default]}_render_input(){return this.input_el=(0,_.select)({multiple:!0,class:c.input,name:this.model.name,disabled:this.model.disabled})}render(){super.render();const e=new Set(this.model.value),t=this.model.options.map((t=>{let i,s;return(0,r.isString)(t)?i=s=t:[i,s]=t,{value:i,label:s,selected:e.has(i)}})),i=this.model.solid?\"solid\":\"light\",s=`choices__item ${i}`,o=`choices__button ${i}`,l={choices:t,itemSelectText:\"\",duplicateItemsAllowed:!1,shouldSort:!1,removeItemButton:this.model.delete_button,classNames:{item:s,button:o},placeholderValue:this.model.placeholder,maxItemCount:this.model.max_items??-1,renderChoiceLimit:this.model.option_limit??-1,searchResultLimit:this.model.search_option_limit??4};this.choice_el=new m(this.input_el,l),this.input_el.addEventListener(\"change\",(()=>this.change_input()))}set_disabled(){this.model.disabled?this.choice_el.disable():this.choice_el.enable()}get _current_values(){return this.choice_el.getValue().map((e=>e.value))}change_input(){this.model.value=this._current_values,super.change_input()}}i.MultiChoiceView=g,g.__name__=\"MultiChoiceView\";class b extends d.InputWidget{constructor(e){super(e)}}i.MultiChoice=b,l=b,b.__name__=\"MultiChoice\",l.prototype.default_view=g,l.define((({Bool:e,Int:t,Str:i,List:s,Tuple:o,Or:l,Nullable:n})=>({value:[s(i),[]],options:[s(l(i,o(i,i))),[]],max_items:[n(t),null],delete_button:[e,!0],placeholder:[n(i),null],option_limit:[n(t),null],search_option_limit:[n(t),null],solid:[e,!0]})))},\n 634: function _(e,t,i,n,r){\n /*! choices.js v10.2.0 | © 2022 Josh Johnson | https://github.com/jshjohnson/Choices#readme */\n var s,o;s=window,o=function(){return function(){\"use strict\";var e={282:function(e,t,i){Object.defineProperty(t,\"__esModule\",{value:!0}),t.clearChoices=t.activateChoices=t.filterChoices=t.addChoice=void 0;var n=i(883);t.addChoice=function(e){var t=e.value,i=e.label,r=e.id,s=e.groupId,o=e.disabled,a=e.elementId,c=e.customProperties,l=e.placeholder,h=e.keyCode;return{type:n.ACTION_TYPES.ADD_CHOICE,value:t,label:i,id:r,groupId:s,disabled:o,elementId:a,customProperties:c,placeholder:l,keyCode:h}},t.filterChoices=function(e){return{type:n.ACTION_TYPES.FILTER_CHOICES,results:e}},t.activateChoices=function(e){return void 0===e&&(e=!0),{type:n.ACTION_TYPES.ACTIVATE_CHOICES,active:e}},t.clearChoices=function(){return{type:n.ACTION_TYPES.CLEAR_CHOICES}}},783:function(e,t,i){Object.defineProperty(t,\"__esModule\",{value:!0}),t.addGroup=void 0;var n=i(883);t.addGroup=function(e){var t=e.value,i=e.id,r=e.active,s=e.disabled;return{type:n.ACTION_TYPES.ADD_GROUP,value:t,id:i,active:r,disabled:s}}},464:function(e,t,i){Object.defineProperty(t,\"__esModule\",{value:!0}),t.highlightItem=t.removeItem=t.addItem=void 0;var n=i(883);t.addItem=function(e){var t=e.value,i=e.label,r=e.id,s=e.choiceId,o=e.groupId,a=e.customProperties,c=e.placeholder,l=e.keyCode;return{type:n.ACTION_TYPES.ADD_ITEM,value:t,label:i,id:r,choiceId:s,groupId:o,customProperties:a,placeholder:c,keyCode:l}},t.removeItem=function(e,t){return{type:n.ACTION_TYPES.REMOVE_ITEM,id:e,choiceId:t}},t.highlightItem=function(e,t){return{type:n.ACTION_TYPES.HIGHLIGHT_ITEM,id:e,highlighted:t}}},137:function(e,t,i){Object.defineProperty(t,\"__esModule\",{value:!0}),t.setIsLoading=t.resetTo=t.clearAll=void 0;var n=i(883);t.clearAll=function(){return{type:n.ACTION_TYPES.CLEAR_ALL}},t.resetTo=function(e){return{type:n.ACTION_TYPES.RESET_TO,state:e}},t.setIsLoading=function(e){return{type:n.ACTION_TYPES.SET_IS_LOADING,isLoading:e}}},373:function(e,t,i){var n=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r=0?this._store.getGroupById(r):null;return this._store.dispatch((0,l.highlightItem)(i,!0)),t&&this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:i,value:o,label:c,groupValue:h&&h.value?h.value:null}),this},e.prototype.unhighlightItem=function(e){if(!e||!e.id)return this;var t=e.id,i=e.groupId,n=void 0===i?-1:i,r=e.value,s=void 0===r?\"\":r,o=e.label,a=void 0===o?\"\":o,c=n>=0?this._store.getGroupById(n):null;return this._store.dispatch((0,l.highlightItem)(t,!1)),this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:t,value:s,label:a,groupValue:c&&c.value?c.value:null}),this},e.prototype.highlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.highlightItem(t)})),this},e.prototype.unhighlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.unhighlightItem(t)})),this},e.prototype.removeActiveItemsByValue=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.value===e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeActiveItems=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.id!==e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeHighlightedItems=function(e){var t=this;return void 0===e&&(e=!1),this._store.highlightedActiveItems.forEach((function(i){t._removeItem(i),e&&t._triggerChange(i.value)})),this},e.prototype.showDropdown=function(e){var t=this;return this.dropdown.isActive||requestAnimationFrame((function(){t.dropdown.show(),t.containerOuter.open(t.dropdown.distanceFromTopWindow),!e&&t._canSearch&&t.input.focus(),t.passedElement.triggerEvent(d.EVENTS.showDropdown,{})})),this},e.prototype.hideDropdown=function(e){var t=this;return this.dropdown.isActive?(requestAnimationFrame((function(){t.dropdown.hide(),t.containerOuter.close(),!e&&t._canSearch&&(t.input.removeActiveDescendant(),t.input.blur()),t.passedElement.triggerEvent(d.EVENTS.hideDropdown,{})})),this):this},e.prototype.getValue=function(e){void 0===e&&(e=!1);var t=this._store.activeItems.reduce((function(t,i){var n=e?i.value:i;return t.push(n),t}),[]);return this._isSelectOneElement?t[0]:t},e.prototype.setValue=function(e){var t=this;return this.initialised?(e.forEach((function(e){return t._setChoiceOrItem(e)})),this):this},e.prototype.setChoiceByValue=function(e){var t=this;return!this.initialised||this._isTextElement||(Array.isArray(e)?e:[e]).forEach((function(e){return t._findAndSelectChoiceByValue(e)})),this},e.prototype.setChoices=function(e,t,i,n){var r=this;if(void 0===e&&(e=[]),void 0===t&&(t=\"value\"),void 0===i&&(i=\"label\"),void 0===n&&(n=!1),!this.initialised)throw new ReferenceError(\"setChoices was called on a non-initialized instance of Choices\");if(!this._isSelectElement)throw new TypeError(\"setChoices can't be used with INPUT based Choices\");if(\"string\"!=typeof t||!t)throw new TypeError(\"value parameter must be a name of 'value' field in passed objects\");if(n&&this.clearChoices(),\"function\"==typeof e){var s=e(this);if(\"function\"==typeof Promise&&s instanceof Promise)return new Promise((function(e){return requestAnimationFrame(e)})).then((function(){return r._handleLoadingState(!0)})).then((function(){return s})).then((function(e){return r.setChoices(e,t,i,n)})).catch((function(e){r.config.silent||console.error(e)})).then((function(){return r._handleLoadingState(!1)})).then((function(){return r}));if(!Array.isArray(s))throw new TypeError(\".setChoices first argument function must return either array of choices or Promise, got: \".concat(typeof s));return this.setChoices(s,t,i,!1)}if(!Array.isArray(e))throw new TypeError(\".setChoices must be called either with array of choices with a function resulting into Promise of array of choices\");return this.containerOuter.removeLoadingState(),this._startLoading(),e.forEach((function(e){if(e.choices)r._addGroup({id:e.id?parseInt(\"\".concat(e.id),10):null,group:e,valueKey:t,labelKey:i});else{var n=e;r._addChoice({value:n[t],label:n[i],isSelected:!!n.selected,isDisabled:!!n.disabled,placeholder:!!n.placeholder,customProperties:n.customProperties})}})),this._stopLoading(),this},e.prototype.clearChoices=function(){return this._store.dispatch((0,a.clearChoices)()),this},e.prototype.clearStore=function(){return this._store.dispatch((0,h.clearAll)()),this},e.prototype.clearInput=function(){var e=!this._isSelectOneElement;return this.input.clear(e),!this._isTextElement&&this._canSearch&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))),this},e.prototype._render=function(){if(!this._store.isLoading()){this._currentState=this._store.state;var e=this._currentState.choices!==this._prevState.choices||this._currentState.groups!==this._prevState.groups||this._currentState.items!==this._prevState.items,t=this._isSelectElement,i=this._currentState.items!==this._prevState.items;e&&(t&&this._renderChoices(),i&&this._renderItems(),this._prevState=this._currentState)}},e.prototype._renderChoices=function(){var e=this,t=this._store,i=t.activeGroups,n=t.activeChoices,r=document.createDocumentFragment();if(this.choiceList.clear(),this.config.resetScrollPosition&&requestAnimationFrame((function(){return e.choiceList.scrollToTop()})),i.length>=1&&!this._isSearching){var s=n.filter((function(e){return!0===e.placeholder&&-1===e.groupId}));s.length>=1&&(r=this._createChoicesFragment(s,r)),r=this._createGroupsFragment(i,n,r)}else n.length>=1&&(r=this._createChoicesFragment(n,r));if(r.childNodes&&r.childNodes.length>0){var o=this._store.activeItems,a=this._canAddItem(o,this.input.value);if(a.response)this.choiceList.append(r),this._highlightChoice();else{var c=this._getTemplate(\"notice\",a.notice);this.choiceList.append(c)}}else{var l=void 0;c=void 0,this._isSearching?(c=\"function\"==typeof this.config.noResultsText?this.config.noResultsText():this.config.noResultsText,l=this._getTemplate(\"notice\",c,\"no-results\")):(c=\"function\"==typeof this.config.noChoicesText?this.config.noChoicesText():this.config.noChoicesText,l=this._getTemplate(\"notice\",c,\"no-choices\")),this.choiceList.append(l)}},e.prototype._renderItems=function(){var e=this._store.activeItems||[];this.itemList.clear();var t=this._createItemsFragment(e);t.childNodes&&this.itemList.append(t)},e.prototype._createGroupsFragment=function(e,t,i){var n=this;return void 0===i&&(i=document.createDocumentFragment()),this.config.shouldSort&&e.sort(this.config.sorter),e.forEach((function(e){var r=function(e){return t.filter((function(t){return n._isSelectOneElement?t.groupId===e.id:t.groupId===e.id&&(\"always\"===n.config.renderSelectedChoices||!t.selected)}))}(e);if(r.length>=1){var s=n._getTemplate(\"choiceGroup\",e);i.appendChild(s),n._createChoicesFragment(r,i,!0)}})),i},e.prototype._createChoicesFragment=function(e,t,i){var r=this;void 0===t&&(t=document.createDocumentFragment()),void 0===i&&(i=!1);var s=this.config,o=s.renderSelectedChoices,a=s.searchResultLimit,c=s.renderChoiceLimit,l=this._isSearching?f.sortByScore:this.config.sorter,h=function(e){if(\"auto\"!==o||r._isSelectOneElement||!e.selected){var i=r._getTemplate(\"choice\",e,r.config.itemSelectText);t.appendChild(i)}},u=e;\"auto\"!==o||this._isSelectOneElement||(u=e.filter((function(e){return!e.selected})));var d=u.reduce((function(e,t){return t.placeholder?e.placeholderChoices.push(t):e.normalChoices.push(t),e}),{placeholderChoices:[],normalChoices:[]}),p=d.placeholderChoices,m=d.normalChoices;(this.config.shouldSort||this._isSearching)&&m.sort(l);var v=u.length,g=this._isSelectOneElement?n(n([],p,!0),m,!0):m;this._isSearching?v=a:c&&c>0&&!i&&(v=c);for(var _=0;_=n){var o=r?this._searchChoices(e):0;this.passedElement.triggerEvent(d.EVENTS.search,{value:e,resultCount:o})}else s&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0)))}},e.prototype._canAddItem=function(e,t){var i=!0,n=\"function\"==typeof this.config.addItemText?this.config.addItemText(t):this.config.addItemText;if(!this._isSelectOneElement){var r=(0,f.existsInArray)(e,t);this.config.maxItemCount>0&&this.config.maxItemCount<=e.length&&(i=!1,n=\"function\"==typeof this.config.maxItemText?this.config.maxItemText(this.config.maxItemCount):this.config.maxItemText),!this.config.duplicateItemsAllowed&&r&&i&&(i=!1,n=\"function\"==typeof this.config.uniqueItemText?this.config.uniqueItemText(t):this.config.uniqueItemText),this._isTextElement&&this.config.addItems&&i&&\"function\"==typeof this.config.addItemFilter&&!this.config.addItemFilter(t)&&(i=!1,n=\"function\"==typeof this.config.customAddItemText?this.config.customAddItemText(t):this.config.customAddItemText)}return{response:i,notice:n}},e.prototype._searchChoices=function(e){var t=\"string\"==typeof e?e.trim():e,i=\"string\"==typeof this._currentValue?this._currentValue.trim():this._currentValue;if(t.length<1&&t===\"\".concat(i,\" \"))return 0;var r=this._store.searchableChoices,s=t,c=Object.assign(this.config.fuseOptions,{keys:n([],this.config.searchFields,!0),includeMatches:!0}),l=new o.default(r,c).search(s);return this._currentValue=t,this._highlightPosition=0,this._isSearching=!0,this._store.dispatch((0,a.filterChoices)(l)),l.length},e.prototype._addEventListeners=function(){var e=document.documentElement;e.addEventListener(\"touchend\",this._onTouchEnd,!0),this.containerOuter.element.addEventListener(\"keydown\",this._onKeyDown,!0),this.containerOuter.element.addEventListener(\"mousedown\",this._onMouseDown,!0),e.addEventListener(\"click\",this._onClick,{passive:!0}),e.addEventListener(\"touchmove\",this._onTouchMove,{passive:!0}),this.dropdown.element.addEventListener(\"mouseover\",this._onMouseOver,{passive:!0}),this._isSelectOneElement&&(this.containerOuter.element.addEventListener(\"focus\",this._onFocus,{passive:!0}),this.containerOuter.element.addEventListener(\"blur\",this._onBlur,{passive:!0})),this.input.element.addEventListener(\"keyup\",this._onKeyUp,{passive:!0}),this.input.element.addEventListener(\"focus\",this._onFocus,{passive:!0}),this.input.element.addEventListener(\"blur\",this._onBlur,{passive:!0}),this.input.element.form&&this.input.element.form.addEventListener(\"reset\",this._onFormReset,{passive:!0}),this.input.addEventListeners()},e.prototype._removeEventListeners=function(){var e=document.documentElement;e.removeEventListener(\"touchend\",this._onTouchEnd,!0),this.containerOuter.element.removeEventListener(\"keydown\",this._onKeyDown,!0),this.containerOuter.element.removeEventListener(\"mousedown\",this._onMouseDown,!0),e.removeEventListener(\"click\",this._onClick),e.removeEventListener(\"touchmove\",this._onTouchMove),this.dropdown.element.removeEventListener(\"mouseover\",this._onMouseOver),this._isSelectOneElement&&(this.containerOuter.element.removeEventListener(\"focus\",this._onFocus),this.containerOuter.element.removeEventListener(\"blur\",this._onBlur)),this.input.element.removeEventListener(\"keyup\",this._onKeyUp),this.input.element.removeEventListener(\"focus\",this._onFocus),this.input.element.removeEventListener(\"blur\",this._onBlur),this.input.element.form&&this.input.element.form.removeEventListener(\"reset\",this._onFormReset),this.input.removeEventListeners()},e.prototype._onKeyDown=function(e){var t=e.keyCode,i=this._store.activeItems,n=this.input.isFocussed,r=this.dropdown.isActive,s=this.itemList.hasChildren(),o=String.fromCharCode(t),a=/[^\\x00-\\x1F]/.test(o),c=d.KEY_CODES.BACK_KEY,l=d.KEY_CODES.DELETE_KEY,h=d.KEY_CODES.ENTER_KEY,u=d.KEY_CODES.A_KEY,p=d.KEY_CODES.ESC_KEY,f=d.KEY_CODES.UP_KEY,m=d.KEY_CODES.DOWN_KEY,v=d.KEY_CODES.PAGE_UP_KEY,g=d.KEY_CODES.PAGE_DOWN_KEY;switch(this._isTextElement||r||!a||(this.showDropdown(),this.input.isFocussed||(this.input.value+=e.key.toLowerCase())),t){case u:return this._onSelectKey(e,s);case h:return this._onEnterKey(e,i,r);case p:return this._onEscapeKey(r);case f:case v:case m:case g:return this._onDirectionKey(e,r);case l:case c:return this._onDeleteKey(e,i,n)}},e.prototype._onKeyUp=function(e){var t=e.target,i=e.keyCode,n=this.input.value,r=this._store.activeItems,s=this._canAddItem(r,n),o=d.KEY_CODES.BACK_KEY,c=d.KEY_CODES.DELETE_KEY;if(this._isTextElement)if(s.notice&&n){var l=this._getTemplate(\"notice\",s.notice);this.dropdown.element.innerHTML=l.outerHTML,this.showDropdown(!0)}else this.hideDropdown(!0);else{var h=(i===o||i===c)&&t&&!t.value,u=!this._isTextElement&&this._isSearching,p=this._canSearch&&s.response;h&&u?(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))):p&&this._handleSearch(this.input.rawValue)}this._canSearch=this.config.searchEnabled},e.prototype._onSelectKey=function(e,t){var i=e.ctrlKey,n=e.metaKey;(i||n)&&t&&(this._canSearch=!1,this.config.removeItems&&!this.input.value&&this.input.element===document.activeElement&&this.highlightAll())},e.prototype._onEnterKey=function(e,t,i){var n=e.target,r=d.KEY_CODES.ENTER_KEY,s=n&&n.hasAttribute(\"data-button\");if(this._isTextElement&&n&&n.value){var o=this.input.value;this._canAddItem(t,o).response&&(this.hideDropdown(!0),this._addItem({value:o}),this._triggerChange(o),this.clearInput())}if(s&&(this._handleButtonAction(t,n),e.preventDefault()),i){var a=this.dropdown.getChild(\".\".concat(this.config.classNames.highlightedState));a&&(t[0]&&(t[0].keyCode=r),this._handleChoiceAction(t,a)),e.preventDefault()}else this._isSelectOneElement&&(this.showDropdown(),e.preventDefault())},e.prototype._onEscapeKey=function(e){e&&(this.hideDropdown(!0),this.containerOuter.focus())},e.prototype._onDirectionKey=function(e,t){var i=e.keyCode,n=e.metaKey,r=d.KEY_CODES.DOWN_KEY,s=d.KEY_CODES.PAGE_UP_KEY,o=d.KEY_CODES.PAGE_DOWN_KEY;if(t||this._isSelectOneElement){this.showDropdown(),this._canSearch=!1;var a=i===r||i===o?1:-1,c=\"[data-choice-selectable]\",l=void 0;if(n||i===o||i===s)l=a>0?this.dropdown.element.querySelector(\"\".concat(c,\":last-of-type\")):this.dropdown.element.querySelector(c);else{var h=this.dropdown.element.querySelector(\".\".concat(this.config.classNames.highlightedState));l=h?(0,f.getAdjacentEl)(h,c,a):this.dropdown.element.querySelector(c)}l&&((0,f.isScrolledIntoView)(l,this.choiceList.element,a)||this.choiceList.scrollToChildElement(l,a),this._highlightChoice(l)),e.preventDefault()}},e.prototype._onDeleteKey=function(e,t,i){var n=e.target;this._isSelectOneElement||n.value||!i||(this._handleBackspace(t),e.preventDefault())},e.prototype._onTouchMove=function(){this._wasTap&&(this._wasTap=!1)},e.prototype._onTouchEnd=function(e){var t=(e||e.touches[0]).target;this._wasTap&&this.containerOuter.element.contains(t)&&((t===this.containerOuter.element||t===this.containerInner.element)&&(this._isTextElement?this.input.focus():this._isSelectMultipleElement&&this.showDropdown()),e.stopPropagation()),this._wasTap=!0},e.prototype._onMouseDown=function(e){var t=e.target;if(t instanceof HTMLElement){if(_&&this.choiceList.element.contains(t)){var i=this.choiceList.element.firstElementChild,n=\"ltr\"===this._direction?e.offsetX>=i.offsetWidth:e.offsetX0&&this.unhighlightAll(),this.containerOuter.removeFocusState(),this.hideDropdown(!0))},e.prototype._onFocus=function(e){var t,i=this,n=e.target;n&&this.containerOuter.element.contains(n)&&((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&i.containerOuter.addFocusState()},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.addFocusState(),n===i.input.element&&i.showDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.showDropdown(!0),i.containerOuter.addFocusState())},t)[this.passedElement.element.type]()},e.prototype._onBlur=function(e){var t,i=this,n=e.target;if(n&&this.containerOuter.element.contains(n)&&!this._isScrollingOnIe){var r=this._store.activeItems.some((function(e){return e.highlighted}));((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),r&&i.unhighlightAll(),i.hideDropdown(!0))},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.removeFocusState(),(n===i.input.element||n===i.containerOuter.element&&!i._canSearch)&&i.hideDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),i.hideDropdown(!0),r&&i.unhighlightAll())},t)[this.passedElement.element.type]()}else this._isScrollingOnIe=!1,this.input.element.focus()},e.prototype._onFormReset=function(){this._store.dispatch((0,h.resetTo)(this._initialState))},e.prototype._highlightChoice=function(e){var t=this;void 0===e&&(e=null);var i=Array.from(this.dropdown.element.querySelectorAll(\"[data-choice-selectable]\"));if(i.length){var n=e;Array.from(this.dropdown.element.querySelectorAll(\".\".concat(this.config.classNames.highlightedState))).forEach((function(e){e.classList.remove(t.config.classNames.highlightedState),e.setAttribute(\"aria-selected\",\"false\")})),n?this._highlightPosition=i.indexOf(n):(n=i.length>this._highlightPosition?i[this._highlightPosition]:i[i.length-1])||(n=i[0]),n.classList.add(this.config.classNames.highlightedState),n.setAttribute(\"aria-selected\",\"true\"),this.passedElement.triggerEvent(d.EVENTS.highlightChoice,{el:n}),this.dropdown.isActive&&(this.input.setActiveDescendant(n.id),this.containerOuter.setActiveDescendant(n.id))}},e.prototype._addItem=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.choiceId,s=void 0===r?-1:r,o=e.groupId,a=void 0===o?-1:o,c=e.customProperties,h=void 0===c?{}:c,u=e.placeholder,p=void 0!==u&&u,f=e.keyCode,m=void 0===f?-1:f,v=\"string\"==typeof t?t.trim():t,g=this._store.items,_=n||v,y=s||-1,E=a>=0?this._store.getGroupById(a):null,b=g?g.length+1:1;this.config.prependValue&&(v=this.config.prependValue+v.toString()),this.config.appendValue&&(v+=this.config.appendValue.toString()),this._store.dispatch((0,l.addItem)({value:v,label:_,id:b,choiceId:y,groupId:a,customProperties:h,placeholder:p,keyCode:m})),this._isSelectOneElement&&this.removeActiveItems(b),this.passedElement.triggerEvent(d.EVENTS.addItem,{id:b,value:v,label:_,customProperties:h,groupValue:E&&E.value?E.value:null,keyCode:m})},e.prototype._removeItem=function(e){var t=e.id,i=e.value,n=e.label,r=e.customProperties,s=e.choiceId,o=e.groupId,a=o&&o>=0?this._store.getGroupById(o):null;t&&s&&(this._store.dispatch((0,l.removeItem)(t,s)),this.passedElement.triggerEvent(d.EVENTS.removeItem,{id:t,value:i,label:n,customProperties:r,groupValue:a&&a.value?a.value:null}))},e.prototype._addChoice=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.isSelected,s=void 0!==r&&r,o=e.isDisabled,c=void 0!==o&&o,l=e.groupId,h=void 0===l?-1:l,u=e.customProperties,d=void 0===u?{}:u,p=e.placeholder,f=void 0!==p&&p,m=e.keyCode,v=void 0===m?-1:m;if(null!=t){var g=this._store.choices,_=n||t,y=g?g.length+1:1,E=\"\".concat(this._baseId,\"-\").concat(this._idNames.itemChoice,\"-\").concat(y);this._store.dispatch((0,a.addChoice)({id:y,groupId:h,elementId:E,value:t,label:_,disabled:c,customProperties:d,placeholder:f,keyCode:v})),s&&this._addItem({value:t,label:_,choiceId:y,customProperties:d,placeholder:f,keyCode:v})}},e.prototype._addGroup=function(e){var t=this,i=e.group,n=e.id,r=e.valueKey,s=void 0===r?\"value\":r,o=e.labelKey,a=void 0===o?\"label\":o,l=(0,f.isType)(\"Object\",i)?i.choices:Array.from(i.getElementsByTagName(\"OPTION\")),h=n||Math.floor((new Date).valueOf()*Math.random()),u=!!i.disabled&&i.disabled;l?(this._store.dispatch((0,c.addGroup)({value:i.label,id:h,active:!0,disabled:u})),l.forEach((function(e){var i=e.disabled||e.parentNode&&e.parentNode.disabled;t._addChoice({value:e[s],label:(0,f.isType)(\"Object\",e)?e[a]:e.innerHTML,isSelected:e.selected,isDisabled:i,groupId:h,customProperties:e.customProperties,placeholder:e.placeholder})}))):this._store.dispatch((0,c.addGroup)({value:i.label,id:i.id,active:!1,disabled:i.disabled}))},e.prototype._getTemplate=function(e){for(var t,i=[],r=1;r0?this.element.scrollTop+o-r:e.offsetTop;requestAnimationFrame((function(){i._animateScroll(a,t)}))}},e.prototype._scrollDown=function(e,t,i){var n=(i-e)/t,r=n>1?n:1;this.element.scrollTop=e+r},e.prototype._scrollUp=function(e,t,i){var n=(e-i)/t,r=n>1?n:1;this.element.scrollTop=e-r},e.prototype._animateScroll=function(e,t){var i=this,r=n.SCROLLING_SPEED,s=this.element.scrollTop,o=!1;t>0?(this._scrollDown(s,r,e),se&&(o=!0)),o&&requestAnimationFrame((function(){i._animateScroll(e,t)}))},e}();t.default=r},730:function(e,t,i){Object.defineProperty(t,\"__esModule\",{value:!0});var n=i(799),r=function(){function e(e){var t=e.element,i=e.classNames;if(this.element=t,this.classNames=i,!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement))throw new TypeError(\"Invalid element passed\");this.isDisabled=!1}return Object.defineProperty(e.prototype,\"isActive\",{get:function(){return\"active\"===this.element.dataset.choice},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,\"dir\",{get:function(){return this.element.dir},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,\"value\",{get:function(){return this.element.value},set:function(e){this.element.value=e},enumerable:!1,configurable:!0}),e.prototype.conceal=function(){this.element.classList.add(this.classNames.input),this.element.hidden=!0,this.element.tabIndex=-1;var e=this.element.getAttribute(\"style\");e&&this.element.setAttribute(\"data-choice-orig-style\",e),this.element.setAttribute(\"data-choice\",\"active\")},e.prototype.reveal=function(){this.element.classList.remove(this.classNames.input),this.element.hidden=!1,this.element.removeAttribute(\"tabindex\");var e=this.element.getAttribute(\"data-choice-orig-style\");e?(this.element.removeAttribute(\"data-choice-orig-style\"),this.element.setAttribute(\"style\",e)):this.element.removeAttribute(\"style\"),this.element.removeAttribute(\"data-choice\"),this.element.value=this.element.value},e.prototype.enable=function(){this.element.removeAttribute(\"disabled\"),this.element.disabled=!1,this.isDisabled=!1},e.prototype.disable=function(){this.element.setAttribute(\"disabled\",\"\"),this.element.disabled=!0,this.isDisabled=!0},e.prototype.triggerEvent=function(e,t){(0,n.dispatchEvent)(this.element,e,t)},e}();t.default=r},541:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if(\"function\"!=typeof t&&null!==t)throw new TypeError(\"Class extends value \"+String(t)+\" is not a constructor or null\");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,\"__esModule\",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.delimiter,s=e.call(this,{element:i,classNames:n})||this;return s.delimiter=r,s}return r(t,e),Object.defineProperty(t.prototype,\"value\",{get:function(){return this.element.value},set:function(e){this.element.setAttribute(\"value\",e),this.element.value=e},enumerable:!1,configurable:!0}),t}(s(i(730)).default);t.default=o},982:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if(\"function\"!=typeof t&&null!==t)throw new TypeError(\"Class extends value \"+String(t)+\" is not a constructor or null\");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,\"__esModule\",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.template,s=e.call(this,{element:i,classNames:n})||this;return s.template=r,s}return r(t,e),Object.defineProperty(t.prototype,\"placeholderOption\",{get:function(){return this.element.querySelector('option[value=\"\"]')||this.element.querySelector(\"option[placeholder]\")},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,\"optionGroups\",{get:function(){return Array.from(this.element.getElementsByTagName(\"OPTGROUP\"))},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,\"options\",{get:function(){return Array.from(this.element.options)},set:function(e){var t=this,i=document.createDocumentFragment();e.forEach((function(e){return n=e,r=t.template(n),void i.appendChild(r);var n,r})),this.appendDocFragment(i)},enumerable:!1,configurable:!0}),t.prototype.appendDocFragment=function(e){this.element.innerHTML=\"\",this.element.appendChild(e)},t}(s(i(730)).default);t.default=o},883:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0}),t.SCROLLING_SPEED=t.SELECT_MULTIPLE_TYPE=t.SELECT_ONE_TYPE=t.TEXT_TYPE=t.KEY_CODES=t.ACTION_TYPES=t.EVENTS=void 0,t.EVENTS={showDropdown:\"showDropdown\",hideDropdown:\"hideDropdown\",change:\"change\",choice:\"choice\",search:\"search\",addItem:\"addItem\",removeItem:\"removeItem\",highlightItem:\"highlightItem\",highlightChoice:\"highlightChoice\",unhighlightItem:\"unhighlightItem\"},t.ACTION_TYPES={ADD_CHOICE:\"ADD_CHOICE\",FILTER_CHOICES:\"FILTER_CHOICES\",ACTIVATE_CHOICES:\"ACTIVATE_CHOICES\",CLEAR_CHOICES:\"CLEAR_CHOICES\",ADD_GROUP:\"ADD_GROUP\",ADD_ITEM:\"ADD_ITEM\",REMOVE_ITEM:\"REMOVE_ITEM\",HIGHLIGHT_ITEM:\"HIGHLIGHT_ITEM\",CLEAR_ALL:\"CLEAR_ALL\",RESET_TO:\"RESET_TO\",SET_IS_LOADING:\"SET_IS_LOADING\"},t.KEY_CODES={BACK_KEY:46,DELETE_KEY:8,ENTER_KEY:13,A_KEY:65,ESC_KEY:27,UP_KEY:38,DOWN_KEY:40,PAGE_UP_KEY:33,PAGE_DOWN_KEY:34},t.TEXT_TYPE=\"text\",t.SELECT_ONE_TYPE=\"select-one\",t.SELECT_MULTIPLE_TYPE=\"select-multiple\",t.SCROLLING_SPEED=4},789:function(e,t,i){Object.defineProperty(t,\"__esModule\",{value:!0}),t.DEFAULT_CONFIG=t.DEFAULT_CLASSNAMES=void 0;var n=i(799);t.DEFAULT_CLASSNAMES={containerOuter:\"choices\",containerInner:\"choices__inner\",input:\"choices__input\",inputCloned:\"choices__input--cloned\",list:\"choices__list\",listItems:\"choices__list--multiple\",listSingle:\"choices__list--single\",listDropdown:\"choices__list--dropdown\",item:\"choices__item\",itemSelectable:\"choices__item--selectable\",itemDisabled:\"choices__item--disabled\",itemChoice:\"choices__item--choice\",placeholder:\"choices__placeholder\",group:\"choices__group\",groupHeading:\"choices__heading\",button:\"choices__button\",activeState:\"is-active\",focusState:\"is-focused\",openState:\"is-open\",disabledState:\"is-disabled\",highlightedState:\"is-highlighted\",selectedState:\"is-selected\",flippedState:\"is-flipped\",loadingState:\"is-loading\",noResults:\"has-no-results\",noChoices:\"has-no-choices\"},t.DEFAULT_CONFIG={items:[],choices:[],silent:!1,renderChoiceLimit:-1,maxItemCount:-1,addItems:!0,addItemFilter:null,removeItems:!0,removeItemButton:!1,editItems:!1,allowHTML:!0,duplicateItemsAllowed:!0,delimiter:\",\",paste:!0,searchEnabled:!0,searchChoices:!0,searchFloor:1,searchResultLimit:4,searchFields:[\"label\",\"value\"],position:\"auto\",resetScrollPosition:!0,shouldSort:!0,shouldSortItems:!1,sorter:n.sortByAlpha,placeholder:!0,placeholderValue:null,searchPlaceholderValue:null,prependValue:null,appendValue:null,renderSelectedChoices:\"auto\",loadingText:\"Loading...\",noResultsText:\"No results found\",noChoicesText:\"No choices to choose from\",itemSelectText:\"Press to select\",uniqueItemText:\"Only unique values can be added\",customAddItemText:\"Only values matching specific conditions can be added\",addItemText:function(e){return'Press Enter to add \"'.concat((0,n.sanitise)(e),'\"')},maxItemText:function(e){return\"Only \".concat(e,\" values can be added\")},valueComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},labelId:\"\",callbackOnInit:null,callbackOnCreateTemplates:null,classNames:t.DEFAULT_CLASSNAMES}},18:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},978:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},948:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},359:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},285:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},533:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},187:function(e,t,i){var n=this&&this.__createBinding||(Object.create?function(e,t,i,n){void 0===n&&(n=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!(\"get\"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,n,r)}:function(e,t,i,n){void 0===n&&(n=i),e[n]=t[i]}),r=this&&this.__exportStar||function(e,t){for(var i in e)\"default\"===i||Object.prototype.hasOwnProperty.call(t,i)||n(t,e,i)};Object.defineProperty(t,\"__esModule\",{value:!0}),r(i(18),t),r(i(978),t),r(i(948),t),r(i(359),t),r(i(285),t),r(i(533),t),r(i(287),t),r(i(132),t),r(i(837),t),r(i(598),t),r(i(369),t),r(i(37),t),r(i(47),t),r(i(923),t),r(i(876),t)},287:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},132:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},837:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},598:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},37:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},369:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},47:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},923:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},876:function(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},799:function(e,t){var i;Object.defineProperty(t,\"__esModule\",{value:!0}),t.parseCustomProperties=t.diff=t.cloneObject=t.existsInArray=t.dispatchEvent=t.sortByScore=t.sortByAlpha=t.strToEl=t.sanitise=t.isScrolledIntoView=t.getAdjacentEl=t.wrap=t.isType=t.getType=t.generateId=t.generateChars=t.getRandomNumber=void 0,t.getRandomNumber=function(e,t){return Math.floor(Math.random()*(t-e)+e)},t.generateChars=function(e){return Array.from({length:e},(function(){return(0,t.getRandomNumber)(0,36).toString(36)})).join(\"\")},t.generateId=function(e,i){var n=e.id||e.name&&\"\".concat(e.name,\"-\").concat((0,t.generateChars)(2))||(0,t.generateChars)(4);return n=n.replace(/(:|\\.|\\[|\\]|,)/g,\"\"),n=\"\".concat(i,\"-\").concat(n)},t.getType=function(e){return Object.prototype.toString.call(e).slice(8,-1)},t.isType=function(e,i){return null!=i&&(0,t.getType)(i)===e},t.wrap=function(e,t){return void 0===t&&(t=document.createElement(\"div\")),e.parentNode&&(e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t)),t.appendChild(e)},t.getAdjacentEl=function(e,t,i){void 0===i&&(i=1);for(var n=\"\".concat(i>0?\"next\":\"previous\",\"ElementSibling\"),r=e[n];r;){if(r.matches(t))return r;r=r[n]}return r},t.isScrolledIntoView=function(e,t,i){return void 0===i&&(i=1),!!e&&(i>0?t.scrollTop+t.offsetHeight>=e.offsetTop+e.offsetHeight:e.offsetTop>=t.scrollTop)},t.sanitise=function(e){return\"string\"!=typeof e?e:e.replace(/&/g,\"&\").replace(/>/g,\">\").replace(/-1?e.map((function(e){var t=e;return t.id===parseInt(\"\".concat(o.choiceId),10)&&(t.selected=!0),t})):e;case\"REMOVE_ITEM\":var a=n;return a.choiceId&&a.choiceId>-1?e.map((function(e){var t=e;return t.id===parseInt(\"\".concat(a.choiceId),10)&&(t.selected=!1),t})):e;case\"FILTER_CHOICES\":var c=n;return e.map((function(e){var t=e;return t.active=c.results.some((function(e){var i=e.item,n=e.score;return i.id===t.id&&(t.score=n,!0)})),t}));case\"ACTIVATE_CHOICES\":var l=n;return e.map((function(e){var t=e;return t.active=l.active,t}));case\"CLEAR_CHOICES\":return t.defaultState;default:return e}}},871:function(e,t){var i=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r0?\"treeitem\":\"option\"),Object.assign(E.dataset,{choice:\"\",id:d,value:p,selectText:i}),g?(E.classList.add(h),E.dataset.choiceDisabled=\"\",E.setAttribute(\"aria-disabled\",\"true\")):(E.classList.add(c),E.dataset.choiceSelectable=\"\"),E},input:function(e,t){var i=e.classNames,n=i.input,r=i.inputCloned,s=Object.assign(document.createElement(\"input\"),{type:\"search\",name:\"search_terms\",className:\"\".concat(n,\" \").concat(r),autocomplete:\"off\",autocapitalize:\"off\",spellcheck:!1});return s.setAttribute(\"role\",\"textbox\"),s.setAttribute(\"aria-autocomplete\",\"list\"),s.setAttribute(\"aria-label\",t),s},dropdown:function(e){var t=e.classNames,i=t.list,n=t.listDropdown,r=document.createElement(\"div\");return r.classList.add(i,n),r.setAttribute(\"aria-expanded\",\"false\"),r},notice:function(e,t,i){var n,r=e.allowHTML,s=e.classNames,o=s.item,a=s.itemChoice,c=s.noResults,l=s.noChoices;void 0===i&&(i=\"\");var h=[o,a];return\"no-choices\"===i?h.push(l):\"no-results\"===i&&h.push(c),Object.assign(document.createElement(\"div\"),((n={})[r?\"innerHTML\":\"innerText\"]=t,n.className=h.join(\" \"),n))},option:function(e){var t=e.label,i=e.value,n=e.customProperties,r=e.active,s=e.disabled,o=new Option(t,i,!1,r);return n&&(o.dataset.customProperties=\"\".concat(n)),o.disabled=!!s,o}};t.default=i},996:function(e){var t=function(e){return function(e){return!!e&&\"object\"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return\"[object RegExp]\"===t||\"[object Date]\"===t||function(e){return e.$$typeof===i}(e)}(e)},i=\"function\"==typeof Symbol&&Symbol.for?Symbol.for(\"react.element\"):60103;function n(e,t){return!1!==t.clone&&t.isMergeableObject(e)?c((i=e,Array.isArray(i)?[]:{}),e,t):e;var i}function r(e,t,i){return e.concat(t).map((function(e){return n(e,i)}))}function s(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return e.propertyIsEnumerable(t)})):[]}(e))}function o(e,t){try{return t in e}catch(e){return!1}}function a(e,t,i){var r={};return i.isMergeableObject(e)&&s(e).forEach((function(t){r[t]=n(e[t],i)})),s(t).forEach((function(s){(function(e,t){return o(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))})(e,s)||(o(e,s)&&i.isMergeableObject(t[s])?r[s]=function(e,t){if(!t.customMerge)return c;var i=t.customMerge(e);return\"function\"==typeof i?i:c}(s,i)(e[s],t[s],i):r[s]=n(t[s],i))})),r}function c(e,i,s){(s=s||{}).arrayMerge=s.arrayMerge||r,s.isMergeableObject=s.isMergeableObject||t,s.cloneUnlessOtherwiseSpecified=n;var o=Array.isArray(i);return o===Array.isArray(e)?o?s.arrayMerge(e,i,s):a(e,i,s):n(i,s)}c.all=function(e,t){if(!Array.isArray(e))throw new Error(\"first argument should be an array\");return e.reduce((function(e,i){return c(e,i,t)}),{})};var l=c;e.exports=l},221:function(e,t,i){\n /**\n * Fuse.js v6.6.2 - Lightweight fuzzy-search (http://fusejs.io)\n *\n * Copyright (c) 2022 Kiro Risk (http://kiro.me)\n * All Rights Reserved. Apache Software License 2.0\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n function n(e){return Array.isArray?Array.isArray(e):\"[object Array]\"===d(e)}i.r(t),i.d(t,{default:function(){return X}});const r=1/0;function s(e){return null==e?\"\":function(e){if(\"string\"==typeof e)return e;let t=e+\"\";return\"0\"==t&&1/e==-r?\"-0\":t}(e)}function o(e){return\"string\"==typeof e}function a(e){return\"number\"==typeof e}function c(e){return!0===e||!1===e||function(e){return l(e)&&null!==e}(e)&&\"[object Boolean]\"==d(e)}function l(e){return\"object\"==typeof e}function h(e){return null!=e}function u(e){return!e.trim().length}function d(e){return null==e?void 0===e?\"[object Undefined]\":\"[object Null]\":Object.prototype.toString.call(e)}const p=e=>`Missing ${e} property in key`,f=e=>`Property 'weight' in key '${e}' must be a positive integer`,m=Object.prototype.hasOwnProperty;class v{constructor(e){this._keys=[],this._keyMap={};let t=0;e.forEach((e=>{let i=g(e);t+=i.weight,this._keys.push(i),this._keyMap[i.id]=i,t+=i.weight})),this._keys.forEach((e=>{e.weight/=t}))}get(e){return this._keyMap[e]}keys(){return this._keys}toJSON(){return JSON.stringify(this._keys)}}function g(e){let t=null,i=null,r=null,s=1,a=null;if(o(e)||n(e))r=e,t=_(e),i=y(e);else{if(!m.call(e,\"name\"))throw new Error(p(\"name\"));const n=e.name;if(r=n,m.call(e,\"weight\")&&(s=e.weight,s<=0))throw new Error(f(n));t=_(n),i=y(n),a=e.getFn}return{path:t,id:i,weight:s,src:r,getFn:a}}function _(e){return n(e)?e:e.split(\".\")}function y(e){return n(e)?e.join(\".\"):e}var E={isCaseSensitive:!1,includeScore:!1,keys:[],shouldSort:!0,sortFn:(e,t)=>e.score===t.score?e.idx{if(h(e))if(t[u]){const d=e[t[u]];if(!h(d))return;if(u===t.length-1&&(o(d)||a(d)||c(d)))i.push(s(d));else if(n(d)){r=!0;for(let e=0,i=d.length;e{this._keysMap[e.id]=t}))}create(){!this.isCreated&&this.docs.length&&(this.isCreated=!0,o(this.docs[0])?this.docs.forEach(((e,t)=>{this._addString(e,t)})):this.docs.forEach(((e,t)=>{this._addObject(e,t)})),this.norm.clear())}add(e){const t=this.size();o(e)?this._addString(e,t):this._addObject(e,t)}removeAt(e){this.records.splice(e,1);for(let t=e,i=this.size();t{let s=t.getFn?t.getFn(e):this.getFn(e,t.path);if(h(s))if(n(s)){let e=[];const t=[{nestedArrIndex:-1,value:s}];for(;t.length;){const{nestedArrIndex:i,value:r}=t.pop();if(h(r))if(o(r)&&!u(r)){let t={v:r,i,n:this.norm.get(r)};e.push(t)}else n(r)&&r.forEach(((e,i)=>{t.push({nestedArrIndex:i,value:e})}))}i.$[r]=e}else if(o(s)&&!u(s)){let e={v:s,n:this.norm.get(s)};i.$[r]=e}})),this.records.push(i)}toJSON(){return{keys:this.keys,records:this.records}}}function O(e,t,{getFn:i=E.getFn,fieldNormWeight:n=E.fieldNormWeight}={}){const r=new S({getFn:i,fieldNormWeight:n});return r.setKeys(e.map(g)),r.setSources(t),r.create(),r}function I(e,{errors:t=0,currentLocation:i=0,expectedLocation:n=0,distance:r=E.distance,ignoreLocation:s=E.ignoreLocation}={}){const o=t/e.length;if(s)return o;const a=Math.abs(n-i);return r?o+a/r:a?1:o}const C=32;function T(e,t,i,{location:n=E.location,distance:r=E.distance,threshold:s=E.threshold,findAllMatches:o=E.findAllMatches,minMatchCharLength:a=E.minMatchCharLength,includeMatches:c=E.includeMatches,ignoreLocation:l=E.ignoreLocation}={}){if(t.length>C)throw new Error(\"Pattern length exceeds max of 32.\");const h=t.length,u=e.length,d=Math.max(0,Math.min(n,u));let p=s,f=d;const m=a>1||c,v=m?Array(u):[];let g;for(;(g=e.indexOf(t,f))>-1;){let e=I(t,{currentLocation:g,expectedLocation:d,distance:r,ignoreLocation:l});if(p=Math.min(e,p),f=g+h,m){let e=0;for(;e=c;s-=1){let o=s-1,a=i[e.charAt(o)];if(m&&(v[o]=+!!a),E[s]=(E[s+1]<<1|1)&a,n&&(E[s]|=(_[s+1]|_[s])<<1|1|_[s+1]),E[s]&S&&(y=I(t,{errors:n,currentLocation:o,expectedLocation:d,distance:r,ignoreLocation:l}),y<=p)){if(p=y,f=o,f<=d)break;c=Math.max(1,2*d-f)}}if(I(t,{errors:n+1,currentLocation:d,expectedLocation:d,distance:r,ignoreLocation:l})>p)break;_=E}const O={isMatch:f>=0,score:Math.max(.001,y)};if(m){const e=function(e=[],t=E.minMatchCharLength){let i=[],n=-1,r=-1,s=0;for(let o=e.length;s=t&&i.push([n,r]),n=-1)}return e[s-1]&&s-n>=t&&i.push([n,s-1]),i}(v,a);e.length?c&&(O.indices=e):O.isMatch=!1}return O}function L(e){let t={};for(let i=0,n=e.length;i{this.chunks.push({pattern:e,alphabet:L(e),startIndex:t})},h=this.pattern.length;if(h>C){let e=0;const t=h%C,i=h-t;for(;e{const{isMatch:f,score:m,indices:v}=T(e,t,d,{location:n+p,distance:r,threshold:s,findAllMatches:o,minMatchCharLength:a,includeMatches:i,ignoreLocation:c});f&&(u=!0),h+=m,f&&v&&(l=[...l,...v])}));let d={isMatch:u,score:u?h/this.chunks.length:1};return u&&i&&(d.indices=l),d}}class A{constructor(e){this.pattern=e}static isMultiMatch(e){return M(e,this.multiRegex)}static isSingleMatch(e){return M(e,this.singleRegex)}search(){}}function M(e,t){const i=e.match(t);return i?i[1]:null}class P extends A{constructor(e,{location:t=E.location,threshold:i=E.threshold,distance:n=E.distance,includeMatches:r=E.includeMatches,findAllMatches:s=E.findAllMatches,minMatchCharLength:o=E.minMatchCharLength,isCaseSensitive:a=E.isCaseSensitive,ignoreLocation:c=E.ignoreLocation}={}){super(e),this._bitapSearch=new w(e,{location:t,threshold:i,distance:n,includeMatches:r,findAllMatches:s,minMatchCharLength:o,isCaseSensitive:a,ignoreLocation:c})}static get type(){return\"fuzzy\"}static get multiRegex(){return/^\"(.*)\"$/}static get singleRegex(){return/^(.*)$/}search(e){return this._bitapSearch.searchIn(e)}}class x extends A{constructor(e){super(e)}static get type(){return\"include\"}static get multiRegex(){return/^'\"(.*)\"$/}static get singleRegex(){return/^'(.*)$/}search(e){let t,i=0;const n=[],r=this.pattern.length;for(;(t=e.indexOf(this.pattern,i))>-1;)i=t+r,n.push([t,i-1]);const s=!!n.length;return{isMatch:s,score:s?0:1,indices:n}}}const N=[class extends A{constructor(e){super(e)}static get type(){return\"exact\"}static get multiRegex(){return/^=\"(.*)\"$/}static get singleRegex(){return/^=(.*)$/}search(e){const t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},x,class extends A{constructor(e){super(e)}static get type(){return\"prefix-exact\"}static get multiRegex(){return/^\\^\"(.*)\"$/}static get singleRegex(){return/^\\^(.*)$/}search(e){const t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},class extends A{constructor(e){super(e)}static get type(){return\"inverse-prefix-exact\"}static get multiRegex(){return/^!\\^\"(.*)\"$/}static get singleRegex(){return/^!\\^(.*)$/}search(e){const t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends A{constructor(e){super(e)}static get type(){return\"inverse-suffix-exact\"}static get multiRegex(){return/^!\"(.*)\"\\$$/}static get singleRegex(){return/^!(.*)\\$$/}search(e){const t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends A{constructor(e){super(e)}static get type(){return\"suffix-exact\"}static get multiRegex(){return/^\"(.*)\"\\$$/}static get singleRegex(){return/^(.*)\\$$/}search(e){const t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}},class extends A{constructor(e){super(e)}static get type(){return\"inverse-exact\"}static get multiRegex(){return/^!\"(.*)\"$/}static get singleRegex(){return/^!(.*)$/}search(e){const t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},P],D=N.length,j=/ +(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)/,F=new Set([P.type,x.type]);class k{constructor(e,{isCaseSensitive:t=E.isCaseSensitive,includeMatches:i=E.includeMatches,minMatchCharLength:n=E.minMatchCharLength,ignoreLocation:r=E.ignoreLocation,findAllMatches:s=E.findAllMatches,location:o=E.location,threshold:a=E.threshold,distance:c=E.distance}={}){this.query=null,this.options={isCaseSensitive:t,includeMatches:i,minMatchCharLength:n,findAllMatches:s,ignoreLocation:r,location:o,threshold:a,distance:c},this.pattern=t?e:e.toLowerCase(),this.query=function(e,t={}){return e.split(\"|\").map((e=>{let i=e.trim().split(j).filter((e=>e&&!!e.trim())),n=[];for(let e=0,r=i.length;e!(!e[Y]&&!e[H]),U=e=>({[Y]:Object.keys(e).map((t=>({[t]:e[t]})))});function W(e,t,{auto:i=!0}={}){const r=e=>{let s=Object.keys(e);const a=(e=>!!e[V])(e);if(!a&&s.length>1&&!G(e))return r(U(e));if((e=>!n(e)&&l(e)&&!G(e))(e)){const n=a?e[V]:s[0],r=a?e[B]:e[n];if(!o(r))throw new Error((e=>`Invalid value for key ${e}`)(n));const c={keyId:y(n),pattern:r};return i&&(c.searcher=R(r,t)),c}let c={children:[],operator:s[0]};return s.forEach((t=>{const i=e[t];n(i)&&i.forEach((e=>{c.children.push(r(e))}))})),c};return G(e)||(e=U(e)),r(e)}function $(e,t){const i=e.matches;t.matches=[],h(i)&&i.forEach((e=>{if(!h(e.indices)||!e.indices.length)return;const{indices:i,value:n}=e;let r={indices:i,value:n};e.key&&(r.key=e.key.src),e.idx>-1&&(r.refIndex=e.idx),t.matches.push(r)}))}function q(e,t){t.score=e.score}class X{constructor(e,t={},i){this.options={...E,...t},this.options.useExtendedSearch,this._keyStore=new v(this.options.keys),this.setCollection(e,i)}setCollection(e,t){if(this._docs=e,t&&!(t instanceof S))throw new Error(\"Incorrect 'index' type\");this._myIndex=t||O(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}add(e){h(e)&&(this._docs.push(e),this._myIndex.add(e))}remove(e=(()=>!1)){const t=[];for(let i=0,n=this._docs.length;i{let i=1;e.matches.forEach((({key:e,norm:n,score:r})=>{const s=e?e.weight:null;i*=Math.pow(0===r&&s?Number.EPSILON:r,(s||1)*(t?1:n))})),e.score=i}))}(l,{ignoreFieldNorm:c}),r&&l.sort(s),a(t)&&t>-1&&(l=l.slice(0,t)),function(e,t,{includeMatches:i=E.includeMatches,includeScore:n=E.includeScore}={}){const r=[];return i&&r.push($),n&&r.push(q),e.map((e=>{const{idx:i}=e,n={item:t[i],refIndex:i};return r.length&&r.forEach((t=>{t(e,n)})),n}))}(l,this._docs,{includeMatches:i,includeScore:n})}_searchStringList(e){const t=R(e,this.options),{records:i}=this._myIndex,n=[];return i.forEach((({v:e,i,n:r})=>{if(!h(e))return;const{isMatch:s,score:o,indices:a}=t.searchIn(e);s&&n.push({item:e,idx:i,matches:[{score:o,value:e,norm:r,indices:a}]})})),n}_searchLogical(e){const t=W(e,this.options),i=(e,t,n)=>{if(!e.children){const{keyId:i,searcher:r}=e,s=this._findMatches({key:this._keyStore.get(i),value:this._myIndex.getValueForItemAtKeyId(t,i),searcher:r});return s&&s.length?[{idx:n,item:t,matches:s}]:[]}const r=[];for(let s=0,o=e.children.length;s{if(h(e)){let o=i(t,e,n);o.length&&(r[n]||(r[n]={idx:n,item:e,matches:[]},s.push(r[n])),o.forEach((({matches:e})=>{r[n].matches.push(...e)})))}})),s}_searchObjectList(e){const t=R(e,this.options),{keys:i,records:n}=this._myIndex,r=[];return n.forEach((({$:e,i:n})=>{if(!h(e))return;let s=[];i.forEach(((i,n)=>{s.push(...this._findMatches({key:i,value:e[n],searcher:t}))})),s.length&&r.push({idx:n,item:e,matches:s})})),r}_findMatches({key:e,value:t,searcher:i}){if(!h(t))return[];let r=[];if(n(t))t.forEach((({v:t,i:n,n:s})=>{if(!h(t))return;const{isMatch:o,score:a,indices:c}=i.searchIn(t);o&&r.push({score:a,key:e,value:t,idx:n,norm:s,indices:c})}));else{const{v:n,n:s}=t,{isMatch:o,score:a,indices:c}=i.searchIn(n);o&&r.push({score:a,key:e,value:n,norm:s,indices:c})}return r}}X.version=\"6.6.2\",X.createIndex=O,X.parseIndex=function(e,{getFn:t=E.getFn,fieldNormWeight:i=E.fieldNormWeight}={}){const{keys:n,records:r}=e,s=new S({getFn:t,fieldNormWeight:i});return s.setKeys(n),s.setIndexRecords(r),s},X.config=E,X.parseQuery=W,function(...e){K.push(...e)}(k)},791:function(e,t,i){function n(e){return n=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},n(e)}function r(e){var t=function(e,t){if(\"object\"!==n(e)||null===e)return e;var i=e[Symbol.toPrimitive];if(void 0!==i){var r=i.call(e,t||\"default\");if(\"object\"!==n(r))return r;throw new TypeError(\"@@toPrimitive must return a primitive value.\")}return(\"string\"===t?String:Number)(e)}(e,\"string\");return\"symbol\"===n(t)?t:String(t)}function s(e,t,i){return(t=r(t))in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function o(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,n)}return i}function a(e){for(var t=1;tthis.render_selection())),this.connect(this.model.properties.options.change,(()=>this.render())),this.connect(this.model.properties.name.change,(()=>this.render())),this.connect(this.model.properties.title.change,(()=>this.render())),this.connect(this.model.properties.size.change,(()=>this.render())),this.connect(this.model.properties.disabled.change,(()=>this.render()))}_render_input(){const e=this.model.options.map((e=>{let t,s;return(0,c.isString)(e)?t=s=e:[t,s]=e,(0,r.option)({value:t},s)}));return this.input_el=(0,r.select)({multiple:!0,class:d.input,name:this.model.name,disabled:this.model.disabled},e),this.input_el.addEventListener(\"change\",(()=>this.change_input())),this.input_el}render(){super.render(),this.render_selection()}render_selection(){const e=new Set(this.model.value);for(const t of this.shadow_el.querySelectorAll(\"option\"))t.selected=e.has(t.value);this.input_el.size=this.model.size}change_input(){const e=null!=this.shadow_el.querySelector(\"select:focus\"),t=[];for(const e of this.shadow_el.querySelectorAll(\"option\"))e.selected&&t.push(e.value);this.model.value=t,super.change_input(),e&&this.input_el.focus()}}s.MultiSelectView=p,p.__name__=\"MultiSelectView\";class u extends h.InputWidget{constructor(e){super(e)}}s.MultiSelect=u,l=u,u.__name__=\"MultiSelect\",l.prototype.default_view=p,l.define((({Int:e,Str:t,List:s,Tuple:i,Or:n})=>({value:[s(t),[]],options:[s(n(t,i(t,t))),[]],size:[e,4]})))},\n 637: function _(e,t,a,i,r){var s;i();const l=e(611);class o extends l.BaseDatePickerView{get flatpickr_options(){return{...super.flatpickr_options,mode:\"multiple\",conjunction:this.model.separator}}_on_change(e){this.model.value=e.map((e=>this._format_date(e)))}}a.MultipleDatePickerView=o,o.__name__=\"MultipleDatePickerView\";class c extends l.BaseDatePicker{constructor(e){super(e)}}a.MultipleDatePicker=c,s=c,c.__name__=\"MultipleDatePicker\",s.prototype.default_view=o,s.define((({Str:e,List:t})=>({value:[t(l.DateLike),[]],separator:[e,\", \"]})))},\n 638: function _(e,t,i,a,r){var s;a();const l=e(624),o=e(611);class c extends l.BaseDatetimePickerView{get flatpickr_options(){return{...super.flatpickr_options,mode:\"multiple\",conjunction:this.model.separator}}_on_change(e){this.model.value=e.map((e=>this._format_date(e)))}}i.MultipleDatetimePickerView=c,c.__name__=\"MultipleDatetimePickerView\";class n extends l.BaseDatetimePicker{constructor(e){super(e)}}i.MultipleDatetimePicker=n,s=n,n.__name__=\"MultipleDatetimePicker\",s.prototype.default_view=c,s.define((({Str:e,List:t})=>({value:[t(o.DateLike),[]],separator:[e,\", \"]})))},\n 639: function _(e,t,l,i,n){var s;i();const u=e(1),h=u.__importStar(e(245)),o=e(597),a=e(192),r=e(63),d=e(8),p=e(12),_=u.__importStar(e(598)),m=/^[-+]?\\d*$/,c=/^[-+]?\\d*\\.?\\d*(?:(?:\\d|\\d.)[eE][-+]?)*\\d*$/;class v extends o.InputWidgetView{connect_signals(){super.connect_signals(),this.connect(this.model.properties.name.change,(()=>this.input_el.name=this.model.name??\"\")),this.connect(this.model.properties.value.change,(()=>{this.input_el.value=this.format_value,this.old_value=this.input_el.value})),this.connect(this.model.properties.low.change,(()=>{const{value:e,low:t,high:l}=this.model;null!=t&&null!=l&&(0,p.assert)(t<=l,\"Invalid bounds, low must be inferior to high\"),null!=e&&null!=t&&e{const{value:e,low:t,high:l}=this.model;null!=t&&null!=l&&(0,p.assert)(l>=t,\"Invalid bounds, high must be superior to low\"),null!=e&&null!=l&&e>l&&(this.model.value=l)})),this.connect(this.model.properties.high.change,(()=>this.input_el.placeholder=this.model.placeholder)),this.connect(this.model.properties.disabled.change,(()=>this.input_el.disabled=this.model.disabled)),this.connect(this.model.properties.placeholder.change,(()=>this.input_el.placeholder=this.model.placeholder))}get format_value(){return null!=this.model.value?this.model.pretty(this.model.value):\"\"}_set_input_filter(e){this.input_el.addEventListener(\"input\",(()=>{const{selectionStart:t,selectionEnd:l}=this.input_el;if(e(this.input_el.value))this.old_value=this.input_el.value;else{const e=this.old_value.length-this.input_el.value.length;this.input_el.value=this.old_value,null!=t&&null!=l&&this.input_el.setSelectionRange(t-1,l+e)}}))}_render_input(){return this.input_el=(0,r.input)({type:\"text\",class:_.input,name:this.model.name,value:this.format_value,disabled:this.model.disabled,placeholder:this.model.placeholder})}render(){super.render(),this.old_value=this.format_value,this.set_input_filter(),this.input_el.addEventListener(\"change\",(()=>this.change_input())),this.input_el.addEventListener(\"focusout\",(()=>this.input_el.value=this.format_value))}set_input_filter(){const e=\"int\"==this.model.mode?m:c;this._set_input_filter((t=>e.test(t)))}bound_value(e){let t=e;const{low:l,high:i}=this.model;return t=null!=l?Math.max(l,t):t,t=null!=i?Math.min(i,t):t,t}get value(){let e=\"\"!=this.input_el.value?Number(this.input_el.value):null;return null!=e&&(e=this.bound_value(e)),e}change_input(){null==this.value?this.model.value=null:Number.isNaN(this.value)||(this.model.value=this.value)}}l.NumericInputView=v,v.__name__=\"NumericInputView\";class g extends o.InputWidget{constructor(e){super(e)}_formatter(e,t){return(0,d.isString)(t)?h.format(e,t):t.doFormat([e],{loc:0})[0]}pretty(e){return null!=this.format?this._formatter(e,this.format):`${e}`}}l.NumericInput=g,s=g,g.__name__=\"NumericInput\",s.prototype.default_view=v,s.define((({Float:e,Str:t,Enum:l,Ref:i,Or:n,Nullable:s})=>({value:[s(e),null],placeholder:[t,\"\"],mode:[l(\"int\",\"float\"),\"int\"],format:[s(n(t,i(a.TickFormatter))),null],low:[s(e),null],high:[s(e),null]})))},\n 640: function _(e,t,s,n,i){var l;n();const r=e(1),a=e(63),o=e(641),h=e(34),c=e(22),d=e(11),u=e(10),_=e(597),p=r.__importStar(e(598)),v=r.__importStar(e(643)),g=r.__importStar(e(644)),m=r.__importStar(e(645)),w=r.__importStar(e(123)),f=e(21),y=(0,f.Tuple)(f.Str,(0,f.Arrayable)(f.Color));class S extends _.InputWidgetView{stylesheets(){return[...super.stylesheets(),v.default,g.default,w.default]}connect_signals(){super.connect_signals();const{value:e,items:t,ncols:s,swatch_width:n,swatch_height:i}=this.model.properties;this.on_change([t,n,i],(()=>this.render())),this.on_change(e,(()=>this._update_value())),this.on_change(s,(()=>this._update_ncols()))}_update_value(){(0,a.empty)(this._value_el);const e=this._render_value();null!=e&&this._value_el.append(e)}_update_ncols(){const{ncols:e}=this.model;this._pane.el.style.setProperty(\"--number-of-columns\",`${e}`)}_render_image(e){const[t,s]=e,{swatch_width:n,swatch_height:i}=this.model,l=n,r=\"auto\"==i?n:i,o=(0,a.canvas)({width:l,height:r}),d=o.getContext(\"2d\"),u=100/s.length;for(const[e,t]of(0,h.enumerate)(s)){d.beginPath(),d.rect(t*u,0,u,20);const s=(0,c.color2css)(e);d.strokeStyle=s,d.fillStyle=s,d.fill(),d.stroke()}return o}_render_item(e){const[t,s]=e,{swatch_width:n,swatch_height:i}=this.model,l=s.length,r=(0,u.linspace)(0,100,l+1),o=[];for(const[e,t]of(0,h.enumerate)(s)){const[s,n]=[r[t],r[t+1]];o.push(`${(0,c.color2css)(e)} ${s}% ${n}%`)}const d=(0,a.div)();d.style.background=`linear-gradient(to right, ${o.join(\", \")})`,d.style.width=(0,a.px)(n),\"auto\"==i?d.style.alignSelf=\"stretch\":d.style.height=(0,a.px)(i);return(0,a.div)({class:g.entry},d,t)}_render_value(){const{value:e,items:t}=this.model,s=t.find((([t])=>t==e));return null!=s?this._render_item(s):null}_render_input(){this._value_el=(0,a.div)({class:[v.value,g.entry]},this._render_value());const e=(0,a.div)({class:[v.chevron,w.tool_icon_chevron_down]}),t=(0,a.div)({class:[p.input,v.value_input]},this._value_el,e);return this.model.disabled?t.classList.add(p.disabled):t.tabIndex=0,this.input_el=t,this.input_el}render(){super.render();const e=[];for(const[t,s]of(0,h.enumerate)(this.model.items)){const n=this._render_item(t),i=(0,a.div)({class:g.item,tabIndex:0},n);i.addEventListener(\"pointerup\",(()=>{this.select(t)})),i.addEventListener(\"keyup\",(e=>{switch(e.key){case\"Enter\":this.select(t);break;case\"Escape\":this.hide()}}));const l=t=>{const{items:n}=this.model,i=(0,d.cycle)(s+t,0,n.length-1);e[i].focus()};i.addEventListener(\"keydown\",(e=>{const t=(()=>{switch(e.key){case\"ArrowUp\":return-this.model.ncols;case\"ArrowDown\":return+this.model.ncols;case\"ArrowLeft\":return-1;case\"ArrowRight\":return 1;default:return null}})();null!=t&&(e.preventDefault(),l(t))})),e.push(i)}this._pane=new o.DropPane(e,{target:this.group_el,prevent_hide:this.input_el,extra_stylesheets:[g.default,m.default]}),this._update_ncols(),this.input_el.addEventListener(\"pointerup\",(()=>{this.toggle()})),this.input_el.addEventListener(\"keyup\",(e=>{switch(e.key){case\"Enter\":this.toggle();break;case\"Escape\":this.hide()}}));const t=e=>{const{items:t,value:s}=this.model,n=t.findIndex((([e])=>s==e));if(-1!=n){const s=(0,d.cycle)(n+e,0,t.length-1);this.select(t[s])}};this.input_el.addEventListener(\"keydown\",(e=>{const s=(()=>{switch(e.key){case\"ArrowUp\":return-1;case\"ArrowDown\":return 1;default:return null}})();null!=s&&(e.preventDefault(),t(s))}))}select(e){this.hide();const[t]=e;this.model.value=t,super.change_input(),this.input_el.focus()}toggle(){this.model.disabled||this._pane.toggle()}hide(){this._pane.hide()}}s.PaletteSelectView=S,S.__name__=\"PaletteSelectView\";class k extends _.InputWidget{constructor(e){super(e)}}s.PaletteSelect=k,l=k,k.__name__=\"PaletteSelect\",l.prototype.default_view=S,l.define((({Int:e,Str:t,List:s,NonNegative:n,Positive:i,Or:l,Auto:r})=>({value:[t],items:[s(y)],swatch_width:[n(e),100],swatch_height:[l(r,n(e)),\"auto\"],ncols:[i(e),1]})))},\n 641: function _(e,t,s,i,n){i();const o=e(1),h=e(63),_=e(8),l=o.__importDefault(e(642)),r=o.__importDefault(e(66));class d{get is_open(){return this._open}constructor(e,t){this.el=(0,h.div)(),this._open=!1,this._on_mousedown=e=>{if(e.composedPath().includes(this.el))return;const{prevent_hide:t}=this;if(t instanceof HTMLElement){if(e.composedPath().includes(t))return}else if(null!=t&&t(e))return;this.hide()},this._on_keydown=e=>{if(\"Escape\"===e.key)this.hide()},this._on_blur=()=>{this.hide()},this.contents=e,this.target=t.target,this.prevent_hide=t.prevent_hide,this.extra_stylesheets=t.extra_stylesheets??[],this.shadow_el=this.el.attachShadow({mode:\"open\"}),this.class_list=new h.ClassList(this.el.classList)}remove(){this._unlisten(),this.el.remove()}_listen(){document.addEventListener(\"mousedown\",this._on_mousedown),document.addEventListener(\"keydown\",this._on_keydown),window.addEventListener(\"blur\",this._on_blur)}_unlisten(){document.removeEventListener(\"mousedown\",this._on_mousedown),document.removeEventListener(\"keydown\",this._on_keydown),window.removeEventListener(\"blur\",this._on_blur)}stylesheets(){return[r.default,l.default,...this.extra_stylesheets]}empty(){(0,h.empty)(this.shadow_el),this.class_list.clear()}render(){this.empty();for(const e of this.stylesheets()){((0,_.isString)(e)?new h.InlineStyleSheet(e):e).install(this.shadow_el)}this.shadow_el.append(...this.contents)}show(){if(!this._open){this.render();(this.target.shadowRoot??this.target).appendChild(this.el),this._listen(),this._open=!0}}hide(){this._open&&(this._open=!1,this._unlisten(),this.el.remove())}toggle(){this._open?this.hide():this.show()}}s.DropPane=d,d.__name__=\"DropPane\"},\n 642: function _(o,e,r,t,i){t(),r.default=\":host{position:absolute;top:100%;min-width:100%;width:auto;height:auto;user-select:none;-webkit-user-select:none;z-index:var(--bokeh-top-level);cursor:pointer;font-size:var(--font-size);background-color:#fff;border:1px solid #ccc;border-radius:var(--border-radius);box-shadow:2px 4px 8px rgba(0, 0, 0, 0.175);}\"},\n 643: function _(e,t,a,i,n){i(),a.value_input=\"bk-value-input\",a.value=\"bk-value\",a.chevron=\"bk-chevron\",a.default=\".bk-value-input{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center;gap:1em;cursor:pointer;}.bk-value{flex-grow:1;}.bk-chevron{width:16px;height:16px;mask-size:100% 100%;-webkit-mask-size:100% 100%;mask-position:center center;-webkit-mask-position:center center;mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;}\"},\n 644: function _(e,t,i,o,r){o(),i.entry=\"bk-entry\",i.item=\"bk-item\",i.active=\"bk-active\",i.default=\".bk-entry{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center;gap:0.5em;}.bk-item{--active-tool-highlight:#26aae1;border:1px solid transparent;}.bk-item.bk-active{border-color:var(--active-tool-highlight);}.bk-item:hover{background-color:#f9f9f9;}.bk-item:focus,.bk-item:focus-visible{outline:1px dotted var(--active-tool-highlight);outline-offset:-1px;}.bk-item::-moz-focus-inner{border:0;}\"},\n 645: function _(e,n,a,m,o){m(),a.default=\":host{--number-of-columns:1;padding:5px;display:grid;grid-template-columns:repeat(var(--number-of-columns), 1fr);gap:0.25em;}\"},\n 646: function _(e,a,t,r,s){var n;r();const p=e(627),i=e(63);class _ extends p.MarkupView{render(){super.render();const e=(0,i.p)({style:{margin:\"0px\"}});this.has_math_disabled()?e.textContent=this.model.text:e.innerHTML=this.process_tex(this.model.text),this.markup_el.appendChild(e)}}t.ParagraphView=_,_.__name__=\"ParagraphView\";class h extends p.Markup{constructor(e){super(e)}}t.Paragraph=h,n=h,h.__name__=\"Paragraph\",n.prototype.default_view=_},\n 647: function _(e,t,s,l,n){var o;l();const p=e(1),r=e(595),a=e(63),i=p.__importDefault(e(648)),_=p.__importDefault(e(123));class u extends r.TextInputView{stylesheets(){return[...super.stylesheets(),i.default,_.default]}render(){super.render(),this.input_el.type=\"password\",this.toggle_el=(0,a.div)({class:\"bk-toggle\"}),this.toggle_el.addEventListener(\"click\",(()=>{const{input_el:e,toggle_el:t}=this,s=\"text\"==e.type;t.classList.toggle(\"bk-visible\",!s),e.type=s?\"password\":\"text\"})),this.shadow_el.append(this.toggle_el)}}s.PasswordInputView=u,u.__name__=\"PasswordInputView\";class d extends r.TextInput{constructor(e){super(e)}}s.PasswordInput=d,o=d,d.__name__=\"PasswordInput\",o.prototype.default_view=u},\n 648: function _(e,i,o,t,g){t(),o.input=\"bk-input\",o.toggle=\"bk-toggle\",o.visible=\"bk-visible\",o.default=\":host{--toggle-size:14px;--toggle-padding:4px;--toggle-width:calc(var(--toggle-size) + 2*var(--toggle-padding));}.bk-input{padding-right:max();}.bk-toggle{position:absolute;right:0;top:0;width:var(--toggle-width);height:100%;padding:0 var(--toggle-padding);background-color:var(--bokeh-icon-color);mask-image:var(--bokeh-icon-see-off);-webkit-mask-image:var(--bokeh-icon-see-off);mask-size:var(--toggle-size) var(--toggle-size);-webkit-mask-size:var(--toggle-size) var(--toggle-size);mask-position:center center;-webkit-mask-position:center center;mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;cursor:pointer;}.bk-toggle.bk-visible{mask-image:var(--bokeh-icon-see-on);-webkit-mask-image:var(--bokeh-icon-see-on);}\"},\n 649: function _(e,t,r,s,n){var a;s();const o=e(627),_=e(63);class p extends o.MarkupView{render(){super.render();const e=(0,_.pre)({style:{overflow:\"auto\"}},this.model.text);this.markup_el.appendChild(e)}}r.PreTextView=p,p.__name__=\"PreTextView\";class u extends o.Markup{constructor(e){super(e)}}r.PreText=u,a=u,u.__name__=\"PreText\",a.prototype.default_view=p},\n 650: function _(t,o,e,a,i){var n;a();const u=t(1),s=t(602),c=u.__importStar(t(593));class _ extends s.ToggleButtonGroupView{change_active(t){this.model.active!==t&&(this.model.active=t)}_update_active(){const{active:t}=this.model;this._buttons.forEach(((o,e)=>{o.classList.toggle(c.active,t===e)}))}}e.RadioButtonGroupView=_,_.__name__=\"RadioButtonGroupView\";class l extends s.ToggleButtonGroup{constructor(t){super(t)}}e.RadioButtonGroup=l,n=l,l.__name__=\"RadioButtonGroup\",n.prototype.default_view=_,n.define((({Int:t,Nullable:o})=>({active:[o(t),null]})))},\n 651: function _(e,t,n,i,s){var o;i();const a=e(1),l=e(605),c=e(63),d=e(40),p=e(34),u=a.__importStar(e(598));class r extends l.ToggleInputGroupView{connect_signals(){super.connect_signals();const{active:e}=this.model.properties;this.on_change(e,(()=>{const{active:e}=this.model;for(const[t,n]of(0,p.enumerate)(this._inputs))t.checked=e==n}))}render(){super.render();const e=(0,c.div)({class:[u.input_group,this.model.inline?u.inline:null]});this.shadow_el.appendChild(e);const t=(0,d.unique_id)(),{active:n,labels:i}=this.model;this._inputs=[];for(let s=0;sthis.change_active(s))),this._inputs.push(o),this.model.disabled&&(o.disabled=!0),s==n&&(o.checked=!0);const a=(0,c.label)(o,(0,c.span)(i[s]));e.appendChild(a)}}change_active(e){this.model.active=e}}n.RadioGroupView=r,r.__name__=\"RadioGroupView\";class h extends l.ToggleInputGroup{constructor(e){super(e)}}n.RadioGroup=h,o=h,h.__name__=\"RadioGroup\",o.prototype.default_view=r,o.define((({Int:e,Nullable:t})=>({active:[t(e),null]})))},\n 652: function _(e,t,n,i,s){var u;i();const l=e(1),o=e(63),_=e(8),a=e(9),p=e(597),r=l.__importStar(e(598)),c=e(21),h=c.Unknown,d=c.Str,v=(0,c.List)((0,c.Or)(d,(0,c.Tuple)(h,d))),m=(0,c.Dict)(v);class g extends p.InputWidgetView{constructor(){super(...arguments),this._known_values=new Map}connect_signals(){super.connect_signals();const{value:e,options:t}=this.model.properties;this.on_change(e,(()=>{this._update_value()})),this.on_change(t,(()=>{(0,o.empty)(this.input_el),this.input_el.append(...this.options_el()),this._update_value()}))}options_el(){const{_known_values:e}=this;function t(t){return t.map((t=>{let n,i;return(0,_.isString)(t)?n=i=t:[n,i]=t,e.set(n,i),(0,o.option)({value:i},i)}))}e.clear();const{options:n}=this.model;return(0,_.isArray)(n)?t(n):(0,a.entries)(n).map((([e,n])=>(0,o.optgroup)({label:e},t(n))))}_render_input(){return this.input_el=(0,o.select)({class:r.input,name:this.model.name,disabled:this.model.disabled},this.options_el()),this.input_el.addEventListener(\"change\",(()=>this.change_input())),this.input_el}render(){super.render(),this._update_value()}change_input(){const e=this.input_el.value,t=[...this._known_values].find((([t,n])=>e==n)),n=(()=>{if(null==t)return\"\";{const[e,n]=t;return e}})();this.model.value=n,super.change_input()}_update_value(){const{value:e}=this.model,t=this._known_values.get(e);void 0!==t?this.input_el.value=t:(this.input_el.removeAttribute(\"value\"),this.input_el.selectedIndex=-1)}}n.SelectView=g,g.__name__=\"SelectView\";class w extends p.InputWidget{constructor(e){super(e)}}n.Select=w,u=w,w.__name__=\"Select\",u.prototype.default_view=g,u.define((()=>({value:[h,\"\"],options:[(0,c.Or)(v,m),[]]})))},\n 653: function _(e,t,n,i,s){var l;i();const o=e(1),r=e(639),a=o.__importStar(e(18)),_=e(63),{min:h,max:u}=Math;class d extends r.NumericInputView{*buttons(){yield this.btn_up_el,yield this.btn_down_el}initialize(){super.initialize(),this._handles={interval:void 0,timeout:void 0},this._interval=200}connect_signals(){super.connect_signals();const e=this.model.properties;this.on_change(e.disabled,(()=>{for(const e of this.buttons())(0,_.toggle_attribute)(e,\"disabled\",this.model.disabled)}))}_render_input(){super._render_input(),this.btn_up_el=(0,_.button)({class:\"bk-spin-btn bk-spin-btn-up\"}),this.btn_down_el=(0,_.button)({class:\"bk-spin-btn bk-spin-btn-down\"});const{input_el:e,btn_up_el:t,btn_down_el:n}=this;return this.wrapper_el=(0,_.div)({class:\"bk-spin-wrapper\"},e,t,n),this.wrapper_el}render(){super.render();for(const e of this.buttons())(0,_.toggle_attribute)(e,\"disabled\",this.model.disabled),e.addEventListener(\"mousedown\",(e=>this._btn_mouse_down(e))),e.addEventListener(\"mouseup\",(()=>this._btn_mouse_up())),e.addEventListener(\"mouseleave\",(()=>this._btn_mouse_leave()));this.input_el.addEventListener(\"keydown\",(e=>{this._input_key_down(e)})),this.input_el.addEventListener(\"keyup\",(()=>{this.model.value_throttled=this.model.value})),this.input_el.addEventListener(\"wheel\",(e=>{this._input_mouse_wheel(e)})),this.input_el.addEventListener(\"wheel\",function(e,t,n=!1){let i;return function(...s){const l=this,o=n&&void 0===i;void 0!==i&&clearTimeout(i),i=setTimeout((function(){i=void 0,n||e.apply(l,s)}),t),o&&e.apply(l,s)}}((()=>{this.model.value_throttled=this.model.value}),this.model.wheel_wait,!1))}remove(){this._stop_incrementation(),super.remove()}_start_incrementation(e){clearInterval(this._handles.interval),this._counter=0;const{step:t}=this.model,n=e=>{if(this._counter+=1,this._counter%5==0){const t=Math.floor(this._counter/5);t<10?(clearInterval(this._handles.interval),this._handles.interval=setInterval((()=>n(e)),this._interval/(t+1))):t>=10&&t<=13&&(clearInterval(this._handles.interval),this._handles.interval=setInterval((()=>n(2*e)),this._interval/10))}this.increment(e)};this._handles.interval=setInterval((()=>n(e*t)),this._interval)}_stop_incrementation(){clearTimeout(this._handles.timeout),this._handles.timeout=void 0,clearInterval(this._handles.interval),this._handles.interval=void 0,this.model.value_throttled=this.model.value}_btn_mouse_down(e){e.preventDefault();const t=e.currentTarget===this.btn_up_el?1:-1;this.increment(t*this.model.step),this.input_el.focus(),this._handles.timeout=setTimeout((()=>this._start_incrementation(t)),this._interval)}_btn_mouse_up(){this._stop_incrementation()}_btn_mouse_leave(){this._stop_incrementation()}_input_mouse_wheel(e){if(document.activeElement===this.input_el){e.preventDefault();const t=e.deltaY>0?-1:1;this.increment(t*this.model.step)}}_input_key_down(e){switch(e.key){case\"ArrowUp\":return e.preventDefault(),this.increment(this.model.step);case\"ArrowDown\":return e.preventDefault(),this.increment(-this.model.step);case\"PageUp\":return e.preventDefault(),this.increment(this.model.page_step_multiplier*this.model.step);case\"PageDown\":return e.preventDefault(),this.increment(-this.model.page_step_multiplier*this.model.step)}}increment(e){const{low:t,high:n}=this.model;null==this.model.value?e>0?this.model.value=null!=t?t:null!=n?h(0,n):0:e<0&&(this.model.value=null!=n?n:null!=t?u(t,0):0):this.model.value=this.bound_value(this.model.value+e)}change_input(){super.change_input(),this.model.value_throttled=this.model.value}bound_value(e){const{low:t,high:n}=this.model;return null!=t&&en?this.model.value??0:e}}n.SpinnerView=d,d.__name__=\"SpinnerView\";class p extends r.NumericInput{constructor(e){super(e)}}n.Spinner=p,l=p,p.__name__=\"Spinner\",l.prototype.default_view=d,l.define((({Float:e,Nullable:t})=>({value_throttled:[t(e),a.unset,{readonly:!0}],step:[e,1],page_step_multiplier:[e,10],wheel_wait:[e,100]}))),l.override({mode:\"float\"})},\n 654: function _(e,t,s,i,a){var l;i();const d=e(1),n=e(608),c=e(63),_=d.__importDefault(e(655));class h extends n.ToggleInputView{stylesheets(){return[...super.stylesheets(),_.default]}connect_signals(){super.connect_signals(),this.el.addEventListener(\"keydown\",(e=>{switch(e.key){case\"Enter\":case\" \":e.preventDefault(),this._toggle_active()}})),this.el.addEventListener(\"click\",(()=>this._toggle_active()))}render(){super.render(),this.bar_el=(0,c.div)({class:\"bar\"}),this.knob_el=(0,c.div)({class:\"knob\",tabIndex:0});const e=(0,c.div)({class:\"body\"},this.bar_el,this.knob_el);this._update_active(),this._update_disabled(),this.shadow_el.appendChild(e)}_update_active(){this.el.classList.toggle(\"active\",this.model.active)}_update_disabled(){this.el.classList.toggle(\"disabled\",this.model.disabled)}}s.SwitchView=h,h.__name__=\"SwitchView\";class o extends n.ToggleInput{constructor(e){super(e)}}s.Switch=o,l=o,o.__name__=\"Switch\",l.prototype.default_view=h,l.override({width:32})},\n 655: function _(o,r,t,a,i){a(),t.default=\":host{cursor:pointer;}:host(.disabled){cursor:default;}:host{--switch-size:16px;--bar-height:10px;}.body{width:100%;height:var(--switch-size);}.bar{position:relative;top:calc(50% - var(--bar-height)/2);height:var(--bar-height);border-radius:calc(var(--bar-height)/2);background-color:#e5e5e5;transition-property:background-color;}.knob{position:absolute;top:0;left:0;width:var(--switch-size);height:var(--switch-size);border-radius:8px;background-color:#adadad;transition-property:left, background-color;}:host(.active) .bar{background-color:#c2d5f7;}:host(.active) .knob{left:calc(100% - var(--switch-size));background-color:#3b80f0;}\"},\n 656: function _(e,t,s,n,i){var r;n();const o=e(1),l=e(596),c=e(63),p=o.__importStar(e(598));class _ extends l.TextLikeInputView{connect_signals(){super.connect_signals(),this.connect(this.model.properties.rows.change,(()=>this.input_el.rows=this.model.rows)),this.connect(this.model.properties.cols.change,(()=>this.input_el.cols=this.model.cols))}_render_input(){return this.input_el=(0,c.textarea)({class:p.input})}render(){super.render(),this.input_el.cols=this.model.cols,this.input_el.rows=this.model.rows}}s.TextAreaInputView=_,_.__name__=\"TextAreaInputView\";class u extends l.TextLikeInput{constructor(e){super(e)}}s.TextAreaInput=u,r=u,u.__name__=\"TextAreaInput\",r.prototype.default_view=_,r.define((({Int:e})=>({cols:[e,20],rows:[e,2]}))),r.override({max_length:500})},\n 657: function _(e,t,n,i,c){var s;i();const m=e(612),r=e(21),o=e(20),a=e(12);n.TimeLike=(0,r.Or)(r.Str,r.Float);class l extends m.PickerBaseView{_format_time(e){const{picker:t}=this;return t.formatDate(e,t.config.dateFormat)}connect_signals(){super.connect_signals();const{value:e,min_time:t,max_time:n,time_format:i,hour_increment:c,minute_increment:s,second_increment:m,seconds:r,clock:o}=this.model.properties;this.connect(e.change,(()=>{const{value:e}=this.model;null!=e?this.picker.setDate(e):this.picker.clear()})),this.connect(t.change,(()=>this.picker.set(\"minTime\",this.model.min_time))),this.connect(n.change,(()=>this.picker.set(\"maxTime\",this.model.max_time))),this.connect(i.change,(()=>this.picker.set(\"altFormat\",this.model.time_format))),this.connect(c.change,(()=>this.picker.set(\"hourIncrement\",this.model.hour_increment))),this.connect(s.change,(()=>this.picker.set(\"minuteIncrement\",this.model.minute_increment))),this.connect(m.change,(()=>this._update_second_increment())),this.connect(r.change,(()=>this.picker.set(\"enableSeconds\",this.model.seconds))),this.connect(o.change,(()=>this.picker.set(\"time_24hr\",\"24h\"==this.model.clock)))}get flatpickr_options(){const{value:e,min_time:t,max_time:n,time_format:i,hour_increment:c,minute_increment:s,seconds:m,clock:r}=this.model,o=super.flatpickr_options;return o.enableTime=!0,o.noCalendar=!0,o.altInput=!0,o.altFormat=i,o.dateFormat=\"H:i:S\",o.hourIncrement=c,o.minuteIncrement=s,o.enableSeconds=m,o.time_24hr=\"24h\"==r,null!=e&&(o.defaultDate=e),null!=t&&(o.minTime=t),null!=n&&(o.maxTime=n),o}render(){super.render(),this._update_second_increment()}_update_second_increment(){const{second_increment:e}=this.model;this.picker.secondElement?.setAttribute(\"step\",e.toString())}_on_change(e){(0,a.assert)(e.length<=1),this.model.value=(()=>{if(0==e.length)return null;{const[t]=e;return this._format_time(t)}})()}}n.TimePickerView=l,l.__name__=\"TimePickerView\";class h extends m.PickerBase{constructor(e){super(e)}}n.TimePicker=h,s=h,h.__name__=\"TimePicker\",s.prototype.default_view=l,s.define((({Bool:e,Str:t,Nullable:i,Positive:c,Int:s})=>({value:[i(n.TimeLike),null],min_time:[i(n.TimeLike),null],max_time:[i(n.TimeLike),null],time_format:[t,\"H:i\"],hour_increment:[c(s),1],minute_increment:[c(s),1],second_increment:[c(s),1],seconds:[e,!1],clock:[o.Clock,\"24h\"]})))},\n 658: function _(e,t,i,s,c){var o;s();const a=e(1),n=e(590),l=e(53),r=a.__importStar(e(593));class _ extends n.AbstractButtonView{connect_signals(){super.connect_signals(),this.connect(this.model.properties.active.change,(()=>this._update_active()))}render(){super.render(),this._update_active()}click(){this.model.active=!this.model.active,this.model.trigger_event(new l.ButtonClick),super.click()}_update_active(){this.button_el.classList.toggle(r.active,this.model.active)}}i.ToggleView=_,_.__name__=\"ToggleView\";class g extends n.AbstractButton{constructor(e){super(e)}}i.Toggle=g,o=g,g.__name__=\"Toggle\",o.prototype.default_view=_,o.define((({Bool:e})=>({active:[e,!1]}))),o.override({label:\"Toggle\"})},\n 659: function _(e,i,a,l,r){l(),r(\"CategoricalSlider\",e(660).CategoricalSlider),r(\"DateRangeSlider\",e(665).DateRangeSlider),r(\"DateSlider\",e(668).DateSlider),r(\"DatetimeRangeSlider\",e(670).DatetimeRangeSlider),r(\"RangeSlider\",e(671).RangeSlider),r(\"Slider\",e(672).Slider)},\n 660: function _(e,t,r,s,i){var o;s();const a=e(661),c=e(8);class n extends a.AbstractSliderView{constructor(){super(...arguments),this.behaviour=\"tap\"}connect_signals(){super.connect_signals();const{categories:e}=this.model.properties;this.on_change([e],(()=>this._update_slider()))}_calc_to(){const{categories:e}=this.model;return{range:{min:0,max:e.length-1},start:[this.model.value],step:1,format:{to:t=>e[t],from:t=>e.indexOf(t)}}}_calc_from([e]){const{categories:t}=this.model;return t[0|e]}pretty(e){return(0,c.isNumber)(e)?this.model.categories[e]:e}}r.CategoricalSliderView=n,n.__name__=\"CategoricalSliderView\";class l extends a.AbstractSlider{constructor(e){super(e)}}r.CategoricalSlider=l,o=l,l.__name__=\"CategoricalSlider\",o.prototype.default_view=n,o.define((({List:e,Str:t})=>({categories:[e(t)]})))},\n 661: function _(t,e,i,s,l){var o;s();const r=t(1),n=r.__importDefault(t(662)),_=r.__importStar(t(18)),h=t(63),d=t(10),a=t(22),c=t(603),u=r.__importStar(t(663)),p=u,m=r.__importDefault(t(664)),g=r.__importStar(t(598));class b extends c.OrientedControlView{constructor(){super(...arguments),this.connected=!1,this._auto_width=\"auto\",this._auto_height=\"auto\"}*controls(){yield this.slider_el}get _steps(){return this._noUiSlider.steps}_update_slider(){this._noUiSlider.updateOptions(this._calc_to(),!0)}connect_signals(){super.connect_signals();const{direction:t,orientation:e,tooltips:i}=this.model.properties;this.on_change([t,e,i],(()=>this.render()));const{bar_color:s}=this.model.properties;this.on_change(s,(()=>{this._set_bar_color()}));const{value:l,title:o,show_value:r}=this.model.properties;this.on_change([l,o,r],(()=>this._update_title())),this.on_change(l,(()=>this._update_slider()))}stylesheets(){return[...super.stylesheets(),m.default,u.default]}_update_title(){(0,h.empty)(this.title_el);const t=null==this.model.title||0==this.model.title.length&&!this.model.show_value;if(this.title_el.style.display=t?\"none\":\"\",!t){const{title:t}=this.model;if(null!=t&&t.length>0&&(this.contains_tex_string(t)?this.title_el.innerHTML=`${this.process_tex(t)}: `:this.title_el.textContent=`${t}: `),this.model.show_value){const{start:t}=this._calc_to(),e=t.map((t=>this.pretty(t))).join(\" .. \");this.title_el.appendChild((0,h.span)({class:p.slider_value},e))}}}_set_bar_color(){if(!1!==this.connected&&!this.model.disabled&&null!=this.slider_el){this.slider_el.querySelector(\".noUi-connect\").style.backgroundColor=(0,a.color2css)(this.model.bar_color)}}render(){let t;if(super.render(),this.model.tooltips){const e={to:t=>this.pretty(t)},{start:i}=this._calc_to();t=(0,d.repeat)(e,i.length)}else t=null;if(null==this.slider_el){this.slider_el=(0,h.div)(),this._noUiSlider=n.default.create(this.slider_el,{...this._calc_to(),behaviour:this.behaviour,connect:this.connected,tooltips:t??!1,orientation:this.model.orientation,direction:this.model.direction}),this._noUiSlider.on(\"slide\",((t,e,i)=>this._slide(i))),this._noUiSlider.on(\"change\",((t,e,i)=>this._change(i)));const e=(e,i)=>{if(null==t||null==this.slider_el)return;this.slider_el.querySelectorAll(\".noUi-handle\")[e].querySelector(\".noUi-tooltip\").style.display=i?\"block\":\"\"};this._noUiSlider.on(\"start\",(()=>this._toggle_user_select(!1))),this._noUiSlider.on(\"end\",(()=>this._toggle_user_select(!0))),this._noUiSlider.on(\"start\",((t,i)=>e(i,!0))),this._noUiSlider.on(\"end\",((t,i)=>e(i,!1)))}else this._update_slider();this._set_bar_color(),this.model.disabled?this.slider_el.setAttribute(\"disabled\",\"true\"):this.slider_el.removeAttribute(\"disabled\"),this.title_el=(0,h.div)({class:p.slider_title}),this._update_title(),this.group_el=(0,h.div)({class:g.input_group},this.title_el,this.slider_el),this.shadow_el.appendChild(this.group_el),this._has_finished=!0}_toggle_user_select(t){const{style:e}=document.body,i=t?\"\":\"none\";e.userSelect=i,e.webkitUserSelect=i}_slide(t){this.model.value=this._calc_from(t)}_change(t){const e=this._calc_from(t);this.model.setv({value:e,value_throttled:e})}}i.AbstractSliderView=b,b.__name__=\"AbstractSliderView\";class S extends c.OrientedControl{constructor(t){super(t)}}i.AbstractSlider=S,o=S,S.__name__=\"AbstractSlider\",o.define((({Unknown:t,Bool:e,Str:i,Color:s,Enum:l,Nullable:o})=>({title:[o(i),\"\"],show_value:[e,!0],value:[t],value_throttled:[t,_.unset,{readonly:!0}],direction:[l(\"ltr\",\"rtl\"),\"ltr\"],tooltips:[e,!0],bar_color:[s,\"#e6e6e6\"]}))),o.override({width:300})},\n 662: function _(t,e,r,n,i){var o,s;function a(t){return\"object\"==typeof t&&\"function\"==typeof t.to}function l(t){t.parentElement.removeChild(t)}function u(t){return null!=t}function c(t){t.preventDefault()}function p(t){return\"number\"==typeof t&&!isNaN(t)&&isFinite(t)}function f(t,e,r){r>0&&(g(t,e),setTimeout((function(){v(t,e)}),r))}function d(t){return Math.max(Math.min(t,100),0)}function h(t){return Array.isArray(t)?t:[t]}function m(t){var e=(t=String(t)).split(\".\");return e.length>1?e[1].length:0}function g(t,e){t.classList&&!/\\s/.test(e)?t.classList.add(e):t.className+=\" \"+e}function v(t,e){t.classList&&!/\\s/.test(e)?t.classList.remove(e):t.className=t.className.replace(new RegExp(\"(^|\\\\b)\"+e.split(\" \").join(\"|\")+\"(\\\\b|$)\",\"gi\"),\" \")}function b(t){var e=void 0!==window.pageXOffset,r=\"CSS1Compat\"===(t.compatMode||\"\");return{x:e?window.pageXOffset:r?t.documentElement.scrollLeft:t.body.scrollLeft,y:e?window.pageYOffset:r?t.documentElement.scrollTop:t.body.scrollTop}}function S(t,e){return 100/(e-t)}function x(t,e,r){return 100*e/(t[r+1]-t[r])}function y(t,e){for(var r=1;t>=e[r];)r+=1;return r}function w(t,e,r){if(r>=t.slice(-1)[0])return 100;var n=y(r,t),i=t[n-1],o=t[n],s=e[n-1],a=e[n];return s+function(t,e){return x(t,t[0]<0?e+Math.abs(t[0]):e-t[0],0)}([i,o],r)/S(s,a)}function E(t,e,r,n){if(100===n)return n;var i=y(n,t),o=t[i-1],s=t[i];return r?n-o>(s-o)/2?s:o:e[i-1]?t[i-1]+function(t,e){return Math.round(t/e)*e}(n-t[i-1],e[i-1]):n}n(),(o=r.PipsMode||(r.PipsMode={})).Range=\"range\",o.Steps=\"steps\",o.Positions=\"positions\",o.Count=\"count\",o.Values=\"values\",(s=r.PipsType||(r.PipsType={}))[s.None=-1]=\"None\",s[s.NoValue=0]=\"NoValue\",s[s.LargeValue=1]=\"LargeValue\",s[s.SmallValue=2]=\"SmallValue\";var P=function(){function t(t,e,r){var n;this.xPct=[],this.xVal=[],this.xSteps=[],this.xNumSteps=[],this.xHighestCompleteStep=[],this.xSteps=[r||!1],this.xNumSteps=[!1],this.snap=e;var i=[];for(Object.keys(t).forEach((function(e){i.push([h(t[e]),e])})),i.sort((function(t,e){return t[0][0]-e[0][0]})),n=0;nthis.xPct[i+1];)i++;else t===this.xPct[this.xPct.length-1]&&(i=this.xPct.length-2);r||t!==this.xPct[i+1]||i++,null===e&&(e=[]);var o=1,s=e[i],a=0,l=0,u=0,c=0;for(n=r?(t-this.xPct[i])/(this.xPct[i+1]-this.xPct[i]):(this.xPct[i+1]-t)/(this.xPct[i+1]-this.xPct[i]);s>0;)a=this.xPct[i+1+c]-this.xPct[i+c],e[i+c]*o+100-100*n>100?(l=a*n,o=(s-100*n)/e[i+c],n=1):(l=e[i+c]*a/100*o,o=0),r?(u-=l,this.xPct.length+c>=1&&c--):(u+=l,this.xPct.length-c>=1&&c++),s=e[i+c]*o;return t+u},t.prototype.toStepping=function(t){return t=w(this.xVal,this.xPct,t)},t.prototype.fromStepping=function(t){return function(t,e,r){if(r>=100)return t.slice(-1)[0];var n=y(r,e),i=t[n-1],o=t[n],s=e[n-1];return function(t,e){return e*(t[1]-t[0])/100+t[0]}([i,o],(r-s)*S(s,e[n]))}(this.xVal,this.xPct,t)},t.prototype.getStep=function(t){return t=E(this.xPct,this.xSteps,this.snap,t)},t.prototype.getDefaultStep=function(t,e,r){var n=y(t,this.xPct);return(100===t||e&&t===this.xPct[n-1])&&(n=Math.max(n-1,1)),(this.xVal[n]-this.xVal[n-1])/r},t.prototype.getNearbySteps=function(t){var e=y(t,this.xPct);return{stepBefore:{startValue:this.xVal[e-2],step:this.xNumSteps[e-2],highestStep:this.xHighestCompleteStep[e-2]},thisStep:{startValue:this.xVal[e-1],step:this.xNumSteps[e-1],highestStep:this.xHighestCompleteStep[e-1]},stepAfter:{startValue:this.xVal[e],step:this.xNumSteps[e],highestStep:this.xHighestCompleteStep[e]}}},t.prototype.countStepDecimals=function(){var t=this.xNumSteps.map(m);return Math.max.apply(null,t)},t.prototype.hasNoSize=function(){return this.xVal[0]===this.xVal[this.xVal.length-1]},t.prototype.convert=function(t){return this.getStep(this.toStepping(t))},t.prototype.handleEntryPoint=function(t,e){var r;if(!p(r=\"min\"===t?0:\"max\"===t?100:parseFloat(t))||!p(e[0]))throw new Error(\"noUiSlider: 'range' value isn't numeric.\");this.xPct.push(r),this.xVal.push(e[0]);var n=Number(e[1]);r?this.xSteps.push(!isNaN(n)&&n):isNaN(n)||(this.xSteps[0]=n),this.xHighestCompleteStep.push(0)},t.prototype.handleStepPoint=function(t,e){if(e)if(this.xVal[t]!==this.xVal[t+1]){this.xSteps[t]=x([this.xVal[t],this.xVal[t+1]],e,0)/S(this.xPct[t],this.xPct[t+1]);var r=(this.xVal[t+1]-this.xVal[t])/this.xNumSteps[t],n=Math.ceil(Number(r.toFixed(3))-1),i=this.xVal[t]+this.xNumSteps[t]*n;this.xHighestCompleteStep[t]=i}else this.xSteps[t]=this.xHighestCompleteStep[t]=this.xVal[t]},t}(),C={to:function(t){return void 0===t?\"\":t.toFixed(2)},from:Number},N={target:\"target\",base:\"base\",origin:\"origin\",handle:\"handle\",handleLower:\"handle-lower\",handleUpper:\"handle-upper\",touchArea:\"touch-area\",horizontal:\"horizontal\",vertical:\"vertical\",background:\"background\",connect:\"connect\",connects:\"connects\",ltr:\"ltr\",rtl:\"rtl\",textDirectionLtr:\"txt-dir-ltr\",textDirectionRtl:\"txt-dir-rtl\",draggable:\"draggable\",drag:\"state-drag\",tap:\"state-tap\",active:\"active\",tooltip:\"tooltip\",pips:\"pips\",pipsHorizontal:\"pips-horizontal\",pipsVertical:\"pips-vertical\",marker:\"marker\",markerHorizontal:\"marker-horizontal\",markerVertical:\"marker-vertical\",markerNormal:\"marker-normal\",markerLarge:\"marker-large\",markerSub:\"marker-sub\",value:\"value\",valueHorizontal:\"value-horizontal\",valueVertical:\"value-vertical\",valueNormal:\"value-normal\",valueLarge:\"value-large\",valueSub:\"value-sub\"};r.cssClasses=N;var V={tooltips:\".__tooltips\",aria:\".__aria\"};function A(t,e){if(!p(e))throw new Error(\"noUiSlider: 'step' is not numeric.\");t.singleStep=e}function k(t,e){if(!p(e))throw new Error(\"noUiSlider: 'keyboardPageMultiplier' is not numeric.\");t.keyboardPageMultiplier=e}function M(t,e){if(!p(e))throw new Error(\"noUiSlider: 'keyboardMultiplier' is not numeric.\");t.keyboardMultiplier=e}function U(t,e){if(!p(e))throw new Error(\"noUiSlider: 'keyboardDefaultStep' is not numeric.\");t.keyboardDefaultStep=e}function D(t,e){if(\"object\"!=typeof e||Array.isArray(e))throw new Error(\"noUiSlider: 'range' is not an object.\");if(void 0===e.min||void 0===e.max)throw new Error(\"noUiSlider: Missing 'min' or 'max' in 'range'.\");t.spectrum=new P(e,t.snap||!1,t.singleStep)}function O(t,e){if(e=h(e),!Array.isArray(e)||!e.length)throw new Error(\"noUiSlider: 'start' option is incorrect.\");t.handles=e.length,t.start=e}function L(t,e){if(\"boolean\"!=typeof e)throw new Error(\"noUiSlider: 'snap' option must be a boolean.\");t.snap=e}function T(t,e){if(\"boolean\"!=typeof e)throw new Error(\"noUiSlider: 'animate' option must be a boolean.\");t.animate=e}function z(t,e){if(\"number\"!=typeof e)throw new Error(\"noUiSlider: 'animationDuration' option must be a number.\");t.animationDuration=e}function j(t,e){var r,n=[!1];if(\"lower\"===e?e=[!0,!1]:\"upper\"===e&&(e=[!1,!0]),!0===e||!1===e){for(r=1;r1)throw new Error(\"noUiSlider: 'padding' option must not exceed 100% of the range.\")}}function B(t,e){switch(e){case\"ltr\":t.dir=0;break;case\"rtl\":t.dir=1;break;default:throw new Error(\"noUiSlider: 'direction' option was not recognized.\")}}function q(t,e){if(\"string\"!=typeof e)throw new Error(\"noUiSlider: 'behaviour' must be a string containing options.\");var r=e.indexOf(\"tap\")>=0,n=e.indexOf(\"drag\")>=0,i=e.indexOf(\"fixed\")>=0,o=e.indexOf(\"snap\")>=0,s=e.indexOf(\"hover\")>=0,a=e.indexOf(\"unconstrained\")>=0,l=e.indexOf(\"drag-all\")>=0,u=e.indexOf(\"smooth-steps\")>=0;if(i){if(2!==t.handles)throw new Error(\"noUiSlider: 'fixed' behaviour must be used with 2 handles\");F(t,t.start[1]-t.start[0])}if(a&&(t.margin||t.limit))throw new Error(\"noUiSlider: 'unconstrained' behaviour cannot be used with margin or limit\");t.events={tap:r||o,drag:n,dragAll:l,smoothSteps:u,fixed:i,snap:o,hover:s,unconstrained:a}}function X(t,e){if(!1!==e)if(!0===e||a(e)){t.tooltips=[];for(var r=0;r= 2) required for mode 'count'.\");for(var e=t.values-1,n=100/e,i=[];e--;)i[e]=e*n;return i.push(100),B(i,t.stepped)}return t.mode===r.PipsMode.Positions?B(t.values,t.stepped):t.mode===r.PipsMode.Values?t.stepped?t.values.map((function(t){return E.fromStepping(E.getStep(E.toStepping(t)))})):t.values:[]}(t),i={},o=E.xVal[0],s=E.xVal[E.xVal.length-1],a=!1,l=!1,u=0;return e=n.slice().sort((function(t,e){return t-e})),(n=e.filter((function(t){return!this[t]&&(this[t]=!0)}),{}))[0]!==o&&(n.unshift(o),a=!0),n[n.length-1]!==s&&(n.push(s),l=!0),n.forEach((function(e,o){var s,c,p,f,d,h,m,g,v,b,S=e,x=n[o+1],y=t.mode===r.PipsMode.Steps;for(y&&(s=E.xNumSteps[o]),s||(s=x-S),void 0===x&&(x=S),s=Math.max(s,1e-7),c=S;c<=x;c=Number((c+s).toFixed(7))){for(g=(d=(f=E.toStepping(c))-u)/(t.density||1),b=d/(v=Math.round(g)),p=1;p<=v;p+=1)i[(h=u+p*b).toFixed(5)]=[E.fromStepping(h),0];m=n.indexOf(c)>-1?r.PipsType.LargeValue:y?r.PipsType.SmallValue:r.PipsType.NoValue,!o&&a&&c!==x&&(m=0),c===x&&l||(i[f.toFixed(5)]=[c,m]),u=f}})),i}function X(t,n,i){var o,s,a=M.createElement(\"div\"),l=((o={})[r.PipsType.None]=\"\",o[r.PipsType.NoValue]=e.cssClasses.valueNormal,o[r.PipsType.LargeValue]=e.cssClasses.valueLarge,o[r.PipsType.SmallValue]=e.cssClasses.valueSub,o),u=((s={})[r.PipsType.None]=\"\",s[r.PipsType.NoValue]=e.cssClasses.markerNormal,s[r.PipsType.LargeValue]=e.cssClasses.markerLarge,s[r.PipsType.SmallValue]=e.cssClasses.markerSub,s),c=[e.cssClasses.valueHorizontal,e.cssClasses.valueVertical],p=[e.cssClasses.markerHorizontal,e.cssClasses.markerVertical];function f(t,r){var n=r===e.cssClasses.value,i=n?l:u;return r+\" \"+(n?c:p)[e.ort]+\" \"+i[t]}return g(a,e.cssClasses.pips),g(a,0===e.ort?e.cssClasses.pipsHorizontal:e.cssClasses.pipsVertical),Object.keys(t).forEach((function(o){!function(t,o,s){if((s=n?n(o,s):s)!==r.PipsType.None){var l=L(a,!1);l.className=f(s,e.cssClasses.marker),l.style[e.style]=t+\"%\",s>r.PipsType.NoValue&&((l=L(a,!1)).className=f(s,e.cssClasses.value),l.setAttribute(\"data-value\",String(o)),l.style[e.style]=t+\"%\",l.innerHTML=String(i.to(o)))}}(o,t[o][0],t[o][1])})),a}function Y(){a&&(l(a),a=null)}function I(t){Y();var e=q(t),r=t.filter,n=t.format||{to:function(t){return String(Math.round(t))}};return a=w.appendChild(X(e,r,n))}function W(){var t=i.getBoundingClientRect(),r=\"offset\"+[\"Width\",\"Height\"][e.ort];return 0===e.ort?t.width||i[r]:t.height||i[r]}function $(t,r,n,i){var o=function(o){var s,a,l=function(t,e,r){var n=0===t.type.indexOf(\"touch\"),i=0===t.type.indexOf(\"mouse\"),o=0===t.type.indexOf(\"pointer\"),s=0,a=0;0===t.type.indexOf(\"MSPointer\")&&(o=!0);if(\"mousedown\"===t.type&&!t.buttons&&!t.touches)return!1;if(n){var l=function(e){var n=e.target;return n===r||r.contains(n)||t.composed&&t.composedPath().shift()===r};if(\"touchstart\"===t.type){var u=Array.prototype.filter.call(t.touches,l);if(u.length>1)return!1;s=u[0].pageX,a=u[0].pageY}else{var c=Array.prototype.find.call(t.changedTouches,l);if(!c)return!1;s=c.pageX,a=c.pageY}}e=e||b(M),(i||o)&&(s=t.clientX+e.x,a=t.clientY+e.y);return t.pageOffset=e,t.points=[s,a],t.cursor=i||o,t}(o,i.pageOffset,i.target||r);return!!l&&(!(H()&&!i.doNotReject)&&(s=w,a=e.cssClasses.tap,!((s.classList?s.classList.contains(a):new RegExp(\"\\\\b\"+a+\"\\\\b\").test(s.className))&&!i.doNotReject)&&(!(t===x.start&&void 0!==l.buttons&&l.buttons>1)&&((!i.hover||!l.buttons)&&(y||l.preventDefault(),l.calcPoint=l.points[e.ort],void n(l,i))))))},s=[];return t.split(\" \").forEach((function(t){r.addEventListener(t,o,!!y&&{passive:!0}),s.push([t,o])})),s}function G(t){var r,n,o,s,a,l,u=100*(t-(r=i,n=e.ort,o=r.getBoundingClientRect(),s=r.ownerDocument,a=s.documentElement,l=b(s),/webkit.*Chrome.*Mobile/i.test(navigator.userAgent)&&(l.x=0),n?o.top+l.y-a.clientTop:o.left+l.x-a.clientLeft))/W();return u=d(u),e.dir?100-u:u}function J(t,e){\"mouseout\"===t.type&&\"HTML\"===t.target.nodeName&&null===t.relatedTarget&&Z(t,e)}function K(t,r){if(-1===navigator.appVersion.indexOf(\"MSIE 9\")&&0===t.buttons&&0!==r.buttonsProperty)return Z(t,r);var n=(e.dir?-1:1)*(t.calcPoint-r.startCalcPoint);lt(n>0,100*n/r.baseSize,r.locations,r.handleNumbers,r.connect)}function Z(t,r){r.handle&&(v(r.handle,e.cssClasses.active),A-=1),r.listeners.forEach((function(t){U.removeEventListener(t[0],t[1])})),0===A&&(v(w,e.cssClasses.drag),ct(),t.cursor&&(D.style.cursor=\"\",D.removeEventListener(\"selectstart\",c))),e.events.smoothSteps&&(r.handleNumbers.forEach((function(t){pt(t,C[t],!0,!0,!1,!1)})),r.handleNumbers.forEach((function(t){ot(\"update\",t)}))),r.handleNumbers.forEach((function(t){ot(\"change\",t),ot(\"set\",t),ot(\"end\",t)}))}function tt(t,r){if(!r.handleNumbers.some(F)){var n;if(1===r.handleNumbers.length)n=o[r.handleNumbers[0]].children[0],A+=1,g(n,e.cssClasses.active);t.stopPropagation();var i=[],s=$(x.move,U,K,{target:t.target,handle:n,connect:r.connect,listeners:i,startCalcPoint:t.calcPoint,baseSize:W(),pageOffset:t.pageOffset,handleNumbers:r.handleNumbers,buttonsProperty:t.buttons,locations:C.slice()}),a=$(x.end,U,Z,{target:t.target,handle:n,listeners:i,doNotReject:!0,handleNumbers:r.handleNumbers}),l=$(\"mouseout\",U,J,{target:t.target,handle:n,listeners:i,doNotReject:!0,handleNumbers:r.handleNumbers});i.push.apply(i,s.concat(a,l)),t.cursor&&(D.style.cursor=getComputedStyle(t.target).cursor,o.length>1&&g(w,e.cssClasses.drag),D.addEventListener(\"selectstart\",c,!1)),r.handleNumbers.forEach((function(t){ot(\"start\",t)}))}}function et(t){t.stopPropagation();var r=G(t.calcPoint),n=function(t){var e=100,r=!1;return o.forEach((function(n,i){if(!F(i)){var o=C[i],s=Math.abs(o-t);(so||100===s&&100===e)&&(r=i,e=s)}})),r}(r);!1!==n&&(e.events.snap||f(w,e.cssClasses.tap,e.animationDuration),pt(n,r,!0,!0),ct(),ot(\"slide\",n,!0),ot(\"update\",n,!0),e.events.snap?tt(t,{handleNumbers:[n]}):(ot(\"change\",n,!0),ot(\"set\",n,!0)))}function rt(t){var e=G(t.calcPoint),r=E.getStep(e),n=E.fromStepping(r);Object.keys(k).forEach((function(t){\"hover\"===t.split(\".\")[0]&&k[t].forEach((function(t){t.call(vt,n)}))}))}function nt(t,e){k[t]=k[t]||[],k[t].push(e),\"update\"===t.split(\".\")[0]&&o.forEach((function(t,e){ot(\"update\",e)}))}function it(t){var e=t&&t.split(\".\")[0],r=e?t.substring(e.length):t;Object.keys(k).forEach((function(t){var n=t.split(\".\")[0],i=t.substring(n.length);e&&e!==n||r&&r!==i||function(t){return t===V.aria||t===V.tooltips}(i)&&r!==i||delete k[t]}))}function ot(t,r,n){Object.keys(k).forEach((function(i){var o=i.split(\".\")[0];t===o&&k[i].forEach((function(t){t.call(vt,P.map(e.format.to),r,P.slice(),n||!1,C.slice(),vt)}))}))}function st(t,r,n,i,s,a,l){var u;return o.length>1&&!e.events.unconstrained&&(i&&r>0&&(u=E.getAbsoluteDistance(t[r-1],e.margin,!1),n=Math.max(n,u)),s&&r1&&e.limit&&(i&&r>0&&(u=E.getAbsoluteDistance(t[r-1],e.limit,!1),n=Math.min(n,u)),s&&r1?i.forEach((function(t,e){var n=st(s,t,s[t]+r,u[e],c[e],!1,l);!1===n?r=0:(r=n-s[t],s[t]=n)})):u=c=[!0];var p=!1;i.forEach((function(t,e){p=pt(t,n[t]+r,u[e],c[e],!1,l)||p})),p&&(i.forEach((function(t){ot(\"update\",t),ot(\"slide\",t)})),null!=o&&ot(\"drag\",a))}function ut(t,r){return e.dir?100-t-r:t}function ct(){N.forEach((function(t){var e=C[t]>50?-1:1,r=3+(o.length+e*t);o[t].style.zIndex=String(r)}))}function pt(t,r,n,i,s,a){return s||(r=st(C,t,r,n,i,!1,a)),!1!==r&&(function(t,r){C[t]=r,P[t]=E.fromStepping(r);var n=\"translate(\"+at(ut(r,0)-O+\"%\",\"0\")+\")\";o[t].style[e.transformRule]=n,ft(t),ft(t+1)}(t,r),!0)}function ft(t){if(s[t]){var r=0,n=100;0!==t&&(r=C[t-1]),t!==s.length-1&&(n=C[t]);var i=n-r,o=\"translate(\"+at(ut(r,i)+\"%\",\"0\")+\")\",a=\"scale(\"+at(i/100,\"1\")+\")\";s[t].style[e.transformRule]=o+\" \"+a}}function dt(t,r){return null===t||!1===t||void 0===t?C[r]:(\"number\"==typeof t&&(t=String(t)),!1!==(t=e.format.from(t))&&(t=E.toStepping(t)),!1===t||isNaN(t)?C[r]:t)}function ht(t,r,n){var i=h(t),o=void 0===C[0];r=void 0===r||r,e.animate&&!o&&f(w,e.cssClasses.tap,e.animationDuration),N.forEach((function(t){pt(t,dt(i[t],t),!0,!1,n)}));var s=1===N.length?0:1;if(o&&E.hasNoSize()&&(n=!0,C[0]=0,N.length>1)){var a=100/(N.length-1);N.forEach((function(t){C[t]=t*a}))}for(;sn.stepAfter.startValue&&(o=n.stepAfter.startValue-i),s=i>n.thisStep.startValue?n.thisStep.step:!1!==n.stepBefore.step&&i-n.stepBefore.highestStep,100===r?o=null:0===r&&(s=null);var a=E.countStepDecimals();return null!==o&&!1!==o&&(o=Number(o.toFixed(a))),null!==s&&!1!==s&&(s=Number(s.toFixed(a))),[s,o]}g(m=w,e.cssClasses.target),0===e.dir?g(m,e.cssClasses.ltr):g(m,e.cssClasses.rtl),0===e.ort?g(m,e.cssClasses.horizontal):g(m,e.cssClasses.vertical),g(m,\"rtl\"===getComputedStyle(m).direction?e.cssClasses.textDirectionRtl:e.cssClasses.textDirectionLtr),i=L(m,e.cssClasses.base),function(t,r){var n=L(r,e.cssClasses.connects);o=[],(s=[]).push(z(n,t[0]));for(var i=0;i=0&&tthis._update_slider()))}pretty(e){return this._formatter(e,this.model.format)}}r.BaseNumericalSliderView=l,l.__name__=\"BaseNumericalSliderView\";class o extends n.AbstractSlider{constructor(e){super(e)}}r.BaseNumericalSlider=o,i=o,o.__name__=\"BaseNumericalSlider\",i.define((({Float:e,Str:t,Or:r,Ref:s})=>({start:[e],end:[e],step:[e,1],format:[r(t,s(c.TickFormatter))]})))},\n 668: function _(e,t,r,a,i){var c;a();const o=e(1).__importDefault(e(247)),s=e(669),_=e(8);class l extends s.NumericalSliderView{constructor(){super(...arguments),this.behaviour=\"tap\",this.connected=[!0,!1]}_calc_to(){const e=super._calc_to();return e.step*=864e5,e}_formatter(e,t){return(0,_.isString)(t)?(0,o.default)(e,t):t.compute(e)}}r.DateSliderView=l,l.__name__=\"DateSliderView\";class n extends s.NumericalSlider{constructor(e){super(e)}}r.DateSlider=n,c=n,n.__name__=\"DateSlider\",c.prototype.default_view=l,c.override({format:\"%d %b %Y\"})},\n 669: function _(e,r,t,s,i){s();const a=e(667);class l extends a.BaseNumericalSliderView{_calc_to(){const{start:e,end:r,value:t,step:s}=this.model;return{range:{min:e,max:r},start:[t],step:s}}_calc_from([e]){return Number.isInteger(this.model.start)&&Number.isInteger(this.model.end)&&Number.isInteger(this.model.step)?Math.round(e):e}}t.NumericalSliderView=l,l.__name__=\"NumericalSliderView\";class n extends a.BaseNumericalSlider{constructor(e){super(e)}}t.NumericalSlider=n,n.__name__=\"NumericalSlider\"},\n 670: function _(e,t,r,a,i){var n;a();const o=e(1).__importDefault(e(247)),s=e(666),d=e(8);class c extends s.NumericalRangeSliderView{constructor(){super(...arguments),this.behaviour=\"drag\",this.connected=[!1,!0,!1]}_formatter(e,t){return(0,d.isString)(t)?(0,o.default)(e,t):t.compute(e)}}r.DatetimeRangeSliderView=c,c.__name__=\"DatetimeRangeSliderView\";class l extends s.NumericalRangeSlider{constructor(e){super(e)}}r.DatetimeRangeSlider=l,n=l,l.__name__=\"DatetimeRangeSlider\",n.prototype.default_view=c,n.override({format:\"%d %b %Y %H:%M:%S\",step:36e5})},\n 671: function _(e,r,t,a,i){var n;a();const o=e(1).__importStar(e(245)),s=e(666),c=e(8);class _ extends s.NumericalRangeSliderView{constructor(){super(...arguments),this.behaviour=\"drag\",this.connected=[!1,!0,!1]}_formatter(e,r){return(0,c.isString)(r)?o.format(e,r):r.compute(e)}}t.RangeSliderView=_,_.__name__=\"RangeSliderView\";class d extends s.NumericalRangeSlider{constructor(e){super(e)}}t.RangeSlider=d,n=d,d.__name__=\"RangeSlider\",n.prototype.default_view=_,n.override({format:\"0[.]00\"})},\n 672: function _(e,r,t,i,o){var a;i();const s=e(1).__importStar(e(245)),c=e(669),n=e(8);class _ extends c.NumericalSliderView{constructor(){super(...arguments),this.behaviour=\"tap\",this.connected=[!0,!1]}_formatter(e,r){return(0,n.isString)(r)?s.format(e,r):r.compute(e)}}t.SliderView=_,_.__name__=\"SliderView\";class d extends c.NumericalSlider{constructor(e){super(e)}}t.Slider=d,a=d,d.__name__=\"Slider\",a.prototype.default_view=_,a.override({format:\"0[.]00\"})},\n }, 588, {\"models/widgets/main\":588,\"models/widgets/index\":589,\"models/widgets/abstract_button\":590,\"models/widgets/control\":591,\"models/widgets/widget\":697,\"styles/buttons.css\":593,\"models/widgets/autocomplete_input\":594,\"models/widgets/text_input\":595,\"models/widgets/text_like_input\":596,\"models/widgets/input_widget\":597,\"styles/widgets/inputs.css\":598,\"styles/dropdown.css\":599,\"models/widgets/button\":600,\"models/widgets/checkbox_button_group\":601,\"models/widgets/toggle_button_group\":602,\"models/widgets/oriented_control\":603,\"models/widgets/checkbox_group\":604,\"models/widgets/toggle_input_group\":605,\"styles/widgets/checkbox.css\":606,\"models/widgets/checkbox\":607,\"models/widgets/toggle_input\":608,\"models/widgets/color_picker\":609,\"models/widgets/date_picker\":610,\"models/widgets/base_date_picker\":611,\"models/widgets/picker_base\":612,\"styles/widgets/flatpickr.css\":621,\"models/widgets/date_range_picker\":622,\"models/widgets/datetime_picker\":623,\"models/widgets/base_datetime_picker\":624,\"models/widgets/datetime_range_picker\":625,\"models/widgets/div\":626,\"models/widgets/markup\":627,\"styles/clearfix.css\":628,\"models/widgets/dropdown\":629,\"styles/caret.css\":630,\"models/widgets/file_input\":631,\"models/widgets/help_button\":632,\"models/widgets/multi_choice\":633,\"styles/widgets/choices.css\":635,\"models/widgets/multiselect\":636,\"models/widgets/multiple_date_picker\":637,\"models/widgets/multiple_datetime_picker\":638,\"models/widgets/numeric_input\":639,\"models/widgets/palette_select\":640,\"core/util/panes\":641,\"styles/panes.css\":642,\"styles/widgets/palette_select.css\":643,\"styles/widgets/palette_select_item.css\":644,\"styles/widgets/palette_select_pane.css\":645,\"models/widgets/paragraph\":646,\"models/widgets/password_input\":647,\"styles/widgets/password_input.css\":648,\"models/widgets/pretext\":649,\"models/widgets/radio_button_group\":650,\"models/widgets/radio_group\":651,\"models/widgets/select\":652,\"models/widgets/spinner\":653,\"models/widgets/switch\":654,\"styles/widgets/switch.css\":655,\"models/widgets/textarea_input\":656,\"models/widgets/time_picker\":657,\"models/widgets/toggle\":658,\"models/widgets/sliders/index\":659,\"models/widgets/sliders/categorical_slider\":660,\"models/widgets/sliders/abstract_slider\":661,\"styles/widgets/sliders.css\":663,\"styles/widgets/nouislider.css\":664,\"models/widgets/sliders/date_range_slider\":665,\"models/widgets/sliders/numerical_range_slider\":666,\"models/widgets/sliders/base_numerical_slider\":667,\"models/widgets/sliders/date_slider\":668,\"models/widgets/sliders/numerical_slider\":669,\"models/widgets/sliders/datetime_range_slider\":670,\"models/widgets/sliders/range_slider\":671,\"models/widgets/sliders/slider\":672}, {});});\n\n /* END bokeh-widgets.min.js */\n },\n function(Bokeh) {\n /* BEGIN bokeh-tables.min.js */\n 'use strict';\n /*!\n * Copyright (c) Anaconda, Inc., and Bokeh Contributors\n * All rights reserved.\n * \n * Redistribution and use in source and binary forms, with or without modification,\n * are permitted provided that the following conditions are met:\n * \n * Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n * \n * Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n * \n * Neither the name of Anaconda nor the names of any contributors\n * may be used to endorse or promote products derived from this software\n * without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n * THE POSSIBILITY OF SUCH DAMAGE.\n */\n (function(root, factory) {\n factory(root[\"Bokeh\"], \"3.5.2\");\n })(this, function(Bokeh, version) {\n let define;\n return (function(modules, entry, aliases, externals) {\n const bokeh = typeof Bokeh !== \"undefined\" ? (version != null ? Bokeh[version] : Bokeh) : null;\n if (bokeh != null) {\n return bokeh.register_plugin(modules, entry, aliases);\n } else {\n throw new Error(\"Cannot find Bokeh\" + (version != null ? \" \" + version : \"\") + \". You have to load it prior to loading plugins.\");\n }\n })\n ({\n 673: function _(t,e,o,r,s){r();const _=t(1).__importStar(t(674));o.Tables=_;(0,t(7).register_models)(_)},\n 674: function _(g,a,r,e,t){e();const o=g(1);o.__exportStar(g(675),r),o.__exportStar(g(678),r),t(\"DataTable\",g(681).DataTable),t(\"TableColumn\",g(699).TableColumn),t(\"TableWidget\",g(698).TableWidget);var n=g(701);t(\"AvgAggregator\",n.AvgAggregator),t(\"MinAggregator\",n.MinAggregator),t(\"MaxAggregator\",n.MaxAggregator),t(\"SumAggregator\",n.SumAggregator);var A=g(702);t(\"GroupingInfo\",A.GroupingInfo),t(\"DataCube\",A.DataCube)},\n 675: function _(e,t,i,s,r){var a,l,n,u,d,o,p,_,c;s();const h=e(1),E=e(63),V=e(8),m=e(57),f=e(51),w=e(676),g=h.__importStar(e(677));class x extends m.DOMComponentView{get emptyValue(){return null}constructor(e){const{model:t,parent:i}=e.column;super({model:t,parent:i,...e}),this.args=e,this.initialize(),this.render()}initialize(){super.initialize(),this.inputEl=this._createInput(),this.defaultValue=null}async lazy_initialize(){throw new Error(\"unsupported\")}css_classes(){return super.css_classes().concat(g.cell_editor)}render(){this.args.container.append(this.el),this.shadow_el.appendChild(this.inputEl),this.renderEditor(),this.disableNavigation()}renderEditor(){}disableNavigation(){this.inputEl.addEventListener(\"keydown\",(e=>{switch(e.key){case\"ArrowLeft\":case\"ArrowRight\":case\"ArrowUp\":case\"ArrowDown\":case\"PageUp\":case\"PageDown\":e.stopImmediatePropagation()}}))}destroy(){this.remove()}focus(){this.inputEl.focus()}show(){}hide(){}position(){}getValue(){return this.inputEl.value}setValue(e){this.inputEl.value=e}serializeValue(){return this.getValue()}isValueChanged(){return!(\"\"==this.getValue()&&null==this.defaultValue)&&this.getValue()!==this.defaultValue}applyValue(e,t){const i=this.args.grid.getData(),s=i.index.indexOf(e[w.DTINDEX_NAME]);i.setField(s,this.args.column.field,t)}loadValue(e){const t=e[this.args.column.field];this.defaultValue=null!=t?t:this.emptyValue,this.setValue(this.defaultValue)}validateValue(e){if(this.args.column.validator){const t=this.args.column.validator(e);if(!t.valid)return t}return{valid:!0,msg:null}}validate(){return this.validateValue(this.getValue())}}i.CellEditorView=x,x.__name__=\"CellEditorView\";class v extends f.Model{}i.CellEditor=v,v.__name__=\"CellEditor\";class y extends x{get emptyValue(){return\"\"}_createInput(){return(0,E.input)({type:\"text\"})}renderEditor(){this.inputEl.focus(),this.inputEl.select()}loadValue(e){super.loadValue(e),this.inputEl.defaultValue=this.defaultValue,this.inputEl.select()}}i.StringEditorView=y,y.__name__=\"StringEditorView\";class I extends v{}i.StringEditor=I,a=I,I.__name__=\"StringEditor\",a.prototype.default_view=y,a.define((({Str:e,List:t})=>({completions:[t(e),[]]})));class N extends x{_createInput(){return(0,E.textarea)()}renderEditor(){this.inputEl.focus(),this.inputEl.select()}}i.TextEditorView=N,N.__name__=\"TextEditorView\";class b extends v{}i.TextEditor=b,l=b,b.__name__=\"TextEditor\",l.prototype.default_view=N;class C extends x{_createInput(){return(0,E.select)()}renderEditor(){for(const e of this.model.options)this.inputEl.appendChild((0,E.option)({value:e},e));this.focus()}}i.SelectEditorView=C,C.__name__=\"SelectEditorView\";class S extends v{}i.SelectEditor=S,n=S,S.__name__=\"SelectEditor\",n.prototype.default_view=C,n.define((({Str:e,List:t})=>({options:[t(e),[]]})));class D extends x{_createInput(){return(0,E.input)({type:\"text\"})}}i.PercentEditorView=D,D.__name__=\"PercentEditorView\";class k extends v{}i.PercentEditor=k,u=k,k.__name__=\"PercentEditor\",u.prototype.default_view=D;class z extends x{_createInput(){return(0,E.input)({type:\"checkbox\"})}renderEditor(){this.focus()}loadValue(e){this.defaultValue=!!e[this.args.column.field],this.inputEl.checked=this.defaultValue}serializeValue(){return this.inputEl.checked}}i.CheckboxEditorView=z,z.__name__=\"CheckboxEditorView\";class P extends v{}i.CheckboxEditor=P,d=P,P.__name__=\"CheckboxEditor\",d.prototype.default_view=z;class T extends x{_createInput(){return(0,E.input)({type:\"text\"})}renderEditor(){this.inputEl.focus(),this.inputEl.select()}remove(){super.remove()}serializeValue(){const e=parseInt(this.getValue(),10);return isNaN(e)?0:e}loadValue(e){super.loadValue(e),this.inputEl.defaultValue=this.defaultValue,this.inputEl.select()}validateValue(e){return(0,V.isString)(e)&&(e=Number(e)),(0,V.isInteger)(e)?super.validateValue(e):{valid:!1,msg:\"Please enter a valid integer\"}}}i.IntEditorView=T,T.__name__=\"IntEditorView\";class A extends v{}i.IntEditor=A,o=A,A.__name__=\"IntEditor\",o.prototype.default_view=T,o.define((({Int:e})=>({step:[e,1]})));class L extends x{_createInput(){return(0,E.input)({type:\"text\"})}renderEditor(){this.inputEl.focus(),this.inputEl.select()}remove(){super.remove()}serializeValue(){const e=parseFloat(this.getValue());return isNaN(e)?0:e}loadValue(e){super.loadValue(e),this.inputEl.defaultValue=this.defaultValue,this.inputEl.select()}validateValue(e){return isNaN(e)?{valid:!1,msg:\"Please enter a valid number\"}:super.validateValue(e)}}i.NumberEditorView=L,L.__name__=\"NumberEditorView\";class F extends v{}i.NumberEditor=F,p=F,F.__name__=\"NumberEditor\",p.prototype.default_view=L,p.define((({Float:e})=>({step:[e,.01]})));class M extends x{_createInput(){return(0,E.input)({type:\"text\"})}}i.TimeEditorView=M,M.__name__=\"TimeEditorView\";class O extends v{}i.TimeEditor=O,_=O,O.__name__=\"TimeEditor\",_.prototype.default_view=M;class U extends x{_createInput(){return(0,E.input)({type:\"text\"})}get emptyValue(){return new Date}renderEditor(){this.inputEl.focus(),this.inputEl.select()}destroy(){super.destroy()}show(){super.show()}hide(){super.hide()}position(){return super.position()}getValue(){}setValue(e){}}i.DateEditorView=U,U.__name__=\"DateEditorView\";class R extends v{}i.DateEditor=R,c=R,R.__name__=\"DateEditor\",c.prototype.default_view=U},\n 676: function _(_,n,i,t,d){t(),i.DTINDEX_NAME=\"__bkdt_internal_index__\"},\n 677: function _(e,l,t,i,r){i(),t.data_table=\"bk-data-table\",t.cell_special_defaults=\"bk-cell-special-defaults\",t.cell_select=\"bk-cell-select\",t.cell_index=\"bk-cell-index\",t.header_index=\"bk-header-index\",t.cell_editor=\"bk-cell-editor\",t.cell_editor_completion=\"bk-cell-editor-completion\",t.default=':host{--data-table-font-size:var(--font-size);}.bk-data-table{box-sizing:content-box;width:100%;height:100%;font-size:var(--data-table-font-size);}.bk-data-table input[type=\"checkbox\"]{margin-left:4px;margin-right:4px;}.bk-cell-special-defaults{border-right-color:silver;border-right-style:solid;background:#f5f5f5;}.bk-cell-select{border-right-color:silver;border-right-style:solid;background:#f5f5f5;}.slick-cell.bk-cell-index{border-right-color:silver;border-right-style:solid;background:#f5f5f5;text-align:right;background:#f0f0f0;color:#909090;}.bk-header-index .slick-column-name{float:right;}.slick-row.selected .bk-cell-index{background-color:transparent;}.slick-row.odd{background:#f0f0f0;}.slick-cell{padding-left:4px;padding-right:4px;border-right-color:transparent;border:0.25px solid transparent;}.slick-cell .bk{line-height:inherit;}.slick-cell.active{border-style:dashed;}.slick-cell.selected{background-color:#F0F8FF;}.slick-cell.editable{padding-left:0;padding-right:0;}.bk-cell-editor{display:contents;}.bk-cell-editor input,.bk-cell-editor select{width:100%;height:100%;border:0;margin:0;padding:0;outline:0;background:transparent;vertical-align:baseline;}.bk-cell-editor input{padding-left:4px;padding-right:4px;}.bk-cell-editor-completion{font-size:var(--data-table-font-size);}'},\n 678: function _(e,t,r,o,a){var l,s,n,i,c,u;o();const m=e(1),_=m.__importDefault(e(247)),d=m.__importStar(e(245)),f=e(679),p=m.__importStar(e(18)),g=e(63),F=e(28),h=e(20),b=e(8),S=e(40),x=e(22),y=e(51),C=e(215),v=e(12);class M extends y.Model{constructor(e){super(e)}doFormat(e,t,r,o,a){return null==r?\"\":`${r}`.replace(/&/g,\"&\").replace(//g,\">\")}}r.CellFormatter=M,M.__name__=\"CellFormatter\";class w extends M{constructor(e){super(e)}doFormat(e,t,r,o,a){const{font_style:l,text_align:s,text_color:n,background_color:i}=this,c=(0,g.div)(null==r?\"\":`${r}`);let u;switch((0,F.isValue)(l)?u=l.value:(0,F.isField)(l)?u=a[l.field]:(0,F.isExpr)(l)||(0,v.unreachable)(),u){case\"normal\":break;case\"italic\":c.style.fontStyle=\"italic\";break;case\"bold\":c.style.fontWeight=\"bold\";break;case\"bold italic\":c.style.fontStyle=\"italic\",c.style.fontWeight=\"bold\"}if((0,F.isValue)(s)?c.style.textAlign=s.value:(0,F.isField)(s)?c.style.textAlign=a[s.field]:(0,F.isExpr)(s)||(0,v.unreachable)(),(0,F.isValue)(n))null!=n.value&&(c.style.color=(0,x.color2css)(n.value));else if((0,F.isField)(n))if(null!=n.transform&&n.transform instanceof C.ColorMapper){const e=n.transform.rgba_mapper.v_compute([a[n.field]]),[t,r,o,l]=e;c.style.color=(0,x.rgba2css)([t,r,o,l])}else c.style.color=(0,x.color2css)(a[n.field]);else(0,F.isExpr)(n)||(0,v.unreachable)();if((0,F.isValue)(i))null!=i.value&&(c.style.backgroundColor=(0,x.color2css)(i.value));else if((0,F.isField)(i))if(null!=i.transform&&i.transform instanceof C.ColorMapper){const e=i.transform.rgba_mapper.v_compute([a[i.field]]),[t,r,o,l]=e;c.style.backgroundColor=(0,x.rgba2css)([t,r,o,l])}else c.style.backgroundColor=(0,x.color2css)(a[i.field]);else(0,F.isExpr)(i)||(0,v.unreachable)();return c.outerHTML}}r.StringFormatter=w,l=w,w.__name__=\"StringFormatter\",l.define((({Str:e})=>({font_style:[p.FontStyleSpec,{value:\"normal\"}],text_align:[p.TextAlignSpec,{value:\"left\"}],text_color:[p.ColorSpec,null],background_color:[p.ColorSpec,null],nan_format:[e,\"-\"]})));class N extends w{constructor(e){super(e)}get scientific_limit_low(){return 10**this.power_limit_low}get scientific_limit_high(){return 10**this.power_limit_high}doFormat(e,t,r,o,a){const l=Math.abs(r)<=this.scientific_limit_low||Math.abs(r)>=this.scientific_limit_high;let s=this.precision;return s<1&&(s=1),r=null==r||isNaN(r)?this.nan_format:0==r?(0,S.to_fixed)(r,1):l?r.toExponential(s):(0,S.to_fixed)(r,s),super.doFormat(e,t,r,o,a)}}r.ScientificFormatter=N,s=N,N.__name__=\"ScientificFormatter\",s.define((({Float:e})=>({precision:[e,10],power_limit_high:[e,5],power_limit_low:[e,-3]})));class T extends w{constructor(e){super(e)}doFormat(e,t,r,o,a){const{format:l,language:s,nan_format:n}=this,i=(()=>{switch(this.rounding){case\"round\":case\"nearest\":return Math.round;case\"floor\":case\"rounddown\":return Math.floor;case\"ceil\":case\"roundup\":return Math.ceil}})();return r=null==r||isNaN(r)?n:d.format(r,l,s,i),super.doFormat(e,t,r,o,a)}}r.NumberFormatter=T,n=T,T.__name__=\"NumberFormatter\",n.define((({Str:e})=>({format:[e,\"0,0\"],language:[e,\"en\"],rounding:[h.RoundingFunction,\"round\"]})));class k extends M{constructor(e){super(e)}doFormat(e,t,r,o,a){return r?(0,g.i)({class:this.icon}).outerHTML:\"\"}}r.BooleanFormatter=k,i=k,k.__name__=\"BooleanFormatter\",i.define((({Str:e})=>({icon:[e,\"check\"]})));class R extends w{constructor(e){super(e)}getFormat(){switch(this.format){case\"ATOM\":case\"W3C\":case\"RFC-3339\":case\"ISO-8601\":return\"%Y-%m-%d\";case\"COOKIE\":return\"%a, %d %b %Y\";case\"RFC-850\":return\"%A, %d-%b-%y\";case\"RFC-1123\":case\"RFC-2822\":return\"%a, %e %b %Y\";case\"RSS\":case\"RFC-822\":case\"RFC-1036\":return\"%a, %e %b %y\";case\"TIMESTAMP\":return;default:return this.format}}doFormat(e,t,r,o,a){const l=(()=>{if(null==r||(0,b.isNumber)(r))return r;if((0,b.isString)(r)){const e=Number(r);return isNaN(e)?function(e){const t=/Z$|[+-]\\d\\d((:?)\\d\\d)?$/.test(e);return new Date(t?e:`${e}Z`).getTime()}(r):e}return r instanceof Date?r.valueOf():Number(r)})(),s=(()=>null==l||isNaN(l)||-9223372036854776==l?this.nan_format:(0,_.default)(l,this.getFormat()))();return super.doFormat(e,t,s,o,a)}}r.DateFormatter=R,c=R,R.__name__=\"DateFormatter\",c.define((({Str:e})=>({format:[e,\"ISO-8601\"]})));class E extends M{constructor(e){super(e)}doFormat(e,t,r,o,a){const{template:l}=this;if(null==r)return\"\";return f._.template(l)({...a,value:r})}}r.HTMLTemplateFormatter=E,u=E,E.__name__=\"HTMLTemplateFormatter\",u.define((({Str:e})=>({template:[e,\"<%= value %>\"]})))},\n 679: function _(e,n,t,f,i){var o=e(680),d=o.template;function r(e,n,t){return d(e,n,t)}r._=o,n.exports=r,\"function\"==typeof define&&define.amd?define((function(){return r})):\"undefined\"==typeof window&&\"undefined\"==typeof navigator||(window.UnderscoreTemplate=r)},\n 680: function _(r,e,n,t,a){\n // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n // Underscore may be freely distributed under the MIT license.\n var u={},c=Array.prototype,o=Object.prototype,l=c.slice,i=o.toString,f=o.hasOwnProperty,s=c.forEach,p=Object.keys,_=Array.isArray,h=function(){},v=h.each=h.forEach=function(r,e,n){if(null!=r)if(s&&r.forEach===s)r.forEach(e,n);else if(r.length===+r.length){for(var t=0,a=r.length;t\":\">\",'\"':\""\",\"'\":\"'\"}},y={escape:new RegExp(\"[\"+h.keys(g.escape).join(\"\")+\"]\",\"g\")};h.each([\"escape\"],(function(r){h[r]=function(e){return null==e?\"\":(\"\"+e).replace(y[r],(function(e){return g[r][e]}))}})),h.templateSettings={evaluate:/<%([\\s\\S]+?)%>/g,interpolate:/<%=([\\s\\S]+?)%>/g,escape:/<%-([\\s\\S]+?)%>/g};var j=/(.)^/,b={\"'\":\"'\",\"\\\\\":\"\\\\\",\"\\r\":\"r\",\"\\n\":\"n\",\"\\t\":\"t\",\"\\u2028\":\"u2028\",\"\\u2029\":\"u2029\"},w=/\\\\|'|\\r|\\n|\\t|\\u2028|\\u2029/g;h.template=function(r,e,n){var t;n=h.defaults({},n,h.templateSettings);var a=new RegExp([(n.escape||j).source,(n.interpolate||j).source,(n.evaluate||j).source].join(\"|\")+\"|$\",\"g\"),u=0,c=\"__p+='\";r.replace(a,(function(e,n,t,a,o){return c+=r.slice(u,o).replace(w,(function(r){return\"\\\\\"+b[r]})),n&&(c+=\"'+\\n((__t=(\"+n+\"))==null?'':_.escape(__t))+\\n'\"),t&&(c+=\"'+\\n((__t=(\"+t+\"))==null?'':__t)+\\n'\"),a&&(c+=\"';\\n\"+a+\"\\n__p+='\"),u=o+e.length,e})),c+=\"';\\n\",n.variable||(c=\"with(obj||{}){\\n\"+c+\"}\\n\"),c=\"var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\\n\"+c+\"return __p;\\n\";try{t=new Function(n.variable||\"obj\",\"_\",c)}catch(r){throw r.source=c,r}if(e)return t(e,h);var o=function(r){return t.call(this,r,h)};return o.source=\"function(\"+(n.variable||\"obj\")+\"){\\n\"+c+\"}\",o},e.exports=h},\n 681: function _(e,t,i,s,o){var r;s();const l=e(1),n=e(682),d=e(686),a=e(687),h=e(688),c=e(63),u=e(9),_=e(40),m=e(8),g=e(10),f=e(13),p=e(30),w=e(19),b=e(697),v=e(676),x=e(698),z=e(699),C=e(56),y=l.__importStar(e(677)),A=y,D=l.__importDefault(e(700));i.AutosizeModes={fit_columns:\"FCV\",fit_viewport:\"FVC\",force_fit:\"LFF\",none:\"NOA\"};let M=!1;class N{constructor(e,t){this.init(e,t)}init(e,t){if(v.DTINDEX_NAME in e.data)throw new Error(`special name ${v.DTINDEX_NAME} cannot be used as a data table column`);this.source=e,this.view=t,this.index=[...this.view.indices]}getLength(){return this.index.length}getItem(e){const t={},i=(0,u.dict)(this.source.data);for(const[s,o]of i){const i=this.index[e],r=(0,p.is_NDArray)(o)?o.get(i):o[i];t[s]=r}return t[v.DTINDEX_NAME]=this.index[e],t}getField(e,t){if(t==v.DTINDEX_NAME)return this.index[e];{const i=(0,u.dict)(this.source.data).get(t)??[],s=this.index[e];return(0,p.is_NDArray)(i)?i.get(s):i[s]}}setField(e,t,i){const s=this.index[e],o=new Map([[t,[[s,i]]]]);this.source.patch(o)}getRecords(){return(0,g.range)(0,this.getLength()).map((e=>this.getItem(e)))}getItems(){return this.getRecords()}slice(e,t,i=1){return t=t??this.getLength(),(0,g.range)(e,t,i).map((e=>this.getItem(e)))}sort(e){let t=e.map((e=>[e.sortCol.field,e.sortAsc?1:-1]));0==t.length&&(t=[[v.DTINDEX_NAME,1]]);const i=this.getRecords(),s=this.index.slice();this.index.sort(((e,o)=>{for(const[r,l]of t){const t=i[s.indexOf(e)][r],n=i[s.indexOf(o)][r];if(t!==n){if((0,m.isNumber)(t)&&(0,m.isNumber)(n))return l*(t-n||+isNaN(t)-+isNaN(n));{const e=`${t}`.localeCompare(`${n}`);if(0==e)continue;return l*e}}}return 0}))}}i.TableDataProvider=N,N.__name__=\"TableDataProvider\";class S extends b.WidgetView{constructor(){super(...arguments),this._in_selection_update=!1,this._width=null,this._filtered_selection=[]}get data_source(){return this.model.properties.source}*children(){yield*super.children(),yield this.cds_view}async lazy_initialize(){await super.lazy_initialize(),this.cds_view=await(0,C.build_view)(this.model.view,{parent:this})}remove(){this.cds_view.remove(),this.grid.destroy(),super.remove()}connect_signals(){super.connect_signals(),this.connect(this.model.change,(()=>{this.render(),this.r_after_render()}));for(const e of this.model.columns)this.connect(e.change,(()=>{this.render(),this.r_after_render()}));this.connect(this.model.view.change,(()=>this.updateGrid())),this.connect(this.model.source.selected.change,(()=>this.updateSelection())),this.connect(this.model.source.selected.properties.indices.change,(()=>this.updateSelection()))}stylesheets(){return[...super.stylesheets(),D.default,y.default]}_after_resize(){super._after_resize(),this.grid.resizeCanvas(),this.updateLayout(!0,!1)}_after_layout(){super._after_layout(),this.grid.resizeCanvas(),this.updateLayout(!0,!1)}box_sizing(){const e=super.box_sizing();return\"fit_viewport\"===this.model.autosize_mode&&null!=this._width&&(e.width=this._width),e}updateLayout(e,t){const s=this.autosize;s===i.AutosizeModes.fit_columns||s===i.AutosizeModes.force_fit?(e||this.grid.resizeCanvas(),this.grid.autosizeColumns()):e&&t&&s===i.AutosizeModes.fit_viewport&&this.invalidate_layout()}updateGrid(){if(this.data.init(this.model.source,this.model.view),this.model.sortable){const e=this.grid.getColumns(),t=this.grid.getSortColumns().map((t=>({sortCol:{field:e[this.grid.getColumnIndex(t.columnId)].field},sortAsc:t.sortAsc})));this.data.sort(t)}this._sync_selected_with_view(),this.updateSelection(),this.grid.invalidate(),this.updateLayout(!0,!0)}updateSelection(){if(!1===this.model.selectable||this._in_selection_update)return;const{indices:e}=this.model.source.selected,t=(0,g.sort_by)((0,g.map)(e,(e=>this.data.index.indexOf(e))),(e=>e));this._in_selection_update=!0;try{this.grid.setSelectedRows([...t])}finally{this._in_selection_update=!1}const i=this.grid.getViewport(),s=this.model.get_scroll_index(i,t);null!=s&&this.grid.scrollRowToTop(s)}newIndexColumn(){return{id:(0,_.unique_id)(),name:this.model.index_header,field:v.DTINDEX_NAME,width:this.model.index_width,behavior:\"select\",cannotTriggerInsert:!0,resizable:!1,selectable:!1,sortable:!0,cssClass:A.cell_index,headerCssClass:A.header_index}}get autosize(){let e;return e=!0===this.model.fit_columns?i.AutosizeModes.force_fit:!1===this.model.fit_columns?i.AutosizeModes.none:i.AutosizeModes[this.model.autosize_mode],e}render(){super.render(),this.wrapper_el=(0,c.div)({class:A.data_table}),this.shadow_el.appendChild(this.wrapper_el)}_render_table(){const e=this.model.columns.filter((e=>e.visible)).map((e=>({...e.toColumn(),parent:this})));let t=null;if(\"checkbox\"==this.model.selectable&&(t=new d.CheckboxSelectColumn({cssClass:A.cell_select}),e.unshift(t.getColumnDefinition())),null!=this.model.index_position){const t=this.model.index_position,i=this.newIndexColumn();-1==t?e.push(i):t<-1?e.splice(t+1,0,i):e.splice(t,0,i)}let{reorderable:s}=this.model;!s||\"undefined\"!=typeof $&&void 0!==$.fn&&\"sortable\"in $.fn||(M||(w.logger.warn(\"jquery-ui is required to enable DataTable.reorderable\"),M=!0),s=!1);let o=-1,r=!1;const{frozen_rows:l,frozen_columns:c}=this.model,u=null==c?-1:c-1;null!=l&&(r=l<0,o=Math.abs(l));const _={enableCellNavigation:!1!==this.model.selectable,enableColumnReorder:s,autosizeColsMode:this.autosize,multiColumnSort:this.model.sortable,editable:this.model.editable,autoEdit:this.model.auto_edit,autoHeight:!1,rowHeight:this.model.row_height,frozenColumn:u,frozenRow:o,frozenBottom:r,explicitInitialization:!1};if(this.data=new N(this.model.source,this.model.view),this.grid=new h.Grid(this.wrapper_el,this.data,e,_),this.autosize==i.AutosizeModes.fit_viewport){this.grid.autosizeColumns();let t=0;for(const i of e)t+=i.width??0;this._width=Math.ceil(t)}if(this.grid.onSort.subscribe(((e,t)=>{if(!this.model.sortable)return;const i=t.sortCols;null!=i&&(this.data.sort(i),this.grid.invalidate(),this.updateSelection(),this.grid.render(),this.model.header_row||this._hide_header(),this.model.update_sort_columns(i))})),!1!==this.model.selectable){this.grid.setSelectionModel(new n.RowSelectionModel({selectActiveRow:null==t})),null!=t&&this.grid.registerPlugin(t);const e={dataItemColumnValueExtractor(e,t){let i=e[t.field];return(0,m.isString)(i)&&(i=i.replace(/\\n/g,\"\\\\n\")),i},includeHeaderWhenCopying:!1};this.grid.registerPlugin(new a.CellExternalCopyManager(e)),this.grid.onSelectedRowsChanged.subscribe(((e,t)=>{this._in_selection_update||(this.model.source.selected.indices=t.rows.map((e=>this.data.index[e])))})),this.updateSelection(),this.model.header_row||this._hide_header()}}_after_render(){const e=void 0!==this.grid;this._render_table(),this.updateLayout(e,!1),super._after_render()}_hide_header(){for(const e of this.shadow_el.querySelectorAll(\".slick-header-columns\"))e.style.height=\"0px\";this.grid.resizeCanvas()}get_selected_rows(){return this.grid.getSelectedRows()}_sync_selected_with_view(){const e=this.data.view.indices,{source:t}=this.data,i=(0,f.filter)(t.selected.indices,(t=>e.get(t))),s=new Set((0,f.filter)(this._filtered_selection,(t=>e.get(t))));this._filtered_selection=[...(0,f.filter)(this._filtered_selection,(e=>!s.has(e))),...(0,f.filter)(t.selected.indices,(t=>!e.get(t)))],t.selected.indices=[...s,...i]}}i.DataTableView=S,S.__name__=\"DataTableView\";class I extends x.TableWidget{get sort_columns(){return this._sort_columns}constructor(e){super(e),this._sort_columns=[]}update_sort_columns(e){this._sort_columns=e.map((({sortCol:e,sortAsc:t})=>({field:e.field,sortAsc:t})))}get_scroll_index(e,t){return this.scroll_to_selection&&0!=t.length?(0,g.some)(t,(t=>e.top<=t&&t<=e.bottom))?null:Math.max(0,Math.min(...t)-1):null}}i.DataTable=I,r=I,I.__name__=\"DataTable\",r.prototype.default_view=S,r.define((({List:e,Bool:t,Int:i,Ref:s,Str:o,Enum:r,Or:l,Nullable:n})=>({autosize_mode:[r(\"fit_columns\",\"fit_viewport\",\"none\",\"force_fit\"),\"force_fit\"],auto_edit:[t,!1],columns:[e(s(z.TableColumn)),[]],fit_columns:[n(t),null],frozen_columns:[n(i),null],frozen_rows:[n(i),null],sortable:[t,!0],reorderable:[t,!0],editable:[t,!1],selectable:[l(t,r(\"checkbox\")),!0],index_position:[n(i),0],index_header:[o,\"#\"],index_width:[i,40],scroll_to_selection:[t,!0],header_row:[t,!0],row_height:[i,25]}))),r.override({width:600,height:400})},\n 682: function _(e,t,n,o,r){var l=e(683),i=e(685);t.exports={RowSelectionModel:function(e){var t,n,o,r=[],c=this,u=new i.EventHandler,s={selectActiveRow:!0};function a(e){return function(){n||(n=!0,e.apply(this,arguments),n=!1)}}function f(e){for(var t=[],n=0;n=0&&r0&&t-1 in e)}function S(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}T.fn=T.prototype={jquery:b,constructor:T,length:0,toArray:function(){return i.call(this)},get:function(e){return null==e?i.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=T.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return T.each(this,e)},map:function(e){return this.pushStack(T.map(this,(function(t,n){return e.call(t,n,t)})))},slice:function(){return this.pushStack(i.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(T.grep(this,(function(e,t){return(t+1)%2})))},odd:function(){return this.pushStack(T.grep(this,(function(e,t){return t%2})))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|\"+A+\")\"+A+\"*\"),$=new RegExp(A+\"|>\"),B=new RegExp(R),_=new RegExp(\"^\"+P+\"$\"),z={ID:new RegExp(\"^#(\"+P+\")\"),CLASS:new RegExp(\"^\\\\.(\"+P+\")\"),TAG:new RegExp(\"^(\"+P+\"|[*])\"),ATTR:new RegExp(\"^\"+M),PSEUDO:new RegExp(\"^\"+R),CHILD:new RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+A+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+A+\"*(?:([+-]|)\"+A+\"*(\\\\d+)|))\"+A+\"*\\\\)|)\",\"i\"),bool:new RegExp(\"^(?:\"+O+\")$\",\"i\"),needsContext:new RegExp(\"^\"+A+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+A+\"*((?:-\\\\d)?\\\\d*)\"+A+\"*\\\\)|)(?=[^-]|$)\",\"i\")},X=/^(?:input|select|textarea|button)$/i,U=/^h\\d$/i,V=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,G=/[+~]/,Y=new RegExp(\"\\\\\\\\[\\\\da-fA-F]{1,6}\"+A+\"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\",\"g\"),Q=function(e,t){var n=\"0x\"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},J=function(){ue()},K=pe((function(e){return!0===e.disabled&&S(e,\"fieldset\")}),{dir:\"parentNode\",next:\"legend\"});try{v.apply(n=i.call(L.childNodes),L.childNodes),n[L.childNodes.length].nodeType}catch(e){v={apply:function(e,t){H.apply(e,i.call(t))},call:function(e){H.apply(e,i.call(arguments,1))}}}function Z(e,t,n,r){var i,o,a,s,u,c,f,g=t&&t.ownerDocument,m=t?t.nodeType:9;if(n=n||[],\"string\"!=typeof e||!e||1!==m&&9!==m&&11!==m)return n;if(!r&&(ue(t),t=t||l,p)){if(11!==m&&(u=V.exec(e)))if(i=u[1]){if(9===m){if(!(a=t.getElementById(i)))return n;if(a.id===i)return v.call(n,a),n}else if(g&&(a=g.getElementById(i))&&Z.contains(t,a)&&a.id===i)return v.call(n,a),n}else{if(u[2])return v.apply(n,t.getElementsByTagName(e)),n;if((i=u[3])&&t.getElementsByClassName)return v.apply(n,t.getElementsByClassName(i)),n}if(!(N[e+\" \"]||h&&h.test(e))){if(f=e,g=t,1===m&&($.test(e)||F.test(e))){for((g=G.test(e)&&se(t.parentNode)||t)==t&&d.scope||((s=t.getAttribute(\"id\"))?s=T.escapeSelector(s):t.setAttribute(\"id\",s=y)),o=(c=ce(e)).length;o--;)c[o]=(s?\"#\"+s:\":scope\")+\" \"+fe(c[o]);f=c.join(\",\")}try{return v.apply(n,g.querySelectorAll(f)),n}catch(t){N(e,!0)}finally{s===y&&t.removeAttribute(\"id\")}}}return me(e.replace(D,\"$1\"),t,n,r)}function ee(){var e=[];return function t(n,i){return e.push(n+\" \")>r.cacheLength&&delete t[e.shift()],t[n+\" \"]=i}}function te(e){return e[y]=!0,e}function ne(e){var t=l.createElement(\"fieldset\");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function re(e){return function(t){return S(t,\"input\")&&t.type===e}}function ie(e){return function(t){return(S(t,\"input\")||S(t,\"button\"))&&t.type===e}}function oe(e){return function(t){return\"form\"in t?t.parentNode&&!1===t.disabled?\"label\"in t?\"label\"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&K(t)===e:t.disabled===e:\"label\"in t&&t.disabled===e}}function ae(e){return te((function(t){return t=+t,te((function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))}))}))}function se(e){return e&&void 0!==e.getElementsByTagName&&e}function ue(e){var t,n=e?e.ownerDocument||e:L;return n!=l&&9===n.nodeType&&n.documentElement?(f=(l=n).documentElement,p=!T.isXMLDoc(l),g=f.matches||f.webkitMatchesSelector||f.msMatchesSelector,f.msMatchesSelector&&L!=l&&(t=l.defaultView)&&t.top!==t&&t.addEventListener(\"unload\",J),d.getById=ne((function(e){return f.appendChild(e).id=T.expando,!l.getElementsByName||!l.getElementsByName(T.expando).length})),d.disconnectedMatch=ne((function(e){return g.call(e,\"*\")})),d.scope=ne((function(){return l.querySelectorAll(\":scope\")})),d.cssHas=ne((function(){try{return l.querySelector(\":has(*,:jqfake)\"),!1}catch(e){return!0}})),d.getById?(r.filter.ID=function(e){var t=e.replace(Y,Q);return function(e){return e.getAttribute(\"id\")===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&p){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Y,Q);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode(\"id\");return n&&n.value===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&p){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o]}return[]}}),r.find.TAG=function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},r.find.CLASS=function(e,t){if(void 0!==t.getElementsByClassName&&p)return t.getElementsByClassName(e)},h=[],ne((function(e){var t;f.appendChild(e).innerHTML=\"\",e.querySelectorAll(\"[selected]\").length||h.push(\"\\\\[\"+A+\"*(?:value|\"+O+\")\"),e.querySelectorAll(\"[id~=\"+y+\"-]\").length||h.push(\"~=\"),e.querySelectorAll(\"a#\"+y+\"+*\").length||h.push(\".#.+[+~]\"),e.querySelectorAll(\":checked\").length||h.push(\":checked\"),(t=l.createElement(\"input\")).setAttribute(\"type\",\"hidden\"),e.appendChild(t).setAttribute(\"name\",\"D\"),f.appendChild(e).disabled=!0,2!==e.querySelectorAll(\":disabled\").length&&h.push(\":enabled\",\":disabled\"),(t=l.createElement(\"input\")).setAttribute(\"name\",\"\"),e.appendChild(t),e.querySelectorAll(\"[name='']\").length||h.push(\"\\\\[\"+A+\"*name\"+A+\"*=\"+A+\"*(?:''|\\\"\\\")\")})),d.cssHas||h.push(\":has\"),h=h.length&&new RegExp(h.join(\"|\")),q=function(e,t){if(e===t)return u=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===l||e.ownerDocument==L&&Z.contains(L,e)?-1:t===l||t.ownerDocument==L&&Z.contains(L,t)?1:a?s.call(a,e)-s.call(a,t):0:4&n?-1:1)},l):l}for(t in Z.matches=function(e,t){return Z(e,null,null,t)},Z.matchesSelector=function(e,t){if(ue(e),p&&!N[t+\" \"]&&(!h||!h.test(t)))try{var n=g.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return Z(t,l,null,[e]).length>0},Z.contains=function(e,t){return(e.ownerDocument||e)!=l&&ue(e),T.contains(e,t)},Z.attr=function(e,t){(e.ownerDocument||e)!=l&&ue(e);var n=r.attrHandle[t.toLowerCase()],i=n&&c.call(r.attrHandle,t.toLowerCase())?n(e,t,!p):void 0;return void 0!==i?i:e.getAttribute(t)},Z.error=function(e){throw new Error(\"Syntax error, unrecognized expression: \"+e)},T.uniqueSort=function(e){var t,n=[],r=0,o=0;if(u=!d.sortStable,a=!d.sortStable&&i.call(e,0),k.call(e,q),u){for(;t=e[o++];)t===e[o]&&(r=n.push(o));for(;r--;)j.call(e,n[r],1)}return a=null,e},T.fn.uniqueSort=function(){return this.pushStack(T.uniqueSort(i.apply(this)))},r=T.expr={cacheLength:50,createPseudo:te,match:z,attrHandle:{},find:{},relative:{\">\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Y,Q),e[3]=(e[3]||e[4]||e[5]||\"\").replace(Y,Q),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||Z.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&Z.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return z.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||\"\":n&&B.test(n)&&(t=ce(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Y,Q).toLowerCase();return\"*\"===e?function(){return!0}:function(e){return S(e,t)}},CLASS:function(e){var t=b[e+\" \"];return t||(t=new RegExp(\"(^|\"+A+\")\"+e+\"(\"+A+\"|$)\"))&&b(e,(function(e){return t.test(\"string\"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute(\"class\")||\"\")}))},ATTR:function(e,t,n){return function(r){var i=Z.attr(r,e);return null==i?\"!=\"===t:!t||(i+=\"\",\"=\"===t?i===n:\"!=\"===t?i!==n:\"^=\"===t?n&&0===i.indexOf(n):\"*=\"===t?n&&i.indexOf(n)>-1:\"$=\"===t?n&&i.slice(-n.length)===n:\"~=\"===t?(\" \"+i.replace(I,\" \")+\" \").indexOf(n)>-1:\"|=\"===t&&(i===n||i.slice(0,n.length+1)===n+\"-\"))}},CHILD:function(e,t,n,r,i){var o=\"nth\"!==e.slice(0,3),a=\"last\"!==e.slice(-4),s=\"of-type\"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h=o!==a?\"nextSibling\":\"previousSibling\",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),x=!u&&!s,b=!1;if(g){if(o){for(;h;){for(f=t;f=f[h];)if(s?S(f,v):1===f.nodeType)return!1;d=h=\"only\"===e&&!d&&\"nextSibling\"}return!0}if(d=[a?g.firstChild:g.lastChild],a&&x){for(b=(p=(l=(c=g[y]||(g[y]={}))[e]||[])[0]===m&&l[1])&&l[2],f=p&&g.childNodes[p];f=++p&&f&&f[h]||(b=p=0)||d.pop();)if(1===f.nodeType&&++b&&f===t){c[e]=[m,p,b];break}}else if(x&&(b=p=(l=(c=t[y]||(t[y]={}))[e]||[])[0]===m&&l[1]),!1===b)for(;(f=++p&&f&&f[h]||(b=p=0)||d.pop())&&(!(s?S(f,v):1===f.nodeType)||!++b||(x&&((c=f[y]||(f[y]={}))[e]=[m,b]),f!==t)););return(b-=i)===r||b%r==0&&b/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||Z.error(\"unsupported pseudo: \"+e);return i[y]?i(t):i.length>1?(n=[e,e,\"\",t],r.setFilters.hasOwnProperty(e.toLowerCase())?te((function(e,n){for(var r,o=i(e,t),a=o.length;a--;)e[r=s.call(e,o[a])]=!(n[r]=o[a])})):function(e){return i(e,0,n)}):i}},pseudos:{not:te((function(e){var t=[],n=[],r=ye(e.replace(D,\"$1\"));return r[y]?te((function(e,t,n,i){for(var o,a=r(e,null,i,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))})):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}})),has:te((function(e){return function(t){return Z(e,t).length>0}})),contains:te((function(e){return e=e.replace(Y,Q),function(t){return(t.textContent||T.text(t)).indexOf(e)>-1}})),lang:te((function(e){return _.test(e||\"\")||Z.error(\"unsupported lang: \"+e),e=e.replace(Y,Q).toLowerCase(),function(t){var n;do{if(n=p?t.lang:t.getAttribute(\"xml:lang\")||t.getAttribute(\"lang\"))return(n=n.toLowerCase())===e||0===n.indexOf(e+\"-\")}while((t=t.parentNode)&&1===t.nodeType);return!1}})),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===function(){try{return l.activeElement}catch(e){}}()&&l.hasFocus()&&!!(e.type||e.href||~e.tabIndex)},enabled:oe(!1),disabled:oe(!0),checked:function(e){return S(e,\"input\")&&!!e.checked||S(e,\"option\")&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return U.test(e.nodeName)},input:function(e){return X.test(e.nodeName)},button:function(e){return S(e,\"input\")&&\"button\"===e.type||S(e,\"button\")},text:function(e){var t;return S(e,\"input\")&&\"text\"===e.type&&(null==(t=e.getAttribute(\"type\"))||\"text\"===t.toLowerCase())},first:ae((function(){return[0]})),last:ae((function(e,t){return[t-1]})),eq:ae((function(e,t,n){return[n<0?n+t:n]})),even:ae((function(e,t){for(var n=0;nt?t:n;--r>=0;)e.push(r);return e})),gt:ae((function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function he(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s-1&&(o[c]=!(a[c]=p))}}else d=he(d===a?d.splice(y,d.length):d),i?i(null,a,d,l):v.apply(a,d)}))}function ve(e){for(var t,n,i,a=e.length,u=r.relative[e[0].type],l=u||r.relative[\" \"],c=u?1:0,f=pe((function(e){return e===t}),l,!0),p=pe((function(e){return s.call(t,e)>-1}),l,!0),d=[function(e,n,r){var i=!u&&(r||n!=o)||((t=n).nodeType?f(e,n,r):p(e,n,r));return t=null,i}];c1&&de(d),c>1&&fe(e.slice(0,c-1).concat({value:\" \"===e[c-2].type?\"*\":\"\"})).replace(D,\"$1\"),n,c0,i=e.length>0,a=function(a,s,u,c,f){var d,h,g,y=0,x=\"0\",b=a&&[],w=[],C=o,S=a||i&&r.find.TAG(\"*\",f),k=m+=null==C?1:Math.random()||.1,j=S.length;for(f&&(o=s==l||s||f);x!==j&&null!=(d=S[x]);x++){if(i&&d){for(h=0,s||d.ownerDocument==l||(ue(d),u=!p);g=e[h++];)if(g(d,s||l,u)){v.call(c,d);break}f&&(m=k)}n&&((d=!g&&d)&&y--,a&&b.push(d))}if(y+=x,n&&x!==y){for(h=0;g=t[h++];)g(b,w,s,u);if(a){if(y>0)for(;x--;)b[x]||w[x]||(w[x]=E.call(c));w=he(w)}v.apply(c,w),f&&!a&&w.length>0&&y+t.length>1&&T.uniqueSort(c)}return f&&(m=k,o=C),b};return n?te(a):a}(a,i)),s.selector=e}return s}function me(e,t,n,i){var o,a,s,u,l,c=\"function\"==typeof e&&e,f=!i&&ce(e=c.selector||e);if(n=n||[],1===f.length){if((a=f[0]=f[0].slice(0)).length>2&&\"ID\"===(s=a[0]).type&&9===t.nodeType&&p&&r.relative[a[1].type]){if(!(t=(r.find.ID(s.matches[0].replace(Y,Q),t)||[])[0]))return n;c&&(t=t.parentNode),e=e.slice(a.shift().value.length)}for(o=z.needsContext.test(e)?0:a.length;o--&&(s=a[o],!r.relative[u=s.type]);)if((l=r.find[u])&&(i=l(s.matches[0].replace(Y,Q),G.test(a[0].type)&&se(t.parentNode)||t))){if(a.splice(o,1),!(e=i.length&&fe(a)))return v.apply(n,i),n;break}}return(c||ye(e,f))(i,t,!p,n,!t||G.test(e)&&se(t.parentNode)||t),n}le.prototype=r.filters=r.pseudos,r.setFilters=new le,d.sortStable=y.split(\"\").sort(q).join(\"\")===y,ue(),d.sortDetached=ne((function(e){return 1&e.compareDocumentPosition(l.createElement(\"fieldset\"))})),T.find=Z,T.expr[\":\"]=T.expr.pseudos,T.unique=T.uniqueSort,Z.compile=ye,Z.select=me,Z.setDocument=ue,Z.tokenize=ce,Z.escape=T.escapeSelector,Z.getText=T.text,Z.isXML=T.isXMLDoc,Z.selectors=T.expr,Z.support=T.support,Z.uniqueSort=T.uniqueSort}();var O=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&T(e).is(n))break;r.push(e)}return r},P=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},M=T.expr.match.needsContext,R=/^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i;function I(e,t,n){return h(t)?T.grep(e,(function(e,r){return!!t.call(e,r,e)!==n})):t.nodeType?T.grep(e,(function(e){return e===t!==n})):\"string\"!=typeof t?T.grep(e,(function(e){return s.call(t,e)>-1!==n})):T.filter(t,e,n)}T.filter=function(e,t,n){var r=t[0];return n&&(e=\":not(\"+e+\")\"),1===t.length&&1===r.nodeType?T.find.matchesSelector(r,e)?[r]:[]:T.find.matches(e,T.grep(t,(function(e){return 1===e.nodeType})))},T.fn.extend({find:function(e){var t,n,r=this.length,i=this;if(\"string\"!=typeof e)return this.pushStack(T(e).filter((function(){for(t=0;t1?T.uniqueSort(n):n},filter:function(e){return this.pushStack(I(this,e||[],!1))},not:function(e){return this.pushStack(I(this,e||[],!0))},is:function(e){return!!I(this,\"string\"==typeof e&&M.test(e)?T(e):e||[],!1).length}});var W,F=/^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/;(T.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||W,\"string\"==typeof e){if(!(r=\"<\"===e[0]&&\">\"===e[e.length-1]&&e.length>=3?[null,e,null]:F.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof T?t[0]:t,T.merge(this,T.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:v,!0)),R.test(r[1])&&T.isPlainObject(t))for(r in t)h(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=v.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):h(e)?void 0!==n.ready?n.ready(e):e(T):T.makeArray(e,this)}).prototype=T.fn,W=T(v);var $=/^(?:parents|prev(?:Until|All))/,B={children:!0,contents:!0,next:!0,prev:!0};function _(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}T.fn.extend({has:function(e){var t=T(e,this),n=t.length;return this.filter((function(){for(var e=0;e-1:1===n.nodeType&&T.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?T.uniqueSort(o):o)},index:function(e){return e?\"string\"==typeof e?s.call(T(e),this[0]):s.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(T.uniqueSort(T.merge(this.get(),T(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),T.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return O(e,\"parentNode\")},parentsUntil:function(e,t,n){return O(e,\"parentNode\",n)},next:function(e){return _(e,\"nextSibling\")},prev:function(e){return _(e,\"previousSibling\")},nextAll:function(e){return O(e,\"nextSibling\")},prevAll:function(e){return O(e,\"previousSibling\")},nextUntil:function(e,t,n){return O(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return O(e,\"previousSibling\",n)},siblings:function(e){return P((e.parentNode||{}).firstChild,e)},children:function(e){return P(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(S(e,\"template\")&&(e=e.content||e),T.merge([],e.childNodes))}},(function(e,t){T.fn[e]=function(n,r){var i=T.map(this,t,n);return\"Until\"!==e.slice(-5)&&(r=n),r&&\"string\"==typeof r&&(i=T.filter(r,i)),this.length>1&&(B[e]||T.uniqueSort(i),$.test(e)&&i.reverse()),this.pushStack(i)}}));var z=/[^\\x20\\t\\r\\n\\f]+/g;function X(e){return e}function U(e){throw e}function V(e,t,n,r){var i;try{e&&h(i=e.promise)?i.call(e).done(t).fail(n):e&&h(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}T.Callbacks=function(e){e=\"string\"==typeof e?function(e){var t={};return T.each(e.match(z)||[],(function(e,n){t[n]=!0})),t}(e):T.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1)for(n=a.shift();++s-1;)o.splice(n,1),n<=s&&s--})),this},has:function(e){return e?T.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n=\"\",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=\"\"),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l},T.extend({Deferred:function(t){var n=[[\"notify\",\"progress\",T.Callbacks(\"memory\"),T.Callbacks(\"memory\"),2],[\"resolve\",\"done\",T.Callbacks(\"once memory\"),T.Callbacks(\"once memory\"),0,\"resolved\"],[\"reject\",\"fail\",T.Callbacks(\"once memory\"),T.Callbacks(\"once memory\"),1,\"rejected\"]],r=\"pending\",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return T.Deferred((function(t){T.each(n,(function(n,r){var i=h(e[r[4]])&&e[r[4]];o[r[1]]((function(){var e=i&&i.apply(this,arguments);e&&h(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+\"With\"](this,i?[e]:arguments)}))})),e=null})).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==U&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(T.Deferred.getErrorHook?c.error=T.Deferred.getErrorHook():T.Deferred.getStackHook&&(c.error=T.Deferred.getStackHook()),e.setTimeout(c))}}return T.Deferred((function(e){n[0][3].add(a(0,e,h(i)?i:X,e.notifyWith)),n[1][3].add(a(0,e,h(t)?t:X)),n[2][3].add(a(0,e,h(r)?r:U))})).promise()},promise:function(e){return null!=e?T.extend(e,i):i}},o={};return T.each(n,(function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add((function(){r=s}),n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+\"With\"](this===o?void 0:this,arguments),this},o[t[0]+\"With\"]=a.fireWith})),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),o=i.call(arguments),a=T.Deferred(),s=function(e){return function(n){r[e]=this,o[e]=arguments.length>1?i.call(arguments):n,--t||a.resolveWith(r,o)}};if(t<=1&&(V(e,a.done(s(n)).resolve,a.reject,!t),\"pending\"===a.state()||h(o[n]&&o[n].then)))return a.then();for(;n--;)V(o[n],s(n),a.reject);return a.promise()}});var G=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;T.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&G.test(t.name)&&e.console.warn(\"jQuery.Deferred exception: \"+t.message,t.stack,n)},T.readyException=function(t){e.setTimeout((function(){throw t}))};var Y=T.Deferred();function Q(){v.removeEventListener(\"DOMContentLoaded\",Q),e.removeEventListener(\"load\",Q),T.ready()}T.fn.ready=function(e){return Y.then(e).catch((function(e){T.readyException(e)})),this},T.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--T.readyWait:T.isReady)||(T.isReady=!0,!0!==e&&--T.readyWait>0||Y.resolveWith(v,[T]))}}),T.ready.then=Y.then,\"complete\"===v.readyState||\"loading\"!==v.readyState&&!v.documentElement.doScroll?e.setTimeout(T.ready):(v.addEventListener(\"DOMContentLoaded\",Q),e.addEventListener(\"load\",Q));var J=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if(\"object\"===x(n))for(s in i=!0,n)J(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,h(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(T(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each((function(){oe.remove(this,e)}))}}),T.extend({queue:function(e,t,n){var r;if(e)return t=(t||\"fx\")+\"queue\",r=ie.get(e,t),n&&(!r||Array.isArray(n)?r=ie.access(e,t,T.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||\"fx\";var n=T.queue(e,t),r=n.length,i=n.shift(),o=T._queueHooks(e,t);\"inprogress\"===i&&(i=n.shift(),r--),i&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete o.stop,i.call(e,(function(){T.dequeue(e,t)}),o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return ie.get(e,n)||ie.access(e,n,{empty:T.Callbacks(\"once memory\").add((function(){ie.remove(e,[t+\"queue\",n])}))})}}),T.fn.extend({queue:function(e,t){var n=2;return\"string\"!=typeof e&&(t=e,e=\"fx\",n--),arguments.length\\x20\\t\\r\\n\\f]*)/i,Se=/^$|^module$|\\/(?:java|ecma)script/i;be=v.createDocumentFragment().appendChild(v.createElement(\"div\")),(we=v.createElement(\"input\")).setAttribute(\"type\",\"radio\"),we.setAttribute(\"checked\",\"checked\"),we.setAttribute(\"name\",\"t\"),be.appendChild(we),d.checkClone=be.cloneNode(!0).cloneNode(!0).lastChild.checked,be.innerHTML=\"\",d.noCloneChecked=!!be.cloneNode(!0).lastChild.defaultValue,be.innerHTML=\"\",d.option=!!be.lastChild;var Ee={thead:[1,\"\",\"
\"],col:[2,\"\",\"
\"],tr:[2,\"\",\"
\"],td:[3,\"\",\"
\"],_default:[0,\"\",\"\"]};function ke(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||\"*\"):void 0!==e.querySelectorAll?e.querySelectorAll(t||\"*\"):[],void 0===t||t&&S(e,t)?T.merge([e],n):n}function je(e,t){for(var n=0,r=e.length;n\",\"\"]);var Ae=/<|&#?\\w+;/;function De(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d-1)i&&i.push(o);else if(l=de(o),a=ke(f.appendChild(o),\"script\"),l&&je(a),n)for(c=0;o=a[c++];)Se.test(o.type||\"\")&&n.push(o);return f}var Ne=/^([^.]*)(?:\\.(.+)|)/;function qe(){return!0}function Le(){return!1}function He(e,t,n,r,i,o){var a,s;if(\"object\"==typeof t){for(s in\"string\"!=typeof n&&(r=r||n,n=void 0),t)He(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&(\"string\"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Le;else if(!i)return e;return 1===o&&(a=i,i=function(e){return T().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=T.guid++)),e.each((function(){T.event.add(this,t,i,r,n)}))}function Oe(e,t,n){n?(ie.set(e,t,!1),T.event.add(e,t,{namespace:!1,handler:function(e){var n,r=ie.get(this,t);if(1&e.isTrigger&&this[t]){if(r)(T.event.special[t]||{}).delegateType&&e.stopPropagation();else if(r=i.call(arguments),ie.set(this,t,r),this[t](),n=ie.get(this,t),ie.set(this,t,!1),r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n}else r&&(ie.set(this,t,T.event.trigger(r[0],r.slice(1),this)),e.stopPropagation(),e.isImmediatePropagationStopped=qe)}})):void 0===ie.get(e,t)&&T.event.add(e,t,qe)}T.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=ie.get(e);if(ne(e))for(n.handler&&(n=(o=n).handler,i=o.selector),i&&T.find.matchesSelector(pe,i),n.guid||(n.guid=T.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(t){return void 0!==T&&T.event.triggered!==t.type?T.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||\"\").match(z)||[\"\"]).length;l--;)d=g=(s=Ne.exec(t[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d&&(f=T.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=T.event.special[d]||{},c=T.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&T.expr.match.needsContext.test(i),namespace:h.join(\".\")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),T.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=ie.hasData(e)&&ie.get(e);if(v&&(u=v.events)){for(l=(t=(t||\"\").match(z)||[\"\"]).length;l--;)if(d=g=(s=Ne.exec(t[l])||[])[1],h=(s[2]||\"\").split(\".\").sort(),d){for(f=T.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp(\"(^|\\\\.)\"+h.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&(\"**\"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||T.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)T.event.remove(e,d+t[l],n,r,!0);T.isEmptyObject(u)&&ie.remove(e,\"handle events\")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=T.event.fix(e),l=(ie.get(this,\"events\")||Object.create(null))[u.type]||[],c=T.event.special[u.type]||{};for(s[0]=u,t=1;t=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&(\"click\"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:T.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\\s*$/g;function Ie(e,t){return S(e,\"table\")&&S(11!==t.nodeType?t:t.firstChild,\"tr\")&&T(e).children(\"tbody\")[0]||e}function We(e){return e.type=(null!==e.getAttribute(\"type\"))+\"/\"+e.type,e}function Fe(e){return\"true/\"===(e.type||\"\").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute(\"type\"),e}function $e(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(ie.hasData(e)&&(s=ie.get(e).events))for(i in ie.remove(t,\"handle events\"),s)for(n=0,r=s[i].length;n1&&\"string\"==typeof v&&!d.checkClone&&Me.test(v))return e.each((function(i){var o=e.eq(i);y&&(t[0]=v.call(this,i,o.html())),_e(o,t,n,r)}));if(p&&(a=(i=De(t,e[0].ownerDocument,!1,e,r)).firstChild,1===i.childNodes.length&&(i=a),a||r)){for(u=(s=T.map(ke(i,\"script\"),We)).length;f0&&je(a,!u&&ke(e,\"script\")),s},cleanData:function(e){for(var t,n,r,i=T.event.special,o=0;void 0!==(n=e[o]);o++)if(ne(n)){if(t=n[ie.expando]){if(t.events)for(r in t.events)i[r]?T.event.remove(n,r):T.removeEvent(n,r,t.handle);n[ie.expando]=void 0}n[oe.expando]&&(n[oe.expando]=void 0)}}}),T.fn.extend({detach:function(e){return ze(this,e,!0)},remove:function(e){return ze(this,e)},text:function(e){return J(this,(function(e){return void 0===e?T.text(this):this.empty().each((function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)}))}),null,e,arguments.length)},append:function(){return _e(this,arguments,(function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Ie(this,e).appendChild(e)}))},prepend:function(){return _e(this,arguments,(function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Ie(this,e);t.insertBefore(e,t.firstChild)}}))},before:function(){return _e(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this)}))},after:function(){return _e(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)}))},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(T.cleanData(ke(e,!1)),e.textContent=\"\");return this},clone:function(e,t){return e=e??!1,t=t??e,this.map((function(){return T.clone(this,e,t)}))},html:function(e){return J(this,(function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if(\"string\"==typeof e&&!Pe.test(e)&&!Ee[(Ce.exec(e)||[\"\",\"\"])[1].toLowerCase()]){e=T.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e[\"offset\"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u+l}function st(e,t,n){var r=Ve(e),i=(!d.boxSizingReliable()||n)&&\"border-box\"===T.css(e,\"boxSizing\",!1,r),o=i,a=Qe(e,t,r),s=\"offset\"+t[0].toUpperCase()+t.slice(1);if(Xe.test(a)){if(!n)return a;a=\"auto\"}return(!d.boxSizingReliable()&&i||!d.reliableTrDimensions()&&S(e,\"tr\")||\"auto\"===a||!parseFloat(a)&&\"inline\"===T.css(e,\"display\",!1,r))&&e.getClientRects().length&&(i=\"border-box\"===T.css(e,\"boxSizing\",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+at(e,t,n||(i?\"border\":\"content\"),o,r,a)+\"px\"}function ut(e,t,n,r,i){return new ut.prototype.init(e,t,n,r,i)}T.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Qe(e,\"opacity\");return\"\"===n?\"1\":n}}}},cssNumber:{animationIterationCount:!0,aspectRatio:!0,borderImageSlice:!0,columnCount:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,scale:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeMiterlimit:!0,strokeOpacity:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=te(t),u=Ue.test(t),l=e.style;if(u||(t=tt(s)),a=T.cssHooks[t]||T.cssHooks[s],void 0===n)return a&&\"get\"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];\"string\"===(o=typeof n)&&(i=ce.exec(n))&&i[1]&&(n=ve(e,t,i),o=\"number\"),null!=n&&n==n&&(\"number\"!==o||u||(n+=i&&i[3]||(T.cssNumber[s]?\"\":\"px\")),d.clearCloneStyle||\"\"!==n||0!==t.indexOf(\"background\")||(l[t]=\"inherit\"),a&&\"set\"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=te(t);return Ue.test(t)||(t=tt(s)),(a=T.cssHooks[t]||T.cssHooks[s])&&\"get\"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Qe(e,t,r)),\"normal\"===i&&t in it&&(i=it[t]),\"\"===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),T.each([\"height\",\"width\"],(function(e,t){T.cssHooks[t]={get:function(e,n,r){if(n)return!nt.test(T.css(e,\"display\"))||e.getClientRects().length&&e.getBoundingClientRect().width?st(e,t,r):Ge(e,rt,(function(){return st(e,t,r)}))},set:function(e,n,r){var i,o=Ve(e),a=!d.scrollboxSize()&&\"absolute\"===o.position,s=(a||r)&&\"border-box\"===T.css(e,\"boxSizing\",!1,o),u=r?at(e,t,r,s,o):0;return s&&a&&(u-=Math.ceil(e[\"offset\"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-at(e,t,\"border\",!1,o)-.5)),u&&(i=ce.exec(n))&&\"px\"!==(i[3]||\"px\")&&(e.style[t]=n,n=T.css(e,t)),ot(0,n,u)}}})),T.cssHooks.marginLeft=Je(d.reliableMarginLeft,(function(e,t){if(t)return(parseFloat(Qe(e,\"marginLeft\"))||e.getBoundingClientRect().left-Ge(e,{marginLeft:0},(function(){return e.getBoundingClientRect().left})))+\"px\"})),T.each({margin:\"\",padding:\"\",border:\"Width\"},(function(e,t){T.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o=\"string\"==typeof n?n.split(\" \"):[n];r<4;r++)i[e+fe[r]+t]=o[r]||o[r-2]||o[0];return i}},\"margin\"!==e&&(T.cssHooks[e+t].set=ot)})),T.fn.extend({css:function(e,t){return J(this,(function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ve(e),i=t.length;a1)}}),T.Tween=ut,ut.prototype={constructor:ut,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||T.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(T.cssNumber[n]?\"\":\"px\")},cur:function(){var e=ut.propHooks[this.prop];return e&&e.get?e.get(this):ut.propHooks._default.get(this)},run:function(e){var t,n=ut.propHooks[this.prop];return this.options.duration?this.pos=t=T.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):ut.propHooks._default.set(this),this}},ut.prototype.init.prototype=ut.prototype,ut.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=T.css(e.elem,e.prop,\"\"))&&\"auto\"!==t?t:0},set:function(e){T.fx.step[e.prop]?T.fx.step[e.prop](e):1!==e.elem.nodeType||!T.cssHooks[e.prop]&&null==e.elem.style[tt(e.prop)]?e.elem[e.prop]=e.now:T.style(e.elem,e.prop,e.now+e.unit)}}},ut.propHooks.scrollTop=ut.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},T.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:\"swing\"},T.fx=ut.prototype.init,T.fx.step={};var lt,ct,ft=/^(?:toggle|show|hide)$/,pt=/queueHooks$/;function dt(){ct&&(!1===v.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(dt):e.setTimeout(dt,T.fx.interval),T.fx.tick())}function ht(){return e.setTimeout((function(){lt=void 0})),lt=Date.now()}function gt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i[\"margin\"+(n=fe[r])]=i[\"padding\"+n]=e;return t&&(i.opacity=i.width=e),i}function vt(e,t,n){for(var r,i=(yt.tweeners[t]||[]).concat(yt.tweeners[\"*\"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each((function(){T.removeAttr(this,e)}))}}),T.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===e.getAttribute?T.prop(e,t,n):(1===o&&T.isXMLDoc(e)||(i=T.attrHooks[t.toLowerCase()]||(T.expr.match.bool.test(t)?mt:void 0)),void 0!==n?null===n?void T.removeAttr(e,t):i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+\"\"),n):i&&\"get\"in i&&null!==(r=i.get(e,t))?r:(r=T.find.attr(e,t))??void 0)},attrHooks:{type:{set:function(e,t){if(!d.radioValue&&\"radio\"===t&&S(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(z);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),mt={set:function(e,t,n){return!1===t?T.removeAttr(e,n):e.setAttribute(n,n),n}},T.each(T.expr.match.bool.source.match(/\\w+/g),(function(e,t){var n=xt[t]||T.find.attr;xt[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=xt[a],xt[a]=i,i=null!=n(e,t,r)?a:null,xt[a]=o),i}}));var bt=/^(?:input|select|textarea|button)$/i,wt=/^(?:a|area)$/i;function Tt(e){return(e.match(z)||[]).join(\" \")}function Ct(e){return e.getAttribute&&e.getAttribute(\"class\")||\"\"}function St(e){return Array.isArray(e)?e:\"string\"==typeof e&&e.match(z)||[]}T.fn.extend({prop:function(e,t){return J(this,T.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each((function(){delete this[T.propFix[e]||e]}))}}),T.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&T.isXMLDoc(e)||(t=T.propFix[t]||t,i=T.propHooks[t]),void 0!==n?i&&\"set\"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&\"get\"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=T.find.attr(e,\"tabindex\");return t?parseInt(t,10):bt.test(e.nodeName)||wt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:\"htmlFor\",class:\"className\"}}),d.optSelected||(T.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),T.each([\"tabIndex\",\"readOnly\",\"maxLength\",\"cellSpacing\",\"cellPadding\",\"rowSpan\",\"colSpan\",\"useMap\",\"frameBorder\",\"contentEditable\"],(function(){T.propFix[this.toLowerCase()]=this})),T.fn.extend({addClass:function(e){var t,n,r,i,o,a;return h(e)?this.each((function(t){T(this).addClass(e.call(this,t,Ct(this)))})):(t=St(e)).length?this.each((function(){if(r=Ct(this),n=1===this.nodeType&&\" \"+Tt(r)+\" \"){for(o=0;o-1;)n=n.replace(\" \"+i+\" \",\" \");a=Tt(n),r!==a&&this.setAttribute(\"class\",a)}})):this:this.attr(\"class\",\"\")},toggleClass:function(e,t){var n,r,i,o,a=typeof e,s=\"string\"===a||Array.isArray(e);return h(e)?this.each((function(n){T(this).toggleClass(e.call(this,n,Ct(this),t),t)})):\"boolean\"==typeof t&&s?t?this.addClass(e):this.removeClass(e):(n=St(e),this.each((function(){if(s)for(o=T(this),i=0;i-1)return!0;return!1}});var Et=/\\r/g;T.fn.extend({val:function(e){var t,n,r,i=this[0];return arguments.length?(r=h(e),this.each((function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,T(this).val()):e)?i=\"\":\"number\"==typeof i?i+=\"\":Array.isArray(i)&&(i=T.map(i,(function(e){return null==e?\"\":e+\"\"}))),(t=T.valHooks[this.type]||T.valHooks[this.nodeName.toLowerCase()])&&\"set\"in t&&void 0!==t.set(this,i,\"value\")||(this.value=i))}))):i?(t=T.valHooks[i.type]||T.valHooks[i.nodeName.toLowerCase()])&&\"get\"in t&&void 0!==(n=t.get(i,\"value\"))?n:\"string\"==typeof(n=i.value)?n.replace(Et,\"\"):n??\"\":void 0}}),T.extend({valHooks:{option:{get:function(e){var t=T.find.attr(e,\"value\");return null!=t?t:Tt(T.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a=\"select-one\"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),T.each([\"radio\",\"checkbox\"],(function(){T.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=T.inArray(T(e).val(),t)>-1}},d.checkOn||(T.valHooks[this].get=function(e){return null===e.getAttribute(\"value\")?\"on\":e.value})}));var kt=e.location,jt={guid:Date.now()},At=/\\?/;T.parseXML=function(t){var n,r;if(!t||\"string\"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,\"text/xml\")}catch(e){}return r=n&&n.getElementsByTagName(\"parsererror\")[0],n&&!r||T.error(\"Invalid XML: \"+(r?T.map(r.childNodes,(function(e){return e.textContent})).join(\"\\n\"):t)),n};var Dt=/^(?:focusinfocus|focusoutblur)$/,Nt=function(e){e.stopPropagation()};T.extend(T.event,{trigger:function(t,n,r,i){var o,a,s,u,l,f,p,d,y=[r||v],m=c.call(t,\"type\")?t.type:t,x=c.call(t,\"namespace\")?t.namespace.split(\".\"):[];if(a=d=s=r=r||v,3!==r.nodeType&&8!==r.nodeType&&!Dt.test(m+T.event.triggered)&&(m.indexOf(\".\")>-1&&(x=m.split(\".\"),m=x.shift(),x.sort()),l=m.indexOf(\":\")<0&&\"on\"+m,(t=t[T.expando]?t:new T.Event(m,\"object\"==typeof t&&t)).isTrigger=i?2:3,t.namespace=x.join(\".\"),t.rnamespace=t.namespace?new RegExp(\"(^|\\\\.)\"+x.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:T.makeArray(n,[t]),p=T.event.special[m]||{},i||!p.trigger||!1!==p.trigger.apply(r,n))){if(!i&&!p.noBubble&&!g(r)){for(u=p.delegateType||m,Dt.test(u+m)||(a=a.parentNode);a;a=a.parentNode)y.push(a),s=a;s===(r.ownerDocument||v)&&y.push(s.defaultView||s.parentWindow||e)}for(o=0;(a=y[o++])&&!t.isPropagationStopped();)d=a,t.type=o>1?u:p.bindType||m,(f=(ie.get(a,\"events\")||Object.create(null))[t.type]&&ie.get(a,\"handle\"))&&f.apply(a,n),(f=l&&a[l])&&f.apply&&ne(a)&&(t.result=f.apply(a,n),!1===t.result&&t.preventDefault());return t.type=m,i||t.isDefaultPrevented()||p._default&&!1!==p._default.apply(y.pop(),n)||!ne(r)||l&&h(r[m])&&!g(r)&&((s=r[l])&&(r[l]=null),T.event.triggered=m,t.isPropagationStopped()&&d.addEventListener(m,Nt),r[m](),t.isPropagationStopped()&&d.removeEventListener(m,Nt),T.event.triggered=void 0,s&&(r[l]=s)),t.result}},simulate:function(e,t,n){var r=T.extend(new T.Event,n,{type:e,isSimulated:!0});T.event.trigger(r,null,t)}}),T.fn.extend({trigger:function(e,t){return this.each((function(){T.event.trigger(e,t,this)}))},triggerHandler:function(e,t){var n=this[0];if(n)return T.event.trigger(e,t,n,!0)}});var qt=/\\[\\]$/,Lt=/\\r?\\n/g,Ht=/^(?:submit|button|image|reset|file)$/i,Ot=/^(?:input|select|textarea|keygen)/i;function Pt(e,t,n,r){var i;if(Array.isArray(t))T.each(t,(function(t,i){n||qt.test(e)?r(e,i):Pt(e+\"[\"+(\"object\"==typeof i&&null!=i?t:\"\")+\"]\",i,n,r)}));else if(n||\"object\"!==x(t))r(e,t);else for(i in t)Pt(e+\"[\"+i+\"]\",t[i],n,r)}T.param=function(e,t){var n,r=[],i=function(e,t){var n=h(t)?t():t;r[r.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(n??\"\")};if(null==e)return\"\";if(Array.isArray(e)||e.jquery&&!T.isPlainObject(e))T.each(e,(function(){i(this.name,this.value)}));else for(n in e)Pt(n,e[n],t,i);return r.join(\"&\")},T.fn.extend({serialize:function(){return T.param(this.serializeArray())},serializeArray:function(){return this.map((function(){var e=T.prop(this,\"elements\");return e?T.makeArray(e):this})).filter((function(){var e=this.type;return this.name&&!T(this).is(\":disabled\")&&Ot.test(this.nodeName)&&!Ht.test(e)&&(this.checked||!Te.test(e))})).map((function(e,t){var n=T(this).val();return null==n?null:Array.isArray(n)?T.map(n,(function(e){return{name:t.name,value:e.replace(Lt,\"\\r\\n\")}})):{name:t.name,value:n.replace(Lt,\"\\r\\n\")}})).get()}});var Mt=/%20/g,Rt=/#.*$/,It=/([?&])_=[^&]*/,Wt=/^(.*?):[ \\t]*([^\\r\\n]*)$/gm,Ft=/^(?:GET|HEAD)$/,$t=/^\\/\\//,Bt={},_t={},zt=\"*/\".concat(\"*\"),Xt=v.createElement(\"a\");function Ut(e){return function(t,n){\"string\"!=typeof t&&(n=t,t=\"*\");var r,i=0,o=t.toLowerCase().match(z)||[];if(h(n))for(;r=o[i++];)\"+\"===r[0]?(r=r.slice(1)||\"*\",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function Vt(e,t,n,r){var i={},o=e===_t;function a(s){var u;return i[s]=!0,T.each(e[s]||[],(function(e,s){var l=s(t,n,r);return\"string\"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)})),u}return a(t.dataTypes[0])||!i[\"*\"]&&a(\"*\")}function Gt(e,t){var n,r,i=T.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&T.extend(!0,e,r),e}Xt.href=kt.href,T.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:kt.href,type:\"GET\",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(kt.protocol),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":zt,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/\\bxml\\b/,html:/\\bhtml/,json:/\\bjson\\b/},responseFields:{xml:\"responseXML\",text:\"responseText\",json:\"responseJSON\"},converters:{\"* text\":String,\"text html\":!0,\"text json\":JSON.parse,\"text xml\":T.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Gt(Gt(e,T.ajaxSettings),t):Gt(T.ajaxSettings,e)},ajaxPrefilter:Ut(Bt),ajaxTransport:Ut(_t),ajax:function(t,n){\"object\"==typeof t&&(n=t,t=void 0),n=n||{};var r,i,o,a,s,u,l,c,f,p,d=T.ajaxSetup({},n),h=d.context||d,g=d.context&&(h.nodeType||h.jquery)?T(h):T.event,y=T.Deferred(),m=T.Callbacks(\"once memory\"),x=d.statusCode||{},b={},w={},C=\"canceled\",S={readyState:0,getResponseHeader:function(e){var t;if(l){if(!a)for(a={};t=Wt.exec(o);)a[t[1].toLowerCase()+\" \"]=(a[t[1].toLowerCase()+\" \"]||[]).concat(t[2]);t=a[e.toLowerCase()+\" \"]}return null==t?null:t.join(\", \")},getAllResponseHeaders:function(){return l?o:null},setRequestHeader:function(e,t){return null==l&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==l&&(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(l)S.always(e[S.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return r&&r.abort(t),E(0,t),this}};if(y.promise(S),d.url=((t||d.url||kt.href)+\"\").replace($t,kt.protocol+\"//\"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=(d.dataType||\"*\").toLowerCase().match(z)||[\"\"],null==d.crossDomain){u=v.createElement(\"a\");try{u.href=d.url,u.href=u.href,d.crossDomain=Xt.protocol+\"//\"+Xt.host!=u.protocol+\"//\"+u.host}catch(e){d.crossDomain=!0}}if(d.data&&d.processData&&\"string\"!=typeof d.data&&(d.data=T.param(d.data,d.traditional)),Vt(Bt,d,n,S),l)return S;for(f in(c=T.event&&d.global)&&0==T.active++&&T.event.trigger(\"ajaxStart\"),d.type=d.type.toUpperCase(),d.hasContent=!Ft.test(d.type),i=d.url.replace(Rt,\"\"),d.hasContent?d.data&&d.processData&&0===(d.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&(d.data=d.data.replace(Mt,\"+\")):(p=d.url.slice(i.length),d.data&&(d.processData||\"string\"==typeof d.data)&&(i+=(At.test(i)?\"&\":\"?\")+d.data,delete d.data),!1===d.cache&&(i=i.replace(It,\"$1\"),p=(At.test(i)?\"&\":\"?\")+\"_=\"+jt.guid+++p),d.url=i+p),d.ifModified&&(T.lastModified[i]&&S.setRequestHeader(\"If-Modified-Since\",T.lastModified[i]),T.etag[i]&&S.setRequestHeader(\"If-None-Match\",T.etag[i])),(d.data&&d.hasContent&&!1!==d.contentType||n.contentType)&&S.setRequestHeader(\"Content-Type\",d.contentType),S.setRequestHeader(\"Accept\",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(\"*\"!==d.dataTypes[0]?\", \"+zt+\"; q=0.01\":\"\"):d.accepts[\"*\"]),d.headers)S.setRequestHeader(f,d.headers[f]);if(d.beforeSend&&(!1===d.beforeSend.call(h,S,d)||l))return S.abort();if(C=\"abort\",m.add(d.complete),S.done(d.success),S.fail(d.error),r=Vt(_t,d,n,S)){if(S.readyState=1,c&&g.trigger(\"ajaxSend\",[S,d]),l)return S;d.async&&d.timeout>0&&(s=e.setTimeout((function(){S.abort(\"timeout\")}),d.timeout));try{l=!1,r.send(b,E)}catch(e){if(l)throw e;E(-1,e)}}else E(-1,\"No Transport\");function E(t,n,a,u){var f,p,v,b,w,C=n;l||(l=!0,s&&e.clearTimeout(s),r=void 0,o=u||\"\",S.readyState=t>0?4:0,f=t>=200&&t<300||304===t,a&&(b=function(e,t,n){for(var r,i,o,a,s=e.contents,u=e.dataTypes;\"*\"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader(\"Content-Type\"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+\" \"+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(d,S,a)),!f&&T.inArray(\"script\",d.dataTypes)>-1&&T.inArray(\"json\",d.dataTypes)<0&&(d.converters[\"text script\"]=function(){}),b=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if(\"*\"===o)o=u;else if(\"*\"!==u&&u!==o){if(!(a=l[u+\" \"+o]||l[\"* \"+o]))for(i in l)if((s=i.split(\" \"))[1]===o&&(a=l[u+\" \"+s[0]]||l[\"* \"+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:\"parsererror\",error:a?e:\"No conversion from \"+u+\" to \"+o}}}return{state:\"success\",data:t}}(d,b,S,f),f?(d.ifModified&&((w=S.getResponseHeader(\"Last-Modified\"))&&(T.lastModified[i]=w),(w=S.getResponseHeader(\"etag\"))&&(T.etag[i]=w)),204===t||\"HEAD\"===d.type?C=\"nocontent\":304===t?C=\"notmodified\":(C=b.state,p=b.data,f=!(v=b.error))):(v=C,!t&&C||(C=\"error\",t<0&&(t=0))),S.status=t,S.statusText=(n||C)+\"\",f?y.resolveWith(h,[p,C,S]):y.rejectWith(h,[S,C,v]),S.statusCode(x),x=void 0,c&&g.trigger(f?\"ajaxSuccess\":\"ajaxError\",[S,d,f?p:v]),m.fireWith(h,[S,C]),c&&(g.trigger(\"ajaxComplete\",[S,d]),--T.active||T.event.trigger(\"ajaxStop\")))}return S},getJSON:function(e,t,n){return T.get(e,t,n,\"json\")},getScript:function(e,t){return T.get(e,void 0,t,\"script\")}}),T.each([\"get\",\"post\"],(function(e,t){T[t]=function(e,n,r,i){return h(n)&&(i=i||r,r=n,n=void 0),T.ajax(T.extend({url:e,type:t,dataType:i,data:n,success:r},T.isPlainObject(e)&&e))}})),T.ajaxPrefilter((function(e){var t;for(t in e.headers)\"content-type\"===t.toLowerCase()&&(e.contentType=e.headers[t]||\"\")})),T._evalUrl=function(e,t,n){return T.ajax({url:e,type:\"GET\",dataType:\"script\",cache:!0,async:!1,global:!1,converters:{\"text script\":function(){}},dataFilter:function(e){T.globalEval(e,t,n)}})},T.fn.extend({wrapAll:function(e){var t;return this[0]&&(h(e)&&(e=e.call(this[0])),t=T(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map((function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e})).append(this)),this},wrapInner:function(e){return h(e)?this.each((function(t){T(this).wrapInner(e.call(this,t))})):this.each((function(){var t=T(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)}))},wrap:function(e){var t=h(e);return this.each((function(n){T(this).wrapAll(t?e.call(this,n):e)}))},unwrap:function(e){return this.parent(e).not(\"body\").each((function(){T(this).replaceWith(this.childNodes)})),this}}),T.expr.pseudos.hidden=function(e){return!T.expr.pseudos.visible(e)},T.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},T.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Yt={0:200,1223:204},Qt=T.ajaxSettings.xhr();d.cors=!!Qt&&\"withCredentials\"in Qt,d.ajax=Qt=!!Qt,T.ajaxTransport((function(t){var n,r;if(d.cors||Qt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];for(a in t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i[\"X-Requested-With\"]||(i[\"X-Requested-With\"]=\"XMLHttpRequest\"),i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,\"abort\"===e?s.abort():\"error\"===e?\"number\"!=typeof s.status?o(0,\"error\"):o(s.status,s.statusText):o(Yt[s.status]||s.status,s.statusText,\"text\"!==(s.responseType||\"text\")||\"string\"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n(\"error\"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout((function(){n&&r()}))},n=n(\"abort\");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}})),T.ajaxPrefilter((function(e){e.crossDomain&&(e.contents.script=!1)})),T.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},contents:{script:/\\b(?:java|ecma)script\\b/},converters:{\"text script\":function(e){return T.globalEval(e),e}}}),T.ajaxPrefilter(\"script\",(function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type=\"GET\")})),T.ajaxTransport(\"script\",(function(e){var t,n;if(e.crossDomain||e.scriptAttrs)return{send:function(r,i){t=T(\"" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.0/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.0/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));", + "application/vnd.holoviews_load.v0+json": "" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n", + "application/vnd.holoviews_load.v0+json": "" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ] + }, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p3397" + } + }, + "output_type": "display_data" + }, { "name": "stdout", "output_type": "stream", @@ -45,6 +173,25 @@ } ], "source": [ + "import os\n", + "import warnings\n", + "\n", + "import bokeh\n", + "import bokeh.io\n", + "import hvplot.xarray # noqa\n", + "import numpy as np\n", + "import xarray as xr\n", + "from bokeh.resources import INLINE\n", + "from rex import init_logger\n", + "\n", + "from sup3r.models import Sup3rGan\n", + "from sup3r.preprocessing import DataHandler\n", + "\n", + "bokeh.io.output_notebook(INLINE)\n", + "hvplot.extension('bokeh')\n", + "\n", + "os.environ['CUDA_VISIBLE_DEVICES'] = '-1'\n", + "init_logger('sup3r', log_level='DEBUG')\n", "%load_ext autoreload\n", "%autoreload 2" ] @@ -58,73 +205,65 @@ }, { "cell_type": "code", - "execution_count": 288, + "execution_count": 104, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "INFO - 2025-04-08 20:09:36,501 [base.py:180] : Loading GAN from disk in directory: /datasets/sup3rwind/models/experimental/15x_1x_14f_st_obs_unet_just_embed/gan_e1300\n", - "INFO - 2025-04-08 20:09:36,502 [base.py:186] : Active python environment versions: \n", + "INFO - 2025-04-11 17:46:06,839 [base.py:180] : Loading GAN from disk in directory: /datasets/sup3rwind/models/experimental/15x_1x_14f_st_unet4/gan_e600\n", + "INFO - 2025-04-11 17:46:06,841 [base.py:186] : Active python environment versions: \n", "{ 'cftime': '1.6.4',\n", " 'dask': '2024.11.2',\n", " 'h5netcdf': '1.3.0',\n", " 'netCDF4': '1.6.5',\n", " 'nrel-phygnn': '0.0.30',\n", - " 'nrel-rex': '0.2.99',\n", + " 'nrel-rex': '0.2.100',\n", " 'numpy': '1.26.4',\n", - " 'pandas': '2.2.2',\n", + " 'pandas': '2.2.3',\n", " 'python': '3.11.9 (main, Apr 19 2024, 16:48:06) [GCC 11.2.0]',\n", - " 'sklearn': '1.5.1',\n", - " 'sup3r': '0.2.3.dev34+g9d6a51d2',\n", + " 'sklearn': '1.6.1',\n", + " 'sup3r': '0.2.3.dev105+g020aed3e.d20250410',\n", " 'tensorflow': '2.15.1',\n", - " 'xarray': '2025.1.2'}\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "INFO - 2025-04-08 20:09:36,522 [abstract.py:403] : Loading model from disk that was created with the following package versions: \n", + " 'xarray': '2025.1.2'}\n", + "INFO - 2025-04-11 17:46:06,846 [abstract.py:403] : Loading model from disk that was created with the following package versions: \n", "{ 'cftime': '1.6.4',\n", " 'dask': '2024.11.2',\n", " 'h5netcdf': '1.3.0',\n", " 'netCDF4': '1.6.5',\n", " 'nrel-phygnn': '0.0.30',\n", - " 'nrel-rex': '0.2.99',\n", + " 'nrel-rex': '0.2.100',\n", " 'numpy': '1.26.4',\n", - " 'pandas': '2.2.2',\n", + " 'pandas': '2.2.3',\n", " 'python': '3.11.9 (main, Apr 19 2024, 16:48:06) [GCC 11.2.0]',\n", - " 'sklearn': '1.5.1',\n", - " 'sup3r': '0.2.3.dev34+g9d6a51d2',\n", + " 'sklearn': '1.6.1',\n", + " 'sup3r': '0.2.3.dev105+g020aed3e.d20250410',\n", " 'tensorflow': '2.15.1',\n", " 'xarray': '2025.1.2'}\n" ] } ], "source": [ - "model_dir = '/datasets/sup3rwind/models/experimental/15x_1x_14f_st_obs_unet_just_embed/gan_e1300'\n", - "model = Sup3rGanWithObs.load(model_dir)" + "model_dir = (\n", + " '/datasets/sup3rwind/models/experimental/15x_1x_14f_st_unet4/gan_e600'\n", + ")\n", + "model = Sup3rGan.load(model_dir)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Double check what the model needs as input" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Models take mainly low resolution data" + "### Double check what the model needs as input. \n", + "\n", + "------------------\n", + "Models can injest low-resolution data (`lr_features`), high-resolution \"exogenous data\" (`hr_exo_features`) like topography, and sparse observations (`obs_features`)." ] }, { "cell_type": "code", - "execution_count": 289, + "execution_count": 105, "metadata": {}, "outputs": [ { @@ -133,26 +272,12 @@ "['temperature_2m',\n", " 'relativehumidity_2m',\n", " 'pressure_0m',\n", - " 'ie',\n", - " 'zust',\n", - " 'slhf',\n", - " 'sshf',\n", - " 'd2m',\n", - " 'cape',\n", - " 'kx',\n", - " 'i10fg',\n", " 'u_10m',\n", " 'v_10m',\n", - " 'u_100m',\n", - " 'v_100m',\n", - " 'u_200m',\n", - " 'v_200m',\n", - " 'srl',\n", - " 'sza',\n", " 'topography']" ] }, - "execution_count": 289, + "execution_count": 105, "metadata": {}, "output_type": "execute_result" } @@ -162,83 +287,98 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 106, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['topography']" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "### Models can also injest some high-resolution data (hr_exo_features)." + "model.hr_exo_features" ] }, { "cell_type": "code", - "execution_count": 290, + "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['srl', 'sza', 'topography']" + "[]" ] }, - "execution_count": 290, + "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "model.hr_exo_features" + "model.obs_features" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Finaly, some models can also injest observation data which is mostly NaN except for sparse locations" + "#### Double check model output" ] }, { "cell_type": "code", - "execution_count": 291, + "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['u_10m', 'v_10m', 'temperature_2m', 'relativehumidity_2m', 'pressure_0m']" + "['temperature_2m',\n", + " 'relativehumidity_2m',\n", + " 'pressure_0m',\n", + " 'u_10m',\n", + " 'v_10m',\n", + " 'u_40m',\n", + " 'v_40m',\n", + " 'u_80m',\n", + " 'v_80m']" ] }, - "execution_count": 291, + "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "model.obs_features" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Ok, so we need to load low-res data accordings to lr_features, and high-res data according to hr_exo_features and obs_features" + "model.hr_out_features" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### We'll use some of the high-resolution training data as a proxy for \"observation data\"" + "### Load low-res and exogenous data for model input. Can use the `DataHandler` objects\n", + "-------" ] }, { "cell_type": "code", - "execution_count": 292, + "execution_count": 110, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "INFO - 2025-04-08 20:09:49,906 [utilities.py:125] : Initialized DataHandler with:\n", + "INFO - 2025-04-11 17:46:32,180 [utilities.py:125] : Initialized DataHandler with:\n", "{ 'BaseLoader': None,\n", " 'FeatureRegistry': None,\n", " 'cache_kwargs': None,\n", @@ -246,24 +386,10 @@ " 'features': [ 'temperature_2m',\n", " 'relativehumidity_2m',\n", " 'pressure_0m',\n", - " 'ie',\n", - " 'zust',\n", - " 'slhf',\n", - " 'sshf',\n", - " 'd2m',\n", - " 'cape',\n", - " 'kx',\n", - " 'i10fg',\n", " 'u_10m',\n", " 'v_10m',\n", - " 'u_100m',\n", - " 'v_100m',\n", - " 'u_200m',\n", - " 'v_200m',\n", - " 'srl',\n", - " 'sza',\n", " 'topography'],\n", - " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/lr*.h5',\n", + " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2010_*600x1600*/lr*.h5',\n", " 'hr_spatial_coarsen': 1,\n", " 'interp_kwargs': None,\n", " 'load_features': 'all',\n", @@ -274,24 +400,26 @@ " 'threshold': None,\n", " 'time_roll': 0,\n", " 'time_shift': None,\n", - " 'time_slice': slice(20, 30, None)}\n", - "DEBUG - 2025-04-08 20:09:49,910 [utilities.py:129] : Memory usage is 153.263 GB out of 269.755 GB\n", - "INFO - 2025-04-08 20:09:49,917 [utilities.py:125] : Initialized LoaderH5 with:\n", + " 'time_slice': slice(3624, 3630, None)}\n", + "DEBUG - 2025-04-11 17:46:32,182 [utilities.py:129] : Memory usage is 198.576 GB out of 269.755 GB\n", + "INFO - 2025-04-11 17:46:32,183 [utilities.py:125] : Initialized LoaderH5 with:\n", "{ 'BASE_LOADER': ,\n", " 'BaseLoader': None,\n", " 'chunks': 'auto',\n", " 'features': 'all',\n", - " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/lr*.h5',\n", + " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2010_*600x1600*/lr*.h5',\n", " 'res_kwargs': None}\n", - "DEBUG - 2025-04-08 20:09:49,917 [utilities.py:129] : Memory usage is 153.263 GB out of 269.755 GB\n", - "DEBUG - 2025-04-08 20:09:55,351 [h5.py:174] : Rechunking features with chunks: auto\n" + "DEBUG - 2025-04-11 17:46:32,183 [utilities.py:129] : Memory usage is 198.576 GB out of 269.755 GB\n", + "DEBUG - 2025-04-11 17:46:32,211 [h5.py:174] : Rechunking features with chunks: auto\n", + "INFO - 2025-04-11 17:46:32,323 [base.py:153] : Getting raster index for target / shape: None / None\n", + "INFO - 2025-04-11 17:46:32,360 [base.py:224] : The distance between the closest coordinate: [ 32.16 -121.906] and the requested target: [ 32.16 -121.906] for files: ['/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_cape.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_d2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_i10fg.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_ie.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_kx.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_lsm.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_pressure_0m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_pressure_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_relativehumidity_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_relativehumidity_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_skt.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_slhf.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_srl.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_sshf.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_sza.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_temperature_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_temperature_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_topography.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_u_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_u_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_u_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_v_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_v_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_v_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/lr_zust.h5'] is 0.0.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "/home/bbenton/repos/rex/rex/resource.py:1287: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + "/projects/usaiddata/.miniconda3/envs/sup3r/lib/python3.11/site-packages/rex/resource.py:1287: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", " datetime_index = pd.to_datetime(time_index.astype(str))\n" ] }, @@ -299,23 +427,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "INFO - 2025-04-08 20:09:56,403 [base.py:153] : Getting raster index for target / shape: None / None\n", - "INFO - 2025-04-08 20:09:56,560 [base.py:224] : The distance between the closest coordinate: [ 15.127 -104.881] and the requested target: [ 15.127 -104.881] for files: ['/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_cape.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_d2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_i10fg.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_ie.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_kx.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_lsm.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_pressure_0m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_pressure_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_relativehumidity_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_relativehumidity_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_skt.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_slhf.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_srl.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_sshf.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_sst.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_sza.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_temperature_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_temperature_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_topography.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_u_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_u_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_u_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_v_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_v_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_v_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/lr_zust.h5'] is 0.0.\n", - "INFO - 2025-04-08 20:09:58,684 [base.py:131] : Rasterizing data for target / shape: [ 15.127 -104.881] / [30 80]\n", - "INFO - 2025-04-08 20:09:59,652 [utilities.py:125] : Initialized DataHandler with:\n", + "INFO - 2025-04-11 17:46:32,479 [base.py:131] : Rasterizing data for target / shape: [ 32.16 -121.906] / [ 40 106]\n", + "INFO - 2025-04-11 17:46:33,381 [utilities.py:125] : Initialized DataHandler with:\n", "{ 'BaseLoader': None,\n", " 'FeatureRegistry': None,\n", " 'cache_kwargs': None,\n", " 'chunks': 'auto',\n", - " 'features': [ 'srl',\n", - " 'sza',\n", - " 'topography',\n", - " 'u_10m',\n", - " 'v_10m',\n", + " 'features': [ 'topography',\n", " 'temperature_2m',\n", " 'relativehumidity_2m',\n", - " 'pressure_0m'],\n", - " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/hr*.h5',\n", + " 'pressure_0m',\n", + " 'u_10m',\n", + " 'v_10m',\n", + " 'u_40m',\n", + " 'v_40m',\n", + " 'u_80m',\n", + " 'v_80m'],\n", + " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2010_*600x1600*/hr*.h5',\n", " 'hr_spatial_coarsen': 1,\n", " 'interp_kwargs': None,\n", " 'load_features': 'all',\n", @@ -326,24 +454,24 @@ " 'threshold': None,\n", " 'time_roll': 0,\n", " 'time_shift': None,\n", - " 'time_slice': slice(20, 30, None)}\n", - "DEBUG - 2025-04-08 20:09:59,653 [utilities.py:129] : Memory usage is 153.014 GB out of 269.755 GB\n", - "INFO - 2025-04-08 20:09:59,654 [utilities.py:125] : Initialized LoaderH5 with:\n", + " 'time_slice': slice(3624, 3630, None)}\n", + "DEBUG - 2025-04-11 17:46:33,382 [utilities.py:129] : Memory usage is 198.663 GB out of 269.755 GB\n", + "INFO - 2025-04-11 17:46:33,383 [utilities.py:125] : Initialized LoaderH5 with:\n", "{ 'BASE_LOADER': ,\n", " 'BaseLoader': None,\n", " 'chunks': 'auto',\n", " 'features': 'all',\n", - " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/hr*.h5',\n", + " 'file_paths': '/datasets/sup3rwind/training_data/15x_to_2km_2010_*600x1600*/hr*.h5',\n", " 'res_kwargs': None}\n", - "DEBUG - 2025-04-08 20:09:59,655 [utilities.py:129] : Memory usage is 153.014 GB out of 269.755 GB\n", - "DEBUG - 2025-04-08 20:09:59,725 [h5.py:174] : Rechunking features with chunks: auto\n" + "DEBUG - 2025-04-11 17:46:33,384 [utilities.py:129] : Memory usage is 198.664 GB out of 269.755 GB\n", + "DEBUG - 2025-04-11 17:46:33,407 [h5.py:174] : Rechunking features with chunks: auto\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "/home/bbenton/repos/rex/rex/resource.py:1287: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", + "/projects/usaiddata/.miniconda3/envs/sup3r/lib/python3.11/site-packages/rex/resource.py:1287: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n", " datetime_index = pd.to_datetime(time_index.astype(str))\n" ] }, @@ -351,140 +479,112 @@ "name": "stdout", "output_type": "stream", "text": [ - "INFO - 2025-04-08 20:10:00,012 [base.py:153] : Getting raster index for target / shape: None / None\n", - "INFO - 2025-04-08 20:10:00,069 [base.py:224] : The distance between the closest coordinate: [ 14.996 -105.005] and the requested target: [ 14.996 -105.005] for files: ['/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_pressure_0m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_relativehumidity_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_srl.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_sza.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_temperature_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_topography.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_120m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_160m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_40m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_u_80m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_120m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_160m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_40m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2011_15_-105_450x1200/hr_v_80m.h5'] is 0.0.\n", - "INFO - 2025-04-08 20:10:00,263 [base.py:131] : Rasterizing data for target / shape: [ 14.996 -105.005] / [ 450 1200]\n" + "INFO - 2025-04-11 17:46:33,632 [base.py:153] : Getting raster index for target / shape: None / None\n", + "INFO - 2025-04-11 17:46:33,706 [base.py:224] : The distance between the closest coordinate: [ 32. -122.001] and the requested target: [ 32. -122.001] for files: ['/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_pressure_0m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_relativehumidity_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_srl.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_sza.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_temperature_2m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_topography.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_u_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_u_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_u_120m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_u_160m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_u_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_u_40m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_u_80m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_v_100m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_v_10m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_v_120m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_v_160m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_v_200m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_v_40m.h5', '/datasets/sup3rwind/training_data/15x_to_2km_2010_32_-122_600x1600/hr_v_80m.h5'] is 0.0.\n", + "INFO - 2025-04-11 17:46:33,996 [base.py:131] : Rasterizing data for target / shape: [ 32. -122.001] / [ 600 1590]\n" ] } ], "source": [ - "lr_data = DataHandler('/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/lr*.h5', time_slice=slice(20, 30),\n", - " features=model.lr_features)\n", - "hr_data = DataHandler('/datasets/sup3rwind/training_data/15x_to_2km_2011_*450x1200*/hr*.h5', time_slice=slice(20, 30),\n", - " features=model.hr_exo_features+[feat.replace('_obs', '') for feat in model.obs_features])" + "# conus domain\n", + "lr_data = DataHandler(\n", + " '/datasets/sup3rwind/training_data/15x_to_2km_2010_*600x1600*/lr*.h5',\n", + " time_slice=slice(3624, 3630),\n", + " features=model.lr_features,\n", + ")\n", + "hr_data = DataHandler(\n", + " '/datasets/sup3rwind/training_data/15x_to_2km_2010_*600x1600*/hr*.h5',\n", + " time_slice=slice(3624, 3630),\n", + " features=model.hr_exo_features\n", + " + model.hr_out_features\n", + " + model.obs_features,\n", + ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Now we need to pass the data to the model in the format it expects" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### The 'steps' list to accomodate possible multi-step models. 'combine_type' tells the model how to incorporate the exogenous data (e.g. whether it's part of a mid-network layer or incorporated as part of a multi-step forward pass)" + "### Pass exogenous data to the data to the model as a dictionary\n", + "\n", + "-----\n" ] }, { "cell_type": "code", - "execution_count": 293, + "execution_count": 111, "metadata": {}, "outputs": [], "source": [ - "lr_input = lr_data.values[None, ...] # add batch dimension\n", + "lr_input = lr_data.values[None, ...] # add batch dimension\n", "\n", - "# simulate obs data by masking some of the high-res ground truth data\n", - "onshore_mask = model._get_obs_mask(\n", - " np.zeros((1, *hr_data.shape[:-1])), spatial_frac=1e-4, time_frac=0.8)\n", - "offshore_mask = model._get_obs_mask(\n", - " np.zeros((1, *hr_data.shape[:-1])), spatial_frac=1e-4, time_frac=0.5)\n", - "mask = np.where(hr_data['topography'].isel(time=0).values[None] > 0,\n", - " onshore_mask, offshore_mask)\n", - "\n", - "exo_input = {}\n", - "\n", - "for feature in model.hr_exo_features + model.obs_features:\n", - " if feature in model.obs_features:\n", - " data = np.where(\n", - " mask[0, ..., None], np.nan,\n", - " hr_data[feature.replace('_obs', '')].values)\n", - " else:\n", - " data = hr_data[feature].values\n", - " exo_input[feature] = {\n", - " 'steps': [\n", - " {'model': 0,\n", - " 'combine_type': 'layer',\n", - " 'data': data[None, ...][..., None],\n", - " }\n", - " ]\n", - " }" + "# add batch dimension and feature channel dimension\n", + "exo_input = {\n", + " feature: {'data': hr_data[feature].values[None, ..., None]}\n", + " for feature in model.hr_exo_features + model.obs_features\n", + "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Now we can finally send the data through the model and generate output" + "### Send low-res and exogenous data through the model to downscale\n", + "-----" ] }, { "cell_type": "code", - "execution_count": 294, + "execution_count": 112, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING - 2025-04-11 17:46:59,366 [exo.py:96] : ExoData entry for topography has no \"steps\" key. Assuming this is for a single step.\n", + "WARNING - 2025-04-11 17:46:59,367 [exo.py:104] : ExoData entry for topography, step #1 has no \"combine_type\" key. Assuming this is for a layer combination.\n" + ] + } + ], "source": [ - "hr_output = model.generate(lr_input, exogenous_data=exo_input)" + "with warnings.catch_warnings():\n", + " warnings.simplefilter('ignore')\n", + " hr_output = model.generate(lr_input, exogenous_data=exo_input)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Double check ordering of output features" - ] - }, - { - "cell_type": "code", - "execution_count": 295, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['temperature_2m',\n", - " 'relativehumidity_2m',\n", - " 'pressure_0m',\n", - " 'u_10m',\n", - " 'v_10m',\n", - " 'u_40m',\n", - " 'v_40m',\n", - " 'u_80m',\n", - " 'v_80m',\n", - " 'u_100m',\n", - " 'v_100m',\n", - " 'u_120m',\n", - " 'v_120m',\n", - " 'u_160m',\n", - " 'v_160m',\n", - " 'u_200m',\n", - " 'v_200m']" - ] - }, - "execution_count": 295, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "model.hr_out_features" + "#### Put output in an xarray dataset for easy visualization" ] }, { "cell_type": "code", - "execution_count": 296, + "execution_count": 113, "metadata": {}, "outputs": [], "source": [ + "if len(lr_data.latitude.shape) == 2:\n", + " lr_data['latitude'] = lr_data.latitude.values[:, 0]\n", + " lr_data['longitude'] = lr_data.longitude.values[0, :]\n", + "\n", + "if len(hr_data.latitude.shape) == 2:\n", + " hr_data['latitude'] = hr_data.latitude.values[:, 0]\n", + " hr_data['longitude'] = hr_data.longitude.values[0, :]\n", + "\n", "dims = ('latitude', 'longitude', 'time')\n", "coords = {\n", - " 'latitude': hr_data.latitude.values[:, 0],\n", - " 'longitude': hr_data.longitude.values[0, :],\n", - " 'time': hr_data.time\n", + " 'latitude': hr_data.latitude.values,\n", + " 'longitude': hr_data.longitude.values,\n", + " 'time': hr_data.time,\n", + "}\n", + "data = {\n", + " feature: (dims, hr_output[0, ..., i])\n", + " for i, feature in enumerate(model.hr_out_features)\n", "}\n", - "hr_out = xr.Dataset({feature: (dims, hr_output[0, ..., i]) for i, feature in enumerate(model.hr_out_features)})\n", + "hr_out = xr.Dataset(data)\n", "hr_out = hr_out.assign_coords(coords)" ] }, @@ -492,75 +592,86 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Let's plot windspeed at 10m and 100m" + "### Let's plot windspeed at 10m" ] }, { "cell_type": "code", - "execution_count": 297, + "execution_count": 114, "metadata": {}, "outputs": [], "source": [ - "hr_out['windspeed_10m'] = (dims, np.hypot(hr_out['u_10m'].values, hr_out['v_10m'].values))\n", - "hr_out['windspeed_100m'] = (dims, np.hypot(hr_out['u_100m'].values, hr_out['v_100m'].values))" + "hr_out['windspeed_10m'] = (\n", + " dims,\n", + " np.hypot(hr_out['u_10m'].values, hr_out['v_10m'].values),\n", + ")\n", + "\n", + "lr_data['windspeed_10m'] = (\n", + " dims,\n", + " np.hypot(lr_data['u_10m'].values, lr_data['v_10m'].values),\n", + ")\n", + "hr_data['windspeed_10m'] = (\n", + " dims,\n", + " np.hypot(hr_data['u_10m'].values, hr_data['v_10m'].values),\n", + ")" ] }, { "cell_type": "code", - "execution_count": 298, + "execution_count": 116, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "b53e2b061b654627a7528931e92dcd89", + "model_id": "b8b058b2cecc4c73b899ac0ef99a4410", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "BokehModel(combine_events=True, render_bundle={'docs_json': {'d9a0839b-df62-4939-8602-c3a67b9b838d': {'version…" + "BokehModel(combine_events=True, render_bundle={'docs_json': {'01296032-c094-413a-9a67-93d21db3539b': {'version…" ] }, - "execution_count": 298, + "execution_count": 116, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "hr_out['windspeed_10m'].hvplot.quadmesh(\n", - " groupby='time', cmap='YlGnBu', clim=(0, 10),\n", + "plot1 = lr_data['windspeed_10m'].hvplot.quadmesh(\n", + " groupby='time',\n", + " cmap='YlGnBu',\n", + " clim=(0, 10),\n", " rasterize=True,\n", - " widget_type=\"scrubber\",\n", - " widget_location=\"bottom\")" - ] - }, - { - "cell_type": "code", - "execution_count": 299, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a1eaad432ffa4db7b043814622287030", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "BokehModel(combine_events=True, render_bundle={'docs_json': {'dfed3acb-d406-4da4-909c-791dfb300de2': {'version…" - ] - }, - "execution_count": 299, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "hr_out['windspeed_100m'].hvplot.quadmesh(\n", - " groupby='time', cmap='YlGnBu', clim=(0, 10),\n", + " widget_type='scrubber',\n", + " widget_location='bottom',\n", + " width=1000,\n", + " height=400,\n", + ")\n", + "\n", + "plot2 = hr_out['windspeed_10m'].hvplot.quadmesh(\n", + " groupby='time',\n", + " cmap='YlGnBu',\n", + " clim=(0, 10),\n", " rasterize=True,\n", - " widget_type=\"scrubber\",\n", - " widget_location=\"bottom\")" + " widget_type='scrubber',\n", + " widget_location='bottom',\n", + " width=1000,\n", + " height=400,\n", + ")\n", + "\n", + "plot3 = hr_data['windspeed_10m'].hvplot.quadmesh(\n", + " groupby='time',\n", + " cmap='YlGnBu',\n", + " clim=(0, 10),\n", + " rasterize=True,\n", + " widget_type='scrubber',\n", + " widget_location='bottom',\n", + " width=1000,\n", + " height=400,\n", + ")\n", + "\n", + "(plot1 + plot2 + plot3)" ] }, { From 925f44863ef99a7b0cbf6a93e2d260c7d3164a18 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 11 Apr 2025 19:46:15 -0600 Subject: [PATCH 077/122] netcdf version cap comment in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6db7863b28..0ac68b2c45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "NREL-farms>=1.0.4", "dask>=2022.0", "h5netcdf>=1.1.0", - "netCDF4>=1.5.8,<1.7", + "netCDF4>=1.5.8,<1.6.1", # 1.6.1 and up is not thread safe - https://github.com/pydata/xarray/issues/7079 "cftime>=1.6.2", "matplotlib>=3.1", "numpy>=1.7.0,<2.0.0", From fd0ac6619f43ea78f52b258e1759d2ae1bda8d93 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 11 Apr 2025 19:47:18 -0600 Subject: [PATCH 078/122] dont need to copy inputs in np.meshgrid. set copy=False in `get_lat_lon` --- sup3r/postprocessing/writers/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sup3r/postprocessing/writers/base.py b/sup3r/postprocessing/writers/base.py index 5a792b2de0..4280d45c5a 100644 --- a/sup3r/postprocessing/writers/base.py +++ b/sup3r/postprocessing/writers/base.py @@ -562,9 +562,9 @@ def get_lat_lon(cls, low_res_lat_lon, shape): new_x = np.arange(0, 10, 10 / hr_x) + 5 / hr_x logger.debug('Running meshgrid.') - X, Y = np.meshgrid(x, y) + X, Y = np.meshgrid(x, y, copy=False) old = np.array([Y.flatten(), X.flatten()]).T - X, Y = np.meshgrid(new_x, new_y) + X, Y = np.meshgrid(new_x, new_y, copy=False) new = np.array([Y.flatten(), X.flatten()]).T logger.debug('Running griddata.') From 6310777f64b2141ca123e83d5881de8dfbc033d0 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 11 Apr 2025 21:15:36 -0600 Subject: [PATCH 079/122] reverting netcdf version cap --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0ac68b2c45..6db7863b28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "NREL-farms>=1.0.4", "dask>=2022.0", "h5netcdf>=1.1.0", - "netCDF4>=1.5.8,<1.6.1", # 1.6.1 and up is not thread safe - https://github.com/pydata/xarray/issues/7079 + "netCDF4>=1.5.8,<1.7", "cftime>=1.6.2", "matplotlib>=3.1", "numpy>=1.7.0,<2.0.0", From 2f1470793533aed8d1f1d3496e49a7153ada0f9b Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sat, 12 Apr 2025 07:06:36 -0600 Subject: [PATCH 080/122] numpy < 2.0 - need this to use modules compiled with np 1.x --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6db7863b28..9259c1ff40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "NREL-farms>=1.0.4", "dask>=2022.0", "h5netcdf>=1.1.0", - "netCDF4>=1.5.8,<1.7", + "netCDF4>=1.5.8,<1.6.1", # v1.6.1+ is not thread safe "cftime>=1.6.2", "matplotlib>=3.1", "numpy>=1.7.0,<2.0.0", From 6126d3e4e565095eff8fc577c32b55e87f25afa9 Mon Sep 17 00:00:00 2001 From: Brandon N Benton Date: Sat, 12 Apr 2025 06:13:25 -0700 Subject: [PATCH 081/122] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9259c1ff40..6db7863b28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "NREL-farms>=1.0.4", "dask>=2022.0", "h5netcdf>=1.1.0", - "netCDF4>=1.5.8,<1.6.1", # v1.6.1+ is not thread safe + "netCDF4>=1.5.8,<1.7", "cftime>=1.6.2", "matplotlib>=3.1", "numpy>=1.7.0,<2.0.0", From 8050d1d5d3a0a5d98a3c5030169870ac6e005de9 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 16 Apr 2025 11:48:46 -0600 Subject: [PATCH 082/122] time dependent lat / lon features - need the time dimension for dual rasterizing. --- sup3r/preprocessing/derivers/methods.py | 29 +++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/sup3r/preprocessing/derivers/methods.py b/sup3r/preprocessing/derivers/methods.py index 5c4f60f369..25690c43fb 100644 --- a/sup3r/preprocessing/derivers/methods.py +++ b/sup3r/preprocessing/derivers/methods.py @@ -9,6 +9,7 @@ from sup3r.preprocessing.accessor import Sup3rX from sup3r.preprocessing.base import Sup3rDataset +from sup3r.preprocessing.names import Dimension from .utilities import SolarZenith, invert_uv, transform_rotate_wind @@ -391,6 +392,30 @@ def compute(cls, data): return sza.astype(np.float32) +class Latitude(DerivedFeature): + """latitude feature with time dimension included.""" + + @classmethod + def compute(cls, data): + """Compute method for latitude.""" + lat = data[Dimension.LATITUDE] + lat = lat.expand_dims(Dimension.TIME, axis=-1) + lat = np.repeat(lat, len(data.time_index), axis=-1) + return lat.astype(np.float32) + + +class Longitude(DerivedFeature): + """longitude feature with time dimension included.""" + + @classmethod + def compute(cls, data): + """Compute method for longitude.""" + lon = data[Dimension.LONGITUDE] + lon = lon.expand_dims(Dimension.TIME, axis=-1) + lon = np.repeat(lon, len(data.time_index), axis=-1) + return lon.astype(np.float32) + + RegistryBase = { 'u_(.*)': UWind, 'v_(.*)': VWind, @@ -400,8 +425,8 @@ def compute(cls, data): 'cloud_mask': CloudMask, 'clearsky_ratio': ClearSkyRatio, 'sza': Sza, - 'latitude_feature': 'latitude', - 'longitude_feature': 'longitude', + 'latitude_feature': Latitude, + 'longitude_feature': Longitude, } RegistryH5WindCC = { From 9bd96bf2bad9c2c7372c3efa4da66b9bf334f82b Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 16 Apr 2025 11:49:44 -0600 Subject: [PATCH 083/122] updated sup3rwind example readme with example notebook. --- examples/sup3rwind/README.rst | 2 ++ sup3r/preprocessing/rasterizers/exo.py | 5 +++++ sup3r/utilities/utilities.py | 6 +----- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/sup3rwind/README.rst b/examples/sup3rwind/README.rst index 4cb0c5fa31..6784e15a19 100644 --- a/examples/sup3rwind/README.rst +++ b/examples/sup3rwind/README.rst @@ -32,6 +32,8 @@ The process for running the Sup3rWind models is much the same as for `Sup3rCC `__ for how to run models without config files. + Training from scratch --------------------- diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index ff6a6e47e1..4d71d4a936 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -375,6 +375,11 @@ def _get_data_3d(self): data corresponding to the high-resolution grid (the file_paths input grid * s_enhance * t_enhance) and high-resolution time index. The shape is (lats, lons, time) + + TODO: This does not currently perform any aggregation of the source + data, it just uses the closest source data point to the target + point, within the distance bound, and uses exact matches for + time steps. """ assert ( len(self.source_data.shape) == 2 and self.source_data.shape[1] > 1 diff --git a/sup3r/utilities/utilities.py b/sup3r/utilities/utilities.py index 3a580e81d4..34858128a4 100644 --- a/sup3r/utilities/utilities.py +++ b/sup3r/utilities/utilities.py @@ -47,11 +47,7 @@ def preprocess_datasets(dset): def xr_open_mfdataset(files, **kwargs): """Wrapper for xr.open_mfdataset with default opening options.""" - default_kwargs = { - 'engine': 'netcdf4', - 'coords': 'minimal', - 'compat': 'override', - } + default_kwargs = {'engine': 'netcdf4'} default_kwargs.update(kwargs) if isinstance(files, str): files = [files] From 889f01ce9feaf7bc47f89b90aaa12f9d1e402c63 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 16 Apr 2025 18:36:13 -0600 Subject: [PATCH 084/122] Added `compute_disc` to solar model `calc_loss`. Split up the sub day loss calcs for disc and gen content. --- sup3r/models/solar_cc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sup3r/models/solar_cc.py b/sup3r/models/solar_cc.py index 66bb9e16f6..24edb12f0b 100644 --- a/sup3r/models/solar_cc.py +++ b/sup3r/models/solar_cc.py @@ -98,6 +98,7 @@ def calc_loss( train_gen=True, train_disc=False, compute_disc=False, + compute_disc=False, ): """Calculate the GAN loss function using generated and true high resolution data. From 0d4f6819fd5e6d80ab43cac6284f33566dd56927 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 17 Apr 2025 14:21:30 -0600 Subject: [PATCH 085/122] fix: wasn't updating batch loss details correctly when both disc and gen were trained. --- sup3r/models/base.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 5d8504e42f..d159b598ac 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -1041,7 +1041,7 @@ def _post_batch(self, ib, b_loss_details, n_batches, previous_means): n_batches : int Number of batches in an epoch previous_means : dict - Dictionary of previous loss means over the loss_mean_window + Dictionary of loss means over the last epoch Returns ------- @@ -1070,10 +1070,9 @@ def _post_batch(self, ib, b_loss_details, n_batches, previous_means): gen_loss = self._train_record['train_loss_gen'].values.mean() logger.debug( - 'Batch {} out of {} has (gen / disc) loss of: ' - '({:.2e} / {:.2e}). Running mean (gen / disc): ' - '({:.2e} / {:.2e}). Trained (gen / disc): ' - '({} / {})'.format( + 'Batch {} out of {} has (gen / disc) loss of: ({:.2e} / {:.2e}). ' + 'Running mean (gen / disc): ({:.2e} / {:.2e}). Trained ' + '(gen / disc): ({} / {})'.format( ib + 1, n_batches, b_loss_details['loss_gen'], @@ -1091,8 +1090,7 @@ def _post_batch(self, ib, b_loss_details, n_batches, previous_means): ) logger.warning(msg) warn(msg) - loss_means = self._train_record.mean(axis=0) - return loss_means.to_dict() + return self._train_record.mean(axis=0).to_dict() def _train_epoch( self, From 29d15a989d17fee9c46cd6cabcf01eed91f5af02 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sat, 19 Apr 2025 10:43:13 -0600 Subject: [PATCH 086/122] NaN checks for fwp input and output and some QOL adds. --- sup3r/pipeline/forward_pass.py | 27 ++++++++++++++++++-------- sup3r/preprocessing/accessor.py | 8 +++++++- sup3r/preprocessing/base.py | 6 ++++++ sup3r/preprocessing/rasterizers/exo.py | 2 +- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/sup3r/pipeline/forward_pass.py b/sup3r/pipeline/forward_pass.py index 3616e23317..a9cda726e5 100644 --- a/sup3r/pipeline/forward_pass.py +++ b/sup3r/pipeline/forward_pass.py @@ -384,13 +384,13 @@ def get_node_cmd(cls, config): return cmd.replace('\\', '/') @classmethod - def _constant_output_check(cls, out_data, allowed_const): - """Check if forward pass output is constant. This can happen when the - chunk going through the forward pass is too big. This is due to a - tensorflow padding bug, with the padding mode set to 'reflect'. With - the currently preferred tensorflow version (2.15.1) this results in - scrambled output rather than constant. - https://github.com/tensorflow/tensorflow/issues/91027 + def _output_check(cls, out_data, allowed_const): + """Check if forward pass output is constant or contains NaNs. This can + happen when the chunk going through the forward pass is too big. + This is due to a tensorflow padding bug, with the padding mode + set to 'reflect'. With the currently preferred tensorflow + version (2.15.1) this results in scrambled output rather than + constant. https://github.com/tensorflow/tensorflow/issues/91027 Parameters ---------- @@ -410,6 +410,12 @@ def _constant_output_check(cls, out_data, allowed_const): elif not isinstance(allowed_const, (list, tuple)): allowed_const = [allowed_const] + if np.isnan(out_data).any(): + msg = 'Forward pass output contains NaN values!' + failed = True + logger.error(msg) + return failed + for i in range(out_data.shape[-1]): msg = f'All values are the same for feature channel {i}!' value0 = out_data[0, 0, 0, i] @@ -627,6 +633,11 @@ def run_chunk( model = get_model(model_class, model_kwargs) + if np.isnan(chunk.input_data).any(): + msg = 'Input data contains NaN values!' + logger.error(msg) + raise RuntimeError(msg) + output_data = cls.run_generator( data_chunk=chunk.input_data, hr_crop_slices=chunk.hr_crop_slice, @@ -636,7 +647,7 @@ def run_chunk( model=model, ) - failed = cls._constant_output_check( + failed = cls._output_check( output_data, allowed_const=allowed_const ) diff --git a/sup3r/preprocessing/accessor.py b/sup3r/preprocessing/accessor.py index 1eb9bb360f..018569d160 100644 --- a/sup3r/preprocessing/accessor.py +++ b/sup3r/preprocessing/accessor.py @@ -507,7 +507,13 @@ def time_step(self): def lat_lon(self) -> Union[np.ndarray, da.core.Array]: """Base lat lon for contained data.""" coords = [self._ds[d] for d in Dimension.coords_2d()] - return self._stack_features(coords) + lat_lon = self._stack_features(coords) + + # only one coordinate but this property is assumed to be a 2D array + if len(lat_lon.shape) == 1: + lat_lon = lat_lon.reshape((1, 1, 2)) + + return lat_lon @lat_lon.setter def lat_lon(self, lat_lon): diff --git a/sup3r/preprocessing/base.py b/sup3r/preprocessing/base.py index 7d81f9d273..d6f701e07c 100644 --- a/sup3r/preprocessing/base.py +++ b/sup3r/preprocessing/base.py @@ -456,3 +456,9 @@ def __getattr__(self, attr): except Exception as e: msg = f'{self.__class__.__name__} object has no attribute "{attr}"' raise AttributeError(msg) from e + + def __enter__(self): + return self + + def __exit__(self, *args): + pass diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index 4d71d4a936..6799dfa916 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -477,7 +477,7 @@ def __new__(cls, file_paths, source_file, feature, **kwargs): kwargs = { 'file_paths': file_paths, 'source_file': source_file, - 'feature': feature.replace('_obs', ''), + 'feature': feature, **kwargs, } From ac96bd1f7446b2681e3aea16f62f3d74e277657f Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 20 Apr 2025 09:36:53 -0600 Subject: [PATCH 087/122] add some log info on output values outside of good range. --- sup3r/postprocessing/writers/base.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sup3r/postprocessing/writers/base.py b/sup3r/postprocessing/writers/base.py index 4280d45c5a..eed667e56f 100644 --- a/sup3r/postprocessing/writers/base.py +++ b/sup3r/postprocessing/writers/base.py @@ -405,11 +405,19 @@ def enforce_limits(features, data): f_max = data[..., fidx].max() f_min = data[..., fidx].min() - msg = f'{fn} has a max of {f_max} > {max_val}. {enforcing_msg}' + max_frac = np.sum(data[..., fidx] > max_val) / data[..., fidx].size + min_frac = np.sum(data[..., fidx] < min_val) / data[..., fidx].size + msg = ( + f'{fn} has a max of {f_max} > {max_val}, with ' + f'{max_frac:.4e} of points above this max. {enforcing_msg}' + ) if f_max > max_val: logger.warning(msg) warn(msg) - msg = f'{fn} has a min of {f_min} < {min_val}. {enforcing_msg}' + msg = ( + f'{fn} has a min of {f_min} < {min_val}, with ' + f'{min_frac:.4e} of points below this min. {enforcing_msg}' + ) if f_min < min_val: logger.warning(msg) warn(msg) From 07b86ea813814106515d5ee70ed51601e4226840 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 20 Apr 2025 18:34:31 -0600 Subject: [PATCH 088/122] feat: add nn_fill option to handle data outside limits in forward pass and output handling --- sup3r/pipeline/forward_pass.py | 6 ++ sup3r/pipeline/strategy.py | 4 + sup3r/postprocessing/writers/base.py | 120 +++++---------------------- sup3r/postprocessing/writers/h5.py | 5 ++ sup3r/postprocessing/writers/nc.py | 5 ++ sup3r/utilities/utilities.py | 114 ++++++++++++++++++++++++- 6 files changed, 152 insertions(+), 102 deletions(-) diff --git a/sup3r/pipeline/forward_pass.py b/sup3r/pipeline/forward_pass.py index a9cda726e5..d2c7b44900 100644 --- a/sup3r/pipeline/forward_pass.py +++ b/sup3r/pipeline/forward_pass.py @@ -479,6 +479,7 @@ def _run_serial(cls, strategy, node_index): allowed_const=strategy.allowed_const, output_workers=strategy.output_workers, invert_uv=strategy.invert_uv, + nn_fill=strategy.nn_fill, meta=fwp.meta, ) logger.info( @@ -588,6 +589,7 @@ def run_chunk( allowed_const, invert_uv=None, meta=None, + nn_fill=True, output_workers=None, ): """Run a forward pass on single spatiotemporal chunk. @@ -615,6 +617,9 @@ def run_chunk( Whether to convert uv to windspeed and winddirection for writing output. This defaults to True for H5 output and False for NETCDF output. + nn_fill : bool + Whether to fill data outside of limits with nearest neighbour or + cap to limits. meta : dict | None Meta data to write to forward pass output file. output_workers : int | None @@ -662,6 +667,7 @@ def run_chunk( out_file=chunk.out_file, meta_data=meta, invert_uv=invert_uv, + nn_fill=nn_fill, max_workers=output_workers, gids=chunk.gids, ) diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index 7b8b25680a..e443bb556d 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -175,6 +175,9 @@ class ForwardPassStrategy: Whether to convert u and v wind components to windspeed and direction for writing to output. This defaults to True for H5 output and False for NETCDF output. + nn_fill : bool + Whether to fill data outside of limits with nearest neighbour or cap + to limits. pass_workers : int | None Max number of workers to use for performing forward passes on a single node. If 1 then all forward passes on chunks distributed to a single @@ -210,6 +213,7 @@ class ForwardPassStrategy: incremental: bool = True output_workers: int = 1 invert_uv: Optional[bool] = None + nn_fill: bool = True pass_workers: int = 1 max_nodes: int = 1 head_node: bool = False diff --git a/sup3r/postprocessing/writers/base.py b/sup3r/postprocessing/writers/base.py index eed667e56f..edb732e005 100644 --- a/sup3r/postprocessing/writers/base.py +++ b/sup3r/postprocessing/writers/base.py @@ -8,7 +8,6 @@ import os import re from abc import abstractmethod -from warnings import warn import dask import numpy as np @@ -22,6 +21,8 @@ ) from sup3r.utilities import VERSION_RECORD from sup3r.utilities.utilities import ( + enforce_limits, + get_dset_attrs, pd_date_range, safe_serialize, xr_open_mfdataset, @@ -29,11 +30,6 @@ logger = logging.getLogger(__name__) -ATTR_DIR = os.path.dirname(os.path.realpath(__file__)) -ATTR_FP = os.path.join(ATTR_DIR, 'output_attrs.json') -with open(ATTR_FP) as f: - OUTPUT_ATTRS = json.load(f) - class OutputMixin: """Methods used by various Output and Collection classes""" @@ -60,39 +56,6 @@ def get_time_dim_name(filepath): return time_key[0] return 'time' - @staticmethod - def get_dset_attrs(feature): - """Get attrributes for output feature - - Parameters - ---------- - feature : str - Name of feature to write - - Returns - ------- - attrs : dict - Dictionary of attributes for requested dset - dtype : str - Data type for requested dset. Defaults to float32 - """ - feat_base_name = parse_feature(feature).basename - if feat_base_name in OUTPUT_ATTRS: - attrs = OUTPUT_ATTRS[feat_base_name] - dtype = attrs.get('dtype', 'float32') - else: - attrs = {} - dtype = 'float32' - msg = ( - 'Could not find feature "{}" with base name "{}" in ' - 'OUTPUT_ATTRS global variable. Writing with float32 and no ' - 'chunking.'.format(feature, feat_base_name) - ) - logger.warning(msg) - warn(msg) - - return attrs, dtype - @staticmethod def _init_h5(out_file, time_index, meta, global_attrs): """Initialize the output h5 file to save data to. @@ -135,7 +98,7 @@ def _ensure_dset_in_output(cls, out_file, dset, data=None): with RexOutputs(out_file, mode='a') as f: if dset not in f.dsets: - attrs, dtype = cls.get_dset_attrs(dset) + attrs, dtype = get_dset_attrs(dset) logger.info( 'Initializing dataset "{}" with shape {} and ' 'dtype {}'.format(dset, f.shape, dtype) @@ -176,7 +139,7 @@ def write_data( fh.time_index = time_index for dset, data in zip(dsets, data_list): - attrs, dtype = cls.get_dset_attrs(dset) + attrs, dtype = get_dset_attrs(dset) fh.add_dataset( tmp_file, dset, @@ -337,7 +300,13 @@ def invert_uv_single_pair(data, lat_lon, u_idx, v_idx): @classmethod def _transform_output( - cls, data, features, lat_lon, invert_uv=None, max_workers=None + cls, + data, + features, + lat_lon, + invert_uv=None, + nn_fill=False, + max_workers=None, ): """Transform output data before writing to H5 file @@ -355,6 +324,9 @@ def _transform_output( invert_uv : bool | None Whether to convert u and v wind components to windspeed and direction + nn_fill : bool + Whether to fill values outside limits with nearest neighbors. If + False, values outside limits will be set to the limits. max_workers : int | None Max workers to use for inverse transform. If None the max_workers will be estimated based on memory limits. @@ -368,65 +340,9 @@ def _transform_output( data, features, lat_lon, max_workers=max_workers ) features = cls.get_renamed_features(features) - data = cls.enforce_limits(features=features, data=data) + data = enforce_limits(features=features, data=data, nn_fill=nn_fill) return data, features - @staticmethod - def enforce_limits(features, data): - """Enforce physical limits for feature data - - Parameters - ---------- - features : list - List of features with ordering corresponding to last channel of - data array. - data : ndarray - Array of feature data - - Returns - ------- - data : ndarray - Array of feature data with physical limits enforced - """ - maxes = [] - mins = [] - for fidx, fn in enumerate(features): - dset_name = parse_feature(fn).basename - if dset_name not in OUTPUT_ATTRS: - msg = f'Could not find "{dset_name}" in OUTPUT_ATTRS dict!' - logger.error(msg) - raise KeyError(msg) - - max_val = OUTPUT_ATTRS[dset_name].get('max', np.inf) - min_val = OUTPUT_ATTRS[dset_name].get('min', -np.inf) - enforcing_msg = ( - f'Enforcing range of ({min_val}, {max_val} for "{fn}")' - ) - - f_max = data[..., fidx].max() - f_min = data[..., fidx].min() - max_frac = np.sum(data[..., fidx] > max_val) / data[..., fidx].size - min_frac = np.sum(data[..., fidx] < min_val) / data[..., fidx].size - msg = ( - f'{fn} has a max of {f_max} > {max_val}, with ' - f'{max_frac:.4e} of points above this max. {enforcing_msg}' - ) - if f_max > max_val: - logger.warning(msg) - warn(msg) - msg = ( - f'{fn} has a min of {f_min} < {min_val}, with ' - f'{min_frac:.4e} of points below this min. {enforcing_msg}' - ) - if f_min < min_val: - logger.warning(msg) - warn(msg) - maxes.append(max_val) - mins.append(min_val) - - data = np.maximum(data, mins) - return np.minimum(data, maxes).astype(np.float32) - @staticmethod def pad_lat_lon(lat_lon): """Pad lat lon grid with additional rows and columns to use for @@ -634,6 +550,7 @@ def _write_output( out_file, meta_data, invert_uv=True, + nn_fill=False, max_workers=None, gids=None, ): @@ -649,6 +566,7 @@ def write_output( out_file, meta_data=None, invert_uv=None, + nn_fill=False, max_workers=None, gids=None, ): @@ -673,6 +591,9 @@ def write_output( invert_uv : bool | None Whether to convert u and v wind components to windspeed and direction + nn_fill : bool + Whether to fill data outside of limits with nearest neighbour or + cap to limits max_workers : int | None Max workers to use for inverse uv transform. If None the max_workers will be estimated based on memory limits. @@ -690,6 +611,7 @@ def write_output( out_file, meta_data=meta_data, invert_uv=invert_uv, + nn_fill=nn_fill, max_workers=max_workers, gids=gids, ) diff --git a/sup3r/postprocessing/writers/h5.py b/sup3r/postprocessing/writers/h5.py index 0e00e95634..e033005268 100644 --- a/sup3r/postprocessing/writers/h5.py +++ b/sup3r/postprocessing/writers/h5.py @@ -26,6 +26,7 @@ def _write_output( out_file, meta_data=None, invert_uv=None, + nn_fill=False, max_workers=None, gids=None, ): @@ -51,6 +52,9 @@ def _write_output( invert_uv : bool | None Whether to convert u and v wind components to windspeed and direction + nn_fill : bool + Whether to fill data outside of limits with nearest + neighbour or cap to limits max_workers : int | None Max workers to use for inverse transform. gids : list @@ -74,6 +78,7 @@ def _write_output( lat_lon, max_workers=max_workers, invert_uv=invert_uv, + nn_fill=nn_fill, ) gids = ( gids diff --git a/sup3r/postprocessing/writers/nc.py b/sup3r/postprocessing/writers/nc.py index 35d5a211ea..81fb098a19 100644 --- a/sup3r/postprocessing/writers/nc.py +++ b/sup3r/postprocessing/writers/nc.py @@ -28,6 +28,7 @@ def _write_output( meta_data=None, max_workers=None, invert_uv=None, + nn_fill=False, gids=None, ): """Write forward pass output to NETCDF file @@ -54,6 +55,9 @@ def _write_output( invert_uv : bool | None Whether to convert u and v wind components to windspeed and direction + nn_fill : bool + Whether to fill data outside of limits with nearest neighbour or + cap to limits gids : list List of coordinate indices used to label each lat lon pair and to help with spatial chunk data collection @@ -65,6 +69,7 @@ def _write_output( features=features, lat_lon=lat_lon, invert_uv=invert_uv, + nn_fill=nn_fill, max_workers=max_workers, ) diff --git a/sup3r/utilities/utilities.py b/sup3r/utilities/utilities.py index 34858128a4..48cc06d359 100644 --- a/sup3r/utilities/utilities.py +++ b/sup3r/utilities/utilities.py @@ -2,10 +2,12 @@ import json import logging +import os import random import re import string import time +from warnings import warn import numpy as np import pandas as pd @@ -14,10 +16,17 @@ from packaging import version from scipy import ndimage as nd -logger = logging.getLogger(__name__) +from sup3r.preprocessing.derivers.utilities import parse_feature + +ATTR_DIR = os.path.dirname(os.path.realpath(__file__)) +ATTR_FP = os.path.join(ATTR_DIR, 'output_attrs.json') +with open(ATTR_FP) as f: + OUTPUT_ATTRS = json.load(f) RANDOM_GENERATOR = np.random.default_rng(seed=42) +logger = logging.getLogger(__name__) + def preprocess_datasets(dset): """Standardization preprocessing applied before datasets are concatenated @@ -54,8 +63,10 @@ def xr_open_mfdataset(files, **kwargs): out = xr.open_mfdataset( files, preprocess=preprocess_datasets, **default_kwargs ) - bad_dims = 'latitude' in out and len(out['latitude'].dims) == 2 and ( - out['latitude'].dims != out['longitude'].dims + bad_dims = ( + 'latitude' in out + and len(out['latitude'].dims) == 2 + and (out['latitude'].dims != out['longitude'].dims) ) if bad_dims: out['longitude'] = (out['latitude'].dims, out['longitude'].values.T) @@ -77,6 +88,103 @@ def safe_cast(o): return str(o) +def enforce_limits(features, data, nn_fill=False): + """Enforce physical limits for feature data + + Parameters + ---------- + features : list + List of features with ordering corresponding to last channel of + data array. + data : ndarray + Array of feature data + nn_fill : bool + Whether to fill values outside of limits with nearest neighbor + interpolation. If False, values outside of limits are set to + the limits. + + Returns + ------- + data : ndarray + Array of feature data with physical limits enforced + """ + for fidx, fn in enumerate(features): + dset_name = parse_feature(fn).basename + if dset_name not in OUTPUT_ATTRS: + msg = f'Could not find "{dset_name}" in OUTPUT_ATTRS dict!' + logger.error(msg) + raise KeyError(msg) + + max_val = OUTPUT_ATTRS[dset_name].get('max', np.inf) + min_val = OUTPUT_ATTRS[dset_name].get('min', -np.inf) + enforcing_msg = f'Enforcing range of ({min_val}, {max_val}) for "{fn}"' + + f_max = data[..., fidx].max() + f_min = data[..., fidx].min() + max_frac = np.sum(data[..., fidx] > max_val) / data[..., fidx].size + min_frac = np.sum(data[..., fidx] < min_val) / data[..., fidx].size + msg = ( + f'{fn} has a max of {f_max} > {max_val}, with ' + f'{max_frac:.4e} of points above this max. {enforcing_msg}' + ) + if f_max > max_val: + logger.warning(msg) + warn(msg) + msg = ( + f'{fn} has a min of {f_min} < {min_val}, with ' + f'{min_frac:.4e} of points below this min. {enforcing_msg}' + ) + if f_min < min_val: + logger.warning(msg) + warn(msg) + + if nn_fill: + data[..., fidx] = np.where( + data[..., fidx] > max_val, np.nan, data[..., fidx] + ) + data[..., fidx] = np.where( + data[..., fidx] < min_val, np.nan, data[..., fidx] + ) + data[..., fidx] = nn_fill_array(data[..., fidx]) + else: + data[..., fidx] = np.maximum(data[..., fidx], min_val) + data[..., fidx] = np.minimum(data[..., fidx], max_val) + return data.astype(np.float32) + + +def get_dset_attrs(feature): + """Get attrributes for output feature + + Parameters + ---------- + feature : str + Name of feature to write + + Returns + ------- + attrs : dict + Dictionary of attributes for requested dset + dtype : str + Data type for requested dset. Defaults to float32 + """ + feat_base_name = parse_feature(feature).basename + if feat_base_name in OUTPUT_ATTRS: + attrs = OUTPUT_ATTRS[feat_base_name] + dtype = attrs.get('dtype', 'float32') + else: + attrs = {} + dtype = 'float32' + msg = ( + 'Could not find feature "{}" with base name "{}" in ' + 'OUTPUT_ATTRS global variable. Writing with float32 and no ' + 'chunking.'.format(feature, feat_base_name) + ) + logger.warning(msg) + warn(msg) + + return attrs, dtype + + def safe_serialize(obj, **kwargs): """json.dumps with non-serializable object handling.""" return json.dumps(obj, default=safe_cast, **kwargs) From 543f46470dfb3043ff65120c993cb7ee5f50913c Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 20 Apr 2025 19:37:42 -0600 Subject: [PATCH 089/122] refactor: update nn_fill_array import path and remove unused function definition --- sup3r/postprocessing/__init__.py | 1 - sup3r/preprocessing/derivers/utilities.py | 12 +--- sup3r/qa/qa.py | 3 +- sup3r/solar/solar.py | 3 +- .../writers => utilities}/output_attrs.json | 0 sup3r/utilities/utilities.py | 69 +++++++++++-------- 6 files changed, 49 insertions(+), 39 deletions(-) rename sup3r/{postprocessing/writers => utilities}/output_attrs.json (100%) diff --git a/sup3r/postprocessing/__init__.py b/sup3r/postprocessing/__init__.py index 2225e7dfef..4100e97325 100644 --- a/sup3r/postprocessing/__init__.py +++ b/sup3r/postprocessing/__init__.py @@ -8,4 +8,3 @@ OutputMixin, RexOutputs, ) -from .writers.base import OUTPUT_ATTRS diff --git a/sup3r/preprocessing/derivers/utilities.py b/sup3r/preprocessing/derivers/utilities.py index 10759ad064..bf7f30d7f3 100644 --- a/sup3r/preprocessing/derivers/utilities.py +++ b/sup3r/preprocessing/derivers/utilities.py @@ -8,6 +8,8 @@ import pandas as pd from rex.utilities.solar_position import SolarPosition +from sup3r.utilities.utilities import get_feature_basename + logger = logging.getLogger(__name__) @@ -93,15 +95,7 @@ class FeatureStruct: def __init__(self): height = re.findall(r'_\d+m', feature) press = re.findall(r'_\d+pa', feature) - self.basename = ( - feature.replace(height[0], '') - if height - else feature.replace(press[0], '') - if press - else feature.split('_(.*)')[0] - if '_(.*)' in feature - else feature - ) + self.basename = get_feature_basename(feature) self.height = ( int(round(float(height[0][1:-1]))) if height else None ) diff --git a/sup3r/qa/qa.py b/sup3r/qa/qa.py index 6a3a1a874f..60d8d577f3 100644 --- a/sup3r/qa/qa.py +++ b/sup3r/qa/qa.py @@ -13,7 +13,7 @@ from rex.utilities.fun_utils import get_fun_call_str from sup3r.bias.utilities import bias_correct_feature -from sup3r.postprocessing import OUTPUT_ATTRS, RexOutputs +from sup3r.postprocessing import RexOutputs from sup3r.preprocessing.derivers import Deriver from sup3r.preprocessing.derivers.utilities import parse_feature from sup3r.preprocessing.utilities import ( @@ -24,6 +24,7 @@ from sup3r.utilities import ModuleName from sup3r.utilities.cli import BaseCLI from sup3r.utilities.utilities import ( + OUTPUT_ATTRS, spatial_coarsening, temporal_coarsening, xr_open_mfdataset, diff --git a/sup3r/solar/solar.py b/sup3r/solar/solar.py index 3a701a8e4a..84aedf872a 100644 --- a/sup3r/solar/solar.py +++ b/sup3r/solar/solar.py @@ -16,10 +16,11 @@ from rex.utilities.fun_utils import get_fun_call_str from scipy.spatial import KDTree -from sup3r.postprocessing import OUTPUT_ATTRS, RexOutputs +from sup3r.postprocessing import RexOutputs from sup3r.preprocessing.utilities import expand_paths from sup3r.utilities import ModuleName from sup3r.utilities.cli import BaseCLI +from sup3r.utilities.utilities import OUTPUT_ATTRS logger = logging.getLogger(__name__) diff --git a/sup3r/postprocessing/writers/output_attrs.json b/sup3r/utilities/output_attrs.json similarity index 100% rename from sup3r/postprocessing/writers/output_attrs.json rename to sup3r/utilities/output_attrs.json diff --git a/sup3r/utilities/utilities.py b/sup3r/utilities/utilities.py index 48cc06d359..a6515e4487 100644 --- a/sup3r/utilities/utilities.py +++ b/sup3r/utilities/utilities.py @@ -16,8 +16,6 @@ from packaging import version from scipy import ndimage as nd -from sup3r.preprocessing.derivers.utilities import parse_feature - ATTR_DIR = os.path.dirname(os.path.realpath(__file__)) ATTR_FP = os.path.join(ATTR_DIR, 'output_attrs.json') with open(ATTR_FP) as f: @@ -28,6 +26,46 @@ logger = logging.getLogger(__name__) +def nn_fill_array(array): + """Fill any NaN values in an np.ndarray from the nearest non-nan values. + + Parameters + ---------- + array : Union[np.ndarray, da.core.Array] + Input array with NaN values + + Returns + ------- + array : Union[np.ndarray, da.core.Array] + Output array with NaN values filled + """ + + nan_mask = np.isnan(array) + indices = nd.distance_transform_edt( + nan_mask, return_distances=False, return_indices=True + ) + if hasattr(array, 'vindex'): + return array.vindex[tuple(indices)] + return array[tuple(indices)] + + +def get_feature_basename(feature): + """Get the base name of a feature, removing any height or pressure + suffix""" + height = re.findall(r'_\d+m', feature) + press = re.findall(r'_\d+pa', feature) + basename = ( + feature.replace(height[0], '') + if height + else feature.replace(press[0], '') + if press + else feature.split('_(.*)')[0] + if '_(.*)' in feature + else feature + ) + return basename + + def preprocess_datasets(dset): """Standardization preprocessing applied before datasets are concatenated by ``xr.open_mfdataset``""" @@ -109,7 +147,7 @@ def enforce_limits(features, data, nn_fill=False): Array of feature data with physical limits enforced """ for fidx, fn in enumerate(features): - dset_name = parse_feature(fn).basename + dset_name = get_feature_basename(fn) if dset_name not in OUTPUT_ATTRS: msg = f'Could not find "{dset_name}" in OUTPUT_ATTRS dict!' logger.error(msg) @@ -167,7 +205,7 @@ def get_dset_attrs(feature): dtype : str Data type for requested dset. Defaults to float32 """ - feat_base_name = parse_feature(feature).basename + feat_base_name = get_feature_basename(feature) if feat_base_name in OUTPUT_ATTRS: attrs = OUTPUT_ATTRS[feat_base_name] dtype = attrs.get('dtype', 'float32') @@ -455,29 +493,6 @@ def spatial_coarsening(data, s_enhance=2, obs_axis=True): return data -def nn_fill_array(array): - """Fill any NaN values in an np.ndarray from the nearest non-nan values. - - Parameters - ---------- - array : Union[np.ndarray, da.core.Array] - Input array with NaN values - - Returns - ------- - array : Union[np.ndarray, da.core.Array] - Output array with NaN values filled - """ - - nan_mask = np.isnan(array) - indices = nd.distance_transform_edt( - nan_mask, return_distances=False, return_indices=True - ) - if hasattr(array, 'vindex'): - return array.vindex[tuple(indices)] - return array[tuple(indices)] - - def pd_date_range(*args, **kwargs): """A simple wrapper on the pd.date_range() method that handles the closed vs. inclusive kwarg change in pd 1.4.0""" From d6765940ec02390130544aeac5b3029637f6dc0b Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 22 Apr 2025 10:29:33 -0600 Subject: [PATCH 090/122] feat: enabled forward passes with obs models to run without obs data; added nn_fill on ouput data outside of allowed range, instead of clipping; added collection of netcdf spatial chunks. --- sup3r/models/abstract.py | 17 +++++++++---- sup3r/postprocessing/collectors/nc.py | 31 ++++++++++++++++++------ sup3r/preprocessing/accessor.py | 5 ++-- sup3r/preprocessing/data_handlers/exo.py | 2 +- sup3r/utilities/utilities.py | 4 +++ 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 20dfd831ee..bc41cd3808 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -36,16 +36,15 @@ logger = logging.getLogger(__name__) - -SUP3R_LAYERS = ( - Sup3rAdder, - Sup3rConcat, +SUP3R_OBS_LAYERS = ( Sup3rConcatObs, Sup3rConcatEmbeddedObs, Sup3rConcatWeightedObs, Sup3rConcatWeightedObsWithEmbedding, ) +SUP3R_LAYERS = (Sup3rAdder, Sup3rConcat, *SUP3R_OBS_LAYERS) + # pylint: disable=E1101,W0201,E0203 class AbstractSingleModel(ABC, TensorboardMixIn): @@ -1041,7 +1040,15 @@ def generate( try: for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 - if isinstance(layer, SUP3R_LAYERS): + is_obs_layer = isinstance(layer, SUP3R_OBS_LAYERS) + is_exo_layer = isinstance(layer, SUP3R_LAYERS) + if is_obs_layer and layer.name not in exogenous_data: + msg = (f'Observation data not given for {layer.name}. ' + 'Will run forward pass without it.') + logger.warning(msg) + warn(msg) + hi_res = layer(hi_res) + elif is_exo_layer: msg = ( f'layer.name = {layer.name} does not match any ' 'features in exogenous_data ' diff --git a/sup3r/postprocessing/collectors/nc.py b/sup3r/postprocessing/collectors/nc.py index 300d1444f6..6810d4b524 100644 --- a/sup3r/postprocessing/collectors/nc.py +++ b/sup3r/postprocessing/collectors/nc.py @@ -6,9 +6,11 @@ import logging import os +import xarray as xr from rex.utilities.loggers import init_logger from sup3r.preprocessing.cachers import Cacher +from sup3r.preprocessing.names import Dimension from sup3r.utilities.utilities import xr_open_mfdataset from .base import BaseCollector @@ -32,6 +34,12 @@ def collect( ): """Collect data files from a dir to one output file. + TODO: This assumes that if there is any spatial chunking it is split + by latitude. This should be generalized to allow for any spatial + chunking and any dimension. This will either require a new file + naming scheme with a spatial index for both latitude and + longitude or checking each chunk to see how they are split. + Filename requirements: - Should end with ".nc" @@ -76,10 +84,19 @@ def collect( logger.info(f'overwrite=True, removing {out_file}.') os.remove(out_file) + spatial_chunks = collector.group_spatial_chunks() + tmp_file = out_file + '.tmp' if not os.path.exists(tmp_file): - res_kwargs = res_kwargs or {} - out = xr_open_mfdataset(collector.flist, **res_kwargs) + res_kwargs = res_kwargs or { + 'combine': 'nested', + 'concat_dim': Dimension.TIME, + } + for s_idx in spatial_chunks: + spatial_chunks[s_idx] = xr_open_mfdataset( + spatial_chunks[s_idx], **res_kwargs + ) + out = xr.concat(spatial_chunks.values(), dim=Dimension.SOUTH_NORTH) Cacher.write_netcdf(tmp_file, data=out, features=features) os.replace(tmp_file, out_file) @@ -88,12 +105,12 @@ def collect( logger.info('Finished file collection.') def group_spatial_chunks(self): - """Group same spatial chunks together so each chunk has same spatial + """Group same spatial chunks together so each entry has same spatial footprint but different times""" chunks = {} for file in self.flist: - s_chunk = file.split('_')[0] - dirname = os.path.dirname(file) - s_file = os.path.join(dirname, f's_{s_chunk}.nc') - chunks[s_file] = [*chunks.get(s_file, []), s_file] + _, s_idx = self.get_chunk_indices(file) + chunks[s_idx] = [*chunks.get(s_idx, []), file] + for k, v in chunks.items(): + chunks[k] = sorted(v) return chunks diff --git a/sup3r/preprocessing/accessor.py b/sup3r/preprocessing/accessor.py index 018569d160..cf29500565 100644 --- a/sup3r/preprocessing/accessor.py +++ b/sup3r/preprocessing/accessor.py @@ -372,10 +372,11 @@ def interpolate_na(self, **kwargs): 'use_coordinate', False ) self._ds[feat] = self._ds[feat].interpolate_na(**kwargs) - else: + elif np.isnan(self._ds[feat]).any(): msg = ( 'No dim given for interpolate_na. This will use nearest ' - 'neighbor fill, which could take some time.' + f'neighbor fill to interpolate {feat}, which could take ' + 'some time.' ) logger.warning(msg) warn(msg) diff --git a/sup3r/preprocessing/data_handlers/exo.py b/sup3r/preprocessing/data_handlers/exo.py index 11dfb5e3f9..219bb3ba28 100644 --- a/sup3r/preprocessing/data_handlers/exo.py +++ b/sup3r/preprocessing/data_handlers/exo.py @@ -97,7 +97,7 @@ def __init__(self, steps): warn(msg) steps_list = entry.get('steps', [entry]) for i, step in enumerate(steps_list): - msg = (f'ExoData entry for {feat}, step #{i+1} has no ' + msg = (f'ExoData entry for {feat}, step #{i + 1}, has no ' '"combine_type" key. Assuming this is for a ' 'layer combination.') if 'combine_type' not in step: diff --git a/sup3r/utilities/utilities.py b/sup3r/utilities/utilities.py index a6515e4487..3206a16693 100644 --- a/sup3r/utilities/utilities.py +++ b/sup3r/utilities/utilities.py @@ -156,6 +156,10 @@ def enforce_limits(features, data, nn_fill=False): max_val = OUTPUT_ATTRS[dset_name].get('max', np.inf) min_val = OUTPUT_ATTRS[dset_name].get('min', -np.inf) enforcing_msg = f'Enforcing range of ({min_val}, {max_val}) for "{fn}"' + if nn_fill: + enforcing_msg += ' with nearest neighbor interpolation.' + else: + enforcing_msg += ' with clipping.' f_max = data[..., fidx].max() f_min = data[..., fidx].min() From 489b2f709b65238c8f5d0fa2c0e1484ec73dd0cc Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 23 Apr 2025 11:31:14 -0600 Subject: [PATCH 091/122] refactor: remove weighted observation layers and update related configurations to use embedded observation layer --- sup3r/models/abstract.py | 6 +----- sup3r/models/interface.py | 6 +----- tests/conftest.py | 8 ++++---- tests/training/test_train_conditioned_obs.py | 2 +- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index bc41cd3808..431bf4cc48 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -20,8 +20,6 @@ Sup3rConcat, Sup3rConcatEmbeddedObs, Sup3rConcatObs, - Sup3rConcatWeightedObs, - Sup3rConcatWeightedObsWithEmbedding, ) from rex.utilities.utilities import safe_json_load from tensorflow.keras import optimizers @@ -38,9 +36,7 @@ SUP3R_OBS_LAYERS = ( Sup3rConcatObs, - Sup3rConcatEmbeddedObs, - Sup3rConcatWeightedObs, - Sup3rConcatWeightedObsWithEmbedding, + Sup3rConcatEmbeddedObs ) SUP3R_LAYERS = (Sup3rAdder, Sup3rConcat, *SUP3R_OBS_LAYERS) diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index d1b65c26f0..0d403d40e2 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -15,8 +15,6 @@ Sup3rConcat, Sup3rConcatEmbeddedObs, Sup3rConcatObs, - Sup3rConcatWeightedObs, - Sup3rConcatWeightedObsWithEmbedding, ) from sup3r.preprocessing.data_handlers import ExoData @@ -28,9 +26,7 @@ SUP3R_OBS_LAYERS = ( Sup3rConcatObs, - Sup3rConcatEmbeddedObs, - Sup3rConcatWeightedObs, - Sup3rConcatWeightedObsWithEmbedding, + Sup3rConcatEmbeddedObs ) diff --git a/tests/conftest.py b/tests/conftest.py index 374fd1e324..3dfd41d887 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -147,8 +147,8 @@ def func(): @pytest.fixture(scope='package') -def gen_config_with_concat_masked_weighted(): - """Get generator config with custom concat masked (and weighted) layer.""" +def gen_config_with_concat_embedded(): + """Get generator config with custom concat embedded obs layer.""" def func(): return [ @@ -207,11 +207,11 @@ def func(): }, {'class': 'Cropping2D', 'cropping': 4}, { - 'class': 'Sup3rConcatWeightedObs', + 'class': 'Sup3rConcatEmbeddedObs', 'name': 'u_10m_obs', }, { - 'class': 'Sup3rConcatWeightedObs', + 'class': 'Sup3rConcatEmbeddedObs', 'name': 'v_10m_obs', }, { diff --git a/tests/training/test_train_conditioned_obs.py b/tests/training/test_train_conditioned_obs.py index 23b75aada7..68c6761af3 100644 --- a/tests/training/test_train_conditioned_obs.py +++ b/tests/training/test_train_conditioned_obs.py @@ -23,7 +23,7 @@ 'gen_config', [ 'gen_config_with_concat_masked', - 'gen_config_with_concat_masked_weighted', + 'gen_config_with_concat_embedded', ], ) def test_fixed_wind_obs(gen_config, request): From f628a91b5ccfa8e535aec83a96baba1576cee779 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 24 Apr 2025 13:15:35 -0600 Subject: [PATCH 092/122] feat: enhance output handling by ensuring float32 data type for lat/lon grids and adding regular grid setup method in accessor --- sup3r/postprocessing/writers/base.py | 6 ++--- sup3r/postprocessing/writers/nc.py | 2 +- sup3r/preprocessing/accessor.py | 33 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/sup3r/postprocessing/writers/base.py b/sup3r/postprocessing/writers/base.py index edb732e005..1394016eb3 100644 --- a/sup3r/postprocessing/writers/base.py +++ b/sup3r/postprocessing/writers/base.py @@ -487,9 +487,9 @@ def get_lat_lon(cls, low_res_lat_lon, shape): logger.debug('Running meshgrid.') X, Y = np.meshgrid(x, y, copy=False) - old = np.array([Y.flatten(), X.flatten()]).T + old = np.array([Y.flatten(), X.flatten()], dtype=np.float32).T X, Y = np.meshgrid(new_x, new_y, copy=False) - new = np.array([Y.flatten(), X.flatten()]).T + new = np.array([Y.flatten(), X.flatten()], dtype=np.float32).T logger.debug('Running griddata.') lons = griddata(old, lons, new) @@ -499,7 +499,7 @@ def get_lat_lon(cls, low_res_lat_lon, shape): lat_lon = np.dstack((lats.reshape(shape), lons.reshape(shape))) logger.debug('Finished getting high resolution lat / lon grid') - return lat_lon + return lat_lon.astype(np.float32) @staticmethod def get_times(low_res_times, shape): diff --git a/sup3r/postprocessing/writers/nc.py b/sup3r/postprocessing/writers/nc.py index 81fb098a19..b1be1f548f 100644 --- a/sup3r/postprocessing/writers/nc.py +++ b/sup3r/postprocessing/writers/nc.py @@ -84,7 +84,7 @@ def _write_output( for i, f in enumerate(features): data_vars[f] = ( (Dimension.TIME, *Dimension.dims_2d()), - np.transpose(data[..., i], axes=(2, 0, 1)), + np.transpose(data[..., i], axes=(2, 0, 1)).astype(np.float32), ) attrs = meta_data or {} diff --git a/sup3r/preprocessing/accessor.py b/sup3r/preprocessing/accessor.py index cf29500565..f90e25c38f 100644 --- a/sup3r/preprocessing/accessor.py +++ b/sup3r/preprocessing/accessor.py @@ -578,6 +578,39 @@ def flatten(self): warn(msg) return self + def set_regular_grid(self): + """In the case of a regular grid, use this to set latitude and + and longitude as 1D arrays which enables calls to ``self.sel`` + and combining chunks with different coordinates through + ``xr.combine_by_coords``""" + + lat_lon_2d = ( + len(self._ds[Dimension.LATITUDE].dims) == 2 + and len(self._ds[Dimension.LONGITUDE].dims) == 2 + ) + same_lats = np.allclose( + np.diff(self._ds[Dimension.LATITUDE].values, axis=1), 0 + ) + same_lons = np.allclose( + np.diff(self._ds[Dimension.LONGITUDE].values, axis=0), 0 + ) + if not (lat_lon_2d and same_lats and same_lons): + msg = 'Cannot set regular grid for non-regular data' + logger.warning(msg) + warn(msg) + else: + self._ds[Dimension.LATITUDE] = self._ds[ + Dimension.LATITUDE + ].isel(**{Dimension.WEST_EAST: 0}) + self._ds[Dimension.LONGITUDE] = self._ds[ + Dimension.LONGITUDE + ].isel(**{Dimension.SOUTH_NORTH: 0}) + self._ds = self._ds.swap_dims({ + Dimension.SOUTH_NORTH: Dimension.LATITUDE, + Dimension.WEST_EAST: Dimension.LONGITUDE, + }) + return self + def _qa(self, feature, stats=None): """Get qa info for given feature.""" info = {} From 33e4305251939624ab29bbd989ab5cb8b7c0a997 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 24 Apr 2025 14:40:02 -0600 Subject: [PATCH 093/122] fix: remove duplicate compute_disc parameter in SolarCC class constructor --- sup3r/models/solar_cc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sup3r/models/solar_cc.py b/sup3r/models/solar_cc.py index 24edb12f0b..db36fd86bb 100644 --- a/sup3r/models/solar_cc.py +++ b/sup3r/models/solar_cc.py @@ -97,8 +97,7 @@ def calc_loss( weight_gen_advers=0.001, train_gen=True, train_disc=False, - compute_disc=False, - compute_disc=False, + compute_disc=False ): """Calculate the GAN loss function using generated and true high resolution data. From c37619faad67a1af5a3b0ad230679ced5ed2329c Mon Sep 17 00:00:00 2001 From: Brandon N Benton Date: Thu, 24 Apr 2025 13:47:00 -0700 Subject: [PATCH 094/122] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6db7863b28..f904bfd0b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ classifiers=[ ] dependencies = [ "NREL-rex>=0.2.91", - "NREL-phygnn>=0.0.23", + "NREL-phygnn>=0.0.31", "NREL-gaps>=0.6.13", "NREL-farms>=1.0.4", "dask>=2022.0", From 2f9ec3f4f3eabb3a9ab16813eaf2d0d0cf8d5b5c Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 25 Apr 2025 08:57:57 -0600 Subject: [PATCH 095/122] fix: only compute obs loss calc when training the generator; typo in interp method, resulting in bad logic. --- sup3r/models/abstract.py | 1 + sup3r/models/with_obs.py | 38 +++++++++++---------- sup3r/pipeline/forward_pass.py | 4 +-- sup3r/postprocessing/collectors/h5.py | 3 +- sup3r/preprocessing/rasterizers/exo.py | 11 +++++- sup3r/utilities/interpolation.py | 2 +- sup3r/utilities/pytest/helpers.py | 6 ++-- tests/docs/test_doc_automation.py | 2 +- tests/forward_pass/test_forward_pass_exo.py | 14 +++++--- 9 files changed, 50 insertions(+), 31 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 431bf4cc48..4467e268e8 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -1201,6 +1201,7 @@ def calc_loss( weight_gen_advers=0.001, train_gen=True, train_disc=False, + compute_disc=False ): """Calculate the GAN loss function using generated and true high resolution data.""" diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 6c2dc8ae4b..0aeb43e503 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -163,23 +163,25 @@ def _get_hr_exo_and_loss( low_res, hi_res_true, **calc_loss_kwargs ) loss, loss_details, hi_res_gen, hi_res_exo = out - loss_obs, loss_non_obs = self._get_loss_obs_comparison( - hi_res_true, - hi_res_gen, - hi_res_exo['mask'], - ) - loss_update = { - 'loss_obs': loss_obs, - 'loss_non_obs': loss_non_obs, - 'obs_frac': np.sum(~hi_res_exo['mask']) - / np.size(hi_res_exo['mask']), - } - if self.loss_obs_weight is not None and calc_loss_kwargs['train_gen']: - loss_obs *= self.loss_obs_weight - loss += loss_obs - loss_update['loss_gen'] = loss - loss_update['loss_gen_content'] = ( - loss_details['loss_gen_content'] + loss_obs + + if calc_loss_kwargs.get('train_gen', True): + loss_obs, loss_non_obs = self._get_loss_obs_comparison( + hi_res_true, + hi_res_gen, + hi_res_exo['mask'], ) - loss_details.update(loss_update) + loss_update = { + 'loss_obs': loss_obs, + 'loss_non_obs': loss_non_obs, + 'obs_frac': np.sum(~hi_res_exo['mask']) + / np.size(hi_res_exo['mask']), + } + if self.loss_obs_weight is not None: + loss_obs *= self.loss_obs_weight + loss += loss_obs + loss_update['loss_gen'] = loss + loss_update['loss_gen_content'] = ( + loss_details['loss_gen_content'] + loss_obs + ) + loss_details.update(loss_update) return loss, loss_details, hi_res_gen, hi_res_exo diff --git a/sup3r/pipeline/forward_pass.py b/sup3r/pipeline/forward_pass.py index d2c7b44900..3771c0bf2e 100644 --- a/sup3r/pipeline/forward_pass.py +++ b/sup3r/pipeline/forward_pass.py @@ -491,7 +491,7 @@ def _run_serial(cls, strategy, node_index): if failed: msg = ( f'Forward pass for chunk_index {chunk_index} failed ' - 'with constant output.' + 'with constant output or NaNs.' ) raise MemoryError(msg) @@ -557,7 +557,7 @@ def _run_parallel(cls, strategy, node_index): if failed: msg = ( f'Forward pass for chunk_index {chunk_idx} failed ' - 'with constant output.' + 'with constant output or NaNs.' ) raise MemoryError(msg) msg = ( diff --git a/sup3r/postprocessing/collectors/h5.py b/sup3r/postprocessing/collectors/h5.py index 72ff97f13d..9b9192ff62 100644 --- a/sup3r/postprocessing/collectors/h5.py +++ b/sup3r/postprocessing/collectors/h5.py @@ -12,6 +12,7 @@ from sup3r.postprocessing.writers.base import RexOutputs from sup3r.preprocessing.utilities import _mem_check +from sup3r.utilities.utilities import get_dset_attrs from .base import BaseCollector @@ -528,7 +529,7 @@ def _collect_flist( None uses all available. """ if len(subset_masked_meta) > 0: - attrs, final_dtype = self.get_dset_attrs(feature) + attrs, final_dtype = get_dset_attrs(feature) scale_factor = attrs.get('scale_factor', 1) logger.debug( diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index 6799dfa916..cf1955284d 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -91,6 +91,9 @@ class BaseExoRasterizer(ABC): Maximum distance to map high-resolution data from source_file to the low-resolution file_paths input. None (default) will calculate this based on the median distance between points in source_file + fill_nans : bool + Whether to fill nans in the output data. This should probably be True + for all cases except for sparse observation data. max_workers : int Number of workers used for writing data to cache files. Gets passed to ``Cacher.write_netcdf.`` @@ -112,6 +115,10 @@ class BaseExoRasterizer(ABC): max_workers: int = 1 verbose: bool = False + # These sometimes have a time dimension but we don't need the time in + # the cache file + STATIC_FEATURES = ('topography', 'srl') + @log_args def __post_init__(self): self._source_data = None @@ -134,8 +141,10 @@ def source_data(self): self._source_data = self.source_handler[self.feature].data if 'time' not in self.source_handler[self.feature].dims: self._source_data = self._source_data[..., None] + elif self.feature in self.STATIC_FEATURES: + self._source_data = self._source_data[..., :1] shape = (-1, self._source_data.shape[-1]) - self._source_data = self.source_data.reshape(shape) + self._source_data = self._source_data.reshape(shape) return self._source_data @property diff --git a/sup3r/utilities/interpolation.py b/sup3r/utilities/interpolation.py index e2bf4c08b8..b90bd788f7 100644 --- a/sup3r/utilities/interpolation.py +++ b/sup3r/utilities/interpolation.py @@ -58,7 +58,7 @@ def _lin_interp(cls, lev_samps, var_samps, level): """Linearly interpolate between levels.""" diff = da.map_blocks(lambda x, y: x - y, lev_samps[1], lev_samps[0]) alpha = da.where( - np.abs(diff < 1e-3), # to avoid excessively large alpha values + np.abs(diff) < 1e-3, # to avoid excessively large alpha values 0, da.map_blocks(lambda x, y: x / y, (level - lev_samps[0]), diff), ) diff --git a/sup3r/utilities/pytest/helpers.py b/sup3r/utilities/pytest/helpers.py index bb9912c9fd..cf5654eba6 100644 --- a/sup3r/utilities/pytest/helpers.py +++ b/sup3r/utilities/pytest/helpers.py @@ -73,9 +73,11 @@ def make_fake_dset(shape, features, const=None): data_vars = {} for f in features: if 'zg' in f: - data = da.random.uniform(10, 100, shape) + data = da.random.uniform(10, 1000, shape) elif 'orog' in f: - data = da.random.uniform(0, 1, shape) + data = da.random.uniform(0, 10, shape) + elif 'pressure' in f: + data = da.random.uniform(80000, 100000, shape) else: data = da.random.uniform(-1, 1, shape) data_vars[f] = ( diff --git a/tests/docs/test_doc_automation.py b/tests/docs/test_doc_automation.py index a296b1b332..fcef753458 100644 --- a/tests/docs/test_doc_automation.py +++ b/tests/docs/test_doc_automation.py @@ -65,7 +65,7 @@ def test_h5_solar_sig(): 'hr_spatial_coarsen', 'nan_method_kwargs', 'interp_kwargs', - 'cache_kwargs', + 'cache_kwargs' ] sig = signature(DataHandlerH5SolarCC) params = [p.name for p in sig.parameters.values()] diff --git a/tests/forward_pass/test_forward_pass_exo.py b/tests/forward_pass/test_forward_pass_exo.py index 55f0dc245f..c96c78010b 100644 --- a/tests/forward_pass/test_forward_pass_exo.py +++ b/tests/forward_pass/test_forward_pass_exo.py @@ -23,7 +23,7 @@ from sup3r.utilities.pytest.helpers import make_fake_nc_file from sup3r.utilities.utilities import RANDOM_GENERATOR, xr_open_mfdataset -target = (19.3, -123.5) +target = (39.5, -105) shape = (8, 8) sample_shape = (8, 8, 6) time_slice = slice(None, None, 1) @@ -682,7 +682,10 @@ def test_fwp_wind_hi_res_topo_plus_linear(input_files, gen_config_with_topo): exo_tmp = { 'topography': { 'steps': [ - {'combine_type': 'layer', 'data': np.ones((4, 20, 20, 1))} + { + 'combine_type': 'layer', + 'data': RANDOM_GENERATOR.random((4, 20, 20, 1)), + } ] } } @@ -721,12 +724,13 @@ def test_fwp_wind_hi_res_topo_plus_linear(input_files, gen_config_with_topo): input_files, model_kwargs=model_kwargs, model_class='MultiStepGan', - fwp_chunk_shape=(4, 4, 8), - spatial_pad=1, - temporal_pad=1, + fwp_chunk_shape=(8, 8, 8), + spatial_pad=2, + temporal_pad=2, input_handler_kwargs=input_handler_kwargs, out_pattern=out_files, exo_handler_kwargs=exo_handler_kwargs, + allowed_const=0, max_nodes=1, ) forward_pass = ForwardPass(handler) From fbcb039c0afb4e711180bed69561ee16e89310e9 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 30 Apr 2025 15:19:57 -0600 Subject: [PATCH 096/122] more robust height interpolation which uses lower and higher levels if available. --- sup3r/utilities/interpolation.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/sup3r/utilities/interpolation.py b/sup3r/utilities/interpolation.py index b90bd788f7..d854c2263a 100644 --- a/sup3r/utilities/interpolation.py +++ b/sup3r/utilities/interpolation.py @@ -42,15 +42,27 @@ def get_level_masks(cls, lev_array, level): to the one requested. (lat, lon, time, level) """ - lev_diff = np.abs(lev_array - level) - argmin1 = da.argmin(lev_diff, axis=-1, keepdims=True) lev_indices = da.broadcast_to( da.arange(lev_array.shape[-1]), lev_array.shape ) + + above_mask = lev_array >= level + below_mask = lev_array < level + below = da.ma.masked_array(lev_array, above_mask) + above = da.ma.masked_array(lev_array, below_mask) + + argmin1 = da.argmin(np.abs(below - level), axis=-1, keepdims=True) mask1 = lev_indices == argmin1 - lev_diff = da.abs(da.ma.masked_array(lev_array, mask1) - level) - argmin2 = da.argmin(lev_diff, axis=-1, keepdims=True) + argmin2 = da.argmin(np.abs(above - level), axis=-1, keepdims=True) mask2 = lev_indices == argmin2 + + # Get alternative second level in case there is no level above + alts = da.ma.masked_array(lev_array, mask1) + argmin3 = da.argmin(np.abs(alts - level), axis=-1, keepdims=True) + mask3 = lev_indices == argmin3 + + above_exists = da.any(above_mask, axis=-1, keepdims=True) + mask2 = da.where(above_exists, mask2, mask3) return mask1, mask2 @classmethod @@ -63,7 +75,7 @@ def _lin_interp(cls, lev_samps, var_samps, level): da.map_blocks(lambda x, y: x / y, (level - lev_samps[0]), diff), ) indices = 'ijk'[: lev_samps[0].ndim] - return da.blockwise( + out = da.blockwise( lambda x, y, a: x * (1 - a) + y * a, indices, var_samps[0], @@ -73,6 +85,7 @@ def _lin_interp(cls, lev_samps, var_samps, level): alpha, indices, ) + return out @classmethod def _log_interp(cls, lev_samps, var_samps, level): From 1f573485430e6e66655996b74e288819c6623cc4 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 1 May 2025 06:42:55 -0600 Subject: [PATCH 097/122] Interp method to use closest levels if there are no levels lower or higher than the one requested. --- sup3r/utilities/interpolation.py | 11 +++++++---- tests/derivers/test_height_interp.py | 8 ++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/sup3r/utilities/interpolation.py b/sup3r/utilities/interpolation.py index d854c2263a..9390a68d54 100644 --- a/sup3r/utilities/interpolation.py +++ b/sup3r/utilities/interpolation.py @@ -56,13 +56,16 @@ def get_level_masks(cls, lev_array, level): argmin2 = da.argmin(np.abs(above - level), axis=-1, keepdims=True) mask2 = lev_indices == argmin2 - # Get alternative second level in case there is no level above + # Get alternative levels in case there is no level below or above + below_exists = da.any(below_mask, axis=-1, keepdims=True) + argmin3 = da.argmin(np.abs(lev_array - level), axis=-1, keepdims=True) + mask1 = da.where(below_exists, mask1, lev_indices == argmin3) + + above_exists = da.any(above_mask, axis=-1, keepdims=True) alts = da.ma.masked_array(lev_array, mask1) argmin3 = da.argmin(np.abs(alts - level), axis=-1, keepdims=True) - mask3 = lev_indices == argmin3 + mask2 = da.where(above_exists, mask2, lev_indices == argmin3) - above_exists = da.any(above_mask, axis=-1, keepdims=True) - mask2 = da.where(above_exists, mask2, mask3) return mask1, mask2 @classmethod diff --git a/tests/derivers/test_height_interp.py b/tests/derivers/test_height_interp.py index a747f7b254..54467bd4e7 100644 --- a/tests/derivers/test_height_interp.py +++ b/tests/derivers/test_height_interp.py @@ -161,16 +161,12 @@ def test_only_interp_method(): ws[..., 2] = 12 out = np.asarray(Interpolator.interp_to_level(hgt, ws, [50])) - func = interp1d( - hgt[0, 0, 0, :2], ws[0, 0, 0, :2], fill_value='extrapolate' - ) + func = interp1d(hgt[0, 0, 0], ws[0, 0, 0], fill_value='extrapolate') correct = func(50) assert np.allclose(out, correct) out = np.asarray(Interpolator.interp_to_level(hgt, ws, [60])) - func = interp1d( - hgt[0, 0, 0, 1:], ws[0, 0, 0, 1:], fill_value='extrapolate' - ) + func = interp1d(hgt[0, 0, 0], ws[0, 0, 0], fill_value='extrapolate') correct = func(60) assert np.allclose(out, correct) From 1de8cb318cd27769aee4c7314537eebc13cf9c9c Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 1 May 2025 06:44:45 -0600 Subject: [PATCH 098/122] remove redundant calcs in Sup3rGanDC val loss --- sup3r/models/dc.py | 60 ++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/sup3r/models/dc.py b/sup3r/models/dc.py index c305773503..8c51a48591 100644 --- a/sup3r/models/dc.py +++ b/sup3r/models/dc.py @@ -33,60 +33,34 @@ def calc_val_loss_gen(self, batch_handler, weight_gen_advers): Returns ------- - array + total_losses : np.ndarray Array of total losses for all sample bins, with shape (n_space_bins, n_time_bins) + content_losses : np.ndarray + Array of content losses for all sample bins, with shape + (n_space_bins, n_time_bins) """ - losses = np.zeros( + total_losses = np.zeros( (batch_handler.n_space_bins, batch_handler.n_time_bins), dtype=np.float32, ) - for i, batch in enumerate(batch_handler.val_data): - exo_data = self.get_hr_exo_input(batch.high_res) - loss, _ = self.calc_loss( - hi_res_true=batch.high_res, - hi_res_gen=self._tf_generate(batch.low_res, exo_data), - weight_gen_advers=weight_gen_advers, - train_gen=True, - train_disc=True, - ) - row = i // batch_handler.n_time_bins - col = i % batch_handler.n_time_bins - losses[row, col] = loss - return losses - - def calc_val_loss_gen_content(self, batch_handler): - """Calculate the validation content loss across the validation - samples. e.g. If the sample domain has 100 steps and the - validation set has 10 bins then this will get a list of losses across - step 0 to 10, 10 to 20, etc. Use this to determine performance - within bins and to update how observations are selected from these - bins. - - Parameters - ---------- - batch_handler : sup3r.preprocessing.BatchHandlerDC - BatchHandler object to iterate through - - Returns - ------- - list - List of content losses for all sample bins - """ - losses = np.zeros( + content_losses = np.zeros( (batch_handler.n_space_bins, batch_handler.n_time_bins), dtype=np.float32, ) for i, batch in enumerate(batch_handler.val_data): - exo_data = self.get_hr_exo_input(batch.high_res) - loss, _ = self.calc_loss_gen_content( + logger.info(f'Calculating validation loss for batch {i} / ' + f'{len(batch_handler.val_data)}...') + loss, loss_details, _, _ = self._get_hr_exo_and_loss( + low_res=batch.low_res, hi_res_true=batch.high_res, - hi_res_gen=self._tf_generate(batch.low_res, exo_data), + weight_gen_advers=weight_gen_advers, ) row = i // batch_handler.n_time_bins col = i % batch_handler.n_time_bins - losses[row, col] = loss - return losses + total_losses[row, col] = loss + content_losses[row, col] = loss_details['loss_gen_content'] + return total_losses, content_losses def calc_val_loss(self, batch_handler, weight_gen_advers): """Overloading the base calc_val_loss method. Method updates the @@ -107,9 +81,11 @@ def calc_val_loss(self, batch_handler, weight_gen_advers): Updated loss_details with mean validation loss calculated using the validation samples across the time bins """ + logger.debug('Starting end-of-epoch validation loss calculation...') loss_details = {} - total_losses = self.calc_val_loss_gen(batch_handler, weight_gen_advers) - content_losses = self.calc_val_loss_gen_content(batch_handler) + total_losses, content_losses = self.calc_val_loss_gen( + batch_handler, weight_gen_advers + ) t_weights = total_losses.mean(axis=0) t_weights /= t_weights.sum() From 78c1987c0182387fba7cedf2a4c670350c7038eb Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 30 Apr 2025 09:23:32 -0600 Subject: [PATCH 099/122] feat: previously using time_slice=slice(None) when initializing the input_handler for the fwp, and applying the given time_slice after initialization. This is to keep extra time steps for padding. Instead we can initialize with a padded time slice so we don't have to cache or derive features for the full time period. --- sup3r/pipeline/strategy.py | 63 ++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index e443bb556d..567c950ded 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -176,8 +176,8 @@ class ForwardPassStrategy: for writing to output. This defaults to True for H5 output and False for NETCDF output. nn_fill : bool - Whether to fill data outside of limits with nearest neighbour or cap - to limits. + Whether to fill data outside of accepted limits (e.g. relative + humidity 0-100) with nearest neighbour or cap to limits. pass_workers : int | None Max number of workers to use for performing forward passes on a single node. If 1 then all forward passes on chunks distributed to a single @@ -194,6 +194,11 @@ class ForwardPassStrategy: how to distribute jobs across nodes. Preflight tasks like bias correction will be skipped because they will be performed on the nodes jobs are distributed to by the head node. + redistribute_chunks : bool + Whether to continue to redistribute unfinished chunks across all + requested nodes. This is useful for large runs when some nodes might + finish before others. This is in constrast to determining which chunks + are assigned to each node at the start of the run and not changing. """ file_paths: Union[str, list, pathlib.Path] @@ -217,6 +222,7 @@ class ForwardPassStrategy: pass_workers: int = 1 max_nodes: int = 1 head_node: bool = False + redistribute_chunks: bool = False @log_args def __post_init__(self): @@ -231,10 +237,8 @@ def __post_init__(self): self.input_features = model.lr_features self.output_features = model.hr_out_features self.features, self.exo_features = self._init_features(model) + self.time_slice, self.padded_time_slice = self.get_time_slices() self.input_handler = self.timer(self.init_input_handler, log=True)() - self.time_slice = _parse_time_slice( - self.input_handler_kwargs.get('time_slice', slice(None)) - ) self.fwp_chunk_shape = self._get_fwp_chunk_shape() self.fwp_slicer = ForwardPassSlicer( coarse_shape=self.input_handler.grid_shape, @@ -290,11 +294,41 @@ def meta(self): } return meta_data + def get_time_slices(self): + """Get the time slice for initializaing the input handler and the + time slice applied to the data given by the input handler to get the + actual requested time period. These are different because we want the + data stored by the input handler to have extra time steps at the start + and end of the time period for padding input to the forward pass.""" + time_slice = self.input_handler_kwargs.get('time_slice', slice(None)) + time_slice = _parse_time_slice(time_slice) + step = time_slice.step if time_slice.step else 1 + pstart = ( + 0 + if time_slice.start is None + else time_slice.start - self.temporal_pad * step + ) + pend = ( + None + if time_slice.stop is None + else time_slice.stop + self.temporal_pad * step + ) + padded_slice = slice(pstart, pend, time_slice.step) + start = 0 if time_slice.start is None else self.temporal_pad + stop = ( + None + if time_slice.stop is None or time_slice.stop == 0 + else -self.temporal_pad + ) + unpadded_slice = slice(start, stop) + return unpadded_slice, padded_slice + def init_input_handler(self): """Get input handler instance for given input kwargs. If self.head_node - is False we get all requested features. Otherwise this is part of - initialization on a head node and just used to get the shape of the - input domain, so we don't need to get any features yet.""" + is False or features are being cached we get all requested features. + Otherwise this is part of initialization on a head node and just used + to get the shape of the input domain, so we don't need to get any + features yet.""" self.input_handler_kwargs = self.input_handler_kwargs or {} self.input_handler_kwargs['file_paths'] = self.file_paths self.input_handler_kwargs['features'] = self.features @@ -303,11 +337,11 @@ def init_input_handler(self): input_handler_kwargs = copy.deepcopy(self.input_handler_kwargs) input_handler_kwargs['features'] = self.features - if self.head_node: + if self.head_node and 'cache_kwargs' not in input_handler_kwargs: input_handler_kwargs['features'] = [] input_handler_kwargs['chunks'] = 'auto' - input_handler_kwargs['time_slice'] = slice(None) + input_handler_kwargs['time_slice'] = self.padded_time_slice return InputHandler(**input_handler_kwargs) def _init_features(self, model): @@ -317,13 +351,16 @@ def _init_features(self, model): features = [f for f in model.lr_features if f not in exo_features] return features, exo_features - @cached_property + @property def node_chunks(self): """Get array of lists such that node_chunks[i] is a list of indices for the chunks that will be sent through the generator on the ith node.""" - node_chunks = min(self.max_nodes or np.inf, len(self.unmasked_chunks)) - return np.array_split(self.unmasked_chunks, node_chunks) + chunks = self.unmasked_chunks + if self.redistribute_chunks: + chunks = [c for c in chunks if not self.chunk_finished(c)] + node_chunks = min(self.max_nodes or np.inf, len(chunks)) + return np.array_split(chunks, node_chunks) @property def unmasked_chunks(self): From e2b452f791bdbe2d9dde0dd51bf8217a4f9f4711 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Mon, 5 May 2025 09:57:30 -0600 Subject: [PATCH 100/122] Adding cli entry points to pyproject.toml --- pyproject.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index f904bfd0b4..806a58680a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,16 @@ test = [ "pytest-env" ] +[project.scripts] +sup3r = "sup3r.cli:main" +sup3r-pipeline = "sup3r.pipeline.pipeline_cli:main" +sup3r-batch = "sup3r.batch.batch_cli:main" +sup3r-qa = "sup3r.qa.qa_cli:main" +sup3r-bias-calc = "sup3r.bias.bias_calc_cli:main" +sup3r-solar = "sup3r.solar.solar_cli:main" +sup3r-forward-pass = "sup3r.pipeline.forward_pass_cli:main" +sup3r-collect = "sup3r.postprocessing.data_collect_cli:main" + [project.urls] homepage = "https://github.com/NREL/sup3r" documentation = "https://nrel.github.io/sup3r/" From 53835935860c0fd6516b5712bfaba8ff4d2a8df5 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Mon, 5 May 2025 09:58:21 -0600 Subject: [PATCH 101/122] `_write_single` as classmethod in cacher --- sup3r/preprocessing/cachers/base.py | 11 +++++++---- sup3r/utilities/interpolation.py | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/sup3r/preprocessing/cachers/base.py b/sup3r/preprocessing/cachers/base.py index 82a4bd5c1c..4940460366 100644 --- a/sup3r/preprocessing/cachers/base.py +++ b/sup3r/preprocessing/cachers/base.py @@ -82,8 +82,10 @@ def __init__( ): self.out_files = self.cache_data(**cache_kwargs) + @classmethod def _write_single( - self, + cls, + data, feature, out_file, chunks, @@ -105,9 +107,9 @@ def _write_single( 'Writing %s to %s. %s', feature, tmp_file, _mem_check() ) if ext == '.h5': - func = self.write_h5 + func = cls.write_h5 elif ext == '.nc': - func = self.write_netcdf + func = cls.write_netcdf else: msg = ( 'cache_pattern must have either h5 or nc extension. ' @@ -117,7 +119,7 @@ def _write_single( raise ValueError(msg) func( out_file=tmp_file, - data=self.data, + data=data, features=[feature], chunks=chunks, max_workers=max_workers, @@ -176,6 +178,7 @@ def cache_data( if any(missing_files): for feature, out_file in zip(missing_features, missing_files): self._write_single( + data=self.data, feature=feature, out_file=out_file, chunks=chunks, diff --git a/sup3r/utilities/interpolation.py b/sup3r/utilities/interpolation.py index 9390a68d54..af81133881 100644 --- a/sup3r/utilities/interpolation.py +++ b/sup3r/utilities/interpolation.py @@ -16,7 +16,8 @@ class Interpolator: @classmethod def get_level_masks(cls, lev_array, level): """Get the masks used to select closest surrounding levels in the - lev_array to requested interpolation level. + lev_array to the requested interpolation level. If there are levels + above and below the requested level these are prioritized. Parameters ---------- From 93735f364d84fb46d9a8381a943f99c9dca25864 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 6 May 2025 11:02:52 -0600 Subject: [PATCH 102/122] using _write_single instead of write_netcdf - absorbs handling of tmp files and replacement. --- sup3r/postprocessing/collectors/nc.py | 17 +++++++++++------ sup3r/postprocessing/writers/nc.py | 2 +- sup3r/preprocessing/cachers/base.py | 13 +++++++------ sup3r/preprocessing/rasterizers/exo.py | 10 +++------- sup3r/utilities/era_downloader.py | 7 ++----- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/sup3r/postprocessing/collectors/nc.py b/sup3r/postprocessing/collectors/nc.py index 6810d4b524..1885049bca 100644 --- a/sup3r/postprocessing/collectors/nc.py +++ b/sup3r/postprocessing/collectors/nc.py @@ -31,6 +31,7 @@ def collect( log_file=None, overwrite=True, res_kwargs=None, + cacher_kwargs=None, ): """Collect data files from a dir to one output file. @@ -65,6 +66,8 @@ def collect( Whether to overwrite existing output file res_kwargs : dict | None Dictionary of kwargs to pass to xarray.open_mfdataset. + cacher_kwargs : dict | None + Dictionary of kwargs to pass to Cacher._write_single. """ logger.info(f'Initializing collection for file_paths={file_paths}') @@ -86,8 +89,7 @@ def collect( spatial_chunks = collector.group_spatial_chunks() - tmp_file = out_file + '.tmp' - if not os.path.exists(tmp_file): + if not os.path.exists(out_file): res_kwargs = res_kwargs or { 'combine': 'nested', 'concat_dim': Dimension.TIME, @@ -97,10 +99,13 @@ def collect( spatial_chunks[s_idx], **res_kwargs ) out = xr.concat(spatial_chunks.values(), dim=Dimension.SOUTH_NORTH) - Cacher.write_netcdf(tmp_file, data=out, features=features) - - os.replace(tmp_file, out_file) - logger.info('Moved %s to %s.', tmp_file, out_file) + cacher_kwargs = cacher_kwargs or {} + Cacher._write_single( + out_file=out_file, + data=out, + features=features, + **cacher_kwargs, + ) logger.info('Finished file collection.') diff --git a/sup3r/postprocessing/writers/nc.py b/sup3r/postprocessing/writers/nc.py index b1be1f548f..9f0a4bf1f1 100644 --- a/sup3r/postprocessing/writers/nc.py +++ b/sup3r/postprocessing/writers/nc.py @@ -93,7 +93,7 @@ def _write_output( attrs['date_created'] = attrs.get('date_created', now) ds = xr.Dataset(data_vars=data_vars, coords=coords, attrs=attrs) - Cacher.write_netcdf( + Cacher._write_single( out_file=out_file, data=ds, features=features, diff --git a/sup3r/preprocessing/cachers/base.py b/sup3r/preprocessing/cachers/base.py index 4940460366..751a256902 100644 --- a/sup3r/preprocessing/cachers/base.py +++ b/sup3r/preprocessing/cachers/base.py @@ -85,16 +85,17 @@ def __init__( @classmethod def _write_single( cls, - data, - feature, out_file, - chunks, + data, + features='all', + chunks=None, max_workers=None, mode='w', attrs=None, verbose=False, ): """Write single NETCDF or H5 cache file.""" + features = features if isinstance(features, list) else [features] if os.path.exists(out_file): logger.info( f'{out_file} already exists. Delete if you want to overwrite.' @@ -104,7 +105,7 @@ def _write_single( os.makedirs(os.path.dirname(out_file), exist_ok=True) tmp_file = out_file + '.tmp' logger.info( - 'Writing %s to %s. %s', feature, tmp_file, _mem_check() + 'Writing %s to %s. %s', features, tmp_file, _mem_check() ) if ext == '.h5': func = cls.write_h5 @@ -120,7 +121,7 @@ def _write_single( func( out_file=tmp_file, data=data, - features=[feature], + features=features, chunks=chunks, max_workers=max_workers, mode=mode, @@ -179,7 +180,7 @@ def cache_data( for feature, out_file in zip(missing_features, missing_files): self._write_single( data=self.data, - feature=feature, + features=feature, out_file=out_file, chunks=chunks, max_workers=max_workers, diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index cf1955284d..bb9ee2e43a 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -5,7 +5,6 @@ import logging import os -import shutil from abc import ABC from dataclasses import dataclass from typing import Optional, Union @@ -303,16 +302,13 @@ def data(self): data = self.get_data() if not os.path.exists(cache_fp): - tmp_fp = cache_fp + '.tmp' - Cacher.write_netcdf( - tmp_fp, - data, + Cacher._write_single( + out_file=cache_fp, + data=data, max_workers=self.max_workers, chunks=self.chunks, verbose=self.verbose, ) - shutil.move(tmp_fp, cache_fp) - logger.info('Moved %s to %s', tmp_fp, cache_fp) return Sup3rX(data.chunk(self.chunks)) diff --git a/sup3r/utilities/era_downloader.py b/sup3r/utilities/era_downloader.py index 48a90abc48..30871c0598 100644 --- a/sup3r/utilities/era_downloader.py +++ b/sup3r/utilities/era_downloader.py @@ -835,18 +835,15 @@ def _combine_files(cls, files, outfile, chunks='auto', res_kwargs=None): try: res_kwargs = res_kwargs or {} loader = Loader(files, res_kwargs=res_kwargs) - tmp_file = cls.get_tmp_file(outfile) for ignore_var in IGNORE_VARS: if ignore_var in loader.coords: loader.data = loader.data.drop_vars(ignore_var) - Cacher.write_netcdf( + Cacher._write_single( data=loader.data, - out_file=tmp_file, + out_file=outfile, max_workers=1, chunks=chunks, ) - os.replace(tmp_file, outfile) - logger.info('Moved %s to %s.', tmp_file, outfile) except Exception as e: msg = f'Error combining {files}. {e}' logger.error(msg) From 5046c7b39b8d868ec800b66de8f1266675e539a4 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 6 May 2025 11:02:52 -0600 Subject: [PATCH 103/122] using _write_single instead of write_netcdf - absorbs handling of tmp files and replacement. --- sup3r/preprocessing/cachers/base.py | 60 +++++++++++++++-------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/sup3r/preprocessing/cachers/base.py b/sup3r/preprocessing/cachers/base.py index 751a256902..b1736df5a7 100644 --- a/sup3r/preprocessing/cachers/base.py +++ b/sup3r/preprocessing/cachers/base.py @@ -95,41 +95,43 @@ def _write_single( verbose=False, ): """Write single NETCDF or H5 cache file.""" - features = features if isinstance(features, list) else [features] if os.path.exists(out_file): logger.info( f'{out_file} already exists. Delete if you want to overwrite.' ) + return + if features == 'all': + features = list(data.data_vars) + features = features if isinstance(features, list) else [features] + _, ext = os.path.splitext(out_file) + os.makedirs(os.path.dirname(out_file), exist_ok=True) + tmp_file = out_file + '.tmp' + logger.info( + 'Writing %s to %s. %s', features, tmp_file, _mem_check() + ) + if ext == '.h5': + func = cls.write_h5 + elif ext == '.nc': + func = cls.write_netcdf else: - _, ext = os.path.splitext(out_file) - os.makedirs(os.path.dirname(out_file), exist_ok=True) - tmp_file = out_file + '.tmp' - logger.info( - 'Writing %s to %s. %s', features, tmp_file, _mem_check() + msg = ( + 'cache_pattern must have either h5 or nc extension. ' + f'Received {ext}.' ) - if ext == '.h5': - func = cls.write_h5 - elif ext == '.nc': - func = cls.write_netcdf - else: - msg = ( - 'cache_pattern must have either h5 or nc extension. ' - f'Received {ext}.' - ) - logger.error(msg) - raise ValueError(msg) - func( - out_file=tmp_file, - data=data, - features=features, - chunks=chunks, - max_workers=max_workers, - mode=mode, - attrs=attrs, - verbose=verbose, - ) - os.replace(tmp_file, out_file) - logger.info('Moved %s to %s', tmp_file, out_file) + logger.error(msg) + raise ValueError(msg) + func( + out_file=tmp_file, + data=data, + features=features, + chunks=chunks, + max_workers=max_workers, + mode=mode, + attrs=attrs, + verbose=verbose, + ) + os.replace(tmp_file, out_file) + logger.info('Moved %s to %s', tmp_file, out_file) def cache_data( self, From 7082c877b4616d32350451215806371c918b74f2 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 7 May 2025 08:52:30 -0600 Subject: [PATCH 104/122] ensuring correct ordering of input features to generator --- sup3r/pipeline/strategy.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index 567c950ded..8c4189eee7 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -19,7 +19,7 @@ from sup3r.pipeline.slicer import ForwardPassSlicer from sup3r.pipeline.utilities import get_model from sup3r.postprocessing import OutputHandler -from sup3r.preprocessing import ExoData, ExoDataHandler, Rasterizer +from sup3r.preprocessing import ExoData, ExoDataHandler, Loader from sup3r.preprocessing.names import Dimension from sup3r.preprocessing.utilities import ( _parse_time_slice, @@ -305,21 +305,17 @@ def get_time_slices(self): step = time_slice.step if time_slice.step else 1 pstart = ( 0 - if time_slice.start is None + if not time_slice.start else time_slice.start - self.temporal_pad * step ) pend = ( None - if time_slice.stop is None + if not time_slice.stop else time_slice.stop + self.temporal_pad * step ) padded_slice = slice(pstart, pend, time_slice.step) - start = 0 if time_slice.start is None else self.temporal_pad - stop = ( - None - if time_slice.stop is None or time_slice.stop == 0 - else -self.temporal_pad - ) + start = 0 if not padded_slice.start else self.temporal_pad + stop = None if not padded_slice.stop else -self.temporal_pad unpadded_slice = slice(start, stop) return unpadded_slice, padded_slice @@ -484,7 +480,7 @@ def prep_chunk_data(self, chunk_index=0): kwargs = dict(zip(Dimension.dims_2d(), lr_pad_slice)) kwargs[Dimension.TIME] = ti_pad_slice - input_data = self.input_handler.isel(**kwargs) + input_data = self.input_handler[self.features].isel(**kwargs) logger.info( 'Loading data for chunk_index=%s into memory.', chunk_index ) @@ -581,7 +577,7 @@ def get_exo_kwargs(self, model): ) input_handler_kwargs['target'] = self.input_handler.target input_handler_kwargs['shape'] = self.input_handler.grid_shape - _ = input_handler_kwargs.pop('time_slice', None) + input_handler_kwargs['time_slice'] = self.padded_time_slice exo_kwargs['input_handler_kwargs'] = input_handler_kwargs exo_kwargs = get_class_kwargs(ExoDataHandler, exo_kwargs) exo_kwargs_list.append(exo_kwargs) @@ -625,19 +621,23 @@ def fwp_mask(self): -------- sup3r.pipeline.strategy.ForwardPassStrategy """ - mask = np.zeros(len(self.lr_pad_slices)) + logger.info('Checking for mask in input handler.') input_handler_kwargs = copy.deepcopy(self.input_handler_kwargs) input_handler_kwargs['features'] = 'all' - handler = Rasterizer( - **get_class_kwargs(Rasterizer, input_handler_kwargs) + loader = Loader( + self.file_paths, + **get_class_kwargs(Loader, input_handler_kwargs), ) - if 'mask' in handler.data: + if 'mask' in loader.data: logger.info( 'Found "mask" in DataHandler. Computing forward pass ' 'chunk mask for %s chunks', len(self.lr_pad_slices), ) + InputHandler = get_input_handler_class(self.input_handler_name) + input_handler_kwargs['features'] = ['mask'] + handler = InputHandler(self.file_paths, **input_handler_kwargs) mask_vals = handler.data['mask'].values for s_chunk_idx, lr_slices in enumerate(self.lr_pad_slices): mask_check = mask_vals[lr_slices[0], lr_slices[1]] From d2e7692e4040e1fa3d8bc6b6a8266759c449346f Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 7 May 2025 08:57:01 -0600 Subject: [PATCH 105/122] less verbose logging --- sup3r/pipeline/forward_pass.py | 1 - sup3r/preprocessing/cachers/base.py | 20 ++++++++++---------- sup3r/preprocessing/cachers/utilities.py | 5 ++--- sup3r/preprocessing/data_handlers/base.py | 1 + sup3r/preprocessing/data_handlers/exo.py | 3 +++ sup3r/preprocessing/loaders/base.py | 8 ++------ sup3r/preprocessing/rasterizers/base.py | 9 +++------ sup3r/preprocessing/rasterizers/exo.py | 9 +++++---- sup3r/preprocessing/samplers/base.py | 3 +-- sup3r/preprocessing/utilities.py | 2 +- 10 files changed, 28 insertions(+), 33 deletions(-) diff --git a/sup3r/pipeline/forward_pass.py b/sup3r/pipeline/forward_pass.py index 3771c0bf2e..78ebdbe474 100644 --- a/sup3r/pipeline/forward_pass.py +++ b/sup3r/pipeline/forward_pass.py @@ -237,7 +237,6 @@ def run_generator( ) logger.exception(msg) raise RuntimeError(msg) from e - if len(hi_res.shape) == 4: hi_res = np.expand_dims(np.transpose(hi_res, (1, 2, 0, 3)), axis=0) diff --git a/sup3r/preprocessing/cachers/base.py b/sup3r/preprocessing/cachers/base.py index b1736df5a7..308de07f54 100644 --- a/sup3r/preprocessing/cachers/base.py +++ b/sup3r/preprocessing/cachers/base.py @@ -16,7 +16,7 @@ from warnings import warn from sup3r.preprocessing.base import Container from sup3r.preprocessing.names import Dimension -from sup3r.preprocessing.utilities import _mem_check, log_args, _lowered +from sup3r.preprocessing.utilities import _mem_check, _lowered from sup3r.utilities.utilities import safe_cast, safe_serialize from rex.utilities.utilities import to_records_array @@ -36,7 +36,6 @@ class Cacher(Container): features to the same file call :meth:`write_netcdf` or :meth:`write_h5` directly""" - @log_args def __init__( self, data: Union['Sup3rX', 'Sup3rDataset'], @@ -106,9 +105,7 @@ def _write_single( _, ext = os.path.splitext(out_file) os.makedirs(os.path.dirname(out_file), exist_ok=True) tmp_file = out_file + '.tmp' - logger.info( - 'Writing %s to %s. %s', features, tmp_file, _mem_check() - ) + logger.info('Writing %s to %s. %s', features, tmp_file, _mem_check()) if ext == '.h5': func = cls.write_h5 elif ext == '.nc': @@ -174,11 +171,12 @@ def cache_data( if any(cached_files): logger.info( - 'Cache files %s already exist. Delete to overwrite.', - cached_files, + f'Cache files with pattern {cache_pattern} already exist. ' + 'Delete to overwrite.' ) if any(missing_files): + logger.info('Caching %s to %s', missing_features, missing_files) for feature, out_file in zip(missing_features, missing_files): self._write_single( data=self.data, @@ -517,9 +515,11 @@ def write_netcdf( try: ncfile.setncattr(attr_name, attr_value) except Exception as e: - msg = (f'Could not write {attr_name} as attribute, ' - f'serializing with json dumps, ' - f'received error: "{e}"') + msg = ( + f'Could not write {attr_name} as attribute, ' + f'serializing with json dumps, ' + f'received error: "{e}"' + ) logger.warning(msg) warn(msg) ncfile.setncattr(attr_name, safe_serialize(attr_value)) diff --git a/sup3r/preprocessing/cachers/utilities.py b/sup3r/preprocessing/cachers/utilities.py index a0eef08b53..5c142fb7b2 100644 --- a/sup3r/preprocessing/cachers/utilities.py +++ b/sup3r/preprocessing/cachers/utilities.py @@ -33,8 +33,7 @@ def _check_for_cache(features, cache_kwargs): if any(cached_files): logger.info( - 'Found some cache files: %s. Loading %s from these files.', - cached_files, - cached_features, + 'Found cache files for %s with file pattern: %s', + cached_features, cache_pattern ) return cached_files, cached_features, missing_files, missing_features diff --git a/sup3r/preprocessing/data_handlers/base.py b/sup3r/preprocessing/data_handlers/base.py index 3d8f73125d..d0714b3d22 100644 --- a/sup3r/preprocessing/data_handlers/base.py +++ b/sup3r/preprocessing/data_handlers/base.py @@ -192,6 +192,7 @@ def __init__( self.rasterizer = self.loader = self.cache if any(missing_features) or just_coords: + logger.info('%s not found in cache', missing_features) self.rasterizer = Rasterizer( file_paths=file_paths, res_kwargs=res_kwargs, diff --git a/sup3r/preprocessing/data_handlers/exo.py b/sup3r/preprocessing/data_handlers/exo.py index 219bb3ba28..4c69b87dac 100644 --- a/sup3r/preprocessing/data_handlers/exo.py +++ b/sup3r/preprocessing/data_handlers/exo.py @@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Optional, Union from warnings import warn +import dask.array as da import numpy as np from sup3r.preprocessing.rasterizers import ExoRasterizer @@ -95,6 +96,8 @@ def __init__(self, steps): if 'steps' not in entry: logger.warning(msg) warn(msg) + if isinstance(entry, (np.ndarray, da.core.Array)): + entry = {'data': entry} steps_list = entry.get('steps', [entry]) for i, step in enumerate(steps_list): msg = (f'ExoData entry for {feat}, step #{i + 1}, has no ' diff --git a/sup3r/preprocessing/loaders/base.py b/sup3r/preprocessing/loaders/base.py index 187f78d5cf..76a0b6719a 100644 --- a/sup3r/preprocessing/loaders/base.py +++ b/sup3r/preprocessing/loaders/base.py @@ -14,16 +14,12 @@ from sup3r.preprocessing.names import FEATURE_NAMES, Dimension from sup3r.preprocessing.utilities import ( expand_paths, - log_args, lower_names, ordered_dims, ) from sup3r.utilities.utilities import xr_open_mfdataset -from .utilities import ( - standardize_names, - standardize_values, -) +from .utilities import standardize_names, standardize_values logger = logging.getLogger(__name__) @@ -38,7 +34,6 @@ class BaseLoader(Container, ABC): BASE_LOADER: Callable = xr_open_mfdataset - @log_args def __init__( self, file_paths, @@ -70,6 +65,7 @@ def __init__( Optional base loader update. The default for H5 files is MultiFileResourceX and for NETCDF is xarray.open_mfdataset """ + logger.info('Loading %s from %s', features, file_paths) super().__init__() self.res_kwargs = res_kwargs or {} self.file_paths = file_paths diff --git a/sup3r/preprocessing/rasterizers/base.py b/sup3r/preprocessing/rasterizers/base.py index 897b9c0338..c9f64f9760 100644 --- a/sup3r/preprocessing/rasterizers/base.py +++ b/sup3r/preprocessing/rasterizers/base.py @@ -9,10 +9,7 @@ from sup3r.preprocessing.base import Container from sup3r.preprocessing.names import Dimension -from sup3r.preprocessing.utilities import ( - _parse_time_slice, - compute_if_dask, -) +from sup3r.preprocessing.utilities import _parse_time_slice, compute_if_dask logger = logging.getLogger(__name__) @@ -61,6 +58,7 @@ def __init__( are more than this value away from the target lat/lon, an error is raised. """ + logger.info('Rasterizing %s from %s', features, loader.file_paths) super().__init__(data=loader.data) self.loader = loader self.threshold = threshold @@ -214,8 +212,7 @@ def get_closest_row_col(self, lat_lon, target): msg = ( 'The distance between the closest coordinate: ' f'{np.asarray(lat_lon[row, col])} and the requested ' - f'target: {np.asarray(target)} for files: ' - f'{self.loader.file_paths} is {np.asarray(dist.min())}.' + f'target: {np.asarray(target)} is {np.asarray(dist.min())}. ' ) if self.threshold is not None and dist.min() > self.threshold: add_msg = f'This exceeds the given threshold: {self.threshold}' diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index bb9ee2e43a..d05cad7154 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -149,9 +149,7 @@ def source_data(self): @property def source_handler(self): """Get the Loader object that handles the exogenous data file.""" - msg = f'Getting {self.feature} for full domain from {self.source_file}' if self._source_handler is None: - logger.info(msg) self._source_handler = Loader( file_paths=self.source_file, features=[self.feature] ) @@ -297,6 +295,11 @@ def data(self): cache_fp = self.cache_file if os.path.exists(cache_fp): + logger.info( + 'Loading cached data for {} from {}'.format( + self.feature, cache_fp + ) + ) data = Loader(cache_fp) else: data = self.get_data() @@ -407,9 +410,7 @@ class ObsRasterizer(BaseExoRasterizer): def source_handler(self): """Get the Loader object that handles the exogenous data file.""" feat = self.feature.replace('_obs', '') - msg = f'Getting {self.feature} for full domain from {self.source_file}' if self._source_handler is None: - logger.info(msg) self._source_handler = Loader( file_paths=self.source_file, features=[feat] ) diff --git a/sup3r/preprocessing/samplers/base.py b/sup3r/preprocessing/samplers/base.py index e576c15218..5a13876c27 100644 --- a/sup3r/preprocessing/samplers/base.py +++ b/sup3r/preprocessing/samplers/base.py @@ -14,7 +14,7 @@ uniform_box_sampler, uniform_time_sampler, ) -from sup3r.preprocessing.utilities import compute_if_dask, log_args, lowered +from sup3r.preprocessing.utilities import compute_if_dask, lowered logger = logging.getLogger(__name__) @@ -23,7 +23,6 @@ class Sampler(Container): """Basic Sampler class for iterating through batches of samples from the contained data.""" - @log_args def __init__( self, data, diff --git a/sup3r/preprocessing/utilities.py b/sup3r/preprocessing/utilities.py index db0dd5909a..aec109d6d3 100644 --- a/sup3r/preprocessing/utilities.py +++ b/sup3r/preprocessing/utilities.py @@ -54,7 +54,7 @@ def get_input_handler_class(input_handler_name: Optional[str] = None): DataHandler or Rasterizer class from sup3r.preprocessing. """ if input_handler_name is None: - input_handler_name = 'Rasterizer' + input_handler_name = 'DataHandler' logger.info( '"input_handler_name" arg was not provided. Using ' From 87254ef5af1005a52d25cbab7908d83fa4155399 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Tue, 6 May 2025 11:02:52 -0600 Subject: [PATCH 106/122] using _write_single instead of write_netcdf - absorbs handling of tmp files and replacement. --- sup3r/preprocessing/cachers/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sup3r/preprocessing/cachers/base.py b/sup3r/preprocessing/cachers/base.py index 308de07f54..3c63540a11 100644 --- a/sup3r/preprocessing/cachers/base.py +++ b/sup3r/preprocessing/cachers/base.py @@ -94,6 +94,7 @@ def _write_single( verbose=False, ): """Write single NETCDF or H5 cache file.""" + features = features if isinstance(features, list) else [features] if os.path.exists(out_file): logger.info( f'{out_file} already exists. Delete if you want to overwrite.' From 8741fb98233ac5c5c3a7a1e30a6c167b4ea6dd86 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 7 May 2025 10:52:20 -0600 Subject: [PATCH 107/122] `_write_single` feature parsing fix --- sup3r/preprocessing/cachers/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sup3r/preprocessing/cachers/base.py b/sup3r/preprocessing/cachers/base.py index 3c63540a11..308de07f54 100644 --- a/sup3r/preprocessing/cachers/base.py +++ b/sup3r/preprocessing/cachers/base.py @@ -94,7 +94,6 @@ def _write_single( verbose=False, ): """Write single NETCDF or H5 cache file.""" - features = features if isinstance(features, list) else [features] if os.path.exists(out_file): logger.info( f'{out_file} already exists. Delete if you want to overwrite.' From 17d02eef2b387e837d94544e81470bc49994b5c3 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 7 May 2025 11:51:19 -0600 Subject: [PATCH 108/122] fix: already have file paths in input_handler_kwargs --- sup3r/pipeline/strategy.py | 2 +- sup3r/preprocessing/loaders/base.py | 4 +++- sup3r/preprocessing/rasterizers/base.py | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index 8c4189eee7..b4918c15c6 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -637,7 +637,7 @@ def fwp_mask(self): ) InputHandler = get_input_handler_class(self.input_handler_name) input_handler_kwargs['features'] = ['mask'] - handler = InputHandler(self.file_paths, **input_handler_kwargs) + handler = InputHandler(**input_handler_kwargs) mask_vals = handler.data['mask'].values for s_chunk_idx, lr_slices in enumerate(self.lr_pad_slices): mask_check = mask_vals[lr_slices[0], lr_slices[1]] diff --git a/sup3r/preprocessing/loaders/base.py b/sup3r/preprocessing/loaders/base.py index 76a0b6719a..f2fa2fd465 100644 --- a/sup3r/preprocessing/loaders/base.py +++ b/sup3r/preprocessing/loaders/base.py @@ -65,7 +65,9 @@ def __init__( Optional base loader update. The default for H5 files is MultiFileResourceX and for NETCDF is xarray.open_mfdataset """ - logger.info('Loading %s from %s', features, file_paths) + logger.info( + 'Loading features: %s from files: %s', features, file_paths + ) super().__init__() self.res_kwargs = res_kwargs or {} self.file_paths = file_paths diff --git a/sup3r/preprocessing/rasterizers/base.py b/sup3r/preprocessing/rasterizers/base.py index c9f64f9760..f78ca7a70a 100644 --- a/sup3r/preprocessing/rasterizers/base.py +++ b/sup3r/preprocessing/rasterizers/base.py @@ -58,7 +58,11 @@ def __init__( are more than this value away from the target lat/lon, an error is raised. """ - logger.info('Rasterizing %s from %s', features, loader.file_paths) + logger.info( + 'Rasterizing features: %s from files: %s', + features, + loader.file_paths, + ) super().__init__(data=loader.data) self.loader = loader self.threshold = threshold From d7276a2314ee21ef9378a22c26ff323c1e1ca92c Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 18 May 2025 07:22:20 -0600 Subject: [PATCH 109/122] feat: sample obs frac for each batch element to build obs mask --- sup3r/models/abstract.py | 12 +- sup3r/models/base.py | 12 +- sup3r/models/interface.py | 2 + sup3r/models/with_obs.py | 124 ++++++++++++++----- sup3r/preprocessing/cachers/base.py | 25 +++- sup3r/preprocessing/rasterizers/exo.py | 2 +- tests/training/test_train_conditioned_obs.py | 1 + 7 files changed, 132 insertions(+), 46 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 4467e268e8..4ed807982d 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -424,6 +424,7 @@ def _init_records(self): self._val_record = self._history[val_cols].iloc[-1:] self._val_record = self._val_record.reset_index(drop=True) + @tf.function def get_hr_exo_input(self, hi_res): """Get exogenous feature data from hi_res @@ -438,15 +439,12 @@ def get_hr_exo_input(self, hi_res): Dictionary of exogenous feature data used as input to tf_generate. e.g. ``{'topography': tf.Tensor(...)}`` """ - exo_data = {} - for feature in self.hr_exo_features: - f_idx = self.hr_exo_features.index(feature) - f_idx += len(self.hr_out_features) - exo_fdata = hi_res[..., f_idx : f_idx + 1] - exo_data[feature] = exo_fdata + inds = [self.hr_features.index(f) for f in self.hr_exo_features] + exo = tf.gather(hi_res, inds, axis=-1) + exo = tf.expand_dims(exo, axis=-2) + exo_data = dict(zip(self.hr_exo_features, tf.unstack(exo, axis=-1))) return exo_data - @tf.function def _combine_loss_input(self, hi_res_true, hi_res_gen): """Combine exogenous feature data from hi_res_true with hi_res_gen for loss calculation diff --git a/sup3r/models/base.py b/sup3r/models/base.py index d159b598ac..055c1a9a17 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -419,11 +419,11 @@ def init_weights(self, lr_shape, hr_shape, device=None): logger.info( 'Initializing model weights on device "{}"'.format(device) ) - low_res = np.ones(lr_shape).astype(np.float32) - hi_res = np.ones(hr_shape).astype(np.float32) + low_res = tf.fill(lr_shape, 1.0) + hi_res = tf.fill(hr_shape, 1.0) hr_exo_shape = hr_shape[:-1] + (1,) - hr_exo = np.ones(hr_exo_shape).astype(np.float32) + hr_exo = tf.fill(hr_exo_shape, 1.0) with tf.device(device): hr_exo_data = {} @@ -931,7 +931,7 @@ def calc_val_loss(self, batch_handler, weight_gen_advers): _, v_loss_details, _, _ = self._get_hr_exo_and_loss( batch.low_res, batch.high_res, - weight_gen_advers=weight_gen_advers + weight_gen_advers=weight_gen_advers, ) self._val_record = self.update_loss_details( self._val_record, @@ -1025,6 +1025,7 @@ def _train_batch( ) loss_details.update(b_loss_details) + loss_details = {k: float(v) for k, v in loss_details.items()} loss_details['gen_train_frac'] = float(trained_gen) loss_details['disc_train_frac'] = float(trained_disc) return loss_details @@ -1159,6 +1160,7 @@ def _train_epoch( disc_too_bad = (loss_disc > disc_th_high) and train_disc gen_too_good = disc_too_bad + start = time.time() b_loss_details = self._train_batch( batch, train_gen, @@ -1170,6 +1172,8 @@ def _train_epoch( weight_gen_advers, multi_gpu, ) + elapsed = time.time() - start + logger.info('Finished batch in {:.4f} seconds'.format(elapsed)) loss_means = self._post_batch( ib, diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index 0d403d40e2..02a6f2a38b 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -472,6 +472,8 @@ def set_model_params(self, **kwargs): 'smoothing', ) keys = [k for k in keys if k in kwargs] + if 'hr_out_features' in kwargs: + self.meta['hr_out_features'] = kwargs['hr_out_features'] hr_exo_feat = kwargs.get('hr_exo_features', []) msg = ( diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 0aeb43e503..1505f618b3 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -77,23 +77,74 @@ def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): ) return loss_obs, loss_non_obs - def _get_obs_mask(self, hi_res, spatial_frac, time_frac=None): + @property + def obs_training_inds(self): + """Get the indices of the observation features in the true high res + data. Obs features have an _obs suffix to avoid name conflict with + fully gridded features. During training these are matched with the + true high res data.""" + hr_feats = [f.replace('_obs', '') for f in self.hr_features] + obs_inds = [ + hr_feats.index(f.replace('_obs', '')) for f in self.obs_features + ] + return obs_inds + + def _get_single_obs_mask(self, hi_res, spatial_frac, time_frac=1.0): + """Get observation mask for a given spatial and temporal obs + fraction for a single batch entry. + + Returns + ------- + np.ndarray + Mask which is True for locations that are not observed and False + for locations that are observed. + (spatial_1, spatial_2, n_features) + (spatial_1, spatial_2, n_temporal, n_features) + """ + mask_shape = [*hi_res.shape[:3], 1, len(self.hr_out_features)] + mask_shape[3] = hi_res.shape[3] if self.is_5d else 1 + s_mask = RANDOM_GENERATOR.uniform(size=mask_shape[1:3]) <= spatial_frac + s_mask = s_mask[..., None, None] + t_mask = RANDOM_GENERATOR.uniform(size=mask_shape[-2]) <= time_frac + t_mask = t_mask[None, None, ..., None] + mask = ~(s_mask & t_mask) + mask = np.repeat(mask, mask_shape[-1], axis=-1) + return mask if self.is_5d else np.squeeze(mask, axis=-2) + + def _get_obs_mask(self, hi_res, spatial_frac, time_frac=1.0): """Get observation mask for a given spatial and temporal obs - fraction.""" - obs_mask = RANDOM_GENERATOR.choice( - [True, False], - size=hi_res.shape[1:3], - p=[1 - spatial_frac, spatial_frac], + fraction for an entire batch. + + Returns + ------- + np.ndarray + Mask which is True for locations that are not observed and False + for locations that are observed. + (n_obs, spatial_1, spatial_2, n_features) + (n_obs, spatial_1, spatial_2, n_temporal, n_features) + """ + s_range = ( + spatial_frac + if isinstance(spatial_frac, (list, tuple)) + else [spatial_frac, spatial_frac] ) - if self.is_5d: - sp_mask = obs_mask.copy() - obs_mask = RANDOM_GENERATOR.choice( - [True, False], - size=hi_res.shape[1:-1], - p=[1 - time_frac, time_frac], - ) - obs_mask[sp_mask] = True - return np.repeat(obs_mask[None, ...], hi_res.shape[0], axis=0) + t_range = ( + time_frac + if isinstance(time_frac, (list, tuple)) + else [time_frac, time_frac] + ) + s_fracs = RANDOM_GENERATOR.uniform(*s_range, size=hi_res.shape[0]) + t_fracs = RANDOM_GENERATOR.uniform(*t_range, size=hi_res.shape[0]) + s_fracs = np.clip(s_fracs, 0, 1) + t_fracs = np.clip(t_fracs, 0, 1) + mask = tf.stack( + [ + self._get_single_obs_mask(hi_res, s, t) + for s, t in zip(s_fracs, t_fracs) + ], + axis=0, + ) + return mask def get_obs_mask(self, hi_res): """Define observation mask for the current batch. This is done @@ -102,11 +153,7 @@ def get_obs_mask(self, hi_res): for those locations. This is also divided between onshore and offshore regions""" on_sf = self.onshore_obs_frac['spatial'] - if not isinstance(on_sf, (list, tuple)): - on_sf = [on_sf, on_sf] - on_sf = max(RANDOM_GENERATOR.uniform(*on_sf), 0) - on_tf = self.onshore_obs_frac.get('time', None) - off_tf = self.offshore_obs_frac.get('time', None) + on_tf = self.onshore_obs_frac.get('time', 1.0) obs_mask = self._get_obs_mask(hi_res, on_sf, on_tf) if 'topography' in self.hr_exo_features and self.offshore_obs_frac: topo_idx = len(self.hr_out_features) + self.hr_exo_features.index( @@ -114,9 +161,7 @@ def get_obs_mask(self, hi_res): ) topo = hi_res[..., topo_idx] off_sf = self.offshore_obs_frac['spatial'] - if not isinstance(off_sf, (list, tuple)): - off_sf = [off_sf, off_sf] - off_sf = max(RANDOM_GENERATOR.uniform(*off_sf), 0) + off_tf = self.offshore_obs_frac.get('time', 1.0) offshore_mask = self._get_obs_mask(hi_res, off_sf, off_tf) obs_mask = tf.where(topo > 0, obs_mask, offshore_mask) return obs_mask @@ -136,17 +181,18 @@ def model_params(self): params['loss_obs_weight'] = self.loss_obs_weight return params + @tf.function def get_hr_exo_input(self, hi_res_true): """Mask high res data to act as sparse observation data. Add this to the standard high res exo input""" exo_data = super().get_hr_exo_input(hi_res_true) obs_mask = self.get_obs_mask(hi_res_true) - for feature in self.obs_features: - # obs_features can include a _obs suffix to avoid name conflict - # with fully gridded exo features - f_idx = self.hr_out_features.index(feature.replace('_obs', '')) - tmp = tf.where(obs_mask, np.nan, hi_res_true[..., f_idx]) - exo_data[feature] = tmp[..., None] + nan_const = tf.constant(float('nan'), dtype=hi_res_true.dtype) + obs = tf.gather(hi_res_true, self.obs_training_inds, axis=-1) + obs = tf.where(obs_mask[..., : obs.shape[-1]], nan_const, obs) + obs = tf.expand_dims(obs, axis=-2) + exo_obs = dict(zip(self.obs_features, tf.unstack(obs, axis=-1))) + exo_data.update(exo_obs) exo_data['mask'] = obs_mask return exo_data @@ -170,13 +216,15 @@ def _get_hr_exo_and_loss( hi_res_gen, hi_res_exo['mask'], ) + obs_frac = np.sum(~hi_res_exo['mask']) / np.size( + hi_res_exo['mask'] + ) loss_update = { 'loss_obs': loss_obs, 'loss_non_obs': loss_non_obs, - 'obs_frac': np.sum(~hi_res_exo['mask']) - / np.size(hi_res_exo['mask']), + 'obs_frac': obs_frac, } - if self.loss_obs_weight is not None: + if self.loss_obs_weight and obs_frac > 0: loss_obs *= self.loss_obs_weight loss += loss_obs loss_update['loss_gen'] = loss @@ -185,3 +233,15 @@ def _get_hr_exo_and_loss( ) loss_details.update(loss_update) return loss, loss_details, hi_res_gen, hi_res_exo + + def _post_batch(self, ib, b_loss_details, n_batches, previous_means): + """Update loss details after the current batch and write to log.""" + if 'obs_frac' in b_loss_details: + logger.debug( + 'Batch {} out of {} has obs_frac: {:.4e}'.format( + ib + 1, n_batches, b_loss_details['obs_frac'] + ) + ) + return super()._post_batch( + ib, b_loss_details, n_batches, previous_means + ) diff --git a/sup3r/preprocessing/cachers/base.py b/sup3r/preprocessing/cachers/base.py index 308de07f54..500912855f 100644 --- a/sup3r/preprocessing/cachers/base.py +++ b/sup3r/preprocessing/cachers/base.py @@ -92,9 +92,11 @@ def _write_single( mode='w', attrs=None, verbose=False, + overwrite=False, + keep_dim_order=False, ): """Write single NETCDF or H5 cache file.""" - if os.path.exists(out_file): + if os.path.exists(out_file) and not overwrite: logger.info( f'{out_file} already exists. Delete if you want to overwrite.' ) @@ -126,6 +128,7 @@ def _write_single( mode=mode, attrs=attrs, verbose=verbose, + keep_dim_order=keep_dim_order, ) os.replace(tmp_file, out_file) logger.info('Moved %s to %s', tmp_file, out_file) @@ -138,6 +141,7 @@ def cache_data( mode='w', attrs=None, verbose=False, + keep_dim_order=False, ): """Cache data to file with file type based on user provided cache_pattern. @@ -160,6 +164,10 @@ def cache_data( e.g. {**global_attrs, dset: {...}} verbose : bool Whether to log progress for each chunk written to output files. + keep_dim_order : bool + Whether to keep the original dimension order of the data. If + ``False`` then the data will be transposed to have the time + dimension first. """ msg = 'cache_pattern must have {feature} format key.' assert '{feature}' in cache_pattern, msg @@ -187,6 +195,7 @@ def cache_data( mode=mode, verbose=verbose, attrs=attrs, + keep_dim_order=keep_dim_order, ) logger.info('Finished writing %s', missing_files) return missing_files + cached_files @@ -287,6 +296,7 @@ def write_h5( mode='w', attrs=None, verbose=False, # noqa # pylint: disable=W0613 + keep_dim_order=False, ): """Cache data to h5 file using user provided chunks value. @@ -313,8 +323,16 @@ def write_h5( dataframe that will then be added to the coordinate meta. verbose : bool Dummy arg to match ``write_netcdf`` signature + keep_dim_order : bool + Whether to keep the original dimension order of the data. If + ``False`` then the data will be transposed to have the time + dimension first. """ - if len(data.dims) == 3 and Dimension.TIME in data.dims: + if ( + len(data.dims) == 3 + and Dimension.TIME in data.dims + and not keep_dim_order + ): data = data.transpose(Dimension.TIME, *Dimension.dims_2d()) if features == 'all': features = list(data.data_vars) @@ -442,6 +460,7 @@ def write_netcdf( mode='w', attrs=None, verbose=False, + keep_dim_order=False # noqa # pylint: disable=W0613 ): """Cache data to a netcdf file. @@ -469,6 +488,8 @@ def write_netcdf( e.g. {**global_attrs, dset: {...}} verbose : bool Whether to log output after each chunk is written. + keep_dim_order : bool + Dummy arg to match ``write_h5`` signature """ chunks = chunks or 'auto' global_attrs = data.attrs.copy() diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index d05cad7154..83ab4bef15 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -95,7 +95,7 @@ class BaseExoRasterizer(ABC): for all cases except for sparse observation data. max_workers : int Number of workers used for writing data to cache files. Gets passed to - ``Cacher.write_netcdf.`` + ``Cacher._write_single.`` verbose : bool Whether to log output as each chunk is written to cache file. """ diff --git a/tests/training/test_train_conditioned_obs.py b/tests/training/test_train_conditioned_obs.py index 68c6761af3..5fe5145470 100644 --- a/tests/training/test_train_conditioned_obs.py +++ b/tests/training/test_train_conditioned_obs.py @@ -60,6 +60,7 @@ def test_fixed_wind_obs(gen_config, request): loss_obs_weight=0.1, learning_rate=1e-4, ) + model.meta['hr_out_features'] = ['u_10m', 'v_10m'] test_mask = model.get_obs_mask(np.zeros((1, 20, 20, 1, 1))) frac = 1 - test_mask.sum() / test_mask.size assert np.abs(0.1 - frac) < test_mask.size / (2 * np.sqrt(test_mask.size)) From 264fc4765fd28a8295251c6a60d3aef24b680fa3 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 18 May 2025 14:53:19 -0600 Subject: [PATCH 110/122] removed embedded obs layer and fix tf.gather calls. --- sup3r/models/abstract.py | 11 +-- sup3r/models/interface.py | 44 ++++++---- sup3r/models/with_obs.py | 2 + tests/conftest.py | 86 -------------------- tests/training/test_train_conditioned_obs.py | 8 +- 5 files changed, 33 insertions(+), 118 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 4ed807982d..029c429b88 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -18,7 +18,6 @@ from phygnn.layers.custom_layers import ( Sup3rAdder, Sup3rConcat, - Sup3rConcatEmbeddedObs, Sup3rConcatObs, ) from rex.utilities.utilities import safe_json_load @@ -34,10 +33,7 @@ logger = logging.getLogger(__name__) -SUP3R_OBS_LAYERS = ( - Sup3rConcatObs, - Sup3rConcatEmbeddedObs -) +SUP3R_OBS_LAYERS = (Sup3rConcatObs,) SUP3R_LAYERS = (Sup3rAdder, Sup3rConcat, *SUP3R_OBS_LAYERS) @@ -439,11 +435,12 @@ def get_hr_exo_input(self, hi_res): Dictionary of exogenous feature data used as input to tf_generate. e.g. ``{'topography': tf.Tensor(...)}`` """ + if len(self.hr_exo_features) == 0: + return {} inds = [self.hr_features.index(f) for f in self.hr_exo_features] exo = tf.gather(hi_res, inds, axis=-1) exo = tf.expand_dims(exo, axis=-2) - exo_data = dict(zip(self.hr_exo_features, tf.unstack(exo, axis=-1))) - return exo_data + return dict(zip(self.hr_exo_features, tf.unstack(exo, axis=-1))) def _combine_loss_input(self, hi_res_true, hi_res_gen): """Combine exogenous feature data from hi_res_true with hi_res_gen diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index 02a6f2a38b..e439374258 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -13,7 +13,6 @@ from phygnn.layers.custom_layers import ( Sup3rAdder, Sup3rConcat, - Sup3rConcatEmbeddedObs, Sup3rConcatObs, ) @@ -24,10 +23,9 @@ logger = logging.getLogger(__name__) -SUP3R_OBS_LAYERS = ( - Sup3rConcatObs, - Sup3rConcatEmbeddedObs -) +SUP3R_OBS_LAYERS = (Sup3rConcatObs,) + +SUP3R_EXO_LAYERS = (Sup3rConcat, Sup3rAdder) class AbstractInterface(ABC): @@ -384,6 +382,20 @@ def hr_out_features(self): generative model outputs.""" return self.meta.get('hr_out_features', []) + @property + def obs_features(self): + """Get list of exogenous observation feature names the model uses. + These come from the names of the ``Sup3rObs..`` layers.""" + # pylint: disable=E1101 + features = [] + if hasattr(self, '_gen'): + for layer in self._gen.layers: + check = isinstance(layer, SUP3R_OBS_LAYERS) + check = check and layer.name not in features + if check: + features.append(layer.name) + return features + @property def hr_exo_features(self): """Get list of high-resolution exogenous filter names the model uses. @@ -399,23 +411,19 @@ def hr_exo_features(self): features = [ layer.name for layer in self._gen.layers - if isinstance(layer, (Sup3rAdder, Sup3rConcat)) + if isinstance(layer, SUP3R_EXO_LAYERS) ] + obs_feats = [feat.replace('_obs', '') for feat in self.obs_features] + features += [f for f in obs_feats if f not in self.hr_out_features] return features @property - def obs_features(self): - """Get list of exogenous observation feature names the model uses. - These come from the names of the ``Sup3rObs..`` layers.""" - # pylint: disable=E1101 - features = [] - if hasattr(self, '_gen'): - for layer in self._gen.layers: - check = isinstance(layer, SUP3R_OBS_LAYERS) - check = check and layer.name not in features - if check: - features.append(layer.name) - return features + def hr_features(self): + """Get the list of high-resolution feature names that are included in + the high-resolution data during training. This includes both output + and exogenous features. + """ + return self.hr_out_features + self.hr_exo_features @property def smoothing(self): diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 1505f618b3..a6571a7c74 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -186,6 +186,8 @@ def get_hr_exo_input(self, hi_res_true): """Mask high res data to act as sparse observation data. Add this to the standard high res exo input""" exo_data = super().get_hr_exo_input(hi_res_true) + if len(self.obs_features) == 0: + return exo_data obs_mask = self.get_obs_mask(hi_res_true) nan_const = tf.constant(float('nan'), dtype=hi_res_true.dtype) obs = tf.gather(hi_res_true, self.obs_training_inds, axis=-1) diff --git a/tests/conftest.py b/tests/conftest.py index 3dfd41d887..97cde5a239 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -146,92 +146,6 @@ def func(): return func -@pytest.fixture(scope='package') -def gen_config_with_concat_embedded(): - """Get generator config with custom concat embedded obs layer.""" - - def func(): - return [ - { - 'class': 'FlexiblePadding', - 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], - 'mode': 'REFLECT', - }, - { - 'class': 'Conv2DTranspose', - 'filters': 2, - 'kernel_size': 3, - 'strides': 1, - 'activation': 'relu', - }, - {'class': 'Cropping2D', 'cropping': 4}, - { - 'class': 'FlexiblePadding', - 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], - 'mode': 'REFLECT', - }, - { - 'class': 'Conv2DTranspose', - 'filters': 64, - 'kernel_size': 3, - 'strides': 1, - 'activation': 'relu', - }, - {'class': 'Cropping2D', 'cropping': 4}, - { - 'class': 'FlexiblePadding', - 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], - 'mode': 'REFLECT', - }, - { - 'class': 'Conv2DTranspose', - 'filters': 64, - 'kernel_size': 3, - 'strides': 1, - 'activation': 'relu', - }, - {'class': 'Cropping2D', 'cropping': 4}, - {'class': 'SpatialExpansion', 'spatial_mult': 2}, - {'class': 'Activation', 'activation': 'relu'}, - { - 'class': 'FlexiblePadding', - 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], - 'mode': 'REFLECT', - }, - { - 'class': 'Conv2DTranspose', - 'filters': 2, - 'kernel_size': 3, - 'strides': 1, - 'activation': 'relu', - }, - {'class': 'Cropping2D', 'cropping': 4}, - { - 'class': 'Sup3rConcatEmbeddedObs', - 'name': 'u_10m_obs', - }, - { - 'class': 'Sup3rConcatEmbeddedObs', - 'name': 'v_10m_obs', - }, - { - 'class': 'FlexiblePadding', - 'paddings': [[0, 0], [3, 3], [3, 3], [0, 0]], - 'mode': 'REFLECT', - }, - { - 'class': 'Conv2DTranspose', - 'filters': 2, - 'kernel_size': 3, - 'strides': 1, - 'activation': 'relu', - }, - {'class': 'Cropping2D', 'cropping': 4}, - ] - - return func - - @pytest.fixture(scope='package') def gen_config_with_topo(): """Get generator config with custom topo layer.""" diff --git a/tests/training/test_train_conditioned_obs.py b/tests/training/test_train_conditioned_obs.py index 5fe5145470..6767f2bcc6 100644 --- a/tests/training/test_train_conditioned_obs.py +++ b/tests/training/test_train_conditioned_obs.py @@ -19,13 +19,7 @@ TARGET_W = (39.01, -105.15) -@pytest.mark.parametrize( - 'gen_config', - [ - 'gen_config_with_concat_masked', - 'gen_config_with_concat_embedded', - ], -) +@pytest.mark.parametrize('gen_config', ['gen_config_with_concat_masked']) def test_fixed_wind_obs(gen_config, request): """Test a special model which fixes observations mid network with ``Sup3rConcatObs`` layer.""" From b0bda951dc9e3ca5228234c7bce27d2c39eb3916 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 18 May 2025 16:05:24 -0600 Subject: [PATCH 111/122] test fix - obs mask to numpy from tensor. --- sup3r/models/base.py | 6 +++++- tests/training/test_train_conditioned_obs.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 055c1a9a17..8de1f449d4 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -1173,7 +1173,11 @@ def _train_epoch( multi_gpu, ) elapsed = time.time() - start - logger.info('Finished batch in {:.4f} seconds'.format(elapsed)) + logger.info( + 'Finished batch {} out of {} in {:.4f} seconds'.format( + ib + 1, len(batch_handler), elapsed + ) + ) loss_means = self._post_batch( ib, diff --git a/tests/training/test_train_conditioned_obs.py b/tests/training/test_train_conditioned_obs.py index 6767f2bcc6..b3eac645c2 100644 --- a/tests/training/test_train_conditioned_obs.py +++ b/tests/training/test_train_conditioned_obs.py @@ -55,7 +55,7 @@ def test_fixed_wind_obs(gen_config, request): learning_rate=1e-4, ) model.meta['hr_out_features'] = ['u_10m', 'v_10m'] - test_mask = model.get_obs_mask(np.zeros((1, 20, 20, 1, 1))) + test_mask = model.get_obs_mask(np.zeros((1, 20, 20, 1, 1))).numpy() frac = 1 - test_mask.sum() / test_mask.size assert np.abs(0.1 - frac) < test_mask.size / (2 * np.sqrt(test_mask.size)) assert model.obs_features == ['u_10m_obs', 'v_10m_obs'] From db1be8eb5803921e78a45d6a7dcc2b2065fa9734 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sun, 25 May 2025 08:47:33 -0600 Subject: [PATCH 112/122] feat: enhance loss function handling in Sup3rGan and Sup3rGanWithObs classes fix: update tensor initialization to use tf.cast for consistency refactor: simplify Loader initialization in ForwardPassStrategy refactor: improve docstring handling in Loader class test: convert observation mask to numpy array in test_fixed_wind_obs --- sup3r/models/base.py | 15 +++++++++------ sup3r/models/with_obs.py | 15 ++++++++++----- sup3r/pipeline/strategy.py | 5 +---- sup3r/preprocessing/loaders/__init__.py | 3 ++- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/sup3r/models/base.py b/sup3r/models/base.py index 8de1f449d4..f8a57e8f04 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -56,9 +56,12 @@ def __init__( loss : str | dict Loss function class name from sup3r.utilities.loss_metrics (prioritized) or tensorflow.keras.losses. Defaults to - tf.keras.losses.MeanSquaredError. This can be provided as a dict - with kwargs for loss functions with extra parameters. - e.g. {'SpatialExtremesLoss': {'weight': 0.5}} + tf.keras.losses.MeanSquaredError. As a dictionary this can + include multiple loss function classes, each with + dictionaries of kwargs for that function. Can also include a + key ``term_weights``, which provides a list of weights for + each loss function. e.g. ``{'SpatialExtremesLoss': {}, + 'MeanAbsoluteError': {}, 'term_weights': [0.8, 0.2]}`` optimizer : tf.keras.optimizers.Optimizer | dict | None | str Instantiated tf.keras.optimizers object or a dict optimizer config from tf.keras.optimizers.get_config(). None defaults to Adam. @@ -419,11 +422,11 @@ def init_weights(self, lr_shape, hr_shape, device=None): logger.info( 'Initializing model weights on device "{}"'.format(device) ) - low_res = tf.fill(lr_shape, 1.0) - hi_res = tf.fill(hr_shape, 1.0) + low_res = tf.cast(np.ones(lr_shape), dtype=tf.float32) + hi_res = tf.cast(np.ones(hr_shape), dtype=tf.float32) hr_exo_shape = hr_shape[:-1] + (1,) - hr_exo = tf.fill(hr_exo_shape, 1.0) + hr_exo = tf.cast(np.ones(hr_exo_shape), dtype=tf.float32) with tf.device(device): hr_exo_data = {} diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index a6571a7c74..c9d2102db8 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -4,7 +4,6 @@ import numpy as np import tensorflow as tf -from tensorflow.keras.losses import MeanAbsoluteError from sup3r.utilities.utilities import RANDOM_GENERATOR @@ -23,7 +22,8 @@ def __init__( *args, onshore_obs_frac=None, offshore_obs_frac=None, - loss_obs_weight=None, + loss_obs_weight=0.0, + loss_obs=None, **kwargs, ): """ @@ -48,6 +48,9 @@ def __init__( Same as ``onshore_obs_frac`` but for offshore observations. Offshore observations are frequently sparser than onshore observations. + loss_obs : str | dict + Loss function to use for the additional observation loss term. This + defaults to the content loss function specified with ``loss``. loss_obs_weight : float Value used to weight observation locations in extra content loss term. e.g. The new content loss will include ``obs_loss_weight * @@ -55,24 +58,26 @@ def __init__( kwargs : dict Keyword arguments for the ``Sup3rGan`` parent class. """ + super().__init__(*args, **kwargs) self.onshore_obs_frac = ( {} if onshore_obs_frac is None else onshore_obs_frac ) self.offshore_obs_frac = ( {} if offshore_obs_frac is None else offshore_obs_frac ) + loss_obs = self.loss_name if loss_obs is None else loss_obs + self.loss_obs_fun = self.get_loss_fun(loss_obs) self.loss_obs_weight = loss_obs_weight - super().__init__(*args, **kwargs) @tf.function def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): """Get loss for observation locations and for non observation locations.""" hr_true = hi_res_true[..., : len(self.hr_out_features)] - loss_obs = MeanAbsoluteError()( + loss_obs, _ = self.loss_obs_fun( hr_true[~obs_mask], hi_res_gen[~obs_mask] ) - loss_non_obs = MeanAbsoluteError()( + loss_non_obs, _ = self.loss_obs_fun( hr_true[obs_mask], hi_res_gen[obs_mask] ) return loss_obs, loss_non_obs diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index b4918c15c6..0b06876415 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -625,10 +625,7 @@ def fwp_mask(self): logger.info('Checking for mask in input handler.') input_handler_kwargs = copy.deepcopy(self.input_handler_kwargs) input_handler_kwargs['features'] = 'all' - loader = Loader( - self.file_paths, - **get_class_kwargs(Loader, input_handler_kwargs), - ) + loader = Loader(**get_class_kwargs(Loader, input_handler_kwargs)) if 'mask' in loader.data: logger.info( 'Found "mask" in DataHandler. Computing forward pass ' diff --git a/sup3r/preprocessing/loaders/__init__.py b/sup3r/preprocessing/loaders/__init__.py index 1ffdf7b595..f7ec43b11f 100644 --- a/sup3r/preprocessing/loaders/__init__.py +++ b/sup3r/preprocessing/loaders/__init__.py @@ -22,7 +22,8 @@ def __new__(cls, file_paths, **kwargs): SpecificClass = cls.TypeSpecificClasses[get_source_type(file_paths)] return SpecificClass(file_paths, **kwargs) - __signature__, __doc__ = composite_info(list(TypeSpecificClasses.values())) + _signature_objs = (BaseLoader,) + __doc__ = BaseLoader.__doc__ def __enter__(self): return self From 7de51831791b2003cdcfa456c2b89ca659529875 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Wed, 28 May 2025 07:45:20 -0600 Subject: [PATCH 113/122] bug fix with temporal_pad = 0 --- sup3r/pipeline/strategy.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sup3r/pipeline/strategy.py b/sup3r/pipeline/strategy.py index 0b06876415..acaa885567 100644 --- a/sup3r/pipeline/strategy.py +++ b/sup3r/pipeline/strategy.py @@ -315,7 +315,11 @@ def get_time_slices(self): ) padded_slice = slice(pstart, pend, time_slice.step) start = 0 if not padded_slice.start else self.temporal_pad - stop = None if not padded_slice.stop else -self.temporal_pad + stop = ( + None + if not padded_slice.stop or not self.temporal_pad + else -self.temporal_pad + ) unpadded_slice = slice(start, stop) return unpadded_slice, padded_slice From b06b92c7ec2cc79dde694d24e9a85ddb2364c42d Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 29 May 2025 17:59:40 -0600 Subject: [PATCH 114/122] updated docs for obs model use info. corrected some old doc strings. added tf.function back to _get_parallel_grad. --- README.rst | 2 +- sup3r/models/abstract.py | 1 + sup3r/models/with_obs.py | 57 ++++++++++++++++++++---- sup3r/preprocessing/data_handlers/exo.py | 1 + sup3r/preprocessing/rasterizers/exo.py | 26 +++++++++-- 5 files changed, 73 insertions(+), 14 deletions(-) diff --git a/README.rst b/README.rst index 3d321accce..e68935f4c8 100644 --- a/README.rst +++ b/README.rst @@ -78,4 +78,4 @@ Brandon Benton, Grant Buster, Guilherme Pimenta Castelao, Malik Hassanaly, Pavlo Acknowledgments =============== -This work was authored by the National Renewable Energy Laboratory, operated by Alliance for Sustainable Energy, LLC, for the U.S. Department of Energy (DOE) under Contract No. DE-AC36-08GO28308. This research was supported by the Grid Modernization Initiative of the U.S. Department of Energy (DOE) as part of its Grid Modernization Laboratory Consortium, a strategic partnership between DOE and the national laboratories to bring together leading experts, technologies, and resources to collaborate on the goal of modernizing the nation’s grid. Funding provided by the the DOE Office of Energy Efficiency and Renewable Energy (EERE), the DOE Office of Electricity (OE), DOE Grid Deployment Office (GDO), the DOE Office of Fossil Energy and Carbon Management (FECM), and the DOE Office of Cybersecurity, Energy Security, and Emergency Response (CESER), the DOE Advanced Scientific Computing Research (ASCR) program, the DOE Solar Energy Technologies Office (SETO), the DOE Wind Energy Technologies Office (WETO), the United States Agency for International Development (USAID), and the Laboratory Directed Research and Development (LDRD) program at the National Renewable Energy Laboratory. The research was performed using computational resources sponsored by the Department of Energy's Office of Energy Efficiency and Renewable Energy and located at the National Renewable Energy Laboratory. The views expressed in the article do not necessarily represent the views of the DOE or the U.S. Government. The U.S. Government retains and the publisher, by accepting the article for publication, acknowledges that the U.S. Government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this work, or allow others to do so, for U.S. Government purposes. +This work was authored by the National Renewable Energy Laboratory, operated for the U.S. Department of Energy (DOE) under Contract No. DE-AC36-08GO28308. This research was supported by the Grid Modernization Initiative of the U.S. Department of Energy (DOE) as part of its Grid Modernization Laboratory Consortium, a strategic partnership between DOE and the national laboratories to bring together leading experts, technologies, and resources to collaborate on the goal of modernizing the nation’s grid. Funding provided by the the DOE Office of Energy Efficiency and Renewable Energy (EERE), the DOE Office of Electricity (OE), DOE Grid Deployment Office (GDO), the DOE Office of Fossil Energy and Carbon Management (FECM), and the DOE Office of Cybersecurity, Energy Security, and Emergency Response (CESER), the DOE Advanced Scientific Computing Research (ASCR) program, the DOE Solar Energy Technologies Office (SETO), the DOE Wind Energy Technologies Office (WETO), the United States Agency for International Development (USAID), and the Laboratory Directed Research and Development (LDRD) program at the National Renewable Energy Laboratory. The research was performed using computational resources sponsored by the Department of Energy's Office of Energy Efficiency and Renewable Energy and located at the National Renewable Energy Laboratory. The views expressed in the article do not necessarily represent the views of the DOE or the U.S. Government. The U.S. Government retains and the publisher, by accepting the article for publication, acknowledges that the U.S. Government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this work, or allow others to do so, for U.S. Government purposes. diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 029c429b88..91a7315c8f 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -811,6 +811,7 @@ def _sum_parallel_grad(self, futures, start_time): logger.info(msg) return total_grad, loss_details + @tf.function def _get_parallel_grad( self, low_res, diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index c9d2102db8..a51ced4a55 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -15,7 +15,19 @@ class Sup3rGanWithObs(Sup3rGan): """Sup3r GAN model which includes mid network observation fusion. This model is useful for when production runs will be over a domain for which - observation data is available.""" + observation data is available. + + Note + ---- + During training this model uses sparse sampling of ground truth data to + simulate observation data. This is done by creating masks of ground truth + data and then selecting unmasked data. All model methods which create + observation masks are only used during training. During inference "real" + observation data is passed in as exogenous data with NaN values for where + the observations are not available. These NaN values are then handled by + observation specific model layers - e.g. ``Sup3rObsModel`` or + ``Sup3rConcatObs`` + """ def __init__( self, @@ -98,6 +110,17 @@ def _get_single_obs_mask(self, hi_res, spatial_frac, time_frac=1.0): """Get observation mask for a given spatial and temporal obs fraction for a single batch entry. + Parameters + ---------- + hi_res : np.ndarray + True high resolution data for a single batch entry. + spatial_frac : float + Fraction of the spatial domain that should be treated as + observations. This is a value between 0 and 1. + time_frac : float, optional + Fraction of the temporal domain that should be treated as + observations. This is a value between 0 and 1. Default is 1.0 + Returns ------- np.ndarray @@ -118,7 +141,24 @@ def _get_single_obs_mask(self, hi_res, spatial_frac, time_frac=1.0): def _get_obs_mask(self, hi_res, spatial_frac, time_frac=1.0): """Get observation mask for a given spatial and temporal obs - fraction for an entire batch. + fraction for an entire batch. This is divided between spatial and + temporal fractions because often the spatial fraction is significantly + lower than the temporal fraction in practice, e.g. for a given spatial + location there might be observations for most of the time period but + only a small fraction of the spatial domain is observed. + + Parameters + ---------- + hi_res : np.ndarray + True high resolution data for the entire batch. + spatial_frac : float | list + Fraction of the spatial domain that should be treated as + observations. This is a value between 0 and 1 or a list with + lower and upper bounds for the spatial fraction. + time_frac : float | list, optional + Fraction of the temporal domain that should be treated as + observations. This is a value between 0 and 1 or a list with + lower and upper bounds for the temporal fraction. Default is 1.0 Returns ------- @@ -151,12 +191,11 @@ def _get_obs_mask(self, hi_res, spatial_frac, time_frac=1.0): ) return mask - def get_obs_mask(self, hi_res): - """Define observation mask for the current batch. This is done - with a spatial mask and a temporal mask since often observation data - might be very sparse spatially but cover most of the full time period - for those locations. This is also divided between onshore and offshore - regions""" + def _get_full_obs_mask(self, hi_res): + """Define observation mask for the current batch. This differs from + ``_get_obs_mask`` by defining a composite mask based on separate + onshore and offshore masks. This is because there is often more + observation data available onshore than offshore.""" on_sf = self.onshore_obs_frac['spatial'] on_tf = self.onshore_obs_frac.get('time', 1.0) obs_mask = self._get_obs_mask(hi_res, on_sf, on_tf) @@ -193,7 +232,7 @@ def get_hr_exo_input(self, hi_res_true): exo_data = super().get_hr_exo_input(hi_res_true) if len(self.obs_features) == 0: return exo_data - obs_mask = self.get_obs_mask(hi_res_true) + obs_mask = self._get_full_obs_mask(hi_res_true) nan_const = tf.constant(float('nan'), dtype=hi_res_true.dtype) obs = tf.gather(hi_res_true, self.obs_training_inds, axis=-1) obs = tf.where(obs_mask[..., : obs.shape[-1]], nan_const, obs) diff --git a/sup3r/preprocessing/data_handlers/exo.py b/sup3r/preprocessing/data_handlers/exo.py index 4c69b87dac..13d0e9f53d 100644 --- a/sup3r/preprocessing/data_handlers/exo.py +++ b/sup3r/preprocessing/data_handlers/exo.py @@ -99,6 +99,7 @@ def __init__(self, steps): if isinstance(entry, (np.ndarray, da.core.Array)): entry = {'data': entry} steps_list = entry.get('steps', [entry]) + for i, step in enumerate(steps_list): msg = (f'ExoData entry for {feat}, step #{i + 1}, has no ' '"combine_type" key. Assuming this is for a ' diff --git a/sup3r/preprocessing/rasterizers/exo.py b/sup3r/preprocessing/rasterizers/exo.py index 83ab4bef15..40173f58eb 100644 --- a/sup3r/preprocessing/rasterizers/exo.py +++ b/sup3r/preprocessing/rasterizers/exo.py @@ -74,9 +74,9 @@ class BaseExoRasterizer(ABC): corresponding to ``file_paths``, temporally enhanced 4x to 15 min input_handler_name : str data handler class to use for input data. Provide a string name to - match a :class:`~sup3r.preprocessing.rasterizers.Rasterizer`. If None - the correct handler will be guessed based on file type and time series - properties. + match a ``data_handler`` or ``rasterizer`` imported into + ``~sup3r.preprocessing``. If None the correct handler will be + guessed based on file type and time series properties. input_handler_kwargs : dict | None Any kwargs for initializing the ``input_handler_name`` class. cache_dir : str | './exo_cache' @@ -404,7 +404,25 @@ def _get_data_3d(self): class ObsRasterizer(BaseExoRasterizer): - """Rasterizer for sparse spatiotemporal observation data""" + """Rasterizer for sparse spatiotemporal observation data. This is used in + the same way as other rasterizers (provide netcdf or flattened h5 data) + but it does not aggregate and leaves NaNs in the output data if there are + no observations within the ``distance_upper_bound`` of the target pixel. + + Note + ---- + When setting up a forward pass config file you have to specifiy how to + access exogenous features. To automatically select this rasterizer instead + of the ``BaseExoRasterizer`` name the exogenous feature with an '_obs' + suffix. For example, to use this rasterizer with u_10m data, set the + feature to 'u_10m_obs'. + + See Also + -------- + ExoRasterizer + Type agnostic class that returns the correct rasterizer based on the + feature name. + """ @property def source_handler(self): From 4482ea40caf6cd59c369310752da39292a733ccd Mon Sep 17 00:00:00 2001 From: bnb32 Date: Thu, 29 May 2025 20:51:25 -0600 Subject: [PATCH 115/122] test fix -> renamed get_obs_mask to _get_full_obs_mask to make private / training only designation --- tests/training/test_train_conditioned_obs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/training/test_train_conditioned_obs.py b/tests/training/test_train_conditioned_obs.py index b3eac645c2..9cd8d35720 100644 --- a/tests/training/test_train_conditioned_obs.py +++ b/tests/training/test_train_conditioned_obs.py @@ -55,7 +55,7 @@ def test_fixed_wind_obs(gen_config, request): learning_rate=1e-4, ) model.meta['hr_out_features'] = ['u_10m', 'v_10m'] - test_mask = model.get_obs_mask(np.zeros((1, 20, 20, 1, 1))).numpy() + test_mask = model._get_full_obs_mask(np.zeros((1, 20, 20, 1, 1))).numpy() frac = 1 - test_mask.sum() / test_mask.size assert np.abs(0.1 - frac) < test_mask.size / (2 * np.sqrt(test_mask.size)) assert model.obs_features == ['u_10m_obs', 'v_10m_obs'] From 6ed7795babd1dfb8dd4587393a883a0feefeb4ac Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 30 May 2025 16:26:59 -0600 Subject: [PATCH 116/122] asserts instead of checks in ExoData init. --- sup3r/preprocessing/data_handlers/exo.py | 26 +++++++----------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/sup3r/preprocessing/data_handlers/exo.py b/sup3r/preprocessing/data_handlers/exo.py index 13d0e9f53d..b0d1926f24 100644 --- a/sup3r/preprocessing/data_handlers/exo.py +++ b/sup3r/preprocessing/data_handlers/exo.py @@ -6,9 +6,7 @@ import pathlib from dataclasses import dataclass from typing import TYPE_CHECKING, Optional, Union -from warnings import warn -import dask.array as da import numpy as np from sup3r.preprocessing.rasterizers import ExoRasterizer @@ -91,25 +89,15 @@ def __init__(self, steps): """ # noqa : D301 if isinstance(steps, dict): for feat, entry in steps.items(): - msg = (f'ExoData entry for {feat} has no "steps" key. ' - 'Assuming this is for a single step.') - if 'steps' not in entry: - logger.warning(msg) - warn(msg) - if isinstance(entry, (np.ndarray, da.core.Array)): - entry = {'data': entry} - steps_list = entry.get('steps', [entry]) + msg = f'ExoData entry for {feat} must have a "steps" key.' + assert 'steps' in entry, msg + steps_list = entry['steps'] for i, step in enumerate(steps_list): - msg = (f'ExoData entry for {feat}, step #{i + 1}, has no ' - '"combine_type" key. Assuming this is for a ' - 'layer combination.') - if 'combine_type' not in step: - logger.warning(msg) - warn(msg) - step['combine_type'] = step.get('combine_type', 'layer') - steps_list[i] = step - steps[feat] = {'steps': steps_list} + msg = (f'ExoData entry for {feat}, step #{i + 1}, must ' + 'have a "data" and "combine_type" key.') + assert 'data' in step and 'combine_type' in step, msg + self.update(steps) else: From 41348347bbfc011c752724b173dc0026dfe4a430 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 30 May 2025 16:27:22 -0600 Subject: [PATCH 117/122] bump required phygnn version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 806a58680a..c2658181fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ classifiers=[ ] dependencies = [ "NREL-rex>=0.2.91", - "NREL-phygnn>=0.0.31", + "NREL-phygnn>=0.0.32", "NREL-gaps>=0.6.13", "NREL-farms>=1.0.4", "dask>=2022.0", From 20c4eb306dd904e33ff0744c188369e41e48abe9 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 30 May 2025 16:28:19 -0600 Subject: [PATCH 118/122] enabling loss_obs functions which require 2d or 3d tensors --- sup3r/models/with_obs.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index a51ced4a55..447a26ce95 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -78,6 +78,7 @@ def __init__( {} if offshore_obs_frac is None else offshore_obs_frac ) loss_obs = self.loss_name if loss_obs is None else loss_obs + self.loss_obs_name = loss_obs self.loss_obs_fun = self.get_loss_fun(loss_obs) self.loss_obs_weight = loss_obs_weight @@ -86,11 +87,14 @@ def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): """Get loss for observation locations and for non observation locations.""" hr_true = hi_res_true[..., : len(self.hr_out_features)] + zeros_like = tf.zeros_like(hr_true) loss_obs, _ = self.loss_obs_fun( - hr_true[~obs_mask], hi_res_gen[~obs_mask] + tf.where(~obs_mask, hr_true, zeros_like), + tf.where(~obs_mask, hi_res_gen, zeros_like) ) loss_non_obs, _ = self.loss_obs_fun( - hr_true[obs_mask], hi_res_gen[obs_mask] + tf.where(obs_mask, hr_true, zeros_like), + tf.where(obs_mask, hi_res_gen, zeros_like) ) return loss_obs, loss_non_obs @@ -199,15 +203,13 @@ def _get_full_obs_mask(self, hi_res): on_sf = self.onshore_obs_frac['spatial'] on_tf = self.onshore_obs_frac.get('time', 1.0) obs_mask = self._get_obs_mask(hi_res, on_sf, on_tf) - if 'topography' in self.hr_exo_features and self.offshore_obs_frac: - topo_idx = len(self.hr_out_features) + self.hr_exo_features.index( - 'topography' - ) + if 'topography' in self.hr_features and self.offshore_obs_frac: + topo_idx = self.hr_features.index('topography') topo = hi_res[..., topo_idx] off_sf = self.offshore_obs_frac['spatial'] off_tf = self.offshore_obs_frac.get('time', 1.0) offshore_mask = self._get_obs_mask(hi_res, off_sf, off_tf) - obs_mask = tf.where(topo > 0, obs_mask, offshore_mask) + obs_mask = tf.where(topo[..., None] > 0, obs_mask, offshore_mask) return obs_mask @property @@ -223,6 +225,7 @@ def model_params(self): params['onshore_obs_frac'] = self.onshore_obs_frac params['offshore_obs_frac'] = self.offshore_obs_frac params['loss_obs_weight'] = self.loss_obs_weight + params['loss_obs'] = self.loss_obs_name return params @tf.function From 86ea30b117618e5b37e778bef3c3ed115b16ef38 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 30 May 2025 16:28:50 -0600 Subject: [PATCH 119/122] changed notebook to use all keys required for ExoData --- examples/sup3rwind/running_sup3r_models.ipynb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/sup3rwind/running_sup3r_models.ipynb b/examples/sup3rwind/running_sup3r_models.ipynb index 3f22fd44b9..86588a7fa4 100644 --- a/examples/sup3rwind/running_sup3r_models.ipynb +++ b/examples/sup3rwind/running_sup3r_models.ipynb @@ -512,7 +512,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -520,7 +520,14 @@ "\n", "# add batch dimension and feature channel dimension\n", "exo_input = {\n", - " feature: {'data': hr_data[feature].values[None, ..., None]}\n", + " feature: {\n", + " 'steps': [\n", + " {\n", + " 'data': hr_data[feature].values[None, ..., None],\n", + " 'combine_type': 'layer',\n", + " }\n", + " ]\n", + " }\n", " for feature in model.hr_exo_features + model.obs_features\n", "}" ] From a5002b0290047ca839e69a538e23cda9d40d83ee Mon Sep 17 00:00:00 2001 From: bnb32 Date: Fri, 30 May 2025 16:30:49 -0600 Subject: [PATCH 120/122] run_exo_layer methods for handling sup3r obs model layers --- sup3r/models/abstract.py | 136 +++++++++++++++++--------- sup3r/models/base.py | 6 +- sup3r/models/interface.py | 20 ++-- sup3r/models/utilities.py | 12 +++ sup3r/postprocessing/collectors/h5.py | 6 +- 5 files changed, 115 insertions(+), 65 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 91a7315c8f..77a916a0e1 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -15,11 +15,6 @@ import pandas as pd import tensorflow as tf from phygnn import CustomNetwork -from phygnn.layers.custom_layers import ( - Sup3rAdder, - Sup3rConcat, - Sup3rConcatObs, -) from rex.utilities.utilities import safe_json_load from tensorflow.keras import optimizers @@ -29,14 +24,10 @@ from sup3r.utilities import VERSION_RECORD from sup3r.utilities.utilities import camel_to_underscore, safe_cast -from .utilities import TensorboardMixIn +from .utilities import SUP3R_LAYERS, SUP3R_OBS_LAYERS, TensorboardMixIn logger = logging.getLogger(__name__) -SUP3R_OBS_LAYERS = (Sup3rConcatObs,) - -SUP3R_LAYERS = (Sup3rAdder, Sup3rConcat, *SUP3R_OBS_LAYERS) - # pylint: disable=E1101,W0201,E0203 class AbstractSingleModel(ABC, TensorboardMixIn): @@ -440,7 +431,8 @@ def get_hr_exo_input(self, hi_res): inds = [self.hr_features.index(f) for f in self.hr_exo_features] exo = tf.gather(hi_res, inds, axis=-1) exo = tf.expand_dims(exo, axis=-2) - return dict(zip(self.hr_exo_features, tf.unstack(exo, axis=-1))) + exo = dict(zip(self.hr_exo_features, tf.unstack(exo, axis=-1))) + return exo def _combine_loss_input(self, hi_res_true, hi_res_gen): """Combine exogenous feature data from hi_res_true with hi_res_gen @@ -811,7 +803,6 @@ def _sum_parallel_grad(self, futures, start_time): logger.info(msg) return total_grad, loss_details - @tf.function def _get_parallel_grad( self, low_res, @@ -983,6 +974,66 @@ def _reshape_norm_exo(self, hi_res, hi_res_exo, exo_name, norm_in=True): return hi_res_exo + def run_exo_layer(self, layer, input_array, exogenous_data, norm_in=True): + """run_exo_layer method used in public ``generate`` method. Runs a + layer that combines exogenous data with the hi_res data. These layers + can include single or multiple exogenous features and also single or + multiple gridded exogenous features (in the case when the former + is exogenous observation features). + + Parameters + ---------- + layer : tf.keras.layers.Layer + Layer to run on the hi_res data. This should be a custom layer + that combines exogenous data with the hi_res data. + input_array : np.ndarray + Either high or low-resolution input data, usually a 4D or 5D array + of shape: + (n_obs, spatial_1, spatial_2, n_features) + (n_obs, spatial_1, spatial_2, n_temporal, n_features) + """ + msg = ( + '{} does not match any features in exogenous_data ' + f'({list(exogenous_data)}). ' + ) + feat_stack = [] + extras = [] + features = getattr(layer, 'features', [layer.name]) + exo_features = getattr(layer, 'exo_features', []) + is_obs_layer = isinstance(layer, SUP3R_OBS_LAYERS) + for feat in features + exo_features: + missing_obs = feat in features and feat not in exogenous_data + if is_obs_layer and missing_obs: + logger.warning( + msg.format(feat), + 'Will run without this observation feature.', + ) + continue + assert feat in exogenous_data, msg.format(feat) + exo = exogenous_data.get_combine_type_data(feat, 'layer') + fname = ( + feat.replace('_obs', '') if feat not in self._means else feat + ) + exo = self._reshape_norm_exo( + input_array, + exo, + fname, + norm_in=norm_in, + ) + if feat in features: + feat_stack.append(exo) + else: + extras.append(exo) + hr_exo = ( + np.concatenate(feat_stack, axis=-1) + if len(feat_stack) > 0 + else None + ) + if len(extras) > 0: + extras = np.concatenate(extras, axis=-1) + return layer(input_array, hr_exo, extras) + return layer(input_array, hr_exo) + def generate( self, low_res, norm_in=True, un_norm_out=True, exogenous_data=None ): @@ -1032,31 +1083,11 @@ def generate( try: for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 - is_obs_layer = isinstance(layer, SUP3R_OBS_LAYERS) is_exo_layer = isinstance(layer, SUP3R_LAYERS) - if is_obs_layer and layer.name not in exogenous_data: - msg = (f'Observation data not given for {layer.name}. ' - 'Will run forward pass without it.') - logger.warning(msg) - warn(msg) - hi_res = layer(hi_res) - elif is_exo_layer: - msg = ( - f'layer.name = {layer.name} does not match any ' - 'features in exogenous_data ' - f'({list(exogenous_data)})' - ) - assert layer.name in exogenous_data, msg - hr_exo = exogenous_data.get_combine_type_data( - layer.name, 'layer' - ) - hr_exo = self._reshape_norm_exo( - hi_res, - hr_exo, - layer.name.replace('_obs', ''), - norm_in=norm_in, + if is_exo_layer: + hi_res = self.run_exo_layer( + layer, hi_res, exogenous_data, norm_in=norm_in ) - hi_res = layer(hi_res, hr_exo) else: hi_res = layer(hi_res) except Exception as e: @@ -1073,6 +1104,30 @@ def generate( return self._combine_fwp_output(hi_res, exogenous_data) + def _run_exo_layer(self, layer, input_array, hi_res_exo): + """Private run_exo_layer method used in ``_tf_generate``. Runs a layer + that combines exogenous data with the hi_res data. These layers can + include single or multiple exogenous features.""" + msg = ( + '{} does not match any features in exogenous_data ' + f'({list(hi_res_exo)})' + ) + features = getattr(layer, 'features', [layer.name]) + exo_features = getattr(layer, 'exo_features', []) + feat_stack = [] + extras = [] + for feat in features + exo_features: + assert feat in hi_res_exo, msg.format(feat) + if feat in features: + feat_stack.append(hi_res_exo[feat]) + else: + extras.append(hi_res_exo[feat]) + hr_exo = tf.concat(feat_stack, axis=-1) + if len(extras) > 0: + extras = tf.concat(extras, axis=-1) + return layer(input_array, hr_exo, extras) + return layer(input_array, hr_exo) + @tf.function def _tf_generate(self, low_res, hi_res_exo=None): """Use the generator model to generate high res data from low res input @@ -1105,14 +1160,7 @@ def _tf_generate(self, low_res, hi_res_exo=None): for i, layer in enumerate(self.generator.layers[1:]): layer_num = i + 1 if isinstance(layer, SUP3R_LAYERS): - msg = ( - f'layer.name = {layer.name} does not match any ' - f'features in exogenous_data ({list(hi_res_exo)})' - ) - assert layer.name in hi_res_exo, msg - hr_exo = hi_res_exo[layer.name] - hi_res = layer(hi_res, hr_exo) - + hi_res = self._run_exo_layer(layer, hi_res, hi_res_exo) else: hi_res = layer(hi_res) except Exception as e: @@ -1197,7 +1245,7 @@ def calc_loss( weight_gen_advers=0.001, train_gen=True, train_disc=False, - compute_disc=False + compute_disc=False, ): """Calculate the GAN loss function using generated and true high resolution data.""" diff --git a/sup3r/models/base.py b/sup3r/models/base.py index f8a57e8f04..f7cb6b7014 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -1176,11 +1176,7 @@ def _train_epoch( multi_gpu, ) elapsed = time.time() - start - logger.info( - 'Finished batch {} out of {} in {:.4f} seconds'.format( - ib + 1, len(batch_handler), elapsed - ) - ) + logger.info('Finished batch in {:.4f} seconds'.format(elapsed)) loss_means = self._post_batch( ib, diff --git a/sup3r/models/interface.py b/sup3r/models/interface.py index e439374258..1ea9f2ee43 100644 --- a/sup3r/models/interface.py +++ b/sup3r/models/interface.py @@ -10,22 +10,14 @@ import numpy as np from phygnn import CustomNetwork -from phygnn.layers.custom_layers import ( - Sup3rAdder, - Sup3rConcat, - Sup3rConcatObs, -) from sup3r.preprocessing.data_handlers import ExoData from sup3r.utilities import VERSION_RECORD from sup3r.utilities.utilities import safe_cast -logger = logging.getLogger(__name__) - +from .utilities import SUP3R_EXO_LAYERS, SUP3R_OBS_LAYERS -SUP3R_OBS_LAYERS = (Sup3rConcatObs,) - -SUP3R_EXO_LAYERS = (Sup3rConcat, Sup3rAdder) +logger = logging.getLogger(__name__) class AbstractInterface(ABC): @@ -390,10 +382,10 @@ def obs_features(self): features = [] if hasattr(self, '_gen'): for layer in self._gen.layers: - check = isinstance(layer, SUP3R_OBS_LAYERS) - check = check and layer.name not in features - if check: - features.append(layer.name) + if isinstance(layer, SUP3R_OBS_LAYERS): + obs_feats = getattr(layer, 'features', [layer.name]) + obs_feats = [f for f in obs_feats if f not in features] + features.extend(obs_feats) return features @property diff --git a/sup3r/models/utilities.py b/sup3r/models/utilities.py index dee3c51bc5..d6c23809d7 100644 --- a/sup3r/models/utilities.py +++ b/sup3r/models/utilities.py @@ -7,6 +7,12 @@ import numpy as np import tensorflow as tf +from phygnn.layers.custom_layers import ( + Sup3rAdder, + Sup3rConcat, + Sup3rConcatObs, + Sup3rObsModel, +) from scipy.interpolate import RegularGridInterpolator from tensorflow.keras import optimizers @@ -14,6 +20,12 @@ logger = logging.getLogger(__name__) +SUP3R_OBS_LAYERS = Sup3rObsModel, Sup3rConcatObs + +SUP3R_EXO_LAYERS = Sup3rAdder, Sup3rConcat + +SUP3R_LAYERS = (*SUP3R_EXO_LAYERS, *SUP3R_OBS_LAYERS) + class TrainingSession: """Wrapper to gracefully exit batch handler thread during training, upon a diff --git a/sup3r/postprocessing/collectors/h5.py b/sup3r/postprocessing/collectors/h5.py index 9b9192ff62..5ea5f40914 100644 --- a/sup3r/postprocessing/collectors/h5.py +++ b/sup3r/postprocessing/collectors/h5.py @@ -813,8 +813,9 @@ def collect( flist_chunks = collector.get_flist_chunks( collector.flist, n_writes=n_writes ) + tmp_file = out_file + '.tmp' if not os.path.exists(out_file): - collector._init_h5(out_file, time_index, target_meta, global_attrs) + collector._init_h5(tmp_file, time_index, target_meta, global_attrs) for dset in features: logger.debug('Collecting dataset "%s".', dset) collector.collect_feature( @@ -824,8 +825,9 @@ def collect( time_index=time_index, shape=shape, flist_chunks=flist_chunks, - out_file=out_file, + out_file=tmp_file, threshold=threshold, max_workers=max_workers, ) + os.replace(tmp_file, out_file) logger.info('Finished file collection.') From f79983e4f44653fdd1c58a7b472a1041f220dd04 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Sat, 31 May 2025 06:12:46 -0600 Subject: [PATCH 121/122] fix: check for means is None before norm in run exo layer --- sup3r/models/abstract.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 77a916a0e1..6756905152 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -953,6 +953,11 @@ def _reshape_norm_exo(self, hi_res, hi_res_exo, exo_name, norm_in=True): return hi_res_exo if norm_in and self._means is not None: + exo_name = ( + exo_name.replace('_obs', '') + if exo_name not in self._means + else exo_name + ) hi_res_exo = ( hi_res_exo.copy() - self._means[exo_name] ) / self._stdevs[exo_name] @@ -1011,13 +1016,10 @@ def run_exo_layer(self, layer, input_array, exogenous_data, norm_in=True): continue assert feat in exogenous_data, msg.format(feat) exo = exogenous_data.get_combine_type_data(feat, 'layer') - fname = ( - feat.replace('_obs', '') if feat not in self._means else feat - ) exo = self._reshape_norm_exo( input_array, exo, - fname, + feat, norm_in=norm_in, ) if feat in features: From c76fea137c251af1ac98413eff2ce1050346e359 Mon Sep 17 00:00:00 2001 From: bnb32 Date: Mon, 2 Jun 2025 12:59:55 -0600 Subject: [PATCH 122/122] refactor: use tf methods for obs loss calc and gpu splitting --- sup3r/models/abstract.py | 12 +++++------- sup3r/models/base.py | 4 +++- sup3r/models/conditional.py | 2 +- sup3r/models/with_obs.py | 19 +++++++------------ 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/sup3r/models/abstract.py b/sup3r/models/abstract.py index 6756905152..71ee0fb0f8 100644 --- a/sup3r/models/abstract.py +++ b/sup3r/models/abstract.py @@ -815,19 +815,17 @@ def _get_parallel_grad( futures = [] start_time = time.time() - lr_chunks = np.array_split(low_res, len(self.gpu_list)) - hr_true_chunks = np.array_split(hi_res_true, len(self.gpu_list)) - split_mask = False + lr_chunks = tf.split(low_res, len(self.gpu_list), axis=0) + hr_true_chunks = tf.split(hi_res_true, len(self.gpu_list), axis=0) mask_chunks = None if 'mask' in calc_loss_kwargs: - split_mask = True - mask_chunks = np.array_split( - calc_loss_kwargs['mask'], len(self.gpu_list) + mask_chunks = tf.split( + calc_loss_kwargs['mask'], len(self.gpu_list), axis=0 ) with ThreadPoolExecutor(max_workers=len(self.gpu_list)) as exe: for i in range(len(self.gpu_list)): - if split_mask: + if mask_chunks is not None: calc_loss_kwargs['mask'] = mask_chunks[i] futures.append( exe.submit( diff --git a/sup3r/models/base.py b/sup3r/models/base.py index f7cb6b7014..b489aa21c6 100644 --- a/sup3r/models/base.py +++ b/sup3r/models/base.py @@ -503,7 +503,9 @@ def calc_loss_gen_content(self, hi_res_true, hi_res_gen): if len(self.hr_exo_features) == 0 else slice(0, -len(self.hr_exo_features)) ) - return self.loss_fun(hi_res_true[..., slc], hi_res_gen[..., slc]) + # gen is first since loss can included regularizers which just + # apply to generator output + return self.loss_fun(hi_res_gen[..., slc], hi_res_true[..., slc]) @staticmethod @tf.function diff --git a/sup3r/models/conditional.py b/sup3r/models/conditional.py index 2f2a99e93a..560c63a3b8 100644 --- a/sup3r/models/conditional.py +++ b/sup3r/models/conditional.py @@ -238,7 +238,7 @@ def calc_loss_cond_mom(self, output_true, output_gen, mask): moment predictor """ - return self.loss_fun(output_true * mask, output_gen * mask) + return self.loss_fun(output_gen * mask, output_true * mask) def calc_loss(self, output_true, output_gen, mask): """Calculate the total moment predictor loss diff --git a/sup3r/models/with_obs.py b/sup3r/models/with_obs.py index 447a26ce95..1921774851 100644 --- a/sup3r/models/with_obs.py +++ b/sup3r/models/with_obs.py @@ -87,14 +87,11 @@ def _get_loss_obs_comparison(self, hi_res_true, hi_res_gen, obs_mask): """Get loss for observation locations and for non observation locations.""" hr_true = hi_res_true[..., : len(self.hr_out_features)] - zeros_like = tf.zeros_like(hr_true) loss_obs, _ = self.loss_obs_fun( - tf.where(~obs_mask, hr_true, zeros_like), - tf.where(~obs_mask, hi_res_gen, zeros_like) + hi_res_gen[~obs_mask], hr_true[~obs_mask] ) loss_non_obs, _ = self.loss_obs_fun( - tf.where(obs_mask, hr_true, zeros_like), - tf.where(obs_mask, hi_res_gen, zeros_like) + hi_res_gen[obs_mask], hr_true[obs_mask] ) return loss_obs, loss_non_obs @@ -265,9 +262,9 @@ def _get_hr_exo_and_loss( hi_res_gen, hi_res_exo['mask'], ) - obs_frac = np.sum(~hi_res_exo['mask']) / np.size( - hi_res_exo['mask'] - ) + n_obs = tf.reduce_sum(tf.cast(~hi_res_exo['mask'], tf.float32)) + n_total = tf.cast(tf.size(hi_res_exo['mask']), tf.float32) + obs_frac = n_obs / n_total loss_update = { 'loss_obs': loss_obs, 'loss_non_obs': loss_non_obs, @@ -276,10 +273,8 @@ def _get_hr_exo_and_loss( if self.loss_obs_weight and obs_frac > 0: loss_obs *= self.loss_obs_weight loss += loss_obs - loss_update['loss_gen'] = loss - loss_update['loss_gen_content'] = ( - loss_details['loss_gen_content'] + loss_obs - ) + loss_details['loss_gen'] += loss_obs + loss_details['loss_gen_content'] += loss_obs loss_details.update(loss_update) return loss, loss_details, hi_res_gen, hi_res_exo