voxcity.downloader.ocean

Ocean detection using OSM coastlines via Overpass API.

OSM handles oceans by the “absence of land” principle: 1. The renderer starts with a blue canvas 2. Land polygons (derived from natural=coastline) are drawn on top 3. Anything not covered by land is ocean

This module queries coastlines from Overpass API and determines land/ocean based on the coastline orientation rule: land is on the LEFT of the coastline.

For areas without coastlines, we check if the point is in the ocean using a simple heuristic based on nearby land features.

Attributes

Functions

get_land_polygon_for_area(rectangle_vertices[, use_cache])

Get the land polygon for a given area using OSM coastlines.

get_cache_path(→ pathlib.Path)

Generate a cache filename based on the bounding box and grid shape.

query_coastlines_from_overpass(→ Tuple[dict, bool])

Query coastline ways from Overpass API.

build_coastline_polygons(overpass_data, bbox)

Build land polygons by splitting bbox with coastlines.

is_polygon_land(polygon, coastline_segments)

Determine if a polygon is land based on coastline orientation.

check_if_area_is_ocean_via_land_features(→ bool)

Quick check: if an area has buildings/roads/land-use features, it's not pure ocean.

get_land_mask_from_coastlines(→ numpy.ndarray)

Create a boolean mask where True = land, False = ocean.

get_ocean_class_for_source(→ str)

Get the appropriate ocean/water class name for a given land cover source.

apply_ocean_mask_to_grid(→ numpy.ndarray)

Apply ocean detection to an existing land cover grid.

Module Contents

voxcity.downloader.ocean.CACHE_DIR
voxcity.downloader.ocean.get_land_polygon_for_area(rectangle_vertices: List[Tuple[float, float]], use_cache: bool = False)

Get the land polygon for a given area using OSM coastlines.

This is the main entry point for ocean detection. It queries coastlines from Overpass API and builds a polygon representing land areas.

Parameters:
  • rectangle_vertices – List of (lon, lat) tuples defining the area

  • use_cache – Whether to use disk cache (default False)

Returns:

Shapely Polygon/MultiPolygon representing land, or None if no coastlines found

voxcity.downloader.ocean.get_cache_path(rectangle_vertices: List[Tuple[float, float]], grid_shape: Tuple[int, int]) pathlib.Path

Generate a cache filename based on the bounding box and grid shape.

voxcity.downloader.ocean.query_coastlines_from_overpass(min_lat: float, min_lon: float, max_lat: float, max_lon: float, buffer_deg: float = 0.1, max_retries: int = 2) Tuple[dict, bool]

Query coastline ways from Overpass API.

Parameters:
  • min_lat – Bounding box

  • min_lon – Bounding box

  • max_lat – Bounding box

  • max_lon – Bounding box

  • buffer_deg – Buffer around bbox to catch coastlines that might affect the area

  • max_retries – Per-endpoint retry count for transient errors (e.g., 429/5xx)

Returns:

Tuple (data, ok) where data is the Overpass JSON response ({'elements': []} on failure) and ok is True only if the API actually returned a successful response. Callers should treat ok == False as “API failure” rather than “no coastlines in area”.

voxcity.downloader.ocean.build_coastline_polygons(overpass_data: dict, bbox: Tuple[float, float, float, float])

Build land polygons by splitting bbox with coastlines.

Algorithm: 1. Merge bbox boundary with all clipped coastlines to form a network 2. Use polygonize to create all possible polygons 3. For each polygon, determine if it’s land by checking the coastline direction

(land is on the LEFT of coastline when walking along it)

Returns:

Shapely polygon representing land area, or None if processing fails

voxcity.downloader.ocean.is_polygon_land(polygon, coastline_segments)

Determine if a polygon is land based on coastline orientation.

OSM Rule: Land is on the LEFT of the coastline direction.

Algorithm (orientation-match, robust to non-convex / harbor-shaped polygons):
  1. Force the polygon’s exterior to CCW orientation. With CCW orientation, the polygon’s interior lies on the LEFT of every exterior edge as you walk around it.

  2. Walk consecutive vertex pairs (a, b) of the exterior.

  3. For each edge whose midpoint lies on a coastline (within a tiny tolerance), look up the coastline segment containing that midpoint and compare its direction to the polygon edge direction via dot product:

    • dot > 0 -> edge follows coastline forward -> interior on LEFT of coastline -> LAND vote.

    • dot < 0 -> edge runs against coastline -> interior on RIGHT of coastline -> WATER vote.

  4. Majority vote across all coastline-coincident edges. Defaults to land on tie or when no coastline-coincident edges exist (e.g., a polygon bounded entirely by the bbox boundary).

voxcity.downloader.ocean.check_if_area_is_ocean_via_land_features(rectangle_vertices: List[Tuple[float, float]]) bool

Quick check: if an area has buildings/roads/land-use features, it’s not pure ocean.

Returns True if the area appears to be mostly ocean (few land features).

voxcity.downloader.ocean.get_land_mask_from_coastlines(rectangle_vertices: List[Tuple[float, float]], grid_shape: Tuple[int, int], use_cache: bool = True) numpy.ndarray

Create a boolean mask where True = land, False = ocean.

Uses Overpass API to query coastlines and determine land/ocean areas. Much faster than downloading the full 600MB land polygons file.

Parameters:
  • rectangle_vertices – List of (lon, lat) tuples defining the area

  • grid_shape – (rows, cols) of the output grid

  • use_cache – Whether to cache the result

Returns:

Boolean array where True = land, False = ocean

Return type:

np.ndarray

voxcity.downloader.ocean.get_land_mask_from_osm_land_polygons
voxcity.downloader.ocean.get_ocean_class_for_source(source: str) str

Get the appropriate ocean/water class name for a given land cover source.

voxcity.downloader.ocean.apply_ocean_mask_to_grid(grid: numpy.ndarray, rectangle_vertices: List[Tuple[float, float]], source: str = 'OpenStreetMap', ocean_class: str | None = None) numpy.ndarray

Apply ocean detection to an existing land cover grid.

Cells that are: 1. Currently set to the default class (e.g., ‘Developed space’) 2. Located in ocean areas (outside OSM land polygons)

Will be changed to the ocean/water class.

Parameters:
  • grid – Land cover grid (2D array of class names)

  • rectangle_vertices – Area coordinates

  • source – Land cover source name

  • ocean_class – Override for ocean class name (auto-detected if None)

Returns:

Updated grid with ocean areas classified as water