compare module¶
Map comparison widget for side-by-side comparison of two maps.
MapCompare (AnyWidget)
¶
Map comparison widget for side-by-side comparison of two maps.
Source code in anymap/compare.py
class MapCompare(anywidget.AnyWidget):
"""Map comparison widget for side-by-side comparison of two maps."""
# Map configuration traits
left_map_config = traitlets.Dict({}).tag(sync=True)
right_map_config = traitlets.Dict({}).tag(sync=True)
# Widget dimensions
width = traitlets.Unicode("100%").tag(sync=True)
height = traitlets.Unicode("600px").tag(sync=True)
# Comparison options
orientation = traitlets.Unicode("vertical").tag(
sync=True
) # "vertical" or "horizontal"
mousemove = traitlets.Bool(False).tag(sync=True) # Enable swipe on mouse move
slider_position = traitlets.Float(0.5).tag(sync=True) # Slider position (0-1)
# Backend type
backend = traitlets.Unicode("maplibre").tag(sync=True) # "maplibre" or "mapbox"
# Synchronization options
sync_center = traitlets.Bool(True).tag(sync=True)
sync_zoom = traitlets.Bool(True).tag(sync=True)
sync_bearing = traitlets.Bool(True).tag(sync=True)
sync_pitch = traitlets.Bool(True).tag(sync=True)
# Communication traits
_js_calls = traitlets.List([]).tag(sync=True)
_js_events = traitlets.List([]).tag(sync=True)
def __init__(
self,
left_map: Optional[Dict[str, Any]] = None,
right_map: Optional[Dict[str, Any]] = None,
backend: str = "maplibre",
orientation: str = "vertical",
mousemove: bool = False,
width: str = "100%",
height: str = "600px",
sync_center: bool = True,
sync_zoom: bool = True,
sync_bearing: bool = True,
sync_pitch: bool = True,
**kwargs,
):
"""Initialize MapCompare widget.
Args:
left_map: Configuration for the left/before map
right_map: Configuration for the right/after map
backend: Map backend to use ("maplibre" or "mapbox")
orientation: Comparison orientation ("vertical" or "horizontal")
mousemove: Enable swipe on mouse move
width: Widget width
height: Widget height
sync_center: Synchronize map center
sync_zoom: Synchronize map zoom
sync_bearing: Synchronize map bearing
sync_pitch: Synchronize map pitch
"""
# Set default map configurations
if left_map is None:
left_map = {
"center": [0.0, 0.0],
"zoom": 2.0,
"style": (
"https://demotiles.maplibre.org/style.json"
if backend == "maplibre"
else "mapbox://styles/mapbox/streets-v12"
),
}
if right_map is None:
right_map = {
"center": [0.0, 0.0],
"zoom": 2.0,
"style": (
"https://demotiles.maplibre.org/style.json"
if backend == "maplibre"
else "mapbox://styles/mapbox/satellite-v9"
),
}
super().__init__(
left_map_config=left_map,
right_map_config=right_map,
backend=backend,
orientation=orientation,
mousemove=mousemove,
width=width,
height=height,
sync_center=sync_center,
sync_zoom=sync_zoom,
sync_bearing=sync_bearing,
sync_pitch=sync_pitch,
**kwargs,
)
self._event_handlers = {}
self._js_method_counter = 0
# Set JavaScript and CSS based on backend
if backend == "maplibre":
self._esm = self._load_maplibre_compare_js()
self._css = self._load_maplibre_compare_css()
else: # mapbox
self._esm = self._load_mapbox_compare_js()
self._css = self._load_mapbox_compare_css()
def _load_maplibre_compare_js(self) -> str:
"""Load MapLibre comparison JavaScript code."""
# This will be implemented when we create the JS file
try:
with open(
pathlib.Path(__file__).parent / "static" / "maplibre_compare_widget.js",
"r",
) as f:
return f.read()
except FileNotFoundError:
return ""
def _load_maplibre_compare_css(self) -> str:
"""Load MapLibre comparison CSS styles."""
try:
with open(
pathlib.Path(__file__).parent
/ "static"
/ "maplibre_compare_widget.css",
"r",
) as f:
return f.read()
except FileNotFoundError:
return ""
def _load_mapbox_compare_js(self) -> str:
"""Load Mapbox comparison JavaScript code."""
try:
with open(
pathlib.Path(__file__).parent / "static" / "mapbox_compare_widget.js",
"r",
) as f:
return f.read()
except FileNotFoundError:
return ""
def _load_mapbox_compare_css(self) -> str:
"""Load Mapbox comparison CSS styles."""
try:
with open(
pathlib.Path(__file__).parent / "static" / "mapbox_compare_widget.css",
"r",
) as f:
return f.read()
except FileNotFoundError:
return ""
def call_js_method(self, method_name: str, *args, **kwargs) -> None:
"""Call a JavaScript method on the compare instance."""
call_data = {
"id": self._js_method_counter,
"method": method_name,
"args": args,
"kwargs": kwargs,
}
self._js_method_counter += 1
# Trigger sync by creating new list
current_calls = list(self._js_calls)
current_calls.append(call_data)
self._js_calls = current_calls
def on_event(self, event_type: str, callback):
"""Register a callback for comparison events."""
if event_type not in self._event_handlers:
self._event_handlers[event_type] = []
self._event_handlers[event_type].append(callback)
@traitlets.observe("_js_events")
def _handle_js_events(self, change):
"""Handle events from JavaScript."""
events = change["new"]
for event in events:
event_type = event.get("type")
if event_type in self._event_handlers:
for handler in self._event_handlers[event_type]:
handler(event)
def set_slider_position(self, position: float) -> None:
"""Set the slider position.
Args:
position: Slider position (0.0 to 1.0)
"""
if not 0.0 <= position <= 1.0:
raise ValueError("Position must be between 0.0 and 1.0")
self.slider_position = position
self.call_js_method("setSlider", position)
def set_orientation(self, orientation: str) -> None:
"""Set the comparison orientation.
Args:
orientation: "vertical" or "horizontal"
"""
if orientation not in ["vertical", "horizontal"]:
raise ValueError("Orientation must be 'vertical' or 'horizontal'")
self.orientation = orientation
self.call_js_method("setOrientation", orientation)
def enable_mousemove(self, enabled: bool = True) -> None:
"""Enable or disable swipe on mouse move.
Args:
enabled: Whether to enable mousemove
"""
self.mousemove = enabled
self.call_js_method("setMousemove", enabled)
def set_sync_options(
self,
center: Optional[bool] = None,
zoom: Optional[bool] = None,
bearing: Optional[bool] = None,
pitch: Optional[bool] = None,
) -> None:
"""Set synchronization options.
Args:
center: Synchronize map center
zoom: Synchronize map zoom
bearing: Synchronize map bearing
pitch: Synchronize map pitch
"""
if center is not None:
self.sync_center = center
if zoom is not None:
self.sync_zoom = zoom
if bearing is not None:
self.sync_bearing = bearing
if pitch is not None:
self.sync_pitch = pitch
sync_options = {
"center": self.sync_center,
"zoom": self.sync_zoom,
"bearing": self.sync_bearing,
"pitch": self.sync_pitch,
}
self.call_js_method("setSyncOptions", sync_options)
def update_left_map(self, config: Dict[str, Any]) -> None:
"""Update the left map configuration.
Args:
config: New configuration for the left map
"""
self.left_map_config = config
self.call_js_method("updateLeftMap", config)
def update_right_map(self, config: Dict[str, Any]) -> None:
"""Update the right map configuration.
Args:
config: New configuration for the right map
"""
self.right_map_config = config
self.call_js_method("updateRightMap", config)
def fly_to(self, lat: float, lng: float, zoom: Optional[float] = None) -> None:
"""Fly both maps to a specific location.
Args:
lat: Latitude
lng: Longitude
zoom: Zoom level (optional)
"""
options = {"center": [lat, lng]}
if zoom is not None:
options["zoom"] = zoom
self.call_js_method("flyTo", options)
def to_html(
self,
filename: Optional[str] = None,
title: str = "Map Comparison",
**kwargs,
) -> str:
"""Export the comparison widget to a standalone HTML file.
Args:
filename: Optional filename to save the HTML. If None, returns HTML string.
title: Title for the HTML page
**kwargs: Additional arguments passed to the HTML template
Returns:
HTML string content
"""
# Get the current widget state
widget_state = {
"left_map_config": dict(self.left_map_config),
"right_map_config": dict(self.right_map_config),
"backend": self.backend,
"orientation": self.orientation,
"mousemove": self.mousemove,
"slider_position": self.slider_position,
"sync_center": self.sync_center,
"sync_zoom": self.sync_zoom,
"sync_bearing": self.sync_bearing,
"sync_pitch": self.sync_pitch,
"width": self.width,
"height": self.height,
}
# Generate HTML content
html_content = self._generate_html_template(widget_state, title, **kwargs)
# Save to file if filename is provided
if filename:
with open(filename, "w", encoding="utf-8") as f:
f.write(html_content)
return html_content
def _generate_html_template(
self, widget_state: Dict[str, Any], title: str, **kwargs
) -> str:
"""Generate the HTML template for map comparison."""
# Serialize widget state for JavaScript
widget_state_json = json.dumps(widget_state, indent=2)
# Choose CDN URLs based on backend
if widget_state["backend"] == "maplibre":
map_js_url = "https://unpkg.com/maplibre-gl@5.6.1/dist/maplibre-gl.js"
map_css_url = "https://unpkg.com/maplibre-gl@5.6.1/dist/maplibre-gl.css"
global_var = "maplibregl"
else: # mapbox
map_js_url = "https://api.mapbox.com/mapbox-gl-js/v3.13.0/mapbox-gl.js"
map_css_url = "https://api.mapbox.com/mapbox-gl-js/v3.13.0/mapbox-gl.css"
global_var = "mapboxgl"
# Generate access token warning for Mapbox
access_token_warning = ""
if widget_state["backend"] == "mapbox":
left_token = widget_state["left_map_config"].get("access_token", "")
right_token = widget_state["right_map_config"].get("access_token", "")
if not left_token and not right_token:
access_token_warning = """
<div class="access-token-warning">
<strong>Warning:</strong> This map requires a Mapbox access token.
Get a free token at <a href="https://account.mapbox.com/access-tokens/" target="_blank">Mapbox</a>
and set it in the JavaScript code below.
</div>
"""
html_template = f"""<!DOCTYPE html>
<html>
<head>
<title>{title}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="{map_js_url}"></script>
<link href="{map_css_url}" rel="stylesheet">
<script src="https://unpkg.com/@maplibre/maplibre-gl-compare@0.5.0/dist/maplibre-gl-compare.js"></script>
<link href="https://unpkg.com/@maplibre/maplibre-gl-compare@0.5.0/dist/maplibre-gl-compare.css" rel="stylesheet">
<style>
body {{
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
background-color: #f5f5f5;
}}
.container {{
max-width: 1200px;
margin: 0 auto;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
}}
.header {{
padding: 20px;
background-color: #fff;
border-bottom: 1px solid #eee;
}}
h1 {{
margin: 0;
color: #333;
font-size: 24px;
}}
.map-container {{
position: relative;
width: {widget_state['width']};
height: {widget_state['height']};
margin: 20px;
}}
#comparison-container {{
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
border: 1px solid #ccc;
border-radius: 4px;
}}
#before, #after {{
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
}}
.access-token-warning {{
background-color: #fff3cd;
border: 1px solid #ffeaa7;
color: #856404;
padding: 15px;
margin: 20px;
border-radius: 4px;
}}
.access-token-warning a {{
color: #856404;
text-decoration: underline;
}}
.controls {{
padding: 20px;
background-color: #f8f9fa;
border-top: 1px solid #eee;
}}
.control-group {{
margin-bottom: 15px;
}}
.control-group label {{
display: inline-block;
width: 120px;
font-weight: bold;
color: #333;
}}
.control-group input, .control-group select {{
padding: 5px 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}}
.control-group button {{
padding: 8px 16px;
background-color: #007cba;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}}
.control-group button:hover {{
background-color: #005a8b;
}}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{title}</h1>
<p>Interactive map comparison powered by anymap</p>
</div>
{access_token_warning}
<div class="map-container">
<div id="comparison-container">
<div id="before"></div>
<div id="after"></div>
</div>
</div>
<div class="controls">
<div class="control-group">
<label>Note:</label>
<span>Use the slider on the map to adjust position</span>
</div>
<div class="control-group">
<label for="orientation">Orientation:</label>
<select id="orientation">
<option value="vertical" {"selected" if widget_state['orientation'] == 'vertical' else ""}>Vertical</option>
<option value="horizontal" {"selected" if widget_state['orientation'] == 'horizontal' else ""}>Horizontal</option>
</select>
</div>
<div class="control-group">
<label for="mousemove">Mouse Move:</label>
<input type="checkbox" id="mousemove" {"checked" if widget_state['mousemove'] else ""}>
<span>Enable swipe on mouse move</span>
</div>
<div class="control-group">
<button onclick="flyToSanFrancisco()">Fly to San Francisco</button>
<button onclick="flyToNewYork()">Fly to New York</button>
<button onclick="flyToLondon()">Fly to London</button>
<button onclick="flyToTokyo()">Fly to Tokyo</button>
</div>
</div>
</div>
<script>
// Widget state from Python
const widgetState = {widget_state_json};
// Set access token for Mapbox if needed
if (widgetState.backend === 'mapbox') {{
const accessToken = widgetState.left_map_config.access_token || widgetState.right_map_config.access_token || '';
if (accessToken) {{
{global_var}.accessToken = accessToken;
}}
}}
// Initialize maps
let beforeMap, afterMap, compare;
function initializeMaps() {{
const leftConfig = widgetState.left_map_config;
const rightConfig = widgetState.right_map_config;
// Create before map
beforeMap = new {global_var}.Map({{
container: 'before',
style: leftConfig.style,
center: leftConfig.center ? [leftConfig.center[1], leftConfig.center[0]] : [0, 0],
zoom: leftConfig.zoom || 2,
bearing: leftConfig.bearing || 0,
pitch: leftConfig.pitch || 0,
antialias: leftConfig.antialias !== undefined ? leftConfig.antialias : true
}});
// Create after map
afterMap = new {global_var}.Map({{
container: 'after',
style: rightConfig.style,
center: rightConfig.center ? [rightConfig.center[1], rightConfig.center[0]] : [0, 0],
zoom: rightConfig.zoom || 2,
bearing: rightConfig.bearing || 0,
pitch: rightConfig.pitch || 0,
antialias: rightConfig.antialias !== undefined ? rightConfig.antialias : true
}});
// Wait for both maps to load
Promise.all([
new Promise(resolve => beforeMap.on('load', resolve)),
new Promise(resolve => afterMap.on('load', resolve))
]).then(() => {{
createComparison();
setupEventListeners();
// Note: MapLibre Compare plugin handles synchronization internally
// Custom synchronization disabled to prevent conflicts and improve performance
}});
}}
function createComparison() {{
if (compare) {{
compare.remove();
}}
compare = new {global_var}.Compare(beforeMap, afterMap, "#comparison-container", {{
orientation: widgetState.orientation,
mousemove: widgetState.mousemove
}});
console.log('Compare widget created successfully');
console.log('Before map scrollZoom enabled:', beforeMap.scrollZoom.isEnabled());
console.log('After map scrollZoom enabled:', afterMap.scrollZoom.isEnabled());
}}
function setupSynchronization() {{
if (widgetState.sync_center || widgetState.sync_zoom || widgetState.sync_bearing || widgetState.sync_pitch) {{
let isSync = false;
function syncMaps(sourceMap, targetMap) {{
if (isSync) return; // Prevent infinite loops
isSync = true;
try {{
if (widgetState.sync_center) {{
targetMap.setCenter(sourceMap.getCenter());
}}
if (widgetState.sync_zoom) {{
targetMap.setZoom(sourceMap.getZoom());
}}
if (widgetState.sync_bearing) {{
targetMap.setBearing(sourceMap.getBearing());
}}
if (widgetState.sync_pitch) {{
targetMap.setPitch(sourceMap.getPitch());
}}
}} finally {{
// Use requestAnimationFrame to reset flag after current event loop
requestAnimationFrame(() => {{
isSync = false;
}});
}}
}}
// Use 'moveend' instead of 'move' to avoid interfering with scroll zoom
beforeMap.on('moveend', () => syncMaps(beforeMap, afterMap));
afterMap.on('moveend', () => syncMaps(afterMap, beforeMap));
}}
}}
function setupEventListeners() {{
// Orientation control
document.getElementById('orientation').addEventListener('change', function(e) {{
widgetState.orientation = e.target.value;
createComparison();
}});
// Mousemove control
document.getElementById('mousemove').addEventListener('change', function(e) {{
widgetState.mousemove = e.target.checked;
createComparison();
}});
}}
// Navigation functions
function flyToSanFrancisco() {{
const center = [-122.4194, 37.7749];
const zoom = 12;
beforeMap.flyTo({{ center: center, zoom: zoom, essential: true }});
afterMap.flyTo({{ center: center, zoom: zoom, essential: true }});
}}
function flyToNewYork() {{
const center = [-74.0060, 40.7128];
const zoom = 12;
beforeMap.flyTo({{ center: center, zoom: zoom, essential: true }});
afterMap.flyTo({{ center: center, zoom: zoom, essential: true }});
}}
function flyToLondon() {{
const center = [-0.1278, 51.5074];
const zoom = 12;
beforeMap.flyTo({{ center: center, zoom: zoom, essential: true }});
afterMap.flyTo({{ center: center, zoom: zoom, essential: true }});
}}
function flyToTokyo() {{
const center = [139.6917, 35.6895];
const zoom = 12;
beforeMap.flyTo({{ center: center, zoom: zoom, essential: true }});
afterMap.flyTo({{ center: center, zoom: zoom, essential: true }});
}}
// Initialize the comparison
initializeMaps();
// Log successful initialization
console.log('Map comparison initialized successfully');
</script>
</body>
</html>"""
return html_template
__init__(self, left_map=None, right_map=None, backend='maplibre', orientation='vertical', mousemove=False, width='100%', height='600px', sync_center=True, sync_zoom=True, sync_bearing=True, sync_pitch=True, **kwargs)
special
¶
Initialize MapCompare widget.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
left_map |
Optional[Dict[str, Any]] |
Configuration for the left/before map |
None |
right_map |
Optional[Dict[str, Any]] |
Configuration for the right/after map |
None |
backend |
str |
Map backend to use ("maplibre" or "mapbox") |
'maplibre' |
orientation |
str |
Comparison orientation ("vertical" or "horizontal") |
'vertical' |
mousemove |
bool |
Enable swipe on mouse move |
False |
width |
str |
Widget width |
'100%' |
height |
str |
Widget height |
'600px' |
sync_center |
bool |
Synchronize map center |
True |
sync_zoom |
bool |
Synchronize map zoom |
True |
sync_bearing |
bool |
Synchronize map bearing |
True |
sync_pitch |
bool |
Synchronize map pitch |
True |
Source code in anymap/compare.py
def __init__(
self,
left_map: Optional[Dict[str, Any]] = None,
right_map: Optional[Dict[str, Any]] = None,
backend: str = "maplibre",
orientation: str = "vertical",
mousemove: bool = False,
width: str = "100%",
height: str = "600px",
sync_center: bool = True,
sync_zoom: bool = True,
sync_bearing: bool = True,
sync_pitch: bool = True,
**kwargs,
):
"""Initialize MapCompare widget.
Args:
left_map: Configuration for the left/before map
right_map: Configuration for the right/after map
backend: Map backend to use ("maplibre" or "mapbox")
orientation: Comparison orientation ("vertical" or "horizontal")
mousemove: Enable swipe on mouse move
width: Widget width
height: Widget height
sync_center: Synchronize map center
sync_zoom: Synchronize map zoom
sync_bearing: Synchronize map bearing
sync_pitch: Synchronize map pitch
"""
# Set default map configurations
if left_map is None:
left_map = {
"center": [0.0, 0.0],
"zoom": 2.0,
"style": (
"https://demotiles.maplibre.org/style.json"
if backend == "maplibre"
else "mapbox://styles/mapbox/streets-v12"
),
}
if right_map is None:
right_map = {
"center": [0.0, 0.0],
"zoom": 2.0,
"style": (
"https://demotiles.maplibre.org/style.json"
if backend == "maplibre"
else "mapbox://styles/mapbox/satellite-v9"
),
}
super().__init__(
left_map_config=left_map,
right_map_config=right_map,
backend=backend,
orientation=orientation,
mousemove=mousemove,
width=width,
height=height,
sync_center=sync_center,
sync_zoom=sync_zoom,
sync_bearing=sync_bearing,
sync_pitch=sync_pitch,
**kwargs,
)
self._event_handlers = {}
self._js_method_counter = 0
# Set JavaScript and CSS based on backend
if backend == "maplibre":
self._esm = self._load_maplibre_compare_js()
self._css = self._load_maplibre_compare_css()
else: # mapbox
self._esm = self._load_mapbox_compare_js()
self._css = self._load_mapbox_compare_css()
call_js_method(self, method_name, *args, **kwargs)
¶
Call a JavaScript method on the compare instance.
Source code in anymap/compare.py
def call_js_method(self, method_name: str, *args, **kwargs) -> None:
"""Call a JavaScript method on the compare instance."""
call_data = {
"id": self._js_method_counter,
"method": method_name,
"args": args,
"kwargs": kwargs,
}
self._js_method_counter += 1
# Trigger sync by creating new list
current_calls = list(self._js_calls)
current_calls.append(call_data)
self._js_calls = current_calls
enable_mousemove(self, enabled=True)
¶
Enable or disable swipe on mouse move.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
enabled |
bool |
Whether to enable mousemove |
True |
Source code in anymap/compare.py
def enable_mousemove(self, enabled: bool = True) -> None:
"""Enable or disable swipe on mouse move.
Args:
enabled: Whether to enable mousemove
"""
self.mousemove = enabled
self.call_js_method("setMousemove", enabled)
fly_to(self, lat, lng, zoom=None)
¶
Fly both maps to a specific location.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
lat |
float |
Latitude |
required |
lng |
float |
Longitude |
required |
zoom |
Optional[float] |
Zoom level (optional) |
None |
Source code in anymap/compare.py
def fly_to(self, lat: float, lng: float, zoom: Optional[float] = None) -> None:
"""Fly both maps to a specific location.
Args:
lat: Latitude
lng: Longitude
zoom: Zoom level (optional)
"""
options = {"center": [lat, lng]}
if zoom is not None:
options["zoom"] = zoom
self.call_js_method("flyTo", options)
on_event(self, event_type, callback)
¶
Register a callback for comparison events.
Source code in anymap/compare.py
def on_event(self, event_type: str, callback):
"""Register a callback for comparison events."""
if event_type not in self._event_handlers:
self._event_handlers[event_type] = []
self._event_handlers[event_type].append(callback)
set_orientation(self, orientation)
¶
Set the comparison orientation.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
orientation |
str |
"vertical" or "horizontal" |
required |
Source code in anymap/compare.py
def set_orientation(self, orientation: str) -> None:
"""Set the comparison orientation.
Args:
orientation: "vertical" or "horizontal"
"""
if orientation not in ["vertical", "horizontal"]:
raise ValueError("Orientation must be 'vertical' or 'horizontal'")
self.orientation = orientation
self.call_js_method("setOrientation", orientation)
set_slider_position(self, position)
¶
Set the slider position.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
position |
float |
Slider position (0.0 to 1.0) |
required |
Source code in anymap/compare.py
def set_slider_position(self, position: float) -> None:
"""Set the slider position.
Args:
position: Slider position (0.0 to 1.0)
"""
if not 0.0 <= position <= 1.0:
raise ValueError("Position must be between 0.0 and 1.0")
self.slider_position = position
self.call_js_method("setSlider", position)
set_sync_options(self, center=None, zoom=None, bearing=None, pitch=None)
¶
Set synchronization options.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
center |
Optional[bool] |
Synchronize map center |
None |
zoom |
Optional[bool] |
Synchronize map zoom |
None |
bearing |
Optional[bool] |
Synchronize map bearing |
None |
pitch |
Optional[bool] |
Synchronize map pitch |
None |
Source code in anymap/compare.py
def set_sync_options(
self,
center: Optional[bool] = None,
zoom: Optional[bool] = None,
bearing: Optional[bool] = None,
pitch: Optional[bool] = None,
) -> None:
"""Set synchronization options.
Args:
center: Synchronize map center
zoom: Synchronize map zoom
bearing: Synchronize map bearing
pitch: Synchronize map pitch
"""
if center is not None:
self.sync_center = center
if zoom is not None:
self.sync_zoom = zoom
if bearing is not None:
self.sync_bearing = bearing
if pitch is not None:
self.sync_pitch = pitch
sync_options = {
"center": self.sync_center,
"zoom": self.sync_zoom,
"bearing": self.sync_bearing,
"pitch": self.sync_pitch,
}
self.call_js_method("setSyncOptions", sync_options)
to_html(self, filename=None, title='Map Comparison', **kwargs)
¶
Export the comparison widget to a standalone HTML file.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
filename |
Optional[str] |
Optional filename to save the HTML. If None, returns HTML string. |
None |
title |
str |
Title for the HTML page |
'Map Comparison' |
**kwargs |
Additional arguments passed to the HTML template |
{} |
Returns:
Type | Description |
---|---|
str |
HTML string content |
Source code in anymap/compare.py
def to_html(
self,
filename: Optional[str] = None,
title: str = "Map Comparison",
**kwargs,
) -> str:
"""Export the comparison widget to a standalone HTML file.
Args:
filename: Optional filename to save the HTML. If None, returns HTML string.
title: Title for the HTML page
**kwargs: Additional arguments passed to the HTML template
Returns:
HTML string content
"""
# Get the current widget state
widget_state = {
"left_map_config": dict(self.left_map_config),
"right_map_config": dict(self.right_map_config),
"backend": self.backend,
"orientation": self.orientation,
"mousemove": self.mousemove,
"slider_position": self.slider_position,
"sync_center": self.sync_center,
"sync_zoom": self.sync_zoom,
"sync_bearing": self.sync_bearing,
"sync_pitch": self.sync_pitch,
"width": self.width,
"height": self.height,
}
# Generate HTML content
html_content = self._generate_html_template(widget_state, title, **kwargs)
# Save to file if filename is provided
if filename:
with open(filename, "w", encoding="utf-8") as f:
f.write(html_content)
return html_content
update_left_map(self, config)
¶
Update the left map configuration.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
config |
Dict[str, Any] |
New configuration for the left map |
required |
Source code in anymap/compare.py
def update_left_map(self, config: Dict[str, Any]) -> None:
"""Update the left map configuration.
Args:
config: New configuration for the left map
"""
self.left_map_config = config
self.call_js_method("updateLeftMap", config)
update_right_map(self, config)
¶
Update the right map configuration.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
config |
Dict[str, Any] |
New configuration for the right map |
required |
Source code in anymap/compare.py
def update_right_map(self, config: Dict[str, Any]) -> None:
"""Update the right map configuration.
Args:
config: New configuration for the right map
"""
self.right_map_config = config
self.call_js_method("updateRightMap", config)