|
4 | 4 | import logging
|
5 | 5 | from pathlib import Path
|
6 | 6 | from typing import Any, Dict, List, Literal, Optional, Union
|
| 7 | +from uuid import uuid4 |
7 | 8 |
|
8 | 9 | from pycrdt import Array, Doc, Map
|
9 | 10 | from pydantic import BaseModel
|
10 | 11 | from ypywidgets.comm import CommWidget
|
11 | 12 |
|
12 |
| -from uuid import uuid4 |
13 |
| - |
14 |
| -from .utils import normalize_path, get_source_layer_names |
15 |
| - |
16 | 13 | from .objects import (
|
17 |
| - LayerType, |
18 |
| - SourceType, |
| 14 | + IGeoJSONSource, |
19 | 15 | IHillshadeLayer,
|
20 | 16 | IImageLayer,
|
| 17 | + IImageSource, |
21 | 18 | IRasterLayer,
|
22 | 19 | IRasterSource,
|
23 |
| - IVectorTileSource, |
24 | 20 | IVectorLayer,
|
25 | 21 | IVectorTileLayer,
|
26 |
| - IGeoJSONSource, |
27 |
| - IImageSource, |
| 22 | + IVectorTileSource, |
28 | 23 | IVideoSource,
|
29 |
| - IWebGlLayer |
| 24 | + IGeoTiffSource, |
| 25 | + IWebGlLayer, |
| 26 | + LayerType, |
| 27 | + SourceType, |
30 | 28 | )
|
| 29 | +from .utils import get_source_layer_names, normalize_path |
31 | 30 |
|
32 | 31 | logger = logging.getLogger(__file__)
|
33 | 32 |
|
@@ -360,7 +359,102 @@ def add_video_layer(
|
360 | 359 | }
|
361 | 360 |
|
362 | 361 | return self._add_layer(OBJECT_FACTORY.create_layer(layer, self))
|
| 362 | + |
| 363 | + def add_tiff_layer( |
| 364 | + self, |
| 365 | + url: str, |
| 366 | + min: int = None, |
| 367 | + max: int = None, |
| 368 | + name: str = "Tiff Layer", |
| 369 | + normalize: bool = True, |
| 370 | + wrapX: bool = False, |
| 371 | + attribution: str = "", |
| 372 | + opacity: float = 1.0, |
| 373 | + color_expr = None |
| 374 | + ): |
| 375 | + """ |
| 376 | + Add a tiff layer |
| 377 | +
|
| 378 | + :param str url: URL of the tif |
| 379 | + :param int min: Minimum pixel value to be displayed, defaults to letting the map display set the value |
| 380 | + :param int max: Maximum pixel value to be displayed, defaults to letting the map display set the value |
| 381 | + :param str name: The name that will be used for the object in the document, defaults to "Tiff Layer" |
| 382 | + :param bool normalize: Select whether to normalize values between 0..1, if false than min/max have no effect, defaults to True |
| 383 | + :param bool wrapX: Render tiles beyond the tile grid extent, defaults to False |
| 384 | + :param float opacity: The opacity, between 0 and 1, defaults to 1.0 |
| 385 | + :param _type_ color_expr: The style expression used to style the layer, defaults to None |
| 386 | + """ |
| 387 | + |
| 388 | + source = { |
| 389 | + "type": SourceType.GeoTiffSource, |
| 390 | + "name": f"{name} Source", |
| 391 | + "parameters": { |
| 392 | + "urls": [{ |
| 393 | + "url": url, |
| 394 | + "min": min, |
| 395 | + "max": max |
| 396 | + }], |
| 397 | + "normalize": normalize, |
| 398 | + "wrapX": wrapX, |
| 399 | + }, |
| 400 | + } |
| 401 | + source_id = self._add_source(OBJECT_FACTORY.create_source(source, self)) |
| 402 | + |
| 403 | + layer = { |
| 404 | + "type": LayerType.WebGlLayer, |
| 405 | + "name": name, |
| 406 | + "visible": True, |
| 407 | + "parameters": {"source": source_id, "opacity": opacity, "color": color_expr}, |
| 408 | + } |
363 | 409 |
|
| 410 | + return self._add_layer(OBJECT_FACTORY.create_layer(layer, self)) |
| 411 | + |
| 412 | + def create_color_expr(self, color_stops: Dict, band: float = 1.0, interpolation_type: str = 'linear', ): |
| 413 | + """ |
| 414 | + Create a color expression used to style the layer |
| 415 | +
|
| 416 | + :param Dict color_stops: Dictionary of stop values to [r, g, b, a] colors |
| 417 | + :param float band: The band to be colored, defaults to 1.0 |
| 418 | + :param str interpolation_type: The interpolation function. Can be linear, discrete, or exact, defaults to 'linear' |
| 419 | + """ |
| 420 | + |
| 421 | + if interpolation_type not in ["linear", "discrete", "exact"]: |
| 422 | + raise ValueError("Interpolation type must be one of linear, discrete, or exact") |
| 423 | + |
| 424 | + color = [] |
| 425 | + if interpolation_type == 'linear': |
| 426 | + color = ['interpolate',['linear']] |
| 427 | + color.append(['band', band]) |
| 428 | + # Transparency for nodata |
| 429 | + color.append(0.0) |
| 430 | + color.append([0.0, 0.0, 0.0, 0.0]) |
| 431 | + |
| 432 | + for value, colorVal in color_stops.items(): |
| 433 | + color.append(value) |
| 434 | + color.append(colorVal) |
| 435 | + |
| 436 | + return color |
| 437 | + |
| 438 | + if interpolation_type == 'discrete': |
| 439 | + operator = '<=' |
| 440 | + |
| 441 | + if interpolation_type == 'exact': |
| 442 | + operator = '==' |
| 443 | + |
| 444 | + color = ['case'] |
| 445 | + # Transparency for nodata |
| 446 | + color.append(["==", ["band", band], 0.0]) |
| 447 | + color.append([0.0, 0.0, 0.0, 0.0]) |
| 448 | + |
| 449 | + for value, colorVal in color_stops.items(): |
| 450 | + color.append([operator, ["band", band], value]) |
| 451 | + color.append(colorVal) |
| 452 | + |
| 453 | + # Fallback color |
| 454 | + color.append([0.0, 0.0, 0.0, 1.0]) |
| 455 | + |
| 456 | + return color |
| 457 | + |
364 | 458 | def add_filter(self, layer_id: str, logical_op:str, feature:str, operator:str, value:Union[str, number, float]):
|
365 | 459 | """
|
366 | 460 | Add a filter to a layer
|
@@ -529,7 +623,8 @@ class Config:
|
529 | 623 | IVectorTileSource,
|
530 | 624 | IGeoJSONSource,
|
531 | 625 | IImageSource,
|
532 |
| - IVideoSource |
| 626 | + IVideoSource, |
| 627 | + IGeoTiffSource |
533 | 628 | ]
|
534 | 629 | _parent = Optional[GISDocument]
|
535 | 630 |
|
@@ -614,3 +709,4 @@ def create_source(
|
614 | 709 | OBJECT_FACTORY.register_factory(SourceType.GeoJSONSource, IGeoJSONSource)
|
615 | 710 | OBJECT_FACTORY.register_factory(SourceType.ImageSource, IImageSource)
|
616 | 711 | OBJECT_FACTORY.register_factory(SourceType.VideoSource, IVideoSource)
|
| 712 | +OBJECT_FACTORY.register_factory(SourceType.GeoTiffSource, IGeoTiffSource) |
0 commit comments