Skip to content

Commit

Permalink
Merge pull request #74 from TutteInstitute/selection_box_model
Browse files Browse the repository at this point in the history
Multi-SelectionHandler and Improve Selection using new layout model
  • Loading branch information
lmcinnes authored Jan 24, 2025
2 parents 0dfb54b + 29b1fb7 commit 71b9412
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 130 deletions.
67 changes: 42 additions & 25 deletions datamapplot/deckgl_template.html

Large diffs are not rendered by default.

85 changes: 72 additions & 13 deletions datamapplot/interactive_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from scipy.spatial import Delaunay
from colorspacious import cspace_convert
from sklearn.cluster import KMeans
from collections.abc import Iterable

from pandas.api.types import is_string_dtype, is_numeric_dtype, is_datetime64_any_dtype

Expand All @@ -33,6 +34,7 @@
from datamapplot.medoids import medoid
from datamapplot.config import ConfigManager
from datamapplot import offline_mode_caching
from datamapplot.selection_handlers import SelectionHandlerBase

try:
import matplotlib
Expand Down Expand Up @@ -473,7 +475,15 @@ def _get_js_dependency_urls(
js_dependency_urls.append(f"https://{cdn_url}/d3@latest/dist/d3.min.js")

if selection_handler is not None:
js_dependency_urls.extend(selection_handler.dependencies)
if isinstance(selection_handler, Iterable):
for handler in selection_handler:
js_dependency_urls.extend(handler.dependencies)
elif isinstance(selection_handler, SelectionHandlerBase):
js_dependency_urls.extend(selection_handler.dependencies)
else:
raise ValueError(
"The selection_handler must be an instance of SelectionHandlerBase or an iterable of SelectionHandlerBase instances."
)

js_dependency_urls = list(set(js_dependency_urls))

Expand Down Expand Up @@ -878,6 +888,31 @@ def label_text_and_polygon_dataframes(
return pd.DataFrame(data)


def url_to_base64_img(url):
try:
# Download the image
response = requests.get(url, timeout=10)
response.raise_for_status()

# Determine the image type from the Content-Type header
content_type = response.headers.get('Content-Type', '')
if not content_type.startswith('image/'):
raise ValueError(f'URL does not point to an image (Content-Type: {content_type})')

# Convert the image data to base64
image_data = base64.b64encode(response.content).decode('utf-8')

# Create the complete data URL
return f'data:{content_type};base64,{image_data}'

except requests.RequestException as e:
print(f"Error downloading image: {e}")
return None
except Exception as e:
print(f"Error processing image: {e}")
return None


@cfg.complete(unconfigurable={"point_dataframe", "label_dataframe"})
def render_html(
point_dataframe,
Expand Down Expand Up @@ -1430,7 +1465,7 @@ def render_html(
)
n_swatches = np.max(
[colormap.get("n_colors", 5) for colormap in colormap_metadata]
)
) if len(colormap_metadata) > 0 else 5
quantizer = KMeans(n_clusters=n_swatches, random_state=0, n_init=1).fit(
cielab_colors
)
Expand Down Expand Up @@ -1633,6 +1668,9 @@ def render_html(
if not offline_mode_font_data_file.is_file():
offline_mode_caching.cache_fonts()

if logo is not None:
logo = url_to_base64_img(logo)

else:
offline_mode_data = None

Expand All @@ -1652,20 +1690,41 @@ def render_html(
api_tooltip_fontname = None

if selection_handler is not None:
if custom_html is None:
custom_html = selection_handler.html
else:
custom_html += selection_handler.html
if isinstance(selection_handler, Iterable):
for handler in selection_handler:
if custom_html is None:
custom_html = handler.html
else:
custom_html += handler.html

if custom_js is None:
custom_js = selection_handler.javascript
else:
custom_js += selection_handler.javascript
if custom_js is None:
custom_js = handler.javascript
else:
custom_js += handler.javascript

if custom_css is None:
custom_css = handler.css
else:
custom_css += handler.css
elif isinstance(selection_handler, SelectionHandlerBase):
if custom_html is None:
custom_html = selection_handler.html
else:
custom_html += selection_handler.html

if custom_css is None:
custom_css = selection_handler.css
if custom_js is None:
custom_js = selection_handler.javascript
else:
custom_js += selection_handler.javascript

if custom_css is None:
custom_css = selection_handler.css
else:
custom_css += selection_handler.css
else:
custom_css += selection_handler.css
raise ValueError(
"selection_handler must be an instance of SelectionHandlerBase or an iterable of SelectionHandlerBase instances"
)

html_str = template.render(
title=title if title is not None else "Interactive Data Map",
Expand Down
Loading

0 comments on commit 71b9412

Please sign in to comment.