線的投影管線,以及 D3 地圖的裁剪與繞序約定)
d3-geo 球面幾何引擎從墨卡托點到地理經(jīng)線的投影管線以及 D3 地圖的裁剪與繞序約定【免費下載鏈接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:項目地址: https://gitcode.com/GitHub_Trending/d3/d3本文以 d3 官方文檔 docs/d3-geo.md 為主體系統(tǒng)講解 d3-geo 的核心設計為什么地圖投影不能只做“點變換”、球面幾何大圓線、自適應采樣如何進入投影管線、日界線切割與小圓裁剪、多邊形繞序約定以及 D3 使用的球面 GeoJSON 數(shù)據(jù)表示。讀完后你能理解 d3 投影 APIprojection/stream/shape/math/path的分工并能在 d3 v7當前倉庫 package.json 聲明d3-geo: ^3.1.1整體版本 7.9.0中正確構建、裁剪、旋轉一幅 SVG/Canvas 地圖。點變換地圖投影的最直觀形式地圖投影常被實現(xiàn)為點變換一個接收經(jīng)度 λ 與緯度 φ、返回平面上對應 x/y 位置的函數(shù)。以球面墨卡托投影單位弧度為例function mercator(lambda, phi) { const x lambda; const y Math.log(Math.tan(Math.PI / 4 phi / 2)); return [x, y]; }如果幾何體只由點構成這種方法是可行的。但對多邊形、折線這類離散幾何從球面投影到平面會引入新的難題。大圓線與自適應采樣球面多邊形的邊不是直線而是大圓線great circle 的線段即測地線。除了正射方位角投影gnomonic見 docs/d3-geo/azimuthal.md之外大圓線在幾乎所有投影下都會變成曲線因此精確投影要求沿每條弧線插值。d3 采用自適應采樣adaptive sampling來平衡精度與性能其思想借鑒自 Visvalingam 折線簡化算法當投影后的曲線偏離原始大圓超過閾值時才在中間補充采樣點。這個閾值由投影的precision方法控制從 docs/d3-geo/projection.md 可以看到projection.precision(p)設置自適應重采樣的閾值單位是像素對應 Douglas–Peucker 距離默認值為Math.sqrt(0.5) ? 0.70710…。也就是說你幾乎不需要手動沿大圓插值——把 LineString 傳給d3.geoPath(projection)時投影內(nèi)部會按上述規(guī)則自動沿大圓弧密集采樣docs/d3-geo/shape.md 中的提示也印證了這一點大圓的中間點由投影的測地線插值完成。球面與平面的拓撲差異裁剪與日界線切割多邊形和折線的投影還必須處理球面與平面之間的拓撲差異。從文檔脈絡看d3 把裁剪分成兩級均定義在 docs/d3-geo/projection.mdpreclip球面裁剪在投影之前、以球面坐標進行。兩類典型用途clipAngle(angle)按投影中心的小圓裁剪。angle為度數(shù)設為null時切換為日界線切割antimeridian cutting模式。默認值為null。geoClipAntimeridian把跨越日界線的線/多邊形切成左右兩半常用于世界地圖避免“繞地球一圈”的偽影。geoClipCircle(angle)生成以小圓為界的裁剪流。postclip平面裁剪在投影之后的平面上進行用于把有界的投影約束到視口矩形內(nèi)clipExtent([[x0, y0], [x1, y1]])像素單位的視口裁剪范圍null表示不裁剪默認nullgeoClipRectangle(x0, y0, x1, y1)生成矩形裁剪流。文檔同時提醒小圓裁剪clipAngle與視口裁剪clipExtent相互獨立可以疊加使用。以正射投影geoOrthographic為例文檔中的示例慣用fitExtent加precision(0.2)來控制球體輪廓的采樣精度可參考 docs/d3-geo/shape.md 頂部的示例參數(shù)。繞序約定多邊形的“內(nèi)側”是哪一邊球面多邊形還需要繞序約定winding order來確定多邊形的哪一側是內(nèi)部小于半球的多邊形其外環(huán)必須是順時針大于半球的多邊形例如海洋占超過半個地球其外環(huán)必須是逆時針表示孔洞的內(nèi)環(huán)繞序與外環(huán)相反。::: warning 注意 D3 的繞序約定被 TopoJSON 與 ESRI shapefile 沿用但它與 GeoJSON RFC 7946 的約定正好相反標準 GeoJSON 要求外環(huán)逆時針。另外標準 GeoJSON WGS84 使用的是平面等距圓柱坐標而非球面坐標因此直接投影 WGS84 數(shù)據(jù)可能出現(xiàn)日界線切割殘留需要額外“縫合”stitching處理。這是從 docs/d3-geo.md 的官方警告繼承的關鍵工程細節(jié)處理全球級數(shù)據(jù)海洋、大陸輪廓時必須特別注意。 :::球面 GeoJSONd3-geo 的數(shù)據(jù)表示D3 使用球面 GeoJSON表示 JavaScript 中的地理要素配合三類幾何能力投影docs/d3-geo/projection.md把球面幾何投影到平面內(nèi)置方位角、圓錐、圓柱三大類標準投影路徑docs/d3-geo/path.mdd3.geoPath(projection)把 GeoJSON 生成 SVG path data 或渲染到 Canvas流docs/d3-geo/stream.md不物化中間表示通過函數(shù)調用直接變換球面或平面幾何。數(shù)據(jù)準備方面文檔給出的工具鏈是Shapefile 轉 GeoJSON使用 shapefile 工具包的shp2json命令TopoJSONGeoJSON 的更緊湊擴展編碼拓撲信息。當前倉庫的文檔組件 docs/components/WorldMap.vue 正是這樣加載世界輪廓的import * as topojson from topojson-client; // 從 world-atlas 的 TopoJSON 中提取 GeoJSON Feature d3.json(.../land-110m.json) .then((world) topojson.feature(world, world.objects.land));這解釋了 d3-geo 文檔為何反復以 TopoJSON 作為數(shù)據(jù)入口geoPath消費的是topojson.feature轉換后的標準 GeoJSON。投影 API 精要一次投影發(fā)生了什么projection.stream(stream)見 docs/d3-geo/projection.md揭示了一次完整投影的管線順序輸入幾何先轉換為弧度 → 三軸旋轉 → 小圓裁剪或日界線切割 → 以自適應重采樣投影到平面 → 縮放與平移。據(jù)此常用的投影方法可歸為四組變換組方法說明默認值scale(k)比例因子與投影點間距線性相關但不同投影間絕對值不可比因投影而異translate([tx, ty])平移偏移決定中心點的像素坐標[480, 250]960×500 畫布中心center([λ, φ])投影中心度[0, 0]rotate([λ, φ, γ])三軸球面旋轉偏航、俯仰、翻滾度γ缺省為 0[0, 0, 0]angle(θ)/reflectX/reflectY投影后平面旋轉與軸向反射0° / false / falseprecision(p)自適應重采樣閾值像素√0.5裁剪組clipAngle、clipExtent、preclip、postclip見上文“拓撲差異”一節(jié)。適配組fitExtent(extent, object)設置 scale 與 translate使給定 GeoJSON 對象居中適配指定矩形計算包圍盒時使用有效 scale 為 150 的 precision忽略 clipExtentfitSize(size, object)等價于fitExtent([[0, 0], [width, height]], object)fitWidth/fitHeight按對象寬高比自動推算另一維度。文檔中的實例新澤西州 State Plane 投影var projection d3.geoTransverseMercator() .rotate([74 30 / 60, -38 - 50 / 60]) .fitExtent([[20, 20], [940, 480]], nj);點/逆投影projection(point)接受度數(shù)輸入的[經(jīng)度, 緯度]返回像素[x, y]或null被裁掉時可逆投影另有invert(point)。流Stream幾何變換的同步接口docs/d3-geo/stream.md 定義了流的接口流不構造中間幾何對象僅靠方法調用傳遞幾何從而最小化開銷。流天然是有狀態(tài)的——point的含義取決于它是否處于lineStart之內(nèi)線又靠polygonStart與環(huán)區(qū)分雖然叫“流”這些調用目前是同步的。必需方法point(x, y, z)、lineStart()、lineEnd()、polygonStart()、polygonEnd()、sphere()。入口是d3.geoStream(object, stream)注意流接口只描述幾何Feature 的properties對流不可見。一個細節(jié)與繞序直接相關lineEnd()在環(huán)內(nèi)不重復發(fā)出閉合坐標。輸入{ type: Polygon, coordinates: [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]] }實際觸發(fā)的調用序列是stream.polygonStart(); stream.lineStart(); stream.point(0, 0); stream.point(0, 1); stream.point(1, 1); stream.point(1, 0); stream.lineEnd(); stream.polygonEnd();球面數(shù)學與形狀生成docs/d3-geo/math.md 提供底層球面工具與投影后的平面測量path.area、path.bounds等一一對應geoArea(object)球面面積單位球面度steradiangeoBounds(object)球面包圍盒[[left, bottom], [right, top]]單位度geoCentroid(object)球面質心geoDistance(a, b)/geoLength(object)兩點大圓弧距 / 幾何體大圓弧長單位弧度多邊形為外環(huán)加內(nèi)環(huán)周長geoInterpolate(a, b)返回沿過 a、b 大圓弧的插值器t ∈ [0, 1]對跖點時取任意大圓弧geoContains(object, point)球面點包含測試geoRotation(angles)與projection.rotate同參數(shù)的旋轉函數(shù)支持invert。docs/d3-geo/shape.md 則返回球面 GeoJSON 供geoPath消費geoGraticule()經(jīng)緯網(wǎng)格默認 ±80° 緯度內(nèi)每 10° 一條線、極地 90° 經(jīng)線精度默認 2.5°、geoGraticule10()、geoCircle()等。倉庫的 WorldMap.vue 里const graticule d3.geoGraticule10();就是最小用法。實戰(zhàn)倉庫中的世界地圖組件docs/components/WorldMap.vue 是一個可直接借鑒的完整示例加載 TopoJSON 陸地輪廓用d3.geoPath(projection)依次繪制球體輪廓{type: Sphere}、經(jīng)緯網(wǎng)格、要素本體并通過指針拖動實時修改projection.rotate()的第一軸重繪直觀演示了“因為 D3 使用球面幾何表示數(shù)據(jù)所以任何投影都可以通過旋轉得到任意視角”這一文檔論斷。核心繪制邏輯const path d3.geoPath(projection); function update() { svg.selectAll([nameoutline]).attr(d, path({type: Sphere})); svg.selectAll([namegraticule]).attr(d, path(graticule)); svg.selectAll([namefeature]).attr(d, path(feature)); }自定義投影raw projection 與 geoProjectiondocs/d3-geo/projection.md 的“Raw projections”一節(jié)定義了擴展機制raw projection 是接收弧度[λ, φ]、返回單位坐標[x, y]的點變換函數(shù)無需自己處理縮放、平移與旋轉這些由scale/translate/rotate自動完成。d3.geoProjection(project)由 raw projection 構造標準投影若project暴露invert生成的投影也可逆。var mercator d3.geoProjection(function(x, y) { return [x, Math.log(Math.tan(Math.PI / 4 y / 2))]; });d3.geoProjectionMutator(factory)為帶參數(shù)的投影族如雙標準緯線的圓錐投影構造可變投影通過mutate(phi0, phi1)替換內(nèi)部 raw projectionfunction conicCustom() { var phi0 29.5, phi1 45.5, mutate d3.geoProjectionMutator(conicFactory), projection mutate(phi0, phi1); projection.parallels function(_) { return arguments.length ? mutate(phi0 _[0], phi1 _[1]) : [phi0, phi1]; }; return projection; }d3.geoTransform(methods)用任意方法集合定義幾何變換未定義的方法透傳是“廣義投影”可傳給path.projection但只實現(xiàn)投影方法的一個子集d3.geoIdentity()恒等變換用于對平面幾何做縮放、平移與裁剪實現(xiàn)了 scale/translate/fit*/clipExtent/angle/reflectX/reflectY。小結從文檔骨架到工程結論回看 docs/d3-geo.md 的主線可以提煉為四條工程結論投影 ≠ 點變換大圓線必須沿弧插值d3 用自適應采樣默認閾值√0.5像素自動完成precision是你的精度/性能旋鈕裁剪分兩級球面級clipAngle/日界線切割preclip與平面級clipExtentpostclip相互獨立按需組合繞序有約定小于半球外環(huán)順時針、大于半球逆時針、內(nèi)環(huán)相反——與 GeoJSON RFC 7946 相反全球級數(shù)據(jù)海洋、跨日界線邊界尤其要核對數(shù)據(jù)用球面 GeoJSON/TopoJSON 表示配合geoPath、geoStream與三軸rotate即可在任意投影上自由切換視角如 WorldMap.vue 所示。配套閱讀Paths、Projections、Streams、Shapes、Spherical math以及三大投影類別文檔 azimuthal、conic、cylindrical。【免費下載鏈接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:項目地址: https://gitcode.com/GitHub_Trending/d3/d3創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考