Map Click Polygon Intersection Example¶
This notebook demonstrates how to:
- Observe map clicks using the
clicked
attribute - Find intersecting polygons from a GeoJSON file
- Load the intersecting polygon into the draw control
When you click on the map, it will check if the click point intersects any building polygons from buildings.geojson
and automatically add the intersecting building to the draw control.
In [1]:
Copied!
import json
import geopandas as gpd
from shapely.geometry import Point
from anymap.maplibre import MapLibreMap
import ipywidgets as widgets
from IPython.display import display
import json
import geopandas as gpd
from shapely.geometry import Point
from anymap.maplibre import MapLibreMap
import ipywidgets as widgets
from IPython.display import display
Load Building Data¶
First, let's load the buildings.geojson file which contains building polygons from the Washington state area.
In [2]:
Copied!
# Load buildings data
url = "https://github.com/opengeos/datasets/releases/download/places/las_vegas_buildings.geojson"
buildings_gdf = gpd.read_file(url)
print(f"Loaded {len(buildings_gdf)} building polygons")
print(f"Buildings CRS: {buildings_gdf.crs}")
print(f"Buildings bounds: {buildings_gdf.total_bounds}")
# Display first few buildings
buildings_gdf.head()
# Load buildings data
url = "https://github.com/opengeos/datasets/releases/download/places/las_vegas_buildings.geojson"
buildings_gdf = gpd.read_file(url)
print(f"Loaded {len(buildings_gdf)} building polygons")
print(f"Buildings CRS: {buildings_gdf.crs}")
print(f"Buildings bounds: {buildings_gdf.total_bounds}")
# Display first few buildings
buildings_gdf.head()
Loaded 540 building polygons Buildings CRS: EPSG:4326 Buildings bounds: [-115.20788648 36.11887507 -115.19950696 36.12249608]
Out[2]:
id | height | geometry | |
---|---|---|---|
0 | 08b2986b81b34fff0200120c6aabf673 | 2.539160 | POLYGON ((-115.20758 36.11913, -115.20754 36.1... |
1 | 08b2986b81b34fff0200ce30fa61746d | 3.209284 | POLYGON ((-115.20756 36.11929, -115.20776 36.1... |
2 | 08b2986b81b34fff0200b898b488ab8b | 2.691431 | POLYGON ((-115.20759 36.11932, -115.20759 36.1... |
3 | 08b2986b81b34fff02004b957b4cd490 | 2.795901 | POLYGON ((-115.20762 36.11953, -115.20775 36.1... |
4 | 08b2986b81869fff020091d66f33d09b | 2.649764 | POLYGON ((-115.20727 36.11898, -115.20727 36.1... |
In [3]:
Copied!
geojson = buildings_gdf.__geo_interface__
geojson = buildings_gdf.__geo_interface__
In [4]:
Copied!
point_gdf = buildings_gdf.copy()
point_gdf["geometry"] = point_gdf.to_crs("EPSG:3857").centroid.to_crs("EPSG:4326")
point_gdf = buildings_gdf.copy()
point_gdf["geometry"] = point_gdf.to_crs("EPSG:3857").centroid.to_crs("EPSG:4326")
In [5]:
Copied!
point_gdf.head()
point_gdf.head()
Out[5]:
id | height | geometry | |
---|---|---|---|
0 | 08b2986b81b34fff0200120c6aabf673 | 2.539160 | POINT (-115.20765 36.11914) |
1 | 08b2986b81b34fff0200ce30fa61746d | 3.209284 | POINT (-115.20766 36.11925) |
2 | 08b2986b81b34fff0200b898b488ab8b | 2.691431 | POINT (-115.20766 36.11936) |
3 | 08b2986b81b34fff02004b957b4cd490 | 2.795901 | POINT (-115.20769 36.11948) |
4 | 08b2986b81869fff020091d66f33d09b | 2.649764 | POINT (-115.20739 36.11901) |
In [6]:
Copied!
point_geojson = point_gdf.__geo_interface__
point_geojson = point_gdf.__geo_interface__
In [7]:
Copied!
m = MapLibreMap(style="liberty", center=[-115.20350534, 36.1209240], zoom=16)
m.add_geojson_layer(
"building_point",
point_geojson,
layer_type="circle",
paint={"circle-color": "#0000ff", "circle-radius": 5},
)
m.add_draw_control()
m
m = MapLibreMap(style="liberty", center=[-115.20350534, 36.1209240], zoom=16)
m.add_geojson_layer(
"building_point",
point_geojson,
layer_type="circle",
paint={"circle-color": "#0000ff", "circle-radius": 5},
)
m.add_draw_control()
m
Out[7]:
Create Interactive Map¶
Create a map centered in the Washington state area where the buildings are located, and add a draw control.
In [8]:
Copied!
# Create output widget to show messages
output = widgets.Output()
# Create output widget to show messages
output = widgets.Output()
In [9]:
Copied!
def on_map_clicked(change):
"""Handle map click events."""
clicked_coords = change["new"]
with output:
output.clear_output()
if not clicked_coords:
return
lng = clicked_coords.get("lng")
lat = clicked_coords.get("lat")
print(f"Map clicked at: {lng:.6f}, {lat:.6f}")
# Create point from click coordinates
click_point = Point(lng, lat)
# First check if there's already a polygon in draw data that intersects this point
existing_draw_data = m.get_draw_data()
existing_features = existing_draw_data.get("features", [])
# Check if click point intersects any existing drawn features
for feature in existing_features:
try:
from shapely.geometry import shape
existing_geom = shape(feature["geometry"])
if existing_geom.intersects(click_point):
print(
f"⚠️ Click point already intersects an existing drawn feature"
)
print(
f" Feature: {feature.get('properties', {}).get('name', 'Unnamed')}"
)
print(f" Skipping search for new buildings")
return
except Exception as e:
# Skip if there's an issue with the geometry
continue
# Find intersecting building in the buildings dataset
building = find_intersecting_building(clicked_coords)
if building is not None:
print(f"✅ Found intersecting building!")
print(f" Building ID: {building['id']}")
print(f" Place ID: {building['height']}")
# Convert building geometry to GeoJSON
building_geojson = {
"type": "Feature",
"geometry": building.geometry.__geo_interface__,
"properties": {
"buildingId": str(building["id"]),
"height": str(building["height"]),
"clicked_at": f"{lng:.6f}, {lat:.6f}",
},
}
# Add building to draw control
m.add_draw_data(building_geojson)
print(f"📍 Added building to draw control")
else:
print(f"❌ No building found at this location")
# Observe changes to the clicked attribute
m.observe(on_map_clicked, names="clicked")
display(output)
def on_map_clicked(change):
"""Handle map click events."""
clicked_coords = change["new"]
with output:
output.clear_output()
if not clicked_coords:
return
lng = clicked_coords.get("lng")
lat = clicked_coords.get("lat")
print(f"Map clicked at: {lng:.6f}, {lat:.6f}")
# Create point from click coordinates
click_point = Point(lng, lat)
# First check if there's already a polygon in draw data that intersects this point
existing_draw_data = m.get_draw_data()
existing_features = existing_draw_data.get("features", [])
# Check if click point intersects any existing drawn features
for feature in existing_features:
try:
from shapely.geometry import shape
existing_geom = shape(feature["geometry"])
if existing_geom.intersects(click_point):
print(
f"⚠️ Click point already intersects an existing drawn feature"
)
print(
f" Feature: {feature.get('properties', {}).get('name', 'Unnamed')}"
)
print(f" Skipping search for new buildings")
return
except Exception as e:
# Skip if there's an issue with the geometry
continue
# Find intersecting building in the buildings dataset
building = find_intersecting_building(clicked_coords)
if building is not None:
print(f"✅ Found intersecting building!")
print(f" Building ID: {building['id']}")
print(f" Place ID: {building['height']}")
# Convert building geometry to GeoJSON
building_geojson = {
"type": "Feature",
"geometry": building.geometry.__geo_interface__,
"properties": {
"buildingId": str(building["id"]),
"height": str(building["height"]),
"clicked_at": f"{lng:.6f}, {lat:.6f}",
},
}
# Add building to draw control
m.add_draw_data(building_geojson)
print(f"📍 Added building to draw control")
else:
print(f"❌ No building found at this location")
# Observe changes to the clicked attribute
m.observe(on_map_clicked, names="clicked")
display(output)
In [10]:
Copied!
def find_intersecting_building(click_coords):
"""Find building polygon that intersects with click point."""
if not click_coords:
return None
lng = click_coords.get("lng")
lat = click_coords.get("lat")
if lng is None or lat is None:
return None
# Create point from click coordinates
click_point = Point(lng, lat)
# Find intersecting buildings
intersecting = buildings_gdf[buildings_gdf.geometry.intersects(click_point)]
if len(intersecting) > 0:
# Return the first intersecting building
return intersecting.iloc[0]
return None
def find_intersecting_building(click_coords):
"""Find building polygon that intersects with click point."""
if not click_coords:
return None
lng = click_coords.get("lng")
lat = click_coords.get("lat")
if lng is None or lat is None:
return None
# Create point from click coordinates
click_point = Point(lng, lat)
# Find intersecting buildings
intersecting = buildings_gdf[buildings_gdf.geometry.intersects(click_point)]
if len(intersecting) > 0:
# Return the first intersecting building
return intersecting.iloc[0]
return None