Line-of-Sight Visibility Analysis¶
This notebook demonstrates how to compute visible areas from a viewpoint or multiple points using:
Fast approximate visibility (suitable for quick overviews)
Accurate visibility analysis (respecting occlusions)
Parallelized visibility from multiple locations
# Import necessary libraries
from objectnat import get_visibility, get_visibility_accurate
import geopandas as gpd
from shapely.geometry import Point
1. Load Obstacle Data¶
Load a building layer representing line-of-sight obstacles. This dataset is used to compute occlusions in the urban environment.
# Load buildings as obstacles
obstacles = gpd.read_parquet('examples_data/buildings.parquet')
2. Define Viewpoint¶
Specify the observation point from which visibility will be computed. Coordinates must match the CRS of the obstacles dataset.
# Define a single viewpoint in WGS 84
point_from = gpd.GeoDataFrame(geometry=[Point(30.2312112, 59.9482336)], crs=4326)
radius = 500
3. Fast Visibility Calculation¶
Compute visibility using a fast, approximate method. This is suitable for real-time feedback or exploratory analysis. Note: May produce artifacts (e.g., visibility behind walls).
# Fast visibility (less accurate)
result_fast = get_visibility(point_from, obstacles, view_distance=radius)
# Computes visibility polygon from the viewpoint with a 500-meter radius using low-resolution simulation.
4. Accurate Visibility Calculation¶
Use the more precise get_visibility_accurate() function, which simulates occlusion and limited sightlines.
This method is slower but produces more reliable results.
# Accurate visibility (includes occlusion and bottleneck modeling)
result_accurate = get_visibility_accurate(point_from, obstacles, view_distance=radius)
# Simulates realistic visibility by tracing around buildings and respecting occlusions.
5. Visualization¶
Visualize obstacles and both visibility methods
from shapely import Point
from shapely import Point
# Red area = False Positive (Simple method only)
simple_only = gpd.overlay(result_fast, result_accurate, how="difference")
# Green area = Advantage (Accurate method only)
accurate_only = gpd.overlay(result_accurate, result_fast, how="difference")
# Blue area = Agreement Area (both methods overlap)
common_area = gpd.overlay(result_fast, result_accurate, how="intersection")
# Light gray = Obstacles (context layer)
m = common_area.explore(color='blue', tiles='CartoDB positron')
obstacles.explore(m=m, color='lightgray')
simple_only.explore(m=m, color='red')
accurate_only.explore(m=m, color='green')
6. Visibility from Multiple Viewpoints (Parallelized)¶
For batch visibility simulation, use get_visibilities_from_points() with multiple locations.
The computation is performed in parallel using multiprocessing.
from objectnat import get_visibilities_from_points
obstacles = gpd.read_parquet('examples_data/buildings.parquet')
points = gpd.GeoDataFrame(
geometry=[Point(30.27060176, 59.93546846), Point(30.29586657, 59.94410918), Point(30.2312112, 59.9482336)],
crs=4326)
local_crs = obstacles.estimate_utm_crs()
obstacles.to_crs(local_crs, inplace=True)
points.to_crs(local_crs, inplace=True)
result = get_visibilities_from_points(points, obstacles, 500)
# Calculating visibility from each point in the 'points' GeoDataFrame with a view distance of 500 units.
# This method uses multiprocessing for better performance when dealing with multiple points.
gpd.GeoDataFrame(geometry=result, crs=local_crs).explore()
Calculate visibility catchment area (multiprocessing)¶
import pandas as pd
import geopandas as gpd
from objectnat import calculate_visibility_catchment_area
# Load data for buildings, points, woods, and bridges
builds = gpd.read_file('builds.geojson').to_crs(32636)
points = gpd.read_file('distributed_points.geojson').to_crs(32636)
woods = gpd.read_file('woods.geojson').to_crs(32636)
bridges = gpd.read_file('bridges.geojson').to_crs(32636)
view_dist = 1000
# Setting the visibility distance (catchment radius) to 1000 units.
obstacles = gpd.GeoDataFrame(pd.concat([builds, woods, bridges], ignore_index=True), geometry='geometry',
crs=32636)
# Combining the GeoDataFrames for buildings, woods, and bridges into a single GeoDataFrame that serves as obstacles
# to be considered in the visibility calculation.
res = calculate_visibility_catchment_area(points, obstacles, view_dist)
# Calculating the visibility catchment area for the given points, considering the obstacles and the view distance.
# The result is a GeoDataFrame containing the catchment areas.
res.explore(
column="factor_normalized",
categorical=False,
cmap="plasma",
legend=True,
)
# Visualizing the catchment areas on an interactive map, using the 'factor_normalized' column to color the areas
# with a 'plasma' colormap. A legend is displayed to show the range of values.