Skip to content

Commit 517fc6e

Browse files
committed
add GEE exporter
1 parent 0e76dae commit 517fc6e

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

src/palettize/exporters/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .titiler import TitilerExporter
1414
from .mapgl import MapglExporter
1515
from .observable_plot import ObservablePlotExporter
16+
from .gee import GEEExporter
1617

1718
# Global registry for exporters
1819
# Maps an identifier string to an instantiated exporter object or a callable that returns one.
@@ -84,6 +85,7 @@ def list_available_exporters() -> Dict[str, str]:
8485
TitilerExporter(),
8586
MapglExporter(),
8687
ObservablePlotExporter(),
88+
GEEExporter(),
8789
]
8890

8991
for exporter_instance in _BUILTIN_EXPORTERS:

src/palettize/exporters/gee.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"""Exporter for Google Earth Engine (GEE)."""
2+
3+
from typing import Any, Dict, Optional
4+
5+
from palettize.core import Colormap, ScalingFunction
6+
from ._base import BaseExporter
7+
8+
9+
class GEEExporter(BaseExporter):
10+
"""
11+
Exporter for Google Earth Engine JavaScript code snippets.
12+
"""
13+
14+
@property
15+
def identifier(self) -> str:
16+
return "gee"
17+
18+
@property
19+
def name(self) -> str:
20+
return "Google Earth Engine Snippet"
21+
22+
@property
23+
def default_file_extension(self) -> str:
24+
return "js"
25+
26+
def export(
27+
self,
28+
colormap: Colormap,
29+
scaler: ScalingFunction,
30+
domain_min: float,
31+
domain_max: float,
32+
options: Optional[Dict[str, Any]] = None,
33+
) -> str:
34+
"""
35+
Exports the colormap to a GEE JavaScript snippet.
36+
37+
Accepted options:
38+
type (str): 'default' or 'sld'. Default is 'default'.
39+
num_colors (int): Number of color steps to generate. Default 256.
40+
"""
41+
options = options or {}
42+
export_type = options.get("type", "default")
43+
num_colors = options.get("num_colors", 256)
44+
if not isinstance(num_colors, int):
45+
raise ValueError("Option 'num_colors' must be an integer.")
46+
47+
if export_type == "default":
48+
return self._export_default(colormap, domain_min, domain_max, num_colors)
49+
elif export_type == "sld":
50+
return self._export_sld(colormap, domain_min, domain_max, num_colors)
51+
else:
52+
raise ValueError(f"Unsupported GEE export type: {export_type}")
53+
54+
def _export_default(self, colormap: Colormap, domain_min: float, domain_max: float, num_colors: int) -> str:
55+
"""Exports a GEE visualization palette."""
56+
if num_colors < 2:
57+
raise ValueError("Number of colors must be at least 2.")
58+
59+
# GEE palette colors are hex strings without the '#'
60+
colors = []
61+
for i in range(num_colors):
62+
position = i / (num_colors - 1)
63+
color_hex = colormap.get_color(position, output_format="hex")
64+
if isinstance(color_hex, str):
65+
colors.append(color_hex[1:])
66+
67+
palette_str = ", ".join([f"'{c}'" for c in colors])
68+
return f"var palettize_viz = {{min: {domain_min}, max: {domain_max}, palette: [{palette_str}]}};"
69+
70+
def _export_sld(self, colormap: Colormap, domain_min: float, domain_max: float, num_colors: int) -> str:
71+
"""Exports a GEE SLD-style palette."""
72+
if num_colors < 2:
73+
raise ValueError("Number of colors must be at least 2 for SLD ramp.")
74+
75+
entries = []
76+
for i in range(num_colors):
77+
position = i / (num_colors - 1)
78+
79+
value = domain_min + position * (domain_max - domain_min)
80+
# a bit of rounding to avoid long float strings
81+
if abs(value - round(value)) < 1e-9:
82+
value = int(round(value))
83+
else:
84+
value = round(value, 4)
85+
86+
color_hex_any = colormap.get_color(position, output_format="hex")
87+
if isinstance(color_hex_any, str):
88+
color_hex = color_hex_any
89+
else:
90+
color_hex = "#000000" # Fallback, should not be reached with hex format
91+
# Each entry is a JS string literal concatenated with '+'
92+
entry = f' \'<ColorMapEntry color="{color_hex}" quantity="{value}" label="{value}" />\' +'
93+
entries.append(entry)
94+
95+
# remove the last ' +' from the last entry
96+
if entries:
97+
entries[-1] = entries[-1].rstrip(" +")
98+
99+
entries_str = "\n".join(entries)
100+
101+
# Using a multiline f-string for better readability and correctness
102+
final_string = f"""var palettize_sld =
103+
'<RasterSymbolizer>' +
104+
'<ColorMap type="ramp" extended="false" >' +
105+
{entries_str}
106+
'</ColorMap>' +
107+
'</RasterSymbolizer>';"""
108+
109+
return final_string

0 commit comments

Comments
 (0)