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.
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 crossingClips 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);
}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]]);
}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);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));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
}
});
}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);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);