主题
显示投影几何
字数统计: 3.6k 字
阅读时长: 约 10 分钟
当前版本: 4.29
了解在不同投影下几何的显示。
几何投影变换会将几何形状的节点从一个坐标系(空间参考)变换到另一坐标系。例如,地理坐标系(如 WGS84 (4326))投影到投影坐标系(如 World Sinusoidal (54008))。
每个投影包含以下参数之一:面积、角度或方向。根据应用程序的要求选择对应的坐标系。例如,如果数据是以北极为中心,则通常不会使用 Web Mercator (3857) 空间参考,因为此投影无法正确表示极点要素,存在大面积失真。可以使用 North Pole Gnomonic (102034) 空间参考,因为它保真了北极周围的区域。
在本教程中,我们使用投影引擎,展示列表中选择不同的空间参考来投影 GeoJSON 要素。地图中有一个中心点和缓冲区图形的示例图形。
先决条件
此教程没有先决条件。
步骤
创建新 Pen
- 转至 CodePen,为您的制图应用程序创建新 Pen。
添加 HTML 元素
在 CodePen > HTML 中,添加 HTML 和 CSS,创建包含
viewDiv
和wkid
元素的页面。viewDiv
元素用于显示地图,设置其宽高,在浏览器中满屏显示。wkid
设置为Web Mercator
和WGS84
。创建<script>
和<link>
标签,引入 CSS 和 JS 库。注: CodePen 中是不需要输入
<!DOCTYPE html>
标签的。但如果您要在其他编辑器或是本地服务器上运行页面,需要手动在 HTML 页面顶部添加该标签。html<html> <head> <meta charset="utf-8" /> <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" /> <title>Display projected geometries</title> <style> html, body, #viewDiv { padding: 0; margin: 0; height: 100%; width: 100%; background-color: \#ffffff; } #wkid { position: absolute; top: 15px; right: 15px; width: 220px; font-family: "Avenir Next"; font-size: 1em; } </style> <link rel="stylesheet" href="https://js.geoscene.cn/4.29/geoscene/themes/light/main.css" /> <script src="https://js.geoscene.cn/4.29"></script> </head> <body> <div id="viewDiv"></div> <select id="wkid" class="geoscene-widget geoscene-select"> <option value="3857" disabled>Select a projection</option> <optgroup label="Equidistant (maintain length)"> <option value="4326" selected>WGS84 (GCS) -> pseudo Plate Carrée (Cylindrical)</option> </optgroup> <optgroup label="Compromise (distort all)"> <option value="3857">Web Mercator Auxiliary Sphere (Cylindrical)</option> </optgroup> </select> </body> </html>
添加模块
在
require
语句中,添加模块。更多信息
GeoScene Maps SDK for JavaScript 提供了 AMD 模块和 ES 模块,本教程中我们是以 AMD 为例。指定
"geoscene/Map"
来加载 Map 模块。加载模块后,它们将作为参数 (例如,Map
) 传递给回调函数,以便在应用程序中使用。保持模块引用和回调参数的顺序相同是很重要的。有关不同类型模块的更多信息,请访问工具指南主题。html<script> require([ "geoscene/views/MapView", "geoscene/Map", "geoscene/request", "geoscene/geometry/SpatialReference", "geoscene/Graphic", "geoscene/geometry/Point", "geoscene/geometry/Polyline", "geoscene/layers/GeoJSONLayer", "geoscene/geometry/geometryEngine", "geoscene/geometry/projection", ], ( MapView, Map, geosceneRequest, SpatialReference, Graphic, Point, Polyline, GeoJSONLayer, geometryEngine, projection ) => { }); </script>
定义全局变量和样式
定义一个代表空间参考的全局变量。定义一个多边形和一个点样式,在后续步骤中用于创建缓冲区样式。
定义
spatialReference
和view
变量。js"geoscene/views/MapView", "geoscene/Map", "geoscene/request", "geoscene/geometry/SpatialReference", "geoscene/Graphic", "geoscene/geometry/Point", "geoscene/geometry/Polyline", "geoscene/layers/GeoJSONLayer", "geoscene/geometry/geometryEngine", "geoscene/geometry/projection", ], ( MapView, Map, geosceneRequest, SpatialReference, Graphic, Point, Polyline, GeoJSONLayer, geometryEngine, projection ) => { let spatialReference; let view;
设置在地图上缓冲点时将显示的面和点样式。
js"geoscene/views/MapView", "geoscene/Map", "geoscene/request", "geoscene/geometry/SpatialReference", "geoscene/Graphic", "geoscene/geometry/Point", "geoscene/geometry/Polyline", "geoscene/layers/GeoJSONLayer", "geoscene/geometry/geometryEngine", "geoscene/geometry/projection", ], ( MapView, Map, geosceneRequest, SpatialReference, Graphic, Point, Polyline, GeoJSONLayer, geometryEngine, projection ) => { let spatialReference; let view; const polySym = { type: "simple-fill", // autocasts as new SimpleFillSymbol() color: [150, 130, 220, 0.85], outline: { color: "gray", width: 0.5, }, }; const pointSym = { type: "simple-marker", // autocasts as new SimpleMarkerSymbol() color: "red", outline: { color: "white", width: 0.5, }, size: 5, };
添加 GeoJSON 图层
要访问 GeoJSONLayer 类,可基于 GeoJSON 数据创建一个图层。GeoJSON 要素位于 WGS84 地理坐标系中。添加到地图后,要素将自动进行投影以匹配地图视图中的空间参考。要可视化每个空间参考的世界范围,将显示边界图形。
创建边界图形。设置
symbol
的outline
属性以创建灰色dash
线。将几何type
设置为extent
并将spatialReference
属性设置为WGS84
。jsconst pointSym = { type: "simple-marker", // autocasts as new SimpleMarkerSymbol() color: "red", outline: { color: "white", width: 0.5, }, size: 5, }; const projectionBoundary = { symbol: { type: "simple-fill", color: null, outline: { width: 0.5, color: [50, 50, 50, 0.75], style: "dash", }, }, geometry: { type: "extent", xmin: -180, xmax: 180, ymin: -90, ymax: 90, spatialReference: SpatialReference.WGS84, }, };
创建实例化 GeoJSONLayer 类的
countriesGeoJson
元素。设置renderer
以在国家/地区周围添加紫色轮廓。jsconst projectionBoundary = { symbol: { type: "simple-fill", color: null, outline: { width: 0.5, color: [50, 50, 50, 0.75], style: "dash", }, }, geometry: { type: "extent", xmin: -180, xmax: 180, ymin: -90, ymax: 90, spatialReference: SpatialReference.WGS84, }, }; const countriesGeoJson = new GeoJSONLayer({ url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/ArcGIS/rest/services/World_Countries_(Generalized)/FeatureServer/0/query?where=1%3D1&outFields=\*&f=geojson", copyright: "Esri", spatialReference: { wkid: 4326, }, renderer: { type: "simple", symbol: { type: "simple-fill", color: [255, 255, 255, 1], outline: { width: 0.5, color: [100, 70, 170, 0.75], }, }, }, });
将
countriesGeoJson
图层添加到map
。jsconst countriesGeoJson = new GeoJSONLayer({ url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/ArcGIS/rest/services/World_Countries_(Generalized)/FeatureServer/0/query?where=1%3D1&outFields=\*&f=geojson", copyright: "Esri", spatialReference: { wkid: 4326, }, renderer: { type: "simple", symbol: { type: "simple-fill", color: [255, 255, 255, 1], outline: { width: 0.5, color: [100, 70, 170, 0.75], }, }, }, }); const map = new Map({ layers: [countriesGeoJson], });
设置空间参考
GeoJSON 图层的默认空间参考是 WGS84 (4326)。下拉菜单中在 WGS84 和 Web Mercator (3857) 之间切换。
创建
getSpatialReference
函数,该函数将基于从选择器中选择的wkid
返回SpatialReference
类的实例。jsconst map = new Map({ layers: [countriesGeoJson], }); function getSpatialReference(wkid) { return new SpatialReference({ wkid: wkid, }); }
将
wkidSelect
变量分配给wkid
HTML 元素。通过调用getSpatialReference
函数,将spatialReference
设置为选择器中的 wkid 值。jsconst map = new Map({ layers: [countriesGeoJson], }); const wkidSelect = document.getElementById("wkid"); spatialReference = getSpatialReference(wkidSelect.value);
添加事件监听器以根据选择器中的
event.target.value
来注册更改。jsconst wkidSelect = document.getElementById("wkid"); spatialReference = getSpatialReference(wkidSelect.value); wkidSelect.addEventListener("change", (event) => { spatialReference = getSpatialReference(event.target.value); });
在坐标系之间切换
每次选择新的空间参考时,都需要移除并重新创建视图。创建一个函数,该函数将根据所选的空间参考重新投影视图。
创建
center
点,该点将在每次重新投影时聚焦视图。将latitude
设置为30
,longitude
设置为-10
。将spatialReference
设置为4326
(WGS84)。jswkidSelect.addEventListener("change", (event) => { spatialReference = getSpatialReference(event.target.value); }); let center = new Point({ latitude: 30, longitude: -10, spatialReference: { wkid:4326, }, });
创建
createViewWithSpatialReference
函数。jslet center = new Point({ latitude: 30, longitude: -10, spatialReference: { wkid: 4326, }, }); function createViewWithSpatialReference(){ }
添加一个条件语句,如果存在
view
,则该语句将调用destroy()
方法。jsfunction createViewWithSpatialReference(){ if (view) { view.map = null; view.destroy(); } }
使用
MapView
类的新实例设置view
。使用map
和所选的spatialReference
设置map
和spatialReference
属性。使用center
点设置center
属性。jsfunction createViewWithSpatialReference(){ if (view) { view.map = null; view.destroy(); } view = new MapView({ container: "viewDiv", map: map, spatialReference: spatialReference, center: center, //Need to use projection engine scale: 150000000, }); }
将
projectionBoundary
和wkidSelect
元素添加到view UI
jsview = new MapView({ container: "viewDiv", map: map, spatialReference: spatialReference, center: center, //Need to use projection engine scale: 150000000, }); view.graphics.add(projectionBoundary); view.ui.add(wkidSelect, "top-right");
调用
createViewWithSpatialReference
方法以在加载应用程序时显示 GeoJSON 图形。jslet center = new Point({ latitude: 30, longitude: -10, spatialReference: { wkid: 4326, }, }); createViewWithSpatialReference();
更新事件监听器,通过基于新的空间参考调用
createViewWithSpatialReference
函数来重新投影 GeoJSON 图层。jswkidSelect.addEventListener("change", (event) => { spatialReference = getSpatialReference(event.target.value); createViewWithSpatialReference(); });
运行应用程序。在 WGS84 和 Web Mercator 之间切换,以确保每次都重新创建视图。
添加更多空间参考
根据应用程序的需要,可以从许多空间参考中进行选择。
查找感兴趣的 WKID。
更新选择器以添加其他投影坐标系,例如:
- World Eckert VI (54010)
- World Sinusoidal (54008)
- World Fuller (54050)
html<option value="3857" disabled>Select a projection</option> <optgroup label="Equidistant (maintain length)"> <option value="4326" selected>WGS84 (GCS) -> pseudo Plate Carrée (Cylindrical)</option> <option value="54028">World Cassini (Cylindrical)</option> <option value="54027">World Equidistant conic (Conic)</option> </optgroup> <optgroup label="Conformal (maintain angles)"> <option value="54026">World Stereographic (Azimuthal)</option> </optgroup> <optgroup label="Equal-area (maintain area)"> <option value="54010">World Eckert VI (Pseudocylindrical)</option> <option value="54008">World Sinusoidal (Pseudocylindrical)</option> </optgroup> <optgroup label="Gnomonic (distances)"> <option value="102034">North Pole Gnomonic (Azimuthal)</option> </optgroup> <optgroup label="Compromise (distort all)"> <option value="3857">Web Mercator Auxiliary Sphere (Cylindrical)</option> <option value="54016">World Gall Stereographic (Cylindrical)</option> <option value="54042">World Winkel Tripel (Pseudoazimuthal)</option> <option value="54050">World Fuller / Dymaxion map (Polyhedral)</option>
运行应用程序并在新坐标系之间切换。
检查控制台。将出现一个错误,显示:"#center""incompatible spatialReference {"wkid":4326} with view's spatialReference {"wkid": "THE_SELECTED_WKID"}"。
重新投影中心点
您可以在 WGS84 和 Web Mercator 之间进行切换,因为 Web Mercator 的大地坐标由 WGS84 基准面定义。在其他投影坐标系之间切换时,应用程序将失败,因为中心点 [30, -10] 坐标未投影到指定的输出空间参考。默认情况下,视图会自动动态投影几何以匹配地图的空间参考。但是,由于中心点不会作为图形添加到视图中,因此需要使用投影引擎根据从选择器中选择的内容来变换坐标。
将
createViewWithSpatialReference
函数更新为异步,以便它awaits
projection
引擎load
引擎的依赖项。jslet center = new Point({ latitude: 30, longitude: -10, spatialReference: { wkid: 4326, }, }); createViewWithSpatialReference(); async function createViewWithSpatialReference() { await projection.load(); if (view) { view.map = null; view.destroy(); } view = new MapView({ container: "viewDiv", map: map, spatialReference: spatialReference, center: center, //Need to use projection engine scale: 150000000, }); view.graphics.add(projectionBoundary); view.ui.add(wkidSelect, "top-right"); }
基于从选择器中选择的
spatialReference
,在center
点上调用project
方法。jsasync function createViewWithSpatialReference() { await projection.load(); center = projection.project(center, spatialReference);
重新运行应用程序,并从选择器中选择不同的空间参考。该应用程序将正常工作,因为中心点坐标随每个选择进行投影。
查看投影坐标
要可视化重投影 center
点的效果,可将其作为图形添加到视图中,然后从新的空间参考显示其 x/y 坐标。
创建具有
point
参数的displayCoordinates
函数。定义popupTemplate
以显示point
的wkid
、x
和y
坐标。设置dockOptions
和visibleElements
以防止用户关闭弹出窗口。jsfunction getSpatialReference(wkid) { return new SpatialReference({ wkid: wkid, }); } function displayCoordinates(point) { const popupTemplate = { title: `WKID: ${point.spatialReference.wkid}`, content: `<b>X:</b> ${point.x.toFixed(5)}| <b>Y:</b> ${point.y.toFixed( 5 )}`, overwriteActions: true, }; view.popup.dockOptions = { buttonEnabled: false, }; view.popup.visibleElements = { closeButton: false, featureNavigation: false, }; }
创建
graphic
。使用point
设置geometry
,并使用上一步中定义的popupTemplate
设置popupTemplate
。将graphic
添加至view
,并调用open
方法以在应用程序加载时显示弹出窗口。jsfunction displayCoordinates(point) { const popupTemplate = { title: `WKID: ${point.spatialReference.wkid}`, content: `<b>X:</b> ${point.x.toFixed(5)}| <b>Y:</b> ${point.y.toFixed( 5 )}`, overwriteActions: true, }; view.popup.dockOptions = { buttonEnabled: false, }; view.popup.visibleElements = { closeButton: false, featureNavigation: false, }; const graphic = new Graphic({ geometry: point, popupTemplate: popupTemplate, }); view.graphics.add(graphic); view.openPopup({ features: [graphic], }); }
更新
createViewWithSpatialReference
函数。when
加载视图时,调用displayCoordinates
函数,其中center
点为其参数。jsview = new MapView({ container: "viewDiv", map: map, spatialReference: spatialReference, center: center, //Need to use projection engine scale: 150000000, }); view.graphics.add(projectionBoundary); view.ui.add(wkidSelect, "top-right"); view.when(() => { displayCoordinates(center); });
运行应用程序。在空间参考之间切换以查看中心点的投影坐标。
查看投影的效果
每个投影都保持一个维度的精度,但在另一个维度中会产生不准确性。例如,您可能能够保持面积,但不能保持距离。要查看每个空间参考对圆形形状的影响,请在其中创建一个测地线缓冲区,您可在其中移动鼠标。geodesicBuffer
方法仅适用于 WGS84 (4326) 和 Web Mercator (3857) 空间参考。要在另一个空间参考中查看缓冲区,需要先将点重新投影到 4326 或 3857,然后调用 geodesicBuffer
方法。
创建
bufferPoint
函数,该函数使用point
和view
作为其参数。如果point
位于另一空间参考中,则调用projection
模块并将坐标转换为4326
以创建缓冲区。如果没有point
,则return
。jsfunction displayCoordinates(point) { const popupTemplate = { title: `WKID: ${point.spatialReference.wkid}`, content: `<b>X:</b> ${point.x.toFixed(5)}| <b>Y:</b> ${point.y.toFixed( 5 )}`, overwriteActions: true, }; view.popup.dockOptions = { buttonEnabled: false, }; view.popup.visibleElements = { closeButton: false, featureNavigation: false, }; const graphic = new Graphic({ geometry: point, popupTemplate: popupTemplate, }); view.graphics.add(graphic); view.openPopup({ features: [graphic], }); } function bufferPoint(point) { if ([3857, 4326].indexOf(point.spatialReference.wkid) === -1) { point = projection.project(point, getSpatialReference(4326)); if (!point) { return; } } }
创建
buffer
元素以在point
上调用geodesicBuffer
方法。它将以1000
kilometers
为半径缓冲point
。jsfunction bufferPoint(point) { if ([3857, 4326].indexOf(point.spatialReference.wkid) === -1) { point = projection.project(point, getSpatialReference(4326)); if (!point) { return; } } const buffer = geometryEngine.geodesicBuffer(point, 1000, "kilometers"); }
从
view
中移除现有graphics
,地图投影边界和中心点图形除外。jsif (point && buffer) { // Avoid removing the map projection boundary view.graphics.removeMany([ view.graphics.getItemAt(2), view.graphics.getItemAt(3), ]); }
创建
bufferGraphic
,将buffer
作为其geometry
,将polySym
作为其symbol
样式。将bufferGraphic
以及通过移动鼠标创建的point
及其pointSym
样式添加到view
中。jsif (point && buffer) { // Avoid removing the map projection boundary view.graphics.removeMany([ view.graphics.getItemAt(2), view.graphics.getItemAt(3), ]); const bufferGraphic = { geometry: buffer, symbol: polySym, }; view.graphics.addMany([ bufferGraphic, { geometry: point, symbol: pointSym, }, ]); }
创建
createBuffer
函数,该函数将event
和view
作为其参数。根据event
中的x
和y
坐标定义point
。如果有point
,则调用bufferPoint
函数。jsconst graphic = new Graphic({ geometry: point, popupTemplate: popupTemplate, }); view.graphics.add(graphic); view.openPopup({ features: [graphic], }); } function createBuffer(event) { let point = view.toMap({ x: event.x, y: event.y, }); if (point) { bufferPoint(point); } }
创建一个事件处理程序,该处理程序将根据鼠标的移动来缓冲点。
jsview = new MapView({ container: "viewDiv", map: map, spatialReference: spatialReference, center: center, //Need to use projection engine scale: 150000000, }); view.graphics.add(projectionBoundary); view.ui.add(wkidSelect, "top-right"); view.when(() => { displayCoordinates(center); }); view.on("pointer-move", (event) => { createBuffer(event); });
运行应用程序
在 CodePen 中,运行代码以显示地图。
运行应用程序时,您将看到中心点及其坐标。选择新空间参考时,GeoJSON 图层中的要素以及中心点和缓冲区的几何将被重新投影。在地图上移动鼠标以查看每个空间参考的失真。
下一步是什么?
要了解如何使用其他API 功能,请参阅以下教程: