or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

clipping.mdgeographic-calculations.mdindex.mdpath.mdprojections.mdshapes.mdtransformations.md
tile.json

clipping.mddocs/

Clipping

Advanced clipping algorithms for constraining geometry to specific boundaries. d3-geo provides clipping functions for antimeridian handling, extent clipping, and circular clipping, essential for proper geographic visualization and projection boundary management.

Capabilities

Antimeridian Clipping

Clips geometry that crosses the antimeridian (180° longitude), preventing visual artifacts in map projections.

/**
 * Clips geometry to the antimeridian (±180° longitude)
 * @param stream - Output stream to receive clipped geometry
 * @returns Transform stream that clips antimeridian-crossing geometry
 */
function geoClipAntimeridian(stream: Transform): Transform;

Usage Examples:

import { geoClipAntimeridian, geoMercator, geoPath } from "d3-geo";

// Antimeridian clipping is automatically applied by most projections
const projection = geoMercator();
// projection.preclip() returns geoClipAntimeridian by default

// For custom projections, apply antimeridian clipping
const customProjection = geoProjection(customProjectionFunction)
  .preclip(geoClipAntimeridian);

// Example: Pacific-centered map that handles date line crossing
const pacificProjection = geoMercator()
  .center([180, 0])  // Center on antimeridian
  .rotate([-180, 0]); // Rotate to place Pacific in center

// Geometry crossing the date line will be properly clipped
const transPacificRoute = {
  type: "LineString",
  coordinates: [[170, 50], [-170, 50]] // Crosses antimeridian
};

const path = geoPath(pacificProjection);
const clippedPath = path(transPacificRoute); // Properly handles crossing

Circle Clipping

Clips geometry to a small circle of specified radius, useful for creating circular map views.

/**
 * Creates a clipping function for a small circle of the specified radius
 * @param radius - Clipping radius in degrees
 * @returns Clipping function that clips geometry to the specified circle
 */
function geoClipCircle(radius: number): (stream: Transform) => Transform;

Usage Examples:

import { geoClipCircle, geoOrthographic, geoPath } from "d3-geo";

// Create circular clipping for orthographic projection
const projection = geoOrthographic()
  .scale(250)
  .clipAngle(90); // Equivalent to .preclip(geoClipCircle(90))

// Custom circular viewport
const circularProjection = geoMercator()
  .preclip(geoClipCircle(60)); // Clip to 60-degree radius

// Interactive zoom with circular clipping
function createZoomableGlobe(radius) {
  return geoOrthographic()
    .scale(250)
    .preclip(geoClipCircle(radius));
}

// Animate clipping radius
function animateClipRadius(projection, fromRadius, toRadius, duration) {
  const interpolate = d3.interpolate(fromRadius, toRadius);
  const startTime = Date.now();
  
  function frame() {
    const elapsed = Date.now() - startTime;
    const t = Math.min(elapsed / duration, 1);
    const currentRadius = interpolate(t);
    
    projection.preclip(geoClipCircle(currentRadius));
    render(); // Re-render the map
    
    if (t < 1) requestAnimationFrame(frame);
  }
  
  requestAnimationFrame(frame);
}

Extent Clipping (Deprecated)

Clips geometry to a rectangular extent. Note: This function is deprecated - use geoIdentity().clipExtent() instead.

/**
 * Creates a clipping function for the specified rectangular extent
 * @param extent - Clipping extent as [[x0, y0], [x1, y1]]
 * @returns Clipping function that clips geometry to the specified extent
 * @deprecated Use geoIdentity().clipExtent() instead
 */
function geoClipExtent(extent: [[number, number], [number, number]]): (stream: Transform) => Transform;

Usage Examples (Deprecated - Use Alternative):

import { geoIdentity, geoMercator } from "d3-geo";

// ❌ Deprecated approach
// const clipper = geoClipExtent([[0, 0], [960, 500]]);

// ✅ Recommended approach
const projection = geoIdentity()
  .clipExtent([[0, 0], [960, 500]])
  .scale(1);

// For projected coordinates, use postclip on the projection
const mercator = geoMercator()
  .postclip(geoIdentity().clipExtent([[0, 0], [960, 500]]));

// Clip to viewport bounds
function clipToViewport(width, height) {
  return geoIdentity().clipExtent([[0, 0], [width, height]]);
}

Rectangle Clipping

Clips geometry to a rectangular area defined by coordinate bounds.

/**
 * Creates a clipping function for the specified rectangular coordinates
 * @param x0 - Left boundary
 * @param y0 - Top boundary  
 * @param x1 - Right boundary
 * @param y1 - Bottom boundary
 * @returns Clipping function that clips geometry to the specified rectangle
 */
function geoClipRectangle(x0: number, y0: number, x1: number, y1: number): (stream: Transform) => Transform;

Usage Examples:

import { geoClipRectangle, geoMercator, geoPath } from "d3-geo";

// Clip to specific coordinate bounds
const rectangleClipper = geoClipRectangle(-10, 40, 10, 60); // Europe bounds

// Apply to projection
const projection = geoMercator()
  .postclip(rectangleClipper);

// Create regional view
function createRegionalProjection(bounds) {
  const [[west, south], [east, north]] = bounds;
  return geoMercator()
    .postclip(geoClipRectangle(west, south, east, north))
    .fitExtent([[0, 0], [960, 500]], {
      type: "Polygon",
      coordinates: [[[west, south], [east, south], [east, north], [west, north], [west, south]]]
    });
}

// Example: Clip to continental US bounds
const usBounds = [[-125, 25], [-65, 50]];
const usProjection = createRegionalProjection(usBounds);

Advanced Clipping Techniques

Custom Clipping Functions

import { geoTransform } from "d3-geo";

// Create a custom triangular clipper
function createTriangleClipper(vertices) {
  const [[x0, y0], [x1, y1], [x2, y2]] = vertices;
  
  return function(stream) {
    return geoTransform({
      point(x, y) {
        // Point-in-triangle test
        const denom = (y1 - y2) * (x0 - x2) + (x2 - x1) * (y0 - y2);
        const a = ((y1 - y2) * (x - x2) + (x2 - x1) * (y - y2)) / denom;
        const b = ((y2 - y0) * (x - x2) + (x0 - x2) * (y - y2)) / denom;
        const c = 1 - a - b;
        
        if (a >= 0 && b >= 0 && c >= 0) {
          stream.point(x, y);
        }
      }
    }).stream(stream);
  };
}

// Apply custom clipper
const triangleVertices = [[0, 0], [960, 0], [480, 500]];
const projection = geoMercator()
  .postclip(createTriangleClipper(triangleVertices));

Dynamic Clipping

import { geoClipCircle, geoOrthographic } from "d3-geo";

// Create projection with dynamic clipping
const projection = geoOrthographic().scale(250);

// Function to update clipping based on zoom level
function updateClipping(zoomLevel) {
  const radius = Math.max(30, 90 - zoomLevel * 10);
  projection.preclip(geoClipCircle(radius));
}

// Mouse-based circular clipping
function createMouseClipping(svg, projection) {
  svg.on("mousemove", function(event) {
    const [mouseX, mouseY] = d3.pointer(event);
    const point = projection.invert([mouseX, mouseY]);
    
    if (point) {
      projection.center(point);
      render(); // Re-render map
    }
  });
}

Combining Multiple Clippers

import { geoClipCircle, geoClipRectangle, geoMercator } from "d3-geo";

// Combine circle and rectangle clipping
function createCombinedClipper(circleRadius, rectBounds) {
  const circleClipper = geoClipCircle(circleRadius);
  const [[x0, y0], [x1, y1]] = rectBounds;
  const rectClipper = geoClipRectangle(x0, y0, x1, y1);
  
  return function(stream) {
    return circleClipper(rectClipper(stream));
  };
}

// Apply combined clipping
const combinedClipper = createCombinedClipper(45, [[-10, -10], [10, 10]]);
const projection = geoMercator()
  .preclip(combinedClipper);

Transform Interface

All clipping functions work with the transform stream interface:

interface Transform {
  /**
   * Called for each point in the geometry
   * @param x - X coordinate
   * @param y - Y coordinate
   */
  point?(x: number, y: number): void;
  
  /**
   * Called at the start of each line string
   */
  lineStart?(): void;
  
  /**
   * Called at the end of each line string
   */
  lineEnd?(): void;
  
  /**
   * Called at the start of each polygon
   */
  polygonStart?(): void;
  
  /**
   * Called at the end of each polygon
   */
  polygonEnd?(): void;
  
  /**
   * Called for the sphere (entire globe)
   */
  sphere?(): void;
}

Usage with Custom Streams:

import { geoClipAntimeridian } from "d3-geo";

// Create a custom stream that logs clipped points
const loggingStream = {
  point(x, y) {
    console.log(`Clipped point: ${x}, ${y}`);
  },
  lineStart() {
    console.log("Line start");
  },
  lineEnd() {
    console.log("Line end");
  }
};

// Apply clipping to the logging stream
const clippedStream = geoClipAntimeridian(loggingStream);

// Stream geometry through the clipper
geoStream(someGeometry, clippedStream);